unit EasyListview;

// Version 1.7.0
//
// The contents of this file are subject to the Mozilla Public License
// Version 1.1 (the "License"); you maynot use this file except in compliance
// with the License. You may obtain a copy of the License at http://www.mozilla.org/MPL/
//
// Alternatively, you may redistribute this library, use and/or modify it under the terms of the
// GNU Lesser General Public License as published by the Free Software Foundation;
// either version 2.1 of the License, or (at your option) any later version.
// You may obtain a copy of the LGPL at http://www.gnu.org/copyleft/.
//
// Software distributed under the License is distributed on an "AS IS" basis,
// WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the
// specific language governing rights and limitations under the License.
//
// The initial developer of this code is Jim Kueneman <jimdk@mindspring.com>
//
// Special thanks to the following in no particular order for their help/support/code
//    Danijel Malik, Robert Lee, Werner Lehmann, Alexey Torgashin, Milan Vandrovec,
//    Stanly Xu
//
//  NOTES:
//   1)  If new properties are added to the TCollectionItems and published they
// need to be manually written/read from the stream.  The items are not automatically
// saved to the DFM file.  This was because mimicing TCollectionItem was not
// practical do to the way the VCL was designed.
//----------------------------------------------------------------------------

interface

  {$IFDEF TNTSUPPORT}
//   IMPORTANT - PLEASE READ then comment this line out.
//  If using TNT you MUST include the TNT package for your specific compiler in the
//  Requires section of this package.  It may be possible to compile without doing
//  this but you WILL eventually have strange crashes in your application that will
//  be difficult to understand.  The best way to do this in my opinion is to create
//  a new folder in the package install directory called "Delphi_TNT" (or CBuilder_TNT)
//  and copy all the files from the Delphi (or CBuilder) folder into it.  Now open the
//  EasyListviewDx.dpk (or bpk) file in the "Delphi_TNT" (or CBuilder_TNT) based on your compiler
//  version in a text editor.  In the "Requires" section add "TNTUnicodeVcl_Rx0", where
//  the "x" is the version of Delphi you are using.  Open the dpk (bpk) file in your
//  IDE. Select the menu option Projects>Options>Directories/Conditionals>Conditional
//  and enter TNTSUPPORT. Compile the package, then open the EasyListviewDxD.dpk (or bpk)
//  and compile and press the Install button.
//  Now when you update the packages you won't have to redo all this.  Just install
//  the update then compile the packages in the "Delphi_TNT" (or CBuilder_TNT) folders
//  an you are done.
  {$ENDIF}

{$B-}

{$I Compilers.inc}
{$I ..\Include\Debug.inc}
{$I Options.inc}
{$I ..\Include\Addins.inc}

{.$DEFINE GXDEBUG}

{$IFDEF GXDEBUG}
  {$DEFINE LOADGXUNIT}
{$ENDIF}
{$IFDEF GXDEBUG_PAINT}
  {$DEFINE LOADGXUNIT}
{$ENDIF GXDEBUG_PAINT}
{$IFDEF GXDEBUG_HINT}
  {$DEFINE LOADGXUNIT}
{$ENDIF GXDEBUG_HINT}

uses
  {$IFDEF COMPILER_9_UP}
  Types,    // This MUST come before Windows
  {$ENDIF}
  {$IFDEF COMPILER_6_UP}
  Variants,
  {$ENDIF}
  Windows,
  Messages,
  SysUtils,
  Classes,
  Graphics,
  Controls,
  {$IFDEF LOADGXUNIT}
  DbugIntf,
  {$ENDIF LOADGXUNIT}
  {$IFDEF COMPILER_7_UP}
  Themes,
  UxTheme,
  {$ELSE}
    {$IFDEF USETHEMES}
    TmSchema,
    UxTheme,
    {$ENDIF}
  {$ENDIF}
  ExtCtrls,
  Forms,
  ImgList,
  CommCtrl,
  ActiveX,
  Menus,
  StdCtrls,
  {$IFDEF COMPILER_6_UP}
    RTLConsts,
  {$ELSE}
    Consts,
  {$ENDIF}
  {$IFDEF SpTBX}
  TBX,
  SpTBXItem,
  {$ENDIF}
  {$IFDEF TNTSUPPORT}
  TntStdCtrls,
  {$ENDIF}
  EasyLVResources,
  MPCommonUtilities,
  MPShellTypes,
  MPDataObject,
  MPCommonObjects,
  MPThreadManager,
  EasyTaskPanelForm;

{$R EasyRes.res}

const
  EGT_FIRSTLETTER = $FFFF;

  IID_IEasyCellEditor = '{A1686E7E-7F39-4BD4-BE1F-9C20D7BC6EA0}';
  IID_IEasyCellEditorSink = '{C0AAA3C0-AC98-43C8-8D9A-376A3F64FAD2}';

  IID_IEasyCaptions = '{6C838C0E-22A5-48D4-80C6-E266E950D3CF}';
  IID_IEasyCaptionsEditable = '{F1403B00-7163-4FB7-804F-1A5500CD980A}';
  IID_IEasyImageList = '{298932FB-A0AE-4A0A-BE34-A782743A0579}';
  IID_IEasyStateImageList = '{F0202D63-92A7-4C2F-9CA4-CBDE4DE0ED30}';
  IID_IEasyImages = '{20C419F5-F3DD-40C4-8526-88322E182C49}';
  IID_IEasyImagesEditable = '{DC580B13-1D19-46BB-885F-FC5CE9B2BE66}' ;
  IID_IEasyThumbnail = '{F9CA8297-0CB3-4C47-905F-3D1497C4FC4D}';
  IID_IEasyCustomImage = '{00260055-6915-43B5-9D43-379E7F61EEA9}';
  IID_IEasyDetails = '{AE1D21EB-BA52-4C24-9EB1-B35733299489}';
  IID_IEasyDetailsEditable = '{BBD853F9-D803-4478-B5A8-EE02FF47DC80}';
  IID_IEasyChecks = '{E8820F40-2EE3-4718-B54A-39318D2D1232}';
  IID_IEasyNotficationSink = '{E4F0D3DE-B2BD-4EC0-B24B-8A9B85B23A63}';
  IID_IEasyNotifier = '{F10150F9-17E3-43B6-8C05-33283FF1B14E}';
  IID_IEasyCompare = '{0761D4F5-D451-4A6D-BFDC-B3000FFD0299}';  
  IID_IEasyDividerPersist = '{EE6C3C89-7FAE-46CD-AD30-6954B4308721}'; 
  IID_IEasyGroupKey = '{2B87BB19-A133-4D43-9164-AC456747EB19}';
  IID_IEasyGroupKeyEditable = '{26EFE2C6-2DE2-4795-94E3-0DB0CAA38B09}';

//  EASYLISTVIEW_STREAM_VERSION = 4;
  EASYLISTVIEW_STREAM_VERSION = 5; // Fixed bug in Streaming any other types then TEasyXXXXStored
  STREAM_ID_KEY = $CEFEFF;

  _AUTOSCROLLDELAY = 500;   // 500 ms
  _AUTOSCROLLTIME = 50;     // ms

  CURSOR_VHEADERSPLIT = 'VEASYHEADERSPLIT';
  BITMAP_DEFAULTGROUPEXPANDED = 'DEFAULTEXPANDED';
  BITMAP_DEFAULTGROUPCOLLAPSED = 'DEFAULTCOLLAPSED';
  BITMAP_SORTARROWUP = 'SORTARROWUP';
  BITMAP_SORTARROWDOWN = 'SORTARROWDOWN';
  BITMAP_DEFAULTColumnGLYPHS = 'ColumnGLYPHS';
  BITMAP_DEFAULTColumnGLYPHSBKGND = $00FF00FF;

  SELECTION_OFFSET = 0.15;  // The selection rectangle will select an item +/- x% of the Caption width (like M$ Listview)

  WM_HOOKAPPACTIVATE = WM_APP + 204;
  WM_EDITORRESIZE = WM_APP + 205;
  WM_TABMOVEFOCUS = WM_APP + 206;

  RESIZEHITZONEMARGIN = 4;
//  LABEL_MARGIN = 2;

  crVHeaderSplit = 768;

  DEFAULT_GROUP_NAME = 'Default Group'; // group default name used if a first group is automatically created

  EASYLISTVIEW_HEADER_CLIPFORMAT = 'EasyListview.Header';

// Magic, mystical stuff for CBuilder
(*$HPPEMIT 'namespace Easylistview {'*)
{$HPPEMIT 'class DELPHICLASS TEasyItem;'}
{$HPPEMIT 'class DELPHICLASS TEasyGroup;'}
{$HPPEMIT 'class DELPHICLASS TEasyColumn;'}
{$HPPEMIT 'struct TGroupSortInfoRec;'}
{$HPPEMIT '__interface IEasyNotificationSink;'}
(*$HPPEMIT '}'*)

type
  TCustomEasyListview = class;
  TEasyItems = class;
  TEasyItem = class;
  TEasyItemInterfaced = class;
  TEasyViewItem = class;
  TEasyGroups = class;
  TEasyGroup = class;
  TEasyViewGroup = class;
  TEasyColumns = class;
  TEasyColumn = class;
  TEasyCollectionItem = class;
  TCustomEasyDragManagerBase = class;
  TEasyCellSize = class;
  TEasyHeader = class;
  TEasyCollection = class;
  TEasyPaintInfoBasic = class;
  TEasyHintInfo = class;
  TEasyOLEDragManager = class;
  TEasyHotTrackManager = class;
  TEasyViewColumn = class;
  TEasyHeaderDragManager = class;
  TEasySortManager = class;
  TEasyAlphaBlender = class;
  TEasyPaintInfoBaseGroup = class;
  TEasyColumnStored = class;
  TEasyBaseEditor = class;


  TEasyCollectionItemClass = class of TEasyCollectionItem;
  TEasyViewItemClass = class of TEasyViewItem;
  TEasyViewColumnClass = class of TEasyViewColumn;
  TEasyViewGroupClass = class of TEasyViewGroup;
  TEasyGridGroupClass = class of TEasyGridGroup;
  TEasyGroupClass = class of TEasyGroup;
  TEasyItemClass = class of TEasyItem;
  TEasyColumnClass = class of TEasyColumn;
  TEasyColumnStoredClass = class of TEasyColumnStored;

  TItemNextEnum = function(Item: TEasyItem): TEasyItem of object;
  TItemEnumFirstLast = function: TEasyItem of object;

  TRectArray = array of TRect;
  TCommonMouseButtons = set of TCommonMouseButton;

  TEasyItemArray = array of TEasyItem;
  TEasyGroupArray = array of TEasyGroup;
  TEasyColumnArray = array of TEasyColumn;

  TEasyQueryDragResult = (
    eqdrContinue,            // The drag drop loop should contine
    eqdrQuit,                // The drag drop loop should end
    eqdrDrop                 // The drag drop loop should drop the object where it is
  );

  TEasyCheckType = (
    ectNone,              // No Checks
    ettNoneWithSpace,     // No Checks but leave the space for the checkbox
    ectBox,               // Square Checkbox type checkbox
    ectRadio              // Round Radio button type checkbox
  );

  TEasyListStyle = (
    elsIcon,          // The Listview's Large Icon Mode
    elsSmallIcon,     // The Listview's Small Icon Mode
    elsList,          // The Listview's List Mode
    elsReport,        // The Listview's Report (Details) Mode
    elsThumbnail,     // The Listview's Thumbnail Mode
    elsTile,          // The Listview's Tile Mode
    elsFilmStrip,     // The Listview's FilmStrip Mode
    elsGrid           // The Listview's Grid Mode
  );

  TEasyGroupMarginEdge = (
    egmeBackground,                   // Background of the entire Group
    egmeTop,                          // Header of a Group
    egmeBottom,                       // Footer of a Group
    egmeLeft,                         // Left Margin area of a Group
    egmeRight,                        // Right Margin area of a Group
    egmeForeground                    // Foreground of the entire Group
  );

  TEasyHeaderType = (
    ehtHeader,         // Report view Header
    ehtFooter          // Report view Footer
  );

  TEasyHeaderButtonStyle = (
    ehbsThick,        // "Normal" raised button
    ehbsFlat,         // Flat style button
    ehbsPlate,        // Plate style button
    ehbsThemed        // Draw using current Activation Context theme
  );

  TEasyHeaderImagePosition = (
    ehpLeft,         // The image is to the left of the Text
    ehpTop,          // The image is to the Top of the Text
    ehpRight,        // The image is to the Right of the Text
    ehpBottom        // The image is to the Bottom of the Text
  );

  TEasySortDirection = (
    esdNone,
    esdAscending,            // The sort is ascending
    esdDescending            // The sort is descending
  );

  TEasySortGlyphAlign = (
    esgaLeft,                // Column SortGlpyh placed to left of Caption
    esgaRight                // Column SortGlpyh placed to right of Caption
  );

  TEasyDragType = (
    edtOLE,                  // OLE Dragging
    edtVCL                   // VCL Dragging
  );

  TEasySelectionType = (
    ecstSelect,         // Select all objects
    ecstUnSelect,       // UnSelect all objects
    ecstInvert          // Invert the selection state of objects
  );

  TEasyColumnHitTestInfo = (
    ectOnCheckbox,         // Hit in the Checkbox
    ectOnIcon,             // Hit the icon
    ectOnLabel,            // Hit the Label
    ectOnText              // Hit in the current text in the label area
  );
  TEasyColumnHitTestInfoSet = set of TEasyColumnHitTestInfo;

  TEasyHitInfoColumn = packed record
    Column: TEasyColumn;  // The object hit
    HitInfo: TEasyColumnHitTestInfoSet; // Details of the hit
  end;

  TEasyGroupHitTestInfo = (
    egtOnBand,             // Hit the colored band
    egtOnCheckbox,         // Hit in the Checkbox
    egtOnExpandButton,     // Hit the ExpandButton
    egtOnIcon,             // Hit the icon
    egtOnLabel,            // Hit the Label
    egtOnText,             // Hit in the current text in the label area
    egtOnHeader,           // Hit in the header of the group
    egtOnFooter,           // Hit in the footer of the group
    egtOnLeftMargin,       // Hit in the Left Margin of the group
    egtOnRightMargin       // Hit in the Right Margin of the group
  );
  TEasyGroupHitTestInfoSet = set of TEasyGroupHitTestInfo;

  TEasyDefaultWheelScroll = (
    edwsHorz,                // Mouse Wheel scrolls horizontal by default, vertical with shift pressed
    edwsVert                 // Mouse Wheel scrolls vertical by default, horizontal with shift pressed
  );

  TEasyHitInfoGroup = packed record
    Group: TEasyGroup;  // The object hit
    HitInfo: TEasyGroupHitTestInfoSet; // Details of the hit
  end;

  TEasyItemHitTestInfo = (
    ehtStateIcon,           // Hit the state icon
    ehtOnIcon,              // Hit the icon
    ehtOnLabel,             // Hit the label, the area available for the Text
    ehtOnClickSelectBounds, // Hit the area defined as a selection area for a click
    ehtOnDragSelectBounds,  // Hit the area defined as a selection area for a drag select
    ehtOnText,          // Hit the area in the label that contains text
    ehtOnCheck,         // Hit the area where the check box is drawn
    ehtOnEdit           // Hit the area where the edit is started for the item
  );
  TEasyItemHitTestInfoSet = set of TEasyItemHitTestInfo;

  TEasyHitInfoItem = packed record
    Item: TEasyItem;
    Group: TEasyGroup;   // The group the object is in
    Column: TEasyColumn; // The the Column hit (if applicable)
    HitInfo: TEasyItemHitTestInfoSet; // Details of the hit
  end;

  // CAUTION ADD NEW STATES TO END TO PRESERVE STREAMING
  TEasyStorageObjectState = (
    esosChecked,        // The object is checked
    esosCheckPending,   // The object has a check pending, the mouse has been pressed in the check area but not yet released
    esosCheckHover,     // The object has the mouse hovering over the checkbox (matters mostly themed)
    esosClicking,       // The object is being "clicked"
    esosCut,            // The object is being "cut"
    esosDestroying,     // The object is being destroyed
    esosHilighted,      // The object is currently hilighted, for an item this mean drop hilighed for a Column this means hot track hilighed
    esosEnabled,        // The object is enabled
    esosFocused,        // The object is focused, i.e. it will take the keyboard input
    esosInitialized,    // The object has been initialized
    esosSelected,       // The object is selected, i.e. it will be included for an operation on it
    esosVisible,        // The object is currently visible
    esosHotTracking,    // The object is currently in a hot track state
    esosBold,           // The object is in a Bold state
    esosDropTarget,      // The object is hilighted as a drop target
    esosGhosted        // The object image is drawn "ghosted" (blended with background)
  );
  TEasyStorageObjectStates = set of TEasyStorageObjectState;

  TEasyGroupsState = (
    egsRebuilding      // GroupManager is rebuild the item layout
  );
  TEasyGroupsStates = set of TEasyGroupsState;

  TEasySelectHitType = (
    eshtClickSelect,         // Test for a hit based on a mouse click
    eshtDragSelect           // Test for a hit based on a drag rectangle
  );

  TEasyCellRectType = (
    ertBounds,         // The bounds of the item minus the border
    ertIcon,           // The bounds of the icon image
    ertLabel,          // The bounds of the area dedicated to the text (will be static based on grid)
    ertClickSelectBounds, // The bounds of the area dedicated to a click selection (usually the Text area)
    ertDragSelectBounds,  // The bounds of the area dedicated to a drag selection (depends on the view)
    ertText,           // The bounds of the text for the item (will be dynamic based on current text)
    ertFullText        // The bounds of the actual Complete text for the item independant of current state of control/item
  );

  TEasyImageSize = (
    eisSmall,                   // Paint Small Images
    eisLarge,                   // Paint Large Images
    eisExtraLarge               // Paint JumboImages
  );

  TEasyImageKind = (
    eikNormal,                   // Normal image
    eikOverlay,                  // Overlay for the Normal image
    eikState                     // State image
  );

  TEasyDragManagerState = (
    edmsDragging,      // The Drag Manager is in the middle of a drag operation
    edmsAutoScrolling  // The Drag Manager is autoscrolling during a draw operation
  );
  TEasyDragManagerStates = set of TEasyDragManagerState;

  TEasyControlState = (
    ebcsLButtonDown,        // The Left Mouse Button is down
    ebcsRButtonDown,        // The Right Mouse Button is down
    ebcsMButtonDown,        // The Middle Mouse Button is down
    ebcsScrollButtonDown,   // The scroll wheel is down
    ebcsDragPending,        // The mouse is down and we are ready to check for a drag of an object
    ebcsDragging,           // The control is in the middle of a dragging operation
    ebcsVCLDrag,            // The drag operation is a VCL drag
    ebcsDragSelectPending,  // The mouse is down and we are ready to check for a drag select
    ebcsDragSelecting,      // The control is in a drag selecting mode
    ebcsThemesLoaded,       // The control has loaded themes if avaiable
    ebcsCheckboxClickPending, // The mouse is down over a checkbox and the box now has the attention of the mouse
    ebcsGroupExpandPending,
    ebcsHeaderCapture,       // The Header area has captured the mouse
    ebcsScrolling,           // The control is scrolling
    ebcsOLERegistered,       // Registered for OLE Drag and Drop
    ebcsCancelContextMenu   // A right button drag drop should not show the context menu
  );
  TEasyControlStates = set of TEasyControlState;

  TEasyHeaderState = (
    ehsMouseCaptured,        // The Header captured the mouse (drag column/row; resize column/row)
    ehsResizing,             // One of the columns/headers is resizing
    ehsDragging,             // One of the columns/headers is being dragged
    ehsDragPending,
    ehsClickPending,        // One of the columns/headers was pressed and the mouse is being dragged around
    ehsCheckboxClickPending,
    ehsResizePending,
    ehsLButtonDown,
    ehsRButtonDown,
    ehsMButtonDown
  );
  TEasyHeaderStates = set of TEasyHeaderState;

  TEasyItemState = (
    eisTextRectCached
  );
  TEasyItemStates = set of TEasyItemState;

  TEasyRectArrayObject = packed record
    LabelRect,              // The bounds of the area dedicated to the text
                            //   (will be static based on grid)
    IconRect,               // The bounds of the icon image
    TextRect,               // The bounds of the text for the item (will be
                            //   dynamic based on current text)
                            // If TextRects is used then this is the Union of all
                            // the TextRects
                            // It will be calculated from LabelRect -2 pixels so that
                            // a Frame and a Focus Rect may be drawn around it and
                            // not extend past the Label Rect
    CheckRect: TRect;       // The rectangle used for the Checkbox of an item
    BoundsRect,             // The bounds of the item minus the border
    ClickSelectBoundsRect,   // The bounds of the area dedicated to a click
                             //   selection (may be possible the area is not
                             //   definable by a simple rectangle)
    DragSelectBoundsRect,    // The bounds of the area dedicated to a drag
                             //   selection (may be possible the area is not
                             //   definable by a simple rectangle)

    FullTextRect: TRect;     // The bounds of the largest rect of text that
                             //   can be shown (Icon view when item has focus the
                             //   entire text is shown, overlapping other items)
    SelectionRect: TRect;    // The bounds of the area that is painted in the
                             //   hilighted selection color
    FullFocusSelRect: TRect; // The bounds of the largest rect of selection that
                             //   can be shown (Icon view when item has focus the
                             //   entire text is shown, overlapping other items)
    FocusChangeInvalidRect: TRect; // The Rectangle to Invalidate(True) if the item
                                   //   changes focus or selection
    EditRect: TRect;        // The rectangle used for the editor in Edit Mode
    SortRect: TRect;        // The rectangle used for the Sort Glyphs
    StateRect: TRect;       // The rectangle used for state images
    TextRects: TRectArray;  // Text rectangles for Details
    ExpandButtonRect,       // The rectangle available in the group for the Expand Button (Groups Only)
    BandRect: TRect;        // The rectangle available in the group for the Band stripe (Groups Only)
    BackGndRect: TRect;     // The background of the group minus the Margin area, includes the Border (Groups Only)
    GroupRect: TRect;       // The entire space the group occupies (Groups Only)
    DropDownArrow: TRect;     // The DropDown Button for Columns (Columns Only)
  end;
  TEasyRectArrayObjectArray = array of TEasyRectArrayObject;

  TEasyMakeVisiblePos = (
    emvTop,      // Make visible with the top of the Client Window
    emvMiddle,   // Make visible with the middle of the Client Window
    emvBottom,  // Make visible with the bottom of the Client Window
    emvAuto     // Make visible and scroll based on if the item is above or below the current client window
  );

  TEasySearchDirection = (
    esdForward,
    esdBackward
  );

  TEasyAdjacentCellDir = (
    acdLeft,            // The cell that is to the left of a particular cell
    acdRight,           // The cell that is to the right of a particular cell
    acdUp,              // The cell that is above a particular cell
    acdDown,            // The cell that is below of a particular cell
    acdPageDown,        // The cell that is a single page down from a particular cell
    acdPageUp           // The cell that is a single page up from a particular cell
  );

  TEasyGridLayout = (
    eglHorz,                           // The Grid is a Horizontal grid (scrolls horz sized to fit vert)
    eglVert,                           // The Grid is a Vertical grid (scrolls vert, sized to fit horz)
    eglGrid                            // The Grid can scroll either way (may not fit client window either way)
  );

  TEasyHintType = (
    ehtText,              // Shows the hint in the passed text parameter
    ehtToolTip,           // Shows entire caption text if it does not fit in the assigned caption area
    ehtCustomDraw         // The hint will callback when it needs drawing
  );

  PEasyHintInfoRec = ^TEasyHintInfoRec;  
  // The actual record is below due to a BDS C++ bug in generating the hpp file

  TEasySortAlgorithm = (
    esaQuickSort,
    esaBubbleSort,
    esaMergeSort
  );

  TEasyIncrementalSearchState = (
    eissTimerRunning,       // The Search timeout timer is running
    eissSearching           // Currently in a Search mode
  );
  TEasyIncrementalSearchStates = set of TEasyIncrementalSearchState;

  TCoolIncrementalSearchStart = (
    eissStartOver,          // Always start a search from the beginning of the list
    eissLastHit,            // Start search from the last node found in the incremental search
    eissFocusedNode         // Start search from current focused node
   );

   TEasyIncrementalSearchItemType = (
     eisiAll,               // Search All items in list
     eisiInitializedOnly,   // Search only items with their initialized property set, this will include collapsed groups but not invisible groups
     eisiVisible            // Search only items with their visible property set, this will include collapsed groups but not invisible groups
   );

   TEasyIncrementalSearchDir = (
     eisdForward,           // Search Forward in the list
     eisdBackward           // Search Backward in the list
   );

   TEasyNextItemType = (
     enitAny,
     enitVisible,
     enitInitialized,
     enitVisibleInExpandedGroup,
     enitEditable // Implies visible
   );

   TEasyScrollbarDir = (
     esdVertical,
     esdHorizontal,
     esdBoth
   );

   TEasyHotTrackState = (
    ehsEnable,    // object becoming a hot track target
    ehsDisable    // object losing a hot track status
  );

  TEasyHotTrackRectItem = (
    htiIcon,       // Hot track is defined on the image
    htiText,        // Hot track is defined on the Text
    htiAnyWhere     // Hot track is defined on the entire cell
  );
  TEasyHotTrackRectItems = set of TEasyHotTrackRectItem;

  TEasyHotTrackRectGroup = (
    htgIcon,       // Hot track is defined on the image
    htgText,        // Hot track is defined on the Text
    htgTopMargin,   // Hot track is defined on the Top Group Margin
    htgBottomMargin,// Hot track is defined on the Bottom Group Margin
    htgLeftMargin,  // Hot track is defined on the Left Group Margin
    htgRightMargin, // Hot track is defined on the Right Group Margin
    htgAnyWhere     // Hot track is defined on the entire cell
  );
  TEasyHotTrackRectGroups = set of TEasyHotTrackRectGroup;

  TEasyHotTrackRectColumn = (
    htcIcon,       // Hot track is defined on the image
    htcText,        // Hot track is defined on the Text
    htcAnyWhere     // Hot track is defined on the entire cell
  );
  TEasyHotTrackRectColumns = set of TEasyHotTrackRectColumn;

  //  ****************************************************************
  // Intefaces that EasyListview is aware of such that user data may implement these
  // interfaces to present the data necessary for the control
  // ****************************************************************

  // Implements the read only Caption for the Control
  IEasyCaptions = interface(IUnknown)
  [IID_IEasyCaptions]
    function GetCaption(Column: Integer): Variant;

    property Captions[Column: Integer]: Variant read GetCaption;
  end;

  // Implements the Caption for the Control
  IEasyCaptionsEditable = interface(IEasyCaptions)
  [IID_IEasyCaptionsEditable]
    function SetCaption(Column: Integer; const Value: Variant): Boolean;
  end;

  // Implements a custom ImageList on a per item/column basis for the Control
  IEasyImageList = interface(IUnknown)
  [IID_IEasyImageList]
    function GetImageList(Column: Integer; IconSize: TEasyImageSize): TCustomImageList;
    property ImageList[Column: Integer; IconSize: TEasyImageSize]: TCustomImageList read GetImageList;
  end;

  // Implements a custom State ImageList on a per item/column basis for the Control
  IEasyStateImageList = interface(IUnknown)
  [IID_IEasyStateImageList]
    function GetImageList(Column: Integer): TCustomImageList;
    property ImageList[Column: Integer]: TCustomImageList read GetImageList;
  end;

  // Implements the read only ImageIndex for the Control
  IEasyImages = interface(IUnknown)
  [IID_IEasyImages]
    function GetImageIndex(Column: Integer; ImageKind: TEasyImageKind): Integer;

    property ImageIndexes[Column: Integer; ImageKind: TEasyImageKind]: Integer read GetImageIndex;
  end;

  // Implements the ImageIndex for the Control
  IEasyImagesEditable = interface(IEasyImages)
  [IID_IEasyImagesEditable]
    procedure SetImageIndex(Column: Integer; ImageKind: TEasyImageKind; const Value: Integer);

    property ImageIndexes[Column: Integer; ImageKind: TEasyImageKind]: Integer read GetImageIndex write SetImageIndex;
  end;

  // Implements the Thumbnail (Bitmap) for the Control
  IEasyThumbnail = interface(IUnknown)
  [IID_IEasyThumbnail]
    procedure ThumbnailDraw(ACanvas: TCanvas; ViewportRect: TRect; AlphaBlender: TEasyAlphaBlender; var DoDefault: Boolean);
  end;

  IEasyCustomImage = interface(IUnknown)
  [IID_IEasyCustomImage]
    procedure CustomDrawn(Column: TEasyColumn; var IsCustom: Boolean);
    procedure DrawImage(Column: TEasyColumn; ACanvas: TCanvas; const RectArray: TEasyRectArrayObject; AlphaBlender: TEasyAlphaBlender);
    procedure GetSize(Column: TEasyColumn; var ImageW, ImageH: Integer);
  end;

  // Implements the Tile Details for the Control
  // Points to what Column to get the detail from
  IEasyDetails = interface(IUnknown)
  [IID_IEasyDetails]
    function GetDetailCount: Integer;
    function GetDetail(Line: Integer): Integer; // Return the Column to be used as the detail for the given line

    property Detail[Line: Integer]: Integer read GetDetail;
    property DetailCount: Integer read GetDetailCount;
  end;

  IEasyDetailsEditable = interface(IEasyDetails)
  [IID_IEasyDetailsEditable]
    procedure SetDetail(Line: Integer; Value: Integer);
    procedure SetDetailCount(Value: Integer);

    property Detail[Line: Integer]: Integer read GetDetail write SetDetail;
    property DetailCount: Integer read GetDetailCount write SetDetailCount;
  end;

  // Implements the CheckState for the Control
  IEasyChecks = interface(IUnknown)
  [IID_IEasyChecks]
    function GetChecked(Column: Integer): Boolean;
    procedure SetChecked(Column: Integer; const Value: Boolean);
    property Checked[Column: Integer]: Boolean read GetChecked write SetChecked;
  end;

  IEasyGroupKey = interface(IUnknown)
  [IID_IEasyGroupKey]
    function GetKey(FocusedColumn: Integer): LongWord;

    property Key[FocusedColumn: Integer]: LongWord read GetKey; // Uniquely identifies the item in a particular group
  end;

  IEasyGroupKeyEditable = interface(IEasyGroupKey)
  [IID_IEasyGroupKeyEditable]
    procedure SetKey(FocusedColumn: Integer; Value: LongWord);

    property Key[FocusedColumn: Integer]: LongWord read GetKey write SetKey;
  end;

  IEasyNotificationSink = interface(IUnknown)
  [IID_IEasyNotficationSink]
    procedure InvalidateItem(ImmediateRefresh: Boolean);
    procedure UnRegister;
  end;

  IEasyNotifier = interface(IUnknown)
  [IID_IEasyNotifier]
    procedure OnRegisterNotify(const ANotifier: IEasyNotificationSink);
    procedure OnUnRegisterNotify(const ANotifier: IEasyNotificationSink);
  end;

  IEasyCompare = interface
  [IID_IEasyCompare]
    function Compare(const Data: IUnknown; Column: TEasyColumn): Integer;
  end;

  // Interface for the EasyControl to communicate with a Cells Editor
  IEasyCellEditor = interface(IUnknown)
  [IID_IEasyCellEditor]
    function AcceptEdit: Boolean;
    function GetHandle: HWnd;
    function GetModified: Boolean;
    procedure ControlWndHookProc(var Message: TMessage);
    procedure Hide;
    procedure Initialize(AnItem: TEasyItem; Column: TEasyColumn);
    procedure Finalize;
    function PtInEditControl(WindowPt: TPoint): Boolean;
    procedure SetEditorFocus;
    procedure SelectAll;
    procedure Show;

    property Handle: HWnd read GetHandle;
    property Modified: Boolean read GetModified;
  end;

  // ***************************************
  // Event callback Definitions
  // ***************************************
  TAfterPaintEvent = procedure(Sender: TCustomEasyListview; ACanvas: TCanvas; ClipRect: TRect) of object;
  TAutoGroupGetKeyEvent = procedure(Sender: TCustomEasyListview; Item: TEasyItem; ColumnIndex: Integer; Groups: TEasyGroups; var Key: LongWord) of object;
  TAutoSortGroupCreateEvent = procedure(Sender: TCustomEasyListview; Item: TEasyItem; ColumnIndex: Integer; Groups: TEasyGroups; var Group: TEasyGroup; var DoDefaultAction: Boolean) of object;
  TEasyClipboardEvent = procedure(Sender: TCustomEasyListview; var Handled: Boolean) of object;
  TEasyClipboardCutEvent = procedure(Sender: TCustomEasyListview; var MarkAsCut, Handled: Boolean) of object;
  TColumnCheckChangeEvent = procedure(Sender: TCustomEasyListview; Column: TEasyColumn) of object;
  TColumnCheckChangingEvent = procedure(Sender: TCustomEasyListview; Column: TEasyColumn; var Allow: Boolean) of object;
  TColumnClickEvent = procedure(Sender: TCustomEasyListview; Button: TCommonMouseButton; const Column: TEasyColumn) of object;
  TColumnContextMenuEvent = procedure(Sender: TCustomEasyListview; HitInfo: TEasyHitInfoColumn; WindowPoint: TPoint; var Menu: TPopupMenu) of object;
//  TColumnCreatePaintInfoEvent = procedure(Sender: TCustomEasyListview; var PaintInfo: TEasyColumnPaintInfo) of object;
  TColumnDblClickEvent = procedure(Sender: TCustomEasyListview; Button: TCommonMouseButton; MousePos: TPoint; const Column: TEasyColumn) of object;
  TColumnDropDownButtonClickEvent = procedure(Sender: TCustomEasyListview; Column: TEasyColumn; Button: TCommonMouseButton; ShiftState: TShiftState; WindowPt: TPoint; var DoDefault: Boolean) of object;
  TColumnEnableChangeEvent = procedure(Sender: TCustomEasyListview; Column: TEasyColumn) of object;
  TColumnEnableChangingEvent = procedure(Sender: TCustomEasyListview; Column: TEasyColumn; var Allow: Boolean) of object;
  TColumnFocusChangeEvent = procedure(Sender: TCustomEasyListview; Column: TEasyColumn) of object;
  TColumnFocusChangingEvent = procedure(Sender: TCustomEasyListview; Column: TEasyColumn; var Allow: Boolean) of object;
  TColumnFreeingEvent = procedure(Sender: TCustomEasyListview; Column: TEasyColumn) of object;
  TColumnGetCaptionEvent = procedure(Sender: TCustomEasyListview; Column: TEasyColumn; Line: Integer; var Caption: WideString) of object;
  TColumnGetImageIndexEvent = procedure(Sender: TCustomEasyListview; Column: TEasyColumn; ImageKind: TEasyImageKind; var ImageIndex: TCommonImageIndexInteger) of object;
  TColumnGetImageListEvent = procedure(Sender: TCustomEasyListview; Column: TEasyColumn; var ImageList: TCustomImageList) of object;
  TColumnGetDetailCountEvent = procedure(Sender: TCustomEasyListview; Column: TEasyColumn; var Count: Integer) of object;
  TColumnImageDrawEvent = procedure(Sender: TCustomEasyListview; Column: TEasyColumn; ACanvas: TCanvas; const RectArray: TEasyRectArrayObject; AlphaBlender: TEasyAlphaBlender) of object;
  TColumnImageGetSizeEvent = procedure(Sender: TCustomEasyListview; Column: TEasyColumn; var ImageWidth, ImageHeight: Integer) of object;
  TColumnImageDrawIsCustomEvent = procedure(Sender: TCustomEasyListview; Column: TEasyColumn; var IsCustom: Boolean) of object;
  TColumnGetDetailEvent = procedure(Sender: TCustomEasyListview; Column: TEasyColumn; Line: Integer; var Detail: Integer) of object;
  TColumnInitializeEvent = procedure(Sender: TCustomEasyListview; Column: TEasyColumn) of object;
  TColumnPaintTextEvent = procedure(Sender: TCustomEasyListview; Column: TEasyColumn; ACanvas: TCanvas) of object;
  TEasyColumnLoadFromStreamEvent = procedure(Sender: TCustomEasyListview; Column: TEasyColumn; S: TStream; Version: Integer) of object;
  TEasyColumnSaveToStreamEvent = procedure(Sender: TCustomEasyListview; Column: TEasyColumn; S: TStream; Version: Integer) of object;
  TColumnSelectionChangeEvent = procedure(Sender: TCustomEasyListview; Column: TEasyColumn) of object;
  TColumnSelectionChangingEvent = procedure(Sender: TCustomEasyListview; Column: TEasyColumn; var Allow: Boolean) of object;
  TColumnSetCaptionEvent = procedure(Sender: TCustomEasyListview; Column: TEasyColumn; Caption: WideString) of object;
  TColumnSetImageIndexEvent = procedure(Sender: TCustomEasyListview; Column: TEasyColumn; ImageKind: TEasyImageKind; ImageIndex: Integer) of object;
  TColumnSetDetailEvent = procedure(Sender: TCustomEasyListview; Column: TEasyColumn; Line: Integer; const Detail: Integer) of object;
  TColumnSizeChangingEvent = procedure(Sender: TCustomEasyListview; Column: TEasyColumn; Width, NewWidth: Integer; var Allow: Boolean) of object;
  TColumnSizeChangedEvent = procedure(Sender: TCustomEasyListview; Column: TEasyColumn) of object;
  TColumnThumbnailDrawEvent = procedure(Sender: TCustomEasyListview; Column: TEasyColumn; ACanvas: TCanvas; ARect: TRect; var DoDefault: Boolean) of object;
  TColumnVisibilityChangeEvent = procedure(Sender: TCustomEasyListview; Column: TEasyColumn) of object;
  TColumnVisibilityChangingEvent = procedure(Sender: TCustomEasyListview; Column: TEasyColumn; var Allow: Boolean) of object;
  TContextMenuEvent = procedure(Sender: TCustomEasyListview; MousePt: TPoint; var Handled: Boolean) of object;  
  TColumnCustomViewEvent = procedure(Sender: TCustomEasyListview; Column: TEasyColumn; var View: TEasyViewColumnClass) of object;
  TCustomGridEvent = procedure(Sender: TCustomEasyListview; Group: TEasyGroup; ViewStyle: TEasyListStyle; var Grid: TEasyGridGroupClass) of object;
  TDblClickEvent = procedure(Sender: TCustomEasyListview; Button: TCommonMouseButton; MousePos: TPoint; ShiftState: TShiftState) of object;
  TGetDragImageEvent = procedure(Sender: TCustomEasyListview; Image: TBitmap; DragStartPt: TPoint; var HotSpot: TPoint; var TransparentColor: TColor; var Handled: Boolean) of object;
  TEasyGestureEvent = procedure(Sender: TCustomEasyListview; Gesture: WideString; var DoDefaultMouseAction: Boolean) of object;
  TGroupClickEvent = procedure(Sender: TCustomEasyListview; Group: TEasyGroup; KeyStates: TCommonKeyStates; HitTest: TEasyGroupHitTestInfoSet) of object;
  TGroupCollapseEvent = procedure(Sender: TCustomEasyListview; Group: TEasyGroup) of object;
  TGroupCollapsingEvent = procedure(Sender: TCustomEasyListview;  Group: TEasyGroup; var Allow: Boolean) of object;
  TGroupCompareEvent = function(Sender: TCustomEasyListview; Item1, Item2: TEasyGroup): Integer of object;
  TGroupContextMenuEvent = procedure(Sender: TCustomEasyListview; HitInfo: TEasyHitInfoGroup; WindowPoint: TPoint; var Menu: TPopupMenu; var Handled: Boolean) of object;
  TGroupCustomViewEvent = procedure(Sender: TCustomEasyListview; Group: TEasyGroup; ViewStyle: TEasyListStyle; var View: TEasyViewGroupClass) of object;
  TGroupDblClickEvent = procedure(Sender: TCustomEasyListview; Button: TCommonMouseButton; MousePos: TPoint; HitInfo: TEasyHitInfoGroup) of object;
  TGroupExpandEvent = procedure(Sender: TCustomEasyListview; Group: TEasyGroup) of object;
  TGroupExpandingEvent = procedure(Sender: TCustomEasyListview; Group: TEasyGroup; var Allow: Boolean) of object;
  TGroupFocusChangeEvent = procedure(Sender: TCustomEasyListview; Group: TEasyGroup) of object;
  TGroupFocusChangingEvent = procedure(Sender: TCustomEasyListview; Group: TEasyGroup; var Allow: Boolean) of object;
  TGroupFreeingEvent = procedure(Sender: TCustomEasyListview; Group: TEasyGroup) of object;
  TGroupGetCaptionEvent = procedure(Sender: TCustomEasyListview; Group: TEasyGroup; var Caption: WideString) of object;
  TGroupGetClassEvent = procedure(Sender: TCustomEasyListview; var GroupClass: TEasyCollectionItemClass) of object;
  TGroupGetImageIndexEvent = procedure(Sender: TCustomEasyListview; Group: TEasyGroup; ImageKind: TEasyImageKind; var ImageIndex: TCommonImageIndexInteger) of object;
  TGroupGetImageListEvent = procedure(Sender: TCustomEasyListview; Group: TEasyGroup; var ImageList: TCustomImageList) of object;
  TGroupGetDetailCountEvent = procedure(Sender: TCustomEasyListview; Group: TEasyGroup; var Count: Integer) of object;
  TGroupImageDrawEvent = procedure(Sender: TCustomEasyListview; Group: TEasyGroup; ACanvas: TCanvas; const RectArray: TEasyRectArrayObject; AlphaBlender: TEasyAlphaBlender) of object;
  TGroupImageGetSizeEvent = procedure(Sender: TCustomEasyListview; Group: TEasyGroup; var ImageWidth, ImageHeight: Integer) of object;
  TGroupImageDrawIsCustomEvent = procedure(Sender: TCustomEasyListview; Group: TEasyGroup; var IsCustom: Boolean) of object;
  TGroupGetDetailEvent = procedure(Sender: TCustomEasyListview; Group: TEasyGroup; Line: Integer; var Detail: Integer) of object;
  TGroupInitializeEvent = procedure(Sender: TCustomEasyListview; Group: TEasyGroup) of object;
  TGroupHotTrackEvent = procedure(Sender: TCustomEasyListview; Group: TEasyGroup; State: TEasyHotTrackState; MousePos: TPoint) of object;      
  TGroupLoadFromStreamEvent = procedure(Sender: TCustomEasyListview; Group: TEasyGroup; S: TStream; Version: Integer) of object;
  TGroupPaintTextEvent = procedure(Sender: TCustomEasyListview; Group: TEasyGroup; ACanvas: TCanvas) of object;
  TGroupSaveToStreamEvent = procedure(Sender: TCustomEasyListview; Group: TEasyGroup; S: TStream; Version: Integer) of object;
  TGroupSetCaptionEvent = procedure(Sender: TCustomEasyListview; Group: TEasyGroup; Caption: WideString) of object;
  TGroupSetImageIndexEvent = procedure(Sender: TCustomEasyListview; Group: TEasyGroup; ImageKind: TEasyImageKind; ImageIndex: Integer) of object;
  TGroupSetDetailEvent = procedure(Sender: TCustomEasyListview; Group: TEasyGroup; Line: Integer; const Detail: Integer) of object;
  TGroupSelectionChangeEvent = procedure(Sender: TCustomEasyListview; Group: TEasyGroup) of object;
  TGroupSelectionChangingEvent = procedure(Sender: TCustomEasyListview; Group: TEasyGroup; var Allow: Boolean) of object;
  TGroupThumbnailDrawEvent = procedure(Sender: TCustomEasyListview; Group: TEasyGroup; ACanvas: TCanvas; ARect: TRect; AlphaBlender: TEasyAlphaBlender; var DoDefault: Boolean) of object;
  TGroupVisibilityChangeEvent = procedure(Sender: TCustomEasyListview; Group: TEasyGroup) of object;
  TGroupVisibilityChangingEvent = procedure(Sender: TCustomEasyListview; Group: TEasyGroup; var Allow: Boolean) of object;
  THintCustomDrawEvent = procedure(Sender: TCustomEasyListview; TargetObj: TEasyCollectionItem; const Info: TEasyHintInfo) of object;
  THintCustomizeInfoEvent = procedure(Sender: TCustomEasyListview; TargetObj: TEasyCollectionItem; Info: TEasyHintInfo) of object;
  THintPauseTimeEvent = procedure(Sender: TCustomEasyListview; HintWindowShown: Boolean; var PauseDelay: Integer) of object;
  THintPopupEvent = procedure(Sender: TCustomEasyListview; TargetObj: TEasyCollectionItem; HintType: TEasyHintType; MousePos: TPoint; var AText: WideString; var HideTimeout, ReShowTimeout: Integer; var Allow: Boolean) of object;
  THeaderClickEvent = procedure(Sender: TCustomEasyListview; MouseButton: TCommonMouseButton; Column: TEasyColumn) of object;
  THeaderDblClickEvent = procedure(Sender: TCustomEasyListview; MouseButton: TCommonMouseButton; MousePos: TPoint; ShiftState: TShiftState) of object;
  THeaderMouseEvent = procedure(Sender: TCustomEasyListview; MouseButton: TCommonMouseButton; Shift: TShiftState; X, Y: Integer; Column: TEasyColumn) of object;
  TIncrementalSearchEvent = procedure(Item: TEasyCollectionItem; const SearchBuffer: WideString; var Handled: Boolean; var CompareResult: Integer) of object;
  TItemCheckChangeEvent = procedure(Sender: TCustomEasyListview; Item: TEasyItem) of object;
  TItemCheckChangingEvent = procedure(Sender: TCustomEasyListview; Item: TEasyItem; var Allow: Boolean) of object;
  TItemClickEvent = procedure(Sender: TCustomEasyListview; Item: TEasyItem; KeyStates: TCommonKeyStates; HitInfo: TEasyItemHitTestInfoSet) of object;
  TItemCompareEvent = function(Sender: TCustomEasyListview; Column: TEasyColumn; Group: TEasyGroup; Item1, Item2: TEasyItem; var DoDefault: Boolean): Integer of object;
  TItemContextMenuEvent = procedure(Sender: TCustomEasyListview; HitInfo: TEasyHitInfoItem; WindowPoint: TPoint; var Menu: TPopupMenu; var Handled: Boolean) of object;
  TItemCreateEditorEvent = procedure(Sender: TCustomEasyListview; Item: TEasyItem; var Editor: IEasyCellEditor) of object;
  TItemCustomViewEvent = procedure(Sender: TCustomEasyListview; Item: TEasyItem; ViewStyle: TEasyListStyle; var View: TEasyViewItemClass) of object;
  TItemLoadFromStreamEvent = procedure(Sender: TCustomEasyListview; Item: TEasyItem; S: TStream; Version: Integer) of object;
  TItemSaveToStreamEvent = procedure(Sender: TCustomEasyListview; Item: TEasyItem; S: TStream; Version: Integer) of object;
  TItemDblClickEvent = procedure(Sender: TCustomEasyListview; Button: TCommonMouseButton; MousePos: TPoint; HitInfo: TEasyHitInfoItem) of object;
  TItemEditBegin = procedure(Sender: TCustomEasyListview; Item: TEasyItem; var Column: Integer; var Allow: Boolean) of object;  
  TItemEditedEvent = procedure(Sender: TCustomEasyListview; Item: TEasyItem; var NewValue: Variant; var Accept: Boolean) of object;
  TItemEditEnd = procedure(Sender: TCustomEasyListview; Item: TEasyItem) of object;
  TItemEnableChangeEvent = procedure(Sender: TCustomEasyListview; Item: TEasyItem) of object;
  TItemEnableChangingEvent = procedure(Sender: TCustomEasyListview; Item: TEasyItem; var Allow: Boolean) of object;
  TItemFreeingEvent = procedure(Sender: TCustomEasyListview; Item: TEasyItem) of object;
  TItemFocusChangeEvent = procedure(Sender: TCustomEasyListview; Item: TEasyItem) of object;
  TItemFocusChangingEvent = procedure(Sender: TCustomEasyListview; Item: TEasyItem; var Allow: Boolean) of object;
  TItemGetStreamingCreateClassEvent = procedure(Sender: TCustomEasyListview; var AClass: TEasyColumnStoredClass) of object;
  TItemGetCaptionEvent = procedure(Sender: TCustomEasyListview; Item: TEasyItem; Column: Integer; var Caption: WideString) of object;
  TEasyItemGetCaptionEvent = procedure(Sender: TCustomEasyListview; Item: TEasyItem; Column: TEasyColumn; var Caption: WideString) of object;
  TItemGetEditMenuEvent = procedure(Sender: TCustomEasyListview; Editor: TEasyBaseEditor; var Menu: TPopupMenu) of object;
  TItemGetClassEvent = procedure(Sender: TCustomEasyListview; var ItemClass: TEasyCollectionItemClass) of object;
  TItemGetGroupKeyEvent = procedure(Sender: TCustomEasyListview; Item: TEasyItem; FocusedColumn: Integer; var GroupKey: LongWord) of object;
  TItemGetImageIndexEvent = procedure(Sender: TCustomEasyListview; Item: TEasyItem; Column: Integer; ImageKind: TEasyImageKind; var ImageIndex: TCommonImageIndexInteger) of object;
  TItemGetImageListEvent = procedure(Sender: TCustomEasyListview; Item: TEasyItem; Column: Integer; var ImageList: TCustomImageList) of object;
  TEasyGetTaskPanelEvent = procedure(Sender: TCustomEasyListview; Group: TEasyGroup; var TaskPanel: TEasyTaskPanelFormClass) of object;
  TItemGetTileDetailCountEvent = procedure(Sender: TCustomEasyListview; Item: TEasyItem; var Count: Integer) of object;
  TItemImageDrawEvent = procedure(Sender: TCustomEasyListview; Item: TEasyItem; Column: TEasyColumn; ACanvas: TCanvas; const RectArray: TEasyRectArrayObject; AlphaBlender: TEasyAlphaBlender) of object;
  TItemImageGetSizeEvent = procedure(Sender: TCustomEasyListview; Item: TEasyItem; Column: TEasyColumn; var ImageWidth, ImageHeight: Integer) of object;
  TItemImageDrawIsCustomEvent = procedure(Sender: TCustomEasyListview; Item: TEasyItem; Column: TEasyColumn; var IsCustom: Boolean) of object;
  TItemGetTileDetailEvent = procedure(Sender: TCustomEasyListview; Item: TEasyItem; Line: Integer; var Detail: Integer) of object;
  TItemHotTrackEvent = procedure(Sender: TCustomEasyListview; Item: TEasyItem; State: TEasyHotTrackState; MousePos: TPoint) of object;     
  TItemInitializeEvent = procedure(Sender: TCustomEasyListview; Item: TEasyItem) of object;
  TItemMouseDownEvent = procedure(Sender: TCustomEasyListview; Item: TEasyItem; Button: TCommonMouseButton; var DoDefault: Boolean) of object;
  TItemMouseUpEvent = procedure(Sender: TCustomEasyListview; Item: TEasyItem; Button: TCommonMouseButton; var DoDefault: Boolean) of object;
  TItemPaintTextEvent = procedure(Sender: TCustomEasyListview; Item: TEasyItem; Position: Integer; ACanvas: TCanvas) of object;
  TItemSelectionChangeEvent = procedure(Sender: TCustomEasyListview; Item: TEasyItem) of object;
  TItemSelectionChangingEvent = procedure(Sender: TCustomEasyListview; Item: TEasyItem; var Allow: Boolean) of object;
  TEasyItemSelectionsChangedEvent = procedure(Sender: TCustomEasyListview) of object;
  TItemSetCaptionEvent = procedure(Sender: TCustomEasyListview; Item: TEasyItem; Column: Integer; Caption: WideString) of object;
  TItemSetGroupKeyEvent = procedure(Sender: TCustomEasyListview; Item: TEasyItem; Column: Integer; Key: LongWord) of object;
  TItemSetImageIndexEvent = procedure(Sender: TCustomEasyListview; Item: TEasyItem; Column: Integer; ImageKind: TEasyImageKind; ImageIndex: Integer) of object;
  TItemSetTileDetailEvent = procedure(Sender: TCustomEasyListview; Item: TEasyItem; Line: Integer; const Detail: Integer) of object;
  TItemThumbnailDrawEvent = procedure(Sender: TCustomEasyListview; Item: TEasyItem; ACanvas: TCanvas; ARect: TRect; AlphaBlender: TEasyAlphaBlender; var DoDefault: Boolean) of object;
  TItemVisibilityChangeEvent = procedure(Sender: TCustomEasyListview; Item: TEasyItem) of object;
  TItemVisibilityChangingEvent = procedure(Sender: TCustomEasyListview; Item: TEasyItem; var Allow: Boolean) of object;

  TEasyKeyActionEvent = procedure(Sender: TCustomEasyListview; var CharCode: Word; var Shift: TShiftState; var DoDefault: Boolean) of object;

  TOLEDropSourceDragEndEvent = procedure(Sender: TCustomEasyListview; ADataObject: IDataObject; DragResult: TCommonOLEDragResult; ResultEffect: TCommonDropEffects; KeyStates: TCommonKeyStates) of object;  
  TOLEDropSourceDragStartEvent = procedure(Sender: TCustomEasyListview; ADataObject: IDataObject; var AvailableEffects: TCommonDropEffects; var AllowDrag: Boolean) of object;
  TOLEDropSourceQueryContineDragEvent = procedure(Sender: TCustomEasyListview; EscapeKeyPressed: Boolean; KeyStates: TCommonKeyStates; var QueryResult: TEasyQueryDragResult) of object;
  TOLEDropSourceGiveFeedbackEvent = procedure(Sender: TCustomEasyListview; Effect: TCommonDropEffects; var UseDefaultCursors: Boolean) of object;
  TOLEDropTargetDragEnterEvent = procedure(Sender: TCustomEasyListview; DataObject: IDataObject; KeyState: TCommonKeyStates; WindowPt: TPoint; AvailableEffects: TCommonDropEffects; var DesiredDropEffect: TCommonDropEffect) of object;
  TOLEDropTargetDragOverEvent = procedure(Sender: TCustomEasyListview; KeyState: TCommonKeyStates; WindowPt: TPoint; AvailableEffects: TCommonDropEffects; var DesiredDropEffect: TCommonDropEffect) of object;
  TOLEDropTargetDragLeaveEvent = procedure(Sender: TCustomEasyListview) of object;
  TOLEDropTargetDragDropEvent = procedure(Sender: TCustomEasyListview; DataObject: IDataObject; KeyState: TCommonKeyStates; WindowPt: TPoint; AvailableEffects: TCommonDropEffects; var DesiredDropEffect: TCommonDropEffect; var Handled: Boolean) of object;
  TOLEGetCustomFormatsEvent = procedure(Sender: TCustomEasyListview; var Formats: TFormatEtcArray) of object;
  TOLEGetDataEvent = procedure(Sender: TCustomEasyListview; const FormatEtcIn: TFormatEtc; var Medium: TStgMedium; var Handled: Boolean) of object;
  FOLEGetDataObjectEvent = procedure(Sender: TCustomEasyListview; var DataObject: IDataObject) of object;
  TOLEQueryDataEvent = procedure(Sender: TCustomEasyListview; const FormatEtcIn: TFormatEtc; var FormatAvailable: Boolean; var Handled: Boolean) of object;
  TPaintBkGndEvent = procedure(Sender: TCustomEasyListview; ACanvas: TCanvas; AWindowRect: TRect; AlphaBlender: TEasyAlphaBlender; var DoDefault: Boolean) of object;
  TPaintHeaderBkGndEvent = procedure(Sender: TCustomEasyListview; ACanvas: TCanvas; ARect: TRect; var Handled: Boolean) of object;
  TViewChangingEvent = procedure(Sender: TCustomEasyListview; View: TEasyListStyle; var Allow: Boolean) of object;
  TViewChangedEvent = procedure(Sender: TCustomEasyListview) of object;
  TEasyScrollEvent = procedure(Sender: TCustomEasyListview; DeltaX, DeltaY: Integer) of object;

  TEasyDoGroupCompare = function(Column: TEasyColumn; Group1, Group2: TEasyGroup): Integer of object;
  TEasyDoItemCompare = function(Column: TEasyColumn; Group: TEasyGroup; Item1, Item2: TEasyItem): Integer of object;

  // **************************************************************************
  // TEasyMemo
  //   A class that uses TntMemo when TNTSUPPORT is defined
  // **************************************************************************
  {$IFDEF TNTSUPPORT}
  TEasyMemo = class(TTntMemo);
  TEasyEdit = class(TTntEdit);
  {$ELSE}
  TEasyMemo = class(TMemo);
  TEasyEdit = class(TEdit);
  {$ENDIF}


  // **************************************************************************
  // TEasyInterfacedPersistent
  //   A class that makes a TPersistent class that is an interfaced object
  // **************************************************************************
  TEasyInterfacedPersistent = class(TPersistent, IUnknown, ICommonExtractObj)
  private
    FRefCount: Integer;
  protected
    // IUnknown
    function _AddRef: Integer; virtual; stdcall;
    function _Release: Integer; virtual; stdcall;
    function QueryInterface(const IID: TGUID; out Obj): HResult; virtual; stdcall;

    // IEasyExtractObj
    function GetObj: TObject;
  public

    procedure AfterConstruction; override;
    procedure BeforeDestruction; override;
    class function NewInstance: TObject; override;
    property Obj: TObject read GetObj;
    property RefCount: Integer read FRefCount;
  end;

  // **************************************************************************
  // TEasyOwnedInterfacedPersistent
  //   A class that makes an interfaced object that has knowledge of the
  // TEasyOwnerListview
  // **************************************************************************
  TEasyOwnedInterfacedPersistent = class(TEasyInterfacedPersistent)
  private
    FOwner: TCustomEasyListview;
  public
    constructor Create(AnOwner: TCustomEasyListview); virtual;

    property Owner: TCustomEasyListview read FOwner;
  end;

  // **************************************************************************
  // TEasyPersistent
  // **************************************************************************
  TEasyPersistent = class(TPersistent)
  public
    constructor Create; virtual;
    destructor Destroy; override;
    procedure Assign(Source: TPersistent); override;
  end;

  // **************************************************************************
  // TEasyOwnedPersistent
  //    Basis for Managers and objects that need a link back to the Main Window
  // **************************************************************************
  TEasyOwnedPersistent = class(TEasyPersistent)
  private
    FOwnerListview: TCustomEasyListview;
  protected
    function GetOwner: TPersistent; override;
  public
    constructor Create(AnOwner: TCustomEasyListview); reintroduce; virtual;
    procedure LoadFromStream(S: TStream; Version: Integer = EASYLISTVIEW_STREAM_VERSION); virtual;
    procedure SaveToStream(S: TStream; Version: Integer = EASYLISTVIEW_STREAM_VERSION); virtual;
    property OwnerListview: TCustomEasyListview read FOwnerListview;
  end;

  TEasyCanvasStore = class
  protected
    FBrush: TBrush;
    FFont: TFont;
    FPen: TPen;
  public
    destructor Destroy; override;
    procedure RestoreCanvasState(Canvas: TCanvas);
    procedure StoreCanvasState(Canvas: TCanvas);
    property Brush: TBrush read FBrush write FBrush;
    property Font: TFont read FFont write FFont;
    property Pen: TPen read FPen write FPen;
  end;

  // **************************************************************************
  // TEasyOwnerPersistentView
  //   Basis for any class that will become a view
  // **************************************************************************
  TEasyOwnedPersistentView = class(TEasyOwnedPersistent)
  private
    FCanvasStore: TEasyCanvasStore;
    function GetCanvasStore: TEasyCanvasStore;
  protected
    procedure PaintCheckboxCore(CheckType: TEasyCheckType; OwnerListView: TCustomEasyListView; ACanvas: TCanvas; ARect: TRect; IsEnabled, IsChecked, IsHot, IsFlat, IsHovering, IsPending: Boolean; Obj: TEasyCollectionItem; Size: Integer);
    property CanvasStore: TEasyCanvasStore read GetCanvasStore write FCanvasStore;
  public
    destructor Destroy; override;
  published

  end;

  // **************************************************************************
  // TEasyAlphaBlender
  //   Helper for Alpha blending a canvas
  // **************************************************************************
  TEasyAlphaBlender = class(TEasyPersistent)
  public
    destructor Destroy; override;
    procedure BasicBlend(Listview: TCustomEasyListview; ACanvas: TCanvas; ViewportRect: TRect; Color: TColor; Alpha: Byte = 128; UseScrollPostion: Boolean = True); virtual;
    procedure Blend(Listview: TCustomEasyListview; Obj: TEasyCollectionItem; ACanvas: TCanvas; ViewportRect: TRect; Image: TBitmap); virtual;
    procedure GetBlendParams(Listview: TCustomEasyListview; Obj: TEasyCollectionItem; var BlendAlpha: Byte; var BlendColor: TColor; var DoBlend: Boolean);
  end;

  // **************************************************************************
  // TEasyOwnedPersistentGroupItem
  //   Basis for any class property of a TEasyGroup that allows communication
  // back to the TEasyGroups
  // **************************************************************************
  TEasyOwnedPersistentGroupItem = class(TEasyOwnedPersistentView)
  private
    FOwnerGroup: TEasyGroup;
  public
    constructor Create(AnOwner: TEasyGroup); reintroduce; virtual;
    property OwnerGroup: TEasyGroup read FOwnerGroup;
  end;

  TEasySelectionGroupList = class
  private
    FDisplayRect: TRect;
    FFirstItem: TEasyItem;
    FList: TList;
    FRefCount: Integer;
  protected
    function GetItems(Index: Integer): TEasyItem;
    procedure SetItems(Index: Integer; Value: TEasyItem);
    property List: TList read FList write FList;
    property RefCount: Integer read FRefCount write FRefCount;
  public
    constructor Create;
    destructor Destroy; override;
    function Count: Integer;
    procedure Add(Item: TEasyItem);
    procedure Clear;
    procedure DecRef;
    procedure IncRef;
    property DisplayRect: TRect read FDisplayRect write FDisplayRect;
    property FirstItem: TEasyItem read FFirstItem write FFirstItem;
    property Items[Index: Integer]: TEasyItem  read GetItems write SetItems; default;
  end;

  // **************************************************************************
  // TEasyMargin
  //   Property for TEasyGroupItem to set the attributes of the Group Margins
  // **************************************************************************
  TEasyMargin = class(TEasyOwnedPersistent)
  private
    FSize: Integer;
    FVisible: Boolean;
    procedure SetSize(Value: Integer);
    procedure SetVisible(Value: Boolean);
  protected
    function RuntimeSize: Integer;
  public
    constructor Create(AnOwner: TCustomEasyListview); override;
    destructor Destroy; override;

    procedure Assign(Source: TPersistent); override;
  published
    property Size: Integer read FSize write SetSize default 30;
    property Visible: Boolean read FVisible write SetVisible default False;
  end;

  // **************************************************************************
  // TEasyHeaderMargin
  //   Property for TEasyGroupItem to set the attributes of the Header Group Margin
  // **************************************************************************
  TEasyHeaderMargin = class(TEasyMargin)
  public
    constructor Create(AnOwner: TCustomEasyListview); override;
  published
    property Visible default True;
  end;

  // **************************************************************************
  // TCustomEasyFooterMargin
  //   Property for TEasyGroupItem to set the attributes of the Group Margins
  // **************************************************************************
  TCustomEasyFooterMargin = class(TEasyMargin)
  private
    FCaption: WideString;
    FImageIndex: TCommonImageIndexInteger;
    FImageOverlayIndex: TCommonImageIndexInteger;
    FPaintInfo: TEasyPaintInfoBaseGroup;
    function GetAlignment: TAlignment;
    function GetCaptionIndent: Integer;
    function GetCaptionLines: Integer;
    function GetImageIndent: Integer;
    function GetPaintInfo: TEasyPaintInfoBaseGroup;
    function GetVAlignment: TCommonVAlignment;
    procedure SetAlignment(Value: TAlignment);
    procedure SetCaption(Value: WideString);
    procedure SetCaptionIndent(Value: Integer);
    procedure SetCaptionLines(Value: Integer);
    procedure SetImageIndent(Value: Integer);
    procedure SetImageIndex(const Value: TCommonImageIndexInteger);
    procedure SetImageOveralyIndex(const Value: TCommonImageIndexInteger);
    procedure SetPaintInfo(const Value: TEasyPaintInfoBaseGroup);
    procedure SetVAlignment(Value: TCommonVAlignment);
  protected
    property Alignment: TAlignment read GetAlignment write SetAlignment default taLeftJustify;
    property Caption: WideString read FCaption write SetCaption;
    property CaptionIndent: Integer read GetCaptionIndent write SetCaptionIndent default 2;
    property CaptionLines: Integer read GetCaptionLines write SetCaptionLines default 1;
    property ImageIndent: Integer read GetImageIndent write SetImageIndent default 2;
    property ImageIndex: TCommonImageIndexInteger read FImageIndex write SetImageIndex default -1;
    property ImageOverlayIndex: TCommonImageIndexInteger read FImageOverlayIndex write SetImageOveralyIndex default -1;
    property PaintInfo: TEasyPaintInfoBaseGroup read GetPaintInfo write SetPaintInfo;
    property Size default 30;
    property VAlignment: TCommonVAlignment read GetVAlignment write SetVAlignment default cvaCenter;
    
  public
    constructor Create(AnOwner: TCustomEasyListview); override;
    destructor Destroy; override;

    procedure Assign(Source: TPersistent); override;
  end;
  TEasyFooterMarginCustomClass = class of TCustomEasyFooterMargin;

  // **************************************************************************
  // TEasyFooterMargin
  //   Default Footer Margin PaintInfo that is global to all Groups. Defined under
  // the EasyListview.PaintInfoGroup.MarginBottom property
  // **************************************************************************
  TEasyFooterMargin = class(TCustomEasyFooterMargin)
  published
    property Alignment;
    property Caption;
    property CaptionIndent;
    property CaptionLines;
    property ImageIndent;
    property ImageIndex;
    property ImageOverlayIndex;
    property Size default 30;
    property VAlignment;
  end;

  // **************************************************************************
  // TEasyPaintInfoBasic
  //   Basic information that defines how a particular UI object is Painted
  // **************************************************************************
  TEasyPaintInfoBasic = class(TEasyOwnedPersistent)
  private
    FAlignment: TAlignment;
    FBorder: Integer;
    FBorderColor: TColor;
    FCaptionIndent: Integer;
    FCaptionLines: Integer;
    FCheckFlat: Boolean;
    FCheckIndent: Integer;
    FCheckSize: Integer;
    FCheckType: TEasyCheckType;
    FImageIndent: Integer;
    FShowBorder: Boolean;
    FVAlignment: TCommonVAlignment;
    procedure SetAlignment(Value: TAlignment);
    procedure SetBorder(Value: Integer);
    procedure SetBorderColor(Value: TColor);
    procedure SetCaptionIndent(Value: Integer);
    procedure SetCaptionLines(Value: Integer);
    procedure SetCheckFlat(Value: Boolean);
    procedure SetCheckIndent(Value: Integer);
    procedure SetCheckSize(Value: Integer);
    procedure SetCheckType(Value: TEasyCheckType);
    procedure SetImageIndent(Value: Integer);
    procedure SetShowBorder(const Value: Boolean);
    procedure SetVAlignment(Value: TCommonVAlignment);
  protected
    procedure Invalidate(ImmediateUpdate: Boolean); virtual;

    property Alignment: TAlignment read FAlignment write SetAlignment default taLeftJustify;
    property Border: Integer read FBorder write SetBorder default 4;
    property BorderColor: TColor read FBorderColor write SetBorderColor default clHighlight;
    property CaptionIndent: Integer read FCaptionIndent write SetCaptionIndent default 4;
    property CaptionLines: Integer read FCaptionLines write SetCaptionLines default 1;
    property CheckFlat: Boolean read FCheckFlat write SetCheckFlat default False;
    property CheckIndent: Integer read FCheckIndent write SetCheckIndent default 2;
    property CheckSize: Integer read FCheckSize write SetCheckSize default 12;
    property CheckType: TEasyCheckType read FCheckType write SetCheckType default ectNone;
    property ImageIndent: Integer read FImageIndent write SetImageIndent default 2;
    property ShowBorder: Boolean read FShowBorder write SetShowBorder default True;
    property VAlignment: TCommonVAlignment read FVAlignment write SetVAlignment default cvaCenter;
  public
    constructor Create(AnOwner: TCustomEasyListview); override;

    procedure Assign(Source: TPersistent); override;
  end;
  TEasyPaintInfoBasicClass = class of TEasyPaintInfoBasic;

  // **************************************************************************
  // TEasyPaintInfoBaseItem
  //   Information that defines how an Items UI object is Painted
  // **************************************************************************
  TEasyPaintInfoBaseItem = class(TEasyPaintInfoBasic)
  private
    FGridLineColor: TColor;
    FGridLines: Boolean;
    FHideCaption: Boolean;
    FTileDetailCount: Integer;
    procedure SetGridLineColor(const Value: TColor);
    procedure SetGridLines(const Value: Boolean);
    procedure SetHideCaption(const Value: Boolean);
    procedure SetTileDetailCount(Value: Integer);
  protected
    property GridLineColor: TColor read FGridLineColor write SetGridLineColor default clBtnFace;
    property GridLines: Boolean read FGridLines write SetGridLines default False;
    property HideCaption: Boolean read FHideCaption write SetHideCaption default False;
    property TileDetailCount: Integer read FTileDetailCount write SetTileDetailCount default 1;
  public
    constructor Create(AnOwner: TCustomEasyListview); override;
  end;

  TEasyPaintInfoItem = class(TEasyPaintInfoBaseItem)
  published
    property Border;
    property BorderColor;
    property CaptionIndent;
    property CaptionLines;
    property CheckFlat;
    property CheckIndent;
    property CheckSize;
    property CheckType;
    property GridLineColor;
    property GridLines;
    property HideCaption;
    property ImageIndent;
    property ShowBorder;
    property TileDetailCount;
    property VAlignment;
  end;

  TEasyPaintInfoTaskBandItem = class(TEasyPaintInfoBaseItem)
  published
    property CaptionIndent;
    property CheckFlat;
    property CheckIndent;
    property CheckSize;
    property CheckType;
    property VAlignment;
  end;

  // **************************************************************************
  // TEasyPaintInfoBaseColumn
  //   Information that defines how an Column UI object is Painted
  // **************************************************************************
  TEasyPaintInfoBaseColumn = class(TEasyPaintInfoBasic)
  private
    FColor: TColor;
    FHilightFocused: Boolean;
    FHilightFocusedColor: TColor;
    FHotTrack: Boolean;
    FImagePosition: TEasyHeaderImagePosition;
    FSortGlyphAlign: TEasySortGlyphAlign;
    FSortGlyphIndent: Integer;
    FStyle: TEasyHeaderButtonStyle;
    procedure SetColor(Value: TColor);
    procedure SetHilightFocused(const Value: Boolean);
    procedure SetHilightFocusedColor(const Value: TColor);
    procedure SetImagePosition(Value: TEasyHeaderImagePosition);
    procedure SetSortGlpyhAlign(Value: TEasySortGlyphAlign);
    procedure SetSortGlyphIndent(Value: Integer);
    procedure SetStyle(Value: TEasyHeaderButtonStyle);
  protected
    property Color: TColor read FColor write SetColor default clBtnFace;
    property HilightFocused: Boolean read FHilightFocused write SetHilightFocused default False;
    property HilightFocusedColor: TColor read FHilightFocusedColor write SetHilightFocusedColor default $00F7F7F7;
    property HotTrack: Boolean read FHotTrack write FHotTrack default True;
    property ImagePosition: TEasyHeaderImagePosition read FImagePosition write SetImagePosition default ehpLeft;
    property SortGlyphAlign: TEasySortGlyphAlign read FSortGlyphAlign write SetSortGlpyhAlign default esgaRight;
    property SortGlyphIndent: Integer read FSortGlyphIndent write SetSortGlyphIndent default 2;
    property Style: TEasyHeaderButtonStyle read FStyle write SetStyle default ehbsThick;
  public
    constructor Create(AnOwner: TCustomEasyListview); override;
  published
  end;

  TCustomEasyPaintInfoBaseColumn = class(TEasyPaintInfoBaseColumn)
  end;

  TEasyPaintInfoColumn = class(TCustomEasyPaintInfoBaseColumn)
  private
  published
    property Border;
    property BorderColor;
    property CaptionIndent;
    property CaptionLines;
    property CheckFlat;
    property CheckIndent;
    property CheckSize;
    property CheckType;
    property Color;
    property HilightFocused;
    property HilightFocusedColor;
    property HotTrack;
    property ImageIndent;
    property ImagePosition;
    property SortGlyphAlign;
    property SortGlyphIndent;
    property Style;
    property VAlignment;
  end;

  TEasyPaintInfoTaskBandColumn = class(TCustomEasyPaintInfoBaseColumn)
  published
    // Nothing published
  end;

  // **************************************************************************
  // TEasyPaintInfoBasicGroup
  //   Basic information that defines how an Groups UI object is Painted
  // **************************************************************************
  TEasyPaintInfoBaseGroup = class(TEasyPaintInfoBasic)
  private
    FBandBlended: Boolean;
    FBandColor: TColor;
    FBandColorFade: TColor;
    FBandEnabled: Boolean;
    FBandFullWidth: Boolean;
    FBandIndent: Integer;
    FBandLength: Integer;
    FBandMargin: Integer;
    FBandRadius: Byte;
    FBandThickness: Integer;
    FExpandable: Boolean;
    FExpanded: Boolean;
    FExpandImageIndent: Integer;
    FMarginBottom: TCustomEasyFooterMargin;
    FMarginLeft: TEasyMargin;
    FMarginRight: TEasyMargin;
    FMarginTop: TEasyHeaderMargin;
    function GetMarginBottom: TCustomEasyFooterMargin;
    function GetMarginLeft: TEasyMargin;
    function GetMarginRight: TEasyMargin;
    function GetMarginTop: TEasyHeaderMargin;
    procedure SetBandBlended(Value: Boolean);
    procedure SetBandColor(Value: TColor);
    procedure SetBandColorFade(Value: TColor);
    procedure SetBandEnabled(Value: Boolean);
    procedure SetBandFullWidth(Value: Boolean);
    procedure SetBandIndent(Value: Integer);
    procedure SetBandLength(Value: Integer);
    procedure SetBandMargin(Value: Integer);
    procedure SetBandRadius(Value: Byte);
    procedure SetBandThickness(Value: Integer);
    procedure SetExpandable(Value: Boolean);
    procedure SetExpandImageIndent(Value: Integer);
    procedure SetMarginBottom(Value: TCustomEasyFooterMargin);
    procedure SetMarginLeft(Value: TEasyMargin);
    procedure SetMarginRight(Value: TEasyMargin);
    procedure SetMarginTop(Value: TEasyHeaderMargin);
  protected
    property BandBlended: Boolean read FBandBlended write SetBandBlended default True;
    property BandColor: TColor read FBandColor write SetBandColor default clBlue;
    property BandColorFade: TColor read FBandColorFade write SetBandColorFade default clWindow;
    property BandEnabled: Boolean read FBandEnabled write SetBandEnabled default True;
    property BandFullWidth: Boolean read FBandFullWidth write SetBandFullWidth default False;
    property BandIndent: Integer read FBandIndent write SetBandIndent default 0;
    property BandLength: Integer read FBandLength write SetBandLength default 300;
    property BandMargin: Integer read FBandMargin write SetBandMargin default 2;
    property BandRadius: Byte read FBandRadius write SetBandRadius default 4;
    property BandThickness: Integer read FBandThickness write SetBandThickness default 3;
    property Expandable: Boolean read FExpandable write SetExpandable default True;
    property ExpandImageIndent: Integer read FExpandImageIndent write SetExpandImageIndent default 4;
    property MarginBottom: TCustomEasyFooterMargin read GetMarginBottom write SetMarginBottom;
    property MarginLeft: TEasyMargin read GetMarginLeft write SetMarginLeft;
    property MarginRight: TEasyMargin read GetMarginRight write SetMarginRight;
    property MarginTop: TEasyHeaderMargin read GetMarginTop write SetMarginTop;
  public
    constructor Create(AnOwner: TCustomEasyListview); override;
    destructor Destroy; override;

    procedure Assign(Source: TPersistent); override;
  end;

  // **************************************************************************
  // TEasyPaintInfoGroup
  //   Information that defines how an Groups UI object is Painted
  // **************************************************************************
  TEasyPaintInfoGroup = class(TEasyPaintInfoBaseGroup)
  published
    property Alignment;
    property BandBlended;
    property BandColor;
    property BandColorFade;
    property BandEnabled;
    property BandFullWidth;
    property BandIndent;
    property BandLength;
    property BandMargin;
    property BandRadius;
    property BandThickness;
    property Border;
    property BorderColor;
    property CaptionIndent;
    property CaptionLines;
    property CheckFlat;
    property CheckIndent;
    property CheckSize;
    property CheckType;
    property Expandable;
    property ExpandImageIndent;
    property ImageIndent;
    property MarginBottom;
    property MarginLeft;
    property MarginRight;
    property MarginTop;
    property VAlignment;
  end;

   // **************************************************************************
  // TEasyPaintInfoGroup
  //   Information that defines how an Groups UI object is Painted
  // **************************************************************************
  TEasyPaintInfoTaskbandGroup = class(TEasyPaintInfoBaseGroup)
  published
    property Alignment;
    property CaptionIndent;
    property CheckFlat;
    property CheckIndent;
    property CheckSize;
    property CheckType;
    property Expandable;
    property MarginBottom;
    property MarginLeft;
    property MarginRight;
    property MarginTop;
    property VAlignment;
  end;

  // **************************************************************************
  // TEasyDynamicDataHelper
  //    Helps with multiple captions/image storage for item/groups/columns
  // **************************************************************************
  TEasyDynamicDataHelper = class
  private
    FCaptionArray: TCommonWideStringDynArray;
    FDetailArray: TCommonIntegerDynArray;
    FGroupKeyArray: TCommonIntegerDynArray;
    FImageIndexArray: TCommonIntegerDynArray;
    FOverlayIndexArray: TCommonIntegerDynArray;
    function GetCaptions(Index: Integer): Widestring;
    function GetDetails(Index: Integer): Integer;
    function GetImageIndexes(Index: Integer): Integer;
    function GetImageOverlayIndexes(Index: Integer): Integer;
    procedure LoadIntArrayFromStream(S: TStream; var AnArray: TCommonIntegerDynArray);
    procedure LoadWideStrArrayFromStream(S: TStream; var AnArray: TCommonWideStringDynArray);
    procedure SaveIntArrayToStream(S: TStream; var AnArray: TCommonIntegerDynArray);
    procedure SaveWideStrArrayToStream(S: TStream; var AnArray: TCommonWideStringDynArray);
    procedure SetCaptions(Index: Integer; Value: Widestring);
    procedure SetDetails(Index: Integer; Value: Integer);
    procedure SetImageIndexes(Index: Integer; Value: Integer);
    procedure SetImageOverlayIndexes(Index: Integer; Value: Integer);
    property CaptionArray: TCommonWideStringDynArray read FCaptionArray write FCaptionArray;
    property DetailArray: TCommonIntegerDynArray read FDetailArray write FDetailArray;
    property GroupKeyArray: TCommonIntegerDynArray read FGroupKeyArray write FGroupKeyArray;
    property ImageIndexArray: TCommonIntegerDynArray read FImageIndexArray write FImageIndexArray;
    property OverlayIndexArray: TCommonIntegerDynArray read FOverlayIndexArray write FOverlayIndexArray;
  public
    procedure Clear;
    procedure LoadFromStream(S: TStream; Version: Integer); virtual;
    procedure SaveToStream(S: TStream; Version: Integer); virtual;
    property Captions[Index: Integer]: Widestring read GetCaptions write SetCaptions;
    property Details[Index: Integer]: Integer read GetDetails write SetDetails;
    property ImageIndexes[Index: Integer]: Integer read GetImageIndexes write SetImageIndexes;
    property ImageOverlayIndexes[Index: Integer]: Integer read GetImageOverlayIndexes write SetImageOverlayIndexes;
  end;

  TEasyItemDynamicDataHelper = class(TEasyDynamicDataHelper)
  private
    FStateImageArray: TCommonIntegerDynArray;
    function GetGroupKey(Index: Integer): LongWord;
    function GetStateImageIndexes(Index: Integer): TCommonImageIndexInteger;
    procedure SetGroupKey(Index: Integer; Value: LongWord);
    procedure SetStateImageIndexes(Index: Integer; Value: TCommonImageIndexInteger);
  protected
    property StateImageArray: TCommonIntegerDynArray read FStateImageArray write FStateImageArray;
  public
    procedure LoadFromStream(S: TStream; Version: Integer); override;
    procedure SaveToStream(S: TStream; Version: Integer); override;
    property GroupKey[Index: Integer]: LongWord read GetGroupKey write SetGroupKey;
    property StateImageIndexes[Index: Integer]: TCommonImageIndexInteger read GetStateImageIndexes write SetStateImageIndexes;
  end;

  // **************************************************************************
  // TEasyCollectionItem
  //    Basis for Collection items (, TEasyGroup, TEasyColumn)
  // This Item can access its data through the
  // **************************************************************************
  TEasyCollectionItem = class(TEasyPersistent, IUnknown, IEasyNotificationSink)
  private
    FCollection: TEasyCollection;
    FData: TObject;
    FDataInf: IUnknown;
    FDisplayRect: TRect;               // The viewport coordinates of the object
    FIndex: Integer;                   // Absolute Index of the item within a particular collecton
    FOwnsPaintInfo: Boolean;
    FPaintInfo: TEasyPaintInfoBasic;   // Information to draw the item
    FRefCount: Integer;
    FState: TEasyStorageObjectStates;  // State of the item
    FTag: Integer;
    FVisibleIndex: Integer;            // Index of the item across all collections (flat list across collection in group)
                                       // See TEasyItem.VisibleIndexInGroup
    function GetAlignment: TAlignment;
    function GetBold: Boolean;
    function GetBorder: Integer;
    function GetBorderColor: TColor;
    function GetCaptionIndent: Integer;
    function GetCheckFlat: Boolean;
    function GetCheckHovering: Boolean;
    function GetCheckIndent: Integer;
    function GetCheckPending: Boolean;
    function GetCheckSize: Integer;
    function GetCheckType: TEasyCheckType;
    function GetClicking: Boolean;
    function GetCut: Boolean;
    function GetDataInf: IUnknown;
    function GetDestroying: Boolean;
    function GetGhosted: Boolean;
    function GetHilighted: Boolean;
    function GetEnabled: Boolean;
    function GetFocused: Boolean;
    function GetHotTracking(MousePos: TPoint): Boolean;
    function GetImageIndent: Integer;
    function GetInitialized: Boolean;
    function GetOwnerListview: TCustomEasyListview;
    function GetPaintInfo: TEasyPaintInfoBasic;
    function GetSelected: Boolean;
    function GetVAlignment: TCommonVAlignment;
    function GetViewRect: TRect;
    function GetVisible: Boolean;
    procedure SetAlignment(Value: TAlignment);
    procedure SetBold(const Value: Boolean);
    procedure SetBorder(Value: Integer);
    procedure SetBorderColor(Value: TColor);
    procedure SetCaptionIndent(Value: Integer);
    procedure SetCheckFlat(Value: Boolean);
    procedure SetCheckHovering(Value: Boolean);
    procedure SetCheckIndent(Value: Integer);
    procedure SetCheckPending(Value: Boolean);
    procedure SetCheckSize(Value: Integer);
    procedure SetCheckType(Value: TEasyCheckType);
    procedure SetClicking(Value: Boolean);
    procedure SetCut(Value: Boolean);
    procedure SetData(Value: TObject); virtual;
    procedure SetDataInf(const Value: IUnknown);
    procedure SetGhosted(const Value: Boolean);
    procedure SetHilighted(Value: Boolean);
    procedure SetEnabled(Value: Boolean);
    procedure SetFocused(Value: Boolean);
    procedure SetHotTracking(MousePos: TPoint; Value: Boolean);
    procedure SetImageIndent(Value: Integer);
    procedure SetInitialized(Value: Boolean); virtual;
    procedure SetPaintInfo(Value: TEasyPaintInfoBasic);
    procedure SetSelected(Value: Boolean);
    procedure SetVAlignment(Value: TCommonVAlignment);
    procedure SetVisible(Value: Boolean);
    
  protected
    function AllowDrag(ViewportPt: TPoint): Boolean; virtual;
    // IUnknown
    function _AddRef: Integer; virtual; stdcall;
    function _Release: Integer; virtual; stdcall;
    function QueryInterface(const IID: TGUID; out Obj): HResult; virtual; stdcall;

    function CanChangeBold(NewValue: Boolean): Boolean; virtual; abstract;
    function CanChangeCheck(NewValue: Boolean): Boolean; virtual; abstract;
    function CanChangeEnable(NewValue: Boolean): Boolean; virtual; abstract;
    function CanChangeFocus(NewValue: Boolean): Boolean; virtual; abstract;
    function CanChangeHotTracking(NewValue: Boolean): Boolean; virtual; abstract;
    function CanChangeSelection(NewValue: Boolean): Boolean; virtual; abstract;
    function CanChangeVisibility(NewValue: Boolean): Boolean; virtual; abstract;
    function DefaultImageList(ImageSize: TEasyImageSize): TCustomImageList; virtual;
    function GetChecked: Boolean; virtual;
    function GetDisplayName: WideString; virtual;
    function LocalPaintInfo: TEasyPaintInfoBasic; virtual; abstract;
    procedure Freeing; virtual; abstract;
    procedure GainingBold; virtual; abstract;
    procedure GainingCheck; virtual; abstract;
    procedure GainingEnable; virtual; abstract;
    procedure GainingFocus; virtual; abstract;
    procedure GainingGhosted; virtual; abstract;
    procedure GainingHilight; virtual; abstract;
    procedure GainingHotTracking(MousePos: TPoint); virtual; abstract;
    procedure GainingSelection; virtual; abstract;
    procedure GainingVisibility; virtual; abstract;
    function GetCaption: WideString; virtual;
    function GetCaptions(Column: Integer): Widestring; virtual; abstract;
    function GetImageIndex: TCommonImageIndexInteger; virtual;
    function GetImageIndexes(Column: Integer): TCommonImageIndexInteger; virtual; abstract;
    function GetImageOverlayIndex: TCommonImageIndexInteger; virtual;
    function GetImageOverlayIndexes(Column: Integer): TCommonImageIndexInteger; virtual; abstract;
    function GetOwner: TPersistent; override;
    function GetImageList(Column: Integer; IconSize: TEasyImageSize): TCustomImageList; virtual; abstract;
    function GetIndex: Integer; virtual;
    function GetDetailCount: Integer; virtual; abstract;
    function GetDetails(Line: Integer): Integer; virtual; abstract;
    procedure ImageDraw(Column: TEasyColumn; ACanvas: TCanvas; const RectArray: TEasyRectArrayObject; AlphaBlender: TEasyAlphaBlender); virtual; abstract;
    procedure ImageDrawGetSize(Column: TEasyColumn; var ImageW, ImageH: Integer); virtual; abstract;
    procedure ImageDrawIsCustom(Column: TEasyColumn; var IsCustom: Boolean); virtual; abstract;
    procedure InvalidateItem(ImmediateRefresh: Boolean); // IEasyNotificationSink
    procedure LosingBold; virtual; abstract;
    procedure LosingGhosted; virtual; abstract;
    procedure LosingHotTracking; virtual; abstract;
    procedure ThumbnailDraw(ACanvas: TCanvas; ARect: TRect; AlphaBlender: TEasyAlphaBlender; var DoDefault: Boolean); virtual; abstract;
    procedure Initialize; virtual; abstract;
    procedure LosingCheck; virtual; abstract;
    procedure LosingEnable; virtual; abstract;
    procedure LosingFocus; virtual; abstract;
    procedure LosingHilight; virtual; abstract;
    procedure LosingSelection; virtual; abstract;
    procedure LosingVisibility; virtual; abstract;
    procedure SetCaptions(Column: Integer; Value: Widestring); virtual; abstract;
    procedure SetCaption(Value: WideString); virtual;
    procedure SetChecked(Value: Boolean); virtual;
    procedure SetDestroyFlags;
    procedure SetDetailCount(Value: Integer); virtual; abstract;
    procedure SetDetails(Line: Integer; Value: Integer); virtual; abstract;
    procedure SetImageIndex(const Value: TCommonImageIndexInteger); virtual;
    procedure SetImageIndexes(Column: Integer; Value: TCommonImageIndexInteger); virtual; abstract;
    procedure SetImageOverlayIndex(const Value: TCommonImageIndexInteger); virtual;
    procedure SetImageOverlayIndexes(Column: Integer; Value: TCommonImageIndexInteger); virtual; abstract;
    procedure UnRegister; // IEasyNotificationSink
    property Alignment: TAlignment read GetAlignment write SetAlignment default taLeftJustify;
    property Bold: Boolean read GetBold write SetBold default False;
    property Border: Integer read GetBorder write SetBorder default 0;
    property BorderColor: TColor read GetBorderColor write SetBorderColor default clWindow;
    property CaptionIndent: Integer read GetCaptionIndent write SetCaptionIndent default 2;
    property Checked: Boolean read GetChecked write SetChecked default False;
    property CheckFlat: Boolean read GetCheckFlat write SetCheckFlat default False;
    property CheckHovering: Boolean read GetCheckHovering write SetCheckHovering;
    property CheckIndent: Integer read GetCheckIndent write SetCheckIndent default 2;
    property CheckPending: Boolean read GetCheckPending write SetCheckPending;
    property CheckSize: Integer read GetCheckSize write SetCheckSize default 12;
    property CheckType: TEasyCheckType read GetCheckType write SetCheckType default ectNone;
    property Clicking: Boolean read GetClicking write SetClicking default False;
    property Collection: TEasyCollection read FCollection write FCollection;
    property Cut: Boolean read GetCut write SetCut default False;
    property DataInf: IUnknown read GetDataInf write SetDataInf;
    property Destroying: Boolean read GetDestroying;
    property DisplayRect: TRect read FDisplayRect write FDisplayRect;
    property Enabled: Boolean read GetEnabled write SetEnabled default True;
    property Focused: Boolean read GetFocused write SetFocused default False;
    property Ghosted: Boolean read GetGhosted write SetGhosted default False;
    property Hilighted: Boolean read GetHilighted write SetHilighted default False;
    property ImageIndent: Integer read GetImageIndent write SetImageIndent default 2;
    property Initialized: Boolean read GetInitialized write SetInitialized;
    property OwnsPaintInfo: Boolean read FOwnsPaintInfo write FOwnsPaintInfo default False;
    property PaintInfo: TEasyPaintInfoBasic read GetPaintInfo write SetPaintInfo;
    property Selected: Boolean read GetSelected write SetSelected default False;
    property State: TEasyStorageObjectStates read FState write FState;// The State of the object, checked, selected, focused, etc.
    property VAlignment: TCommonVAlignment read GetVAlignment write SetVAlignment;
    property Visible: Boolean read GetVisible write SetVisible default True;
  public
    constructor Create(ACollection: TEasyCollection); reintroduce; virtual;
    destructor Destroy; override;   

    function EditAreaHitPt(ViewportPoint: TPoint): Boolean; virtual; abstract;
    function SelectionHit(SelectViewportRect: TRect; SelectType: TEasySelectHitType): Boolean; virtual; abstract;
    function SelectionHitPt(ViewportPoint: TPoint; SelectType: TEasySelectHitType): Boolean; virtual; abstract;
    procedure Invalidate(ImmediateUpdate: Boolean); virtual; // IEasyNotificationSink
    procedure LoadFromStream(S: TStream; var Version: Integer); virtual;
    procedure MakeVisible(Position: TEasyMakeVisiblePos); virtual;
    procedure SaveToStream(S: TStream; Version: Integer = EASYLISTVIEW_STREAM_VERSION); virtual;
    property Caption: WideString read GetCaption write SetCaption;
    property Captions[Column: Integer]: Widestring read GetCaptions write SetCaptions;
    property Data: TObject read FData write SetData;
    property DetailCount: Integer read GetDetailCount write SetDetailCount;
    property Details[Line: Integer]: Integer read GetDetails write SetDetails;
    property HotTracking[MousePos: TPoint]: Boolean read GetHotTracking write SetHotTracking;
    property ImageIndex: TCommonImageIndexInteger read GetImageIndex write SetImageIndex default -1;
    property ImageIndexes[Column: Integer]: TCommonImageIndexInteger read GetImageIndexes write SetImageIndexes;
    property ImageList[Column: Integer; IconSize: TEasyImageSize]: TCustomImageList read GetImageList;
    property ImageOverlayIndex: TCommonImageIndexInteger read GetImageOverlayIndex write SetImageOverlayIndex default -1;
    property ImageOverlayIndexes[Column: Integer]: TCommonImageIndexInteger read GetImageOverlayIndexes write SetImageOverlayIndexes;
    property Index: Integer read GetIndex;
    property OwnerListview: TCustomEasyListview read GetOwnerListview;
    property RefCount: Integer read FRefCount;
    property Tag: Integer read FTag write FTag default 0;
    property ViewRect: TRect read GetViewRect;
    property VisibleIndex: Integer read FVisibleIndex;
  end;

  // **************************************************************************
  // TEasyItemBase
  //    Basis for any object that can be stored in a TEasyGroup.  Implements the
  // basic handling of interaction between the item and the Listview
  // **************************************************************************
  TEasyItem = class(TEasyCollectionItem)
  private
    FSelectionGroup: TEasySelectionGroupList;  // If grouped selection is on this points to the selection group this item belongs to (nil if none)
    FView: TEasyViewItem;
    FVisibleIndexInGroup: Integer;             // Index of visible item within a group
    function GetColumnPos: Integer;
    function GetOwnerGroup: TEasyGroup;
    function GetOwnerItems: TEasyItems;
    function GetPaintInfo: TEasyPaintInfoItem;
    function GetRowPos: Integer;
    function GetView: TEasyViewItem;
    function GetViewClass: TEasyViewItemClass;
    procedure SetPaintInfo(const Value: TEasyPaintInfoItem);
    procedure SetSelectionGroup(Value: TEasySelectionGroupList);
  protected
    function AllowDrag(ViewportPt: TPoint): Boolean; override;
    function CanChangeBold(NewValue: Boolean): Boolean; override;
    function CanChangeCheck(NewValue: Boolean): Boolean; override;
    function CanChangeEnable(NewValue: Boolean): Boolean; override;
    function CanChangeFocus(NewValue: Boolean): Boolean; override;
    function CanChangeHotTracking(NewValue: Boolean): Boolean; override;
    function CanChangeSelection(NewValue: Boolean): Boolean; override;
    function CanChangeVisibility(NewValue: Boolean): Boolean; override;
    function DefaultStateImageList: TCustomImageList;
    function GetDefaultViewClass: TEasyViewItemClass; virtual;
    function GetGroupKey(FocusedColumn: Integer): LongWord; virtual;
    function GetIndex: Integer; override;
    function GetStateImageIndex: TCommonImageIndexInteger; virtual;
    function GetStateImageIndexes(Column: Integer): TCommonImageIndexInteger; virtual; abstract;
    function GetStateImageList(Column: Integer): TCustomImageList; virtual; abstract;
    function LocalPaintInfo: TEasyPaintInfoBasic; override;
    procedure Freeing; override;
    procedure GainingBold; override;
    procedure GainingCheck; override;
    procedure GainingEnable; override;
    procedure GainingFocus; override;
    procedure GainingGhosted; override;
    procedure GainingHilight; override;
    procedure GainingHotTracking(MousePos: TPoint); override;
    procedure GainingSelection; override;
    procedure GainingVisibility; override;
    procedure Initialize; override;
    procedure LosingBold; override;
    procedure LosingCheck; override;
    procedure LosingEnable; override;
    procedure LosingFocus; override;
    procedure LosingGhosted; override;
    procedure LosingHilight; override;
    procedure LosingHotTracking; override;
    procedure LosingSelection; override;
    procedure LosingVisibility; override;
    procedure ReleaseSelectionGroup;
    procedure SetStateImageIndex(const Value: TCommonImageIndexInteger); virtual;
    procedure SetStateImageIndexes(Column: Integer; Value: TCommonImageIndexInteger); virtual; abstract;
    property SelectionGroup: TEasySelectionGroupList read FSelectionGroup write SetSelectionGroup;
    procedure SetGroupKey(FocusedColumn: Integer; Value: LongWord); virtual;
  public
    constructor Create(ACollection: TEasyCollection); override;
    destructor Destroy; override;
    function EditAreaHitPt(ViewportPoint: TPoint): Boolean; override;
    function HitTestAt(ViewportPoint: TPoint; var HitInfo: TEasyItemHitTestInfoSet): Boolean;
    function SelectionHit(SelectViewportRect: TRect; SelectType: TEasySelectHitType): Boolean; override;
    function SelectionHitPt(ViewportPoint: TPoint; SelectType: TEasySelectHitType): Boolean; override;
    procedure Edit(Column: TEasyColumn = nil);
    procedure Invalidate(ImmediateUpdate: Boolean); override;
    procedure ItemRectArray(Column: TEasyColumn; ACanvas: TCanvas; var RectArray: TEasyRectArrayObject);
    procedure LoadFromStream(S: TStream; var Version: Integer); override;
    procedure MakeVisible(Position: TEasyMakeVisiblePos); override;
    procedure Paint(ACanvas: TCanvas; ViewportClipRect: TRect; Column: TEasyColumn; ForceSelectionRectDraw: Boolean); virtual;
    procedure SaveToStream(S: TStream; Version: Integer = EASYLISTVIEW_STREAM_VERSION); override;
    property Alignment;
    property Bold;
    property Border;
    property BorderColor;
    property Caption;
    property CaptionIndent;
    property Captions;
    property Checked;
    property CheckFlat;
    property CheckHovering;
    property CheckIndent;
    property CheckPending;
    property CheckSize;
    property CheckType;
    property ColumnPos: Integer read GetColumnPos;
    property Cut;
    property Destroying;
    property DetailCount;
    property Details;
    property Enabled;
    property Focused;
    property GroupKey[FocusedColumn: Integer]: LongWord read GetGroupKey write SetGroupKey;
    property Hilighted;
    property ImageIndent;
    property ImageIndex;
    property ImageIndexes;
    property ImageList;
    property ImageOverlayIndex;
    property ImageOverlayIndexes;
    property Initialized;
    property OwnerGroup: TEasyGroup read GetOwnerGroup;
    property OwnerItems: TEasyItems read GetOwnerItems;
    property OwnsPaintInfo;
    property PaintInfo: TEasyPaintInfoItem read GetPaintInfo write SetPaintInfo;
    property RowPos: Integer read GetRowPos;
    property Selected;
    property State;
    property StateImageIndex: TCommonImageIndexInteger read GetStateImageIndex write SetStateImageIndex default -1;
    property StateImageIndexes[Column: Integer]: TCommonImageIndexInteger read GetStateImageIndexes write SetStateImageIndexes;
    property StateImageList[Column: Integer]: TCustomImageList read GetStateImageList;
    property VAlignment;
    property View: TEasyViewItem read GetView;
    property ViewClass: TEasyViewItemClass read GetViewClass;
    property Visible;
    property VisibleIndexInGroup: Integer read FVisibleIndexInGroup;
  published
  end;

  // **************************************************************************
  // TEasyItemInterfaced
  //    Uses interfaced based data extraction to extract the data from a data
  // source.  The data source can implement any of the following
  //   IEasyCaptions          // Returns Captions for the control
  //   IEasyCaptionsEditable  // Sets Captions in the data from the Control
  //   IEasyImages            // Returns Images for the control
  //   IEasyImagesEditable    // Sets Images in the data from the Control
  //   IEasyThumbnail         // Returns Thumbnail for the control
  //   IEasyThumbnailEditable // Sets Thumbnail in the data from the Control
  //   IEasyChecks            // Sets/Unsets the Checkbox State for the control
  //   IEasyNotifier          // Returns an Inteterface to allow data to notify Control of changes in the data
  //   IEasyCompareData       // Allows sorting of the data set
  // **************************************************************************
  TEasyItemInterfaced = class(TEasyItem)
  private
    function GetCommonImageIndex(Column: Integer; Kind: TEasyImageKind): TCommonImageIndexInteger;
    procedure SetCommonImageIndex(Column: Integer; Kind: TEasyImageKind; Value: TCommonImageIndexInteger);
  protected
    function GetCaptions(Column: Integer): Widestring; override;
    function GetChecked: Boolean; override;
    function GetDetailCount: Integer; override;
    function GetDetails(Line: Integer): Integer; override;
    function GetGroupKey(FocusedColumn: Integer): LongWord; override;
    function GetImageIndexes(Column: Integer): TCommonImageIndexInteger; override;
    function GetImageList(Column: Integer; IconSize: TEasyImageSize): TCustomImageList; override;
    function GetImageOverlayIndexes(Column: Integer): TCommonImageIndexInteger; override;
    function GetStateImageIndexes(Column: Integer): TCommonImageIndexInteger; override;
    function GetStateImageList(Column: Integer): TCustomImageList; override;
    procedure ImageDraw(Column: TEasyColumn; ACanvas: TCanvas; const RectArray: TEasyRectArrayObject; AlphaBlender: TEasyAlphaBlender); override;
    procedure ImageDrawGetSize(Column: TEasyColumn; var ImageW, ImageH: Integer); override;
    procedure ImageDrawIsCustom(Column: TEasyColumn; var IsCustom: Boolean); override;
    procedure SetCaptions(Column: Integer; Value: Widestring); override;
    procedure SetChecked(Value: Boolean); override;
    procedure SetDetailCount(Value: Integer); override;
    procedure SetDetails(Line: Integer; Value: Integer); override;
    procedure SetGroupKey(FocusedColumn: Integer; Value: LongWord); override;
    procedure SetImageIndexes(Column: Integer; Value: TCommonImageIndexInteger); override;
    procedure SetImageOverlayIndexes(Column: Integer; Value: TCommonImageIndexInteger); override;
    procedure SetStateImageIndexes(Column: Integer; Value: TCommonImageIndexInteger); override;
    procedure ThumbnailDraw(ACanvas: TCanvas; ARect: TRect; AlphaBlender: TEasyAlphaBlender; var DoDefault: Boolean); override;
  public
    function ExtractObject: TObject;
    property DataInf;
  end;

  // **************************************************************************
  // TEasyItemVirtual
  //    Calls back through the Controls Events for the data to display
  // **************************************************************************
  TEasyItemVirtual = class(TEasyItem)
  protected
    function GetCaptions(Column: Integer): Widestring; override;
    function GetDetailCount: Integer; override;
    function GetDetails(Line: Integer): Integer; override;
    function GetGroupKey(FocusedColumn: Integer): LongWord; override;
    function GetImageIndexes(Column: Integer): TCommonImageIndexInteger; override;
    function GetImageList(Column: Integer; IconSize: TEasyImageSize): TCustomImageList; override;
    function GetImageOverlayIndexes(Column: Integer): TCommonImageIndexInteger; override;
    function GetStateImageIndexes(Column: Integer): TCommonImageIndexInteger; override;
    function GetStateImageList(Column: Integer): TCustomImageList; override;
    procedure ImageDraw(Column: TEasyColumn; ACanvas: TCanvas; const RectArray: TEasyRectArrayObject; AlphaBlender: TEasyAlphaBlender); override;
    procedure ImageDrawGetSize(Column: TEasyColumn; var ImageW: Integer; var ImageH: Integer); override;
    procedure ImageDrawIsCustom(Column: TEasyColumn; var IsCustom: Boolean); override;
    procedure SetCaptions(Column: Integer; Value: Widestring); override;
    procedure SetDetailCount(Value: Integer); override;
    procedure SetDetails(Line: Integer; Value: Integer); override;
    procedure SetGroupKey(FocusedColumn: Integer; Value: LongWord); override;
    procedure SetImageIndexes(Column: Integer; Value: TCommonImageIndexInteger); override;
    procedure SetImageOverlayIndexes(Column: Integer; Value: TCommonImageIndexInteger); override;
    procedure SetStateImageIndexes(Column: Integer; Value: TCommonImageIndexInteger); override;
    procedure ThumbnailDraw(ACanvas: TCanvas; ARect: TRect; AlphaBlender: TEasyAlphaBlender; var DoDefault: Boolean); override;
  public
  end;

  // **************************************************************************
  // TEasyItemStored
  //    Stores the data local to the Item instance
  // **************************************************************************
  TEasyItemStored = class(TEasyItem)
  private
    FDataHelper: TEasyItemDynamicDataHelper;
  protected
    function GetCaptions(Column: Integer): Widestring; override;
    function GetDetailCount: Integer; override;
    function GetDetails(Line: Integer): Integer; override;
    function GetGroupKey(FocusedColumn: Integer): LongWord; override;
    function GetImageIndexes(Column: Integer): TCommonImageIndexInteger; override;
    function GetImageList(Column: Integer; IconSize: TEasyImageSize): TCustomImageList; override;
    function GetImageOverlayIndexes(Column: Integer): TCommonImageIndexInteger; override;
    function GetStateImageIndexes(Column: Integer): TCommonImageIndexInteger; override;
    function GetStateImageList(Column: Integer): TCustomImageList; override;
    procedure ImageDraw(Column: TEasyColumn; ACanvas: TCanvas; const RectArray: TEasyRectArrayObject; AlphaBlender: TEasyAlphaBlender); override;
    procedure ImageDrawGetSize(Column: TEasyColumn; var ImageW: Integer; var ImageH: Integer); override;
    procedure ImageDrawIsCustom(Column: TEasyColumn; var IsCustom: Boolean); override;
    procedure SetCaptions(Column: Integer; Value: Widestring); override;
    procedure SetDetailCount(Value: Integer); override;
    procedure SetDetails(Column: Integer; Value: Integer); override;
    procedure SetGroupKey(FocusedColumn: Integer; Value: LongWord); override;
    procedure SetImageIndexes(Column: Integer; Value: TCommonImageIndexInteger); override;
    procedure SetImageOverlayIndexes(Column: Integer; Value: TCommonImageIndexInteger); override;
    procedure SetStateImageIndexes(Column: Integer; Value: TCommonImageIndexInteger); override;
    procedure ThumbnailDraw(ACanvas: TCanvas; ARect: TRect; AlphaBlender: TEasyAlphaBlender; var DoDefault: Boolean); override;
    property DataHelper: TEasyItemDynamicDataHelper read FDataHelper write FDataHelper;
  public
    constructor Create(ACollection: TEasyCollection); override;
    destructor Destroy; override;
    procedure LoadFromStream(S: TStream; var Version: Integer); override;
    procedure SaveToStream(S: TStream; Version: Integer = EASYLISTVIEW_STREAM_VERSION); override;
  published
    property Bold;
    property Caption;
    property Checked;
    property Enabled;
    property Ghosted;
    property ImageIndex;
    property ImageOverlayIndex;
    property Selected;
    property StateImageIndex;
    property Tag;
    property Visible;
  end;
  TEasyItemStoredClass = class of TEasyItemStored;

  // **************************************************************************
  // TEasyCollection
  //    Basis for Collection (TEasyItems, TEasyGroups, TEasyColumns)
  // **************************************************************************
  TEasyCollection = class(TEasyOwnedPersistent)
  private
    FHideFromDFM: Boolean;
    FList: TList;
    FReIndexCount: Integer;
    FTag: Integer;
    FVisibleList: TList;
    function GetCount: Integer;
    function GetItem(Index: Integer): TEasyCollectionItem;
    function GetOwnerListview: TCustomEasyListview;
    function GetReIndexDisable: Boolean;
    function GetVisibleCount: Integer;
    procedure SetItem(Index: Integer; Value: TEasyCollectionItem);
    procedure SetReIndexDisable(const Value: Boolean);
  protected
    FItemClass: TEasyCollectionItemClass;
    function DoStore: Boolean; dynamic;
    function GetOwner: TPersistent; override;
    procedure DefineProperties(Filer: TFiler); override;
    procedure DoItemAdd(Item: TEasyCollectionItem; Index: Integer); virtual;
    procedure DoStructureChange; virtual;
    property List: TList read FList write FList;
    property ReIndexCount: Integer read FReIndexCount write FReIndexCount;
    property VisibleList: TList read FVisibleList write FVisibleList;
  public
    constructor Create(AnOwner: TCustomEasyListview); override;
    destructor Destroy; override;

    function Add(Data: TObject = nil): TEasyCollectionItem;
    function FirstVisible: TEasyColumn; virtual;
    function LastVisible: TEasyColumn; virtual;
    function NextVisible(Column: TEasyColumn): TEasyColumn; virtual;
    function PrevVisible(Column: TEasyColumn): TEasyColumn; virtual;
    procedure BeginUpdate(ReIndex: Boolean); virtual;
    procedure Clear(FreeItems: Boolean = True); virtual;
    procedure Delete(Index: Integer);
    procedure EndUpdate(Invalidate: Boolean = True); virtual;
    function Insert(Index: Integer; Data: TObject = nil): TEasyCollectionItem;
    procedure Exchange(Index1, Index2: Integer);
    procedure MakeAllVisible; virtual;
    procedure ReadItems(Stream: TStream); virtual;
    procedure ReIndexItems;
    procedure WriteItems(Stream: TStream); virtual;
    property Count: Integer read GetCount;
    property HideFromDFM: Boolean read FHideFromDFM write FHideFromDFM default False;
    property ItemClass: TEasyCollectionItemClass read FItemClass;
    property Items[Index: Integer]: TEasyCollectionItem read GetItem write SetItem; default;
    property OwnerListview: TCustomEasyListview read GetOwnerListview;
    property ReIndexDisable: Boolean read GetReIndexDisable write SetReIndexDisable;
    property VisibleCount: Integer read GetVisibleCount;
  published
    property Tag: Integer read FTag write FTag default 0;
  end;
  TEasyCollectionClass = class of TEasyCollection;

  // **************************************************************************
  // TEasyViewItem
  //    Basis for the UI (drawing, and mouse interaction) for a TEasyItem
  // **************************************************************************
  TEasyViewItem = class(TEasyOwnedPersistentGroupItem)
  private
    procedure PaintAlphaBlendedRoundRect(ACanvas: TCanvas; AlphaColor: TColor;
      GradientBottom: TColor; GradientTop: TColor;
      var LocalSelWindowClippedRect: TRect; var Rgn: HRGN);
    procedure PaintAlphaBlendedSelection(ACanvas: TCanvas; AlphaColor: TColor;
      HeaderClippedWindowRect: TRect; LocalSelRect: TRect);
    procedure PaintAlphaBlendedGradientFill(ACanvas: TCanvas;
      GradientBottom: TColor; GradientTop: TColor; LocalSelRect: TRect;
      LocalSelWindowClippedRect: TRect; LocalSelClippedRect: TRect);
    procedure PaintNonAlphaBlendedSelection(ACanvas: TCanvas;
      LocalSelRect: TRect);
  protected
    function AllowDrag(Item: TEasyItem; ViewportPoint: TPoint): Boolean; virtual;
    function PaintStateImage: Boolean; virtual;
    function ValidateColumnIndex(Column: TEasyColumn): Integer;
  public
    procedure AfterSelRectCalc(Item: TEasyItem; Column: TEasyColumn; const Caption: WideString; var LocalSelRect: TRect); virtual;
    procedure CalculateTextRect(Item: TEasyItem; Column: TEasyColumn; var TextR: TRect; ACanvas: TControlCanvas);
    function EditAreaHitPt(Item: TEasyItem; ViewportPoint: TPoint): Boolean; virtual;
    function ExpandIconR(Item: TEasyItem; RectArray: TEasyRectArrayObject; SelectType: TEasySelectHitType): TRect; virtual;
    function ExpandTextR(Item: TEasyItem; RectArray: TEasyRectArrayObject; SelectType: TEasySelectHitType): TRect; virtual;
    function FullRowSelect: Boolean; virtual;
    function GetImageList(Column: TEasyColumn; Item: TEasyItem; Image: TEasyImageKind): TCustomImageList; virtual;
    procedure GetImageSize(Item: TEasyItem; Column: TEasyColumn; var ImageW, ImageH: Integer; Image: TEasyImageKind); virtual;
    function GetStateImageList(Column: TEasyColumn; Item: TEasyItem): TCustomImageList; virtual;
    function ItemRect(Item: TEasyItem; Column: TEasyColumn; RectType: TEasyCellRectType): TRect; virtual;
    procedure ItemRectArray(Item: TEasyItem; Column: TEasyColumn; ACanvas: TCanvas; const Caption: WideString; var RectArray: TEasyRectArrayObject); virtual;
    procedure LoadTextFont(Item: TEasyItem; Position: Integer; ACanvas: TCanvas; Hilightable: Boolean); virtual;
    function OverlappedFocus: Boolean; virtual;
    procedure Paint(Item: TEasyItem; Column: TEasyColumn; ACanvas: TCanvas; ViewportClipRect: TRect; ForceSelectionRectDraw: Boolean); virtual;
    procedure PaintAfter(Item: TEasyItem; Column: TEasyColumn; const Caption: WideString; ACanvas: TCanvas; RectArray: TEasyRectArrayObject); virtual;
    procedure PaintBefore(Item: TEasyItem; Column: TEasyColumn; const Caption: WideString; ACanvas: TCanvas; RectArray: TEasyRectArrayObject; var Handled: Boolean); virtual;
    procedure PaintCheckBox(Item: TEasyItem; Column: TEasyColumn; RectArray: TEasyRectArrayObject; ACanvas: TCanvas); virtual;
    procedure PaintFocusRect(Item: TEasyItem; Column: TEasyColumn; const Caption: WideString; RectArray: TEasyRectArrayObject; ACanvas: TCanvas); virtual;
    procedure PaintImage(Item: TEasyItem; Column: TEasyColumn; const Caption: WideString; RectArray: TEasyRectArrayObject; ImageSize: TEasyImageSize; ACanvas: TCanvas); virtual;
    function PaintImageSize: TEasyImageSize; virtual;
    procedure PaintSelectionRect(Item: TEasyItem; Column: TEasyColumn; const Caption: WideString; RectArray: TEasyRectArrayObject; ACanvas: TCanvas; ViewportClipRect: TRect; ForceSelectionRectDraw: Boolean); virtual;
    procedure PaintText(Item: TEasyItem; Column: TEasyColumn; const Caption: WideString; RectArray: TEasyRectArrayObject; ACanvas: TCanvas; LinesToDraw: Integer); virtual;
    function PaintTextAlignment(Item: TEasyItem; Column: TEasyColumn): TAlignment; virtual;
    function PaintTextLineCount(Item: TEasyItem; Column: TEasyColumn): Integer; virtual;
    function PaintTextVAlignment(Item: TEasyItem; Column: TEasyColumn): TCommonVAlignment; virtual;
    function PtInRect(Item: TEasyItem; Column: TEasyColumn; Pt: TPoint): Integer; virtual;
    procedure ReSizeRectArray(var RectArray: TEasyRectArrayObjectArray); virtual;
    function SelectionHit(Item: TEasyItem; SelectViewportRect: TRect; SelectType: TEasySelectHitType): Boolean; virtual;
    function SelectionHitPt(Item: TEasyItem; ViewportPoint: TPoint; SelectType: TEasySelectHitType): Boolean; virtual;
    procedure TestAndClipImage(ACanvas: TCanvas; RectArray: TEasyRectArrayObject; var Rgn: HRgn);
    procedure TestAndUnClipImage(ACanvas: TCanvas; RectArray: TEasyRectArrayObject; Rgn: HRgn);
  end;

  // **************************************************************************
  // TEasyViewIconItem
  //    Basis for the UI (drawing, and mouse interaction) for a TEasyItem
  // **************************************************************************
  TEasyViewIconItem = class(TEasyViewItem)
  public
    function ExpandIconR(Item: TEasyItem; RectArray: TEasyRectArrayObject; SelectType: TEasySelectHitType): TRect; override;
    function OverlappedFocus: Boolean; override;
    function PaintImageSize: TEasyImageSize; override;
    function PaintTextLineCount(Item: TEasyItem; Column: TEasyColumn): Integer; override;
    procedure AfterSelRectCalc(Item: TEasyItem; Column: TEasyColumn; const Caption: WideString; var LocalSelRect: TRect); override;
    procedure ItemRectArray(Item: TEasyItem; Column: TEasyColumn; ACanvas: TCanvas; const Caption: WideString; var RectArray: TEasyRectArrayObject); override;
    procedure PaintBefore(Item: TEasyItem; Column: TEasyColumn; const Caption: WideString; ACanvas: TCanvas; RectArray: TEasyRectArrayObject; var Handled: Boolean); override;
  end;

  // **************************************************************************
  // TEasyViewSmallIconItem
  //    Basis for the UI (drawing, and mouse interaction) for a TEasyItem
  // **************************************************************************
  TEasyViewSmallIconItem = class(TEasyViewItem)
  public
    function CalculateDisplayRect(Item: TEasyItem; Column: TEasyColumn): TRect; virtual;
    function ExpandIconR(Item: TEasyItem; RectArray: TEasyRectArrayObject; SelectType: TEasySelectHitType): TRect; override;
    function ExpandTextR(Item: TEasyItem; RectArray: TEasyRectArrayObject; SelectType: TEasySelectHitType): TRect; override;
    procedure ItemRectArray(Item: TEasyItem; Column: TEasyColumn; ACanvas: TCanvas; const Caption: WideString; var RectArray: TEasyRectArrayObject); override;
    procedure PaintBefore(Item: TEasyItem; Column: TEasyColumn; const Caption: WideString; ACanvas: TCanvas; RectArray: TEasyRectArrayObject; var Handled: Boolean); override;
    function PaintTextAlignment(Item: TEasyItem; Column: TEasyColumn): TAlignment; override;
    function PaintTextLineCount(Item: TEasyItem; Column: TEasyColumn): Integer; override;
    function PaintTextVAlignment(Item: TEasyItem; Column: TEasyColumn): TCommonVAlignment; override;
  end;

  // **************************************************************************
  // TEasyViewListItem
  //    Basis for the UI (drawing, and mouse interaction) for a TEasyItem
  // **************************************************************************
  TEasyViewListItem = class(TEasyViewSmallIconItem)
  end;

  // **************************************************************************
  // TEasyViewReportItem
  //    Basis for the UI (drawing, and mouse interaction) for a TEasyItem
  // **************************************************************************
  TEasyViewReportItem = class(TEasyViewSmallIconItem)
  protected
    function AllowDrag(Item: TEasyItem; ViewportPoint: TPoint): Boolean; override;
    function PaintStateImage: Boolean; override;
  public
    function CalculateDisplayRect(Item: TEasyItem; Column: TEasyColumn): TRect; override;
    function ExpandTextR(Item: TEasyItem; RectArray: TEasyRectArrayObject; SelectType: TEasySelectHitType): TRect; override;
    function FullRowSelect: Boolean; override;
    function SelectionHit(Item: TEasyItem; SelectViewportRect: TRect; SelectType: TEasySelectHitType): Boolean; override;
    function SelectionHitPt(Item: TEasyItem; ViewportPoint: TPoint; SelectType: TEasySelectHitType): Boolean; override;
  end;

  // **************************************************************************
  // TEasyViewGridItem
  //    Basis for the UI (drawing, and mouse interaction) for a TEasyItem
  // **************************************************************************
  TEasyViewGridItem = class(TEasyViewSmallIconItem)
  end;

  // **************************************************************************
  // TEasyViewThumbnailItem
  //    Basis for the UI (drawing, and mouse interaction) for a TEasyItem
  // **************************************************************************
  TEasyViewThumbnailItem = class(TEasyViewItem)
  public
    function ExpandTextR(Item: TEasyItem; RectArray: TEasyRectArrayObject; SelectType: TEasySelectHitType): TRect; override;
    function GetImageList(Column: TEasyColumn; Item: TEasyItem; Image: TEasyImageKind): TCustomImageList; override;
    function PaintImageSize: TEasyImageSize; override;
    procedure AfterSelRectCalc(Item: TEasyItem; Column: TEasyColumn; const Caption: WideString; var LocalSelRect: TRect); override;
    procedure ItemRectArray(Item: TEasyItem; Column: TEasyColumn; ACanvas: TCanvas; const Caption: WideString; var RectArray: TEasyRectArrayObject); override;
    function OverlappedFocus: Boolean; override;
    procedure PaintAfter(Item: TEasyItem; Column: TEasyColumn; const Caption: WideString; ACanvas: TCanvas; RectArray: TEasyRectArrayObject); override;
    procedure PaintBefore(Item: TEasyItem; Column: TEasyColumn; const Caption: WideString; ACanvas: TCanvas; RectArray: TEasyRectArrayObject; var Handled: Boolean); override;
    function PaintTextLineCount(Item: TEasyItem; Column: TEasyColumn): Integer; override;
    function PaintTextVAlignment(Item: TEasyItem; Column: TEasyColumn): TCommonVAlignment; override;
    function SelectionHit(Item: TEasyItem; SelectViewportRect: TRect; SelectType: TEasySelectHitType): Boolean; override;
    function SelectionHitPt(Item: TEasyItem; ViewportPoint: TPoint; SelectType: TEasySelectHitType): Boolean; override;
  end;

  // **************************************************************************
  // TEasyViewTileItem
  //    Basis for the UI (drawing, and mouse interaction) for a TEasyItem
  // **************************************************************************
  TEasyViewTileItem = class(TEasyViewItem)
  public
    function ExpandIconR(Item: TEasyItem; RectArray: TEasyRectArrayObject; SelectType: TEasySelectHitType): TRect; override;
    function ExpandTextR(Item: TEasyItem; RectArray: TEasyRectArrayObject; SelectType: TEasySelectHitType): TRect; override;
    function GetImageList(Column: TEasyColumn; Item: TEasyItem; Image: TEasyImageKind): TCustomImageList; override;
    function PaintImageSize: TEasyImageSize; override;
    function PaintTextAlignment(Item: TEasyItem; Column: TEasyColumn): TAlignment; override;
    procedure AfterSelRectCalc(Item: TEasyItem; Column: TEasyColumn; const Caption: WideString; var LocalSelRect: TRect); override;
    procedure ItemRectArray(Item: TEasyItem; Column: TEasyColumn; ACanvas: TCanvas; const Caption: WideString; var RectArray: TEasyRectArrayObject); override;
    procedure PaintBefore(Item: TEasyItem; Column: TEasyColumn; const Caption: WideString; ACanvas: TCanvas; RectArray: TEasyRectArrayObject; var Handled: Boolean); override;
    procedure PaintText(Item: TEasyItem; Column: TEasyColumn; const Caption: WideString; RectArray: TEasyRectArrayObject; ACanvas: TCanvas; LinesToDraw: Integer); override;
  end;

  // **************************************************************************
  // TEasyViewFilmStripItem
  //    Basis for the UI (drawing, and mouse interaction) for a TEasyItem
  // **************************************************************************
  TEasyViewFilmStripItem = class(TEasyViewThumbnailItem)
  end;

  // **************************************************************************
  // TEasyViewTaskBandItem
  //    Basis for the UI (drawing, and mouse interaction) for a TEasyItem in the
  // Taskband control
  // **************************************************************************
  TEasyViewTaskBandItem = class(TEasyViewSmallIconItem)
  public
  end;

  // **************************************************************************
  // TEasyItems
  //   Collection that contains all the Items within a Group in the control
  // **************************************************************************
  TEasyItems = class(TEasyCollection)
  private
    FOwnerGroup: TEasyGroup;
    function GetItem(Index: Integer): TEasyItem;
    procedure SetItem(Index: Integer; Value: TEasyItem);
  protected
    procedure DoStructureChange; override;
  public
    constructor Create(AnOwner: TCustomEasyListview; AnOwnerGroup: TEasyGroup); reintroduce; virtual;
    destructor Destroy; override;

    function Add(Data: TObject = nil): TEasyItem;
    function AddInterfaced(const DataInf: IUnknown; Data: TObject = nil): TEasyItemInterfaced;
    function AddVirtual(Data: TObject = nil): TEasyItemVirtual;
    function AddCustom(CustomItem: TEasyItemClass; Data: TObject = nil): TEasyItem;
    procedure Clear(FreeItems: Boolean = True); override;
    procedure Delete(Index: Integer); reintroduce;
    procedure Exchange(Index1, Index2: Integer); reintroduce;
    function Insert(Index: Integer; Data: TObject = nil): TEasyItem;
    function InsertCustom(Index: Integer; CustomItem: TEasyItemClass; Data: TObject = nil): TEasyItem;
    function InsertInterfaced(Index: Integer; const DataInf: IUnknown; Data: TObject = nil): TEasyItemInterfaced;
    function InsertVirtual(Index: Integer; Data: TObject = nil): TEasyItemVirtual;
    property Items[Index: Integer]: TEasyItem read GetItem write SetItem; default;
    property OwnerGroup: TEasyGroup read FOwnerGroup;
  end;

  // **************************************************************************
  // TEasyGlobalItems
  //   Convenience TListview migration items
  // **************************************************************************
  TEasyGlobalItems = class
  private
    FOwner: TCustomEasyListview;
    function GetCount: Integer;
    function GetItem(Index: Integer): TEasyItem;
    function GetItemInternal(Index: Integer): TEasyItem;
    function GetLastGroup: TEasyGroup;
    procedure EnsureFirstGroup;
    procedure IndexError(Index: Integer);
    procedure SetItem(Index: Integer; const Value: TEasyItem);
    procedure SetReIndexDisable(const Value: Boolean);
  public
    constructor Create(AnOwner: TCustomEasyListview);

    function Add(Data: TObject = nil): TEasyItem;
    function AddCustom(CustomItem: TEasyItemClass; Data: TObject = nil): TEasyItem;
    function AddInterfaced(const DataInf: IUnknown; Data: TObject = nil): TEasyItemInterfaced;
    function AddVirtual(Data: TObject = nil): TEasyItemVirtual;
    function IndexOf(Item: TEasyItem): Integer;
    function Insert(Index: Integer; Data: TObject = nil): TEasyItem;
    function InsertCustom(Index: Integer; CustomItem: TEasyItemClass; Data: TObject = nil): TEasyItem;
    function InsertInterfaced(Index: Integer; const DataInf: IUnknown; Data: TObject = nil): TEasyItemInterfaced;
    function InsertVirtual(Index: Integer; Data: TObject = nil): TEasyItemVirtual;
    procedure Clear;
    procedure Delete(Index: Integer; ReIndex: Boolean = True);
    procedure Exchange(Index1, Index2: Integer);
    property Count: Integer read GetCount;
    property Items[Index: Integer]: TEasyItem read GetItem write SetItem; default;
    property OwnerListview: TCustomEasyListview read FOwner;
    property ReIndexDisable: Boolean write SetReIndexDisable;
  end;

  // **************************************************************************
  // TEasyGlobalImageManager
  //   Manages images and bitmaps that are used in the EasyControl such as
  // expand "+" buttons, etc.
  // **************************************************************************
  TEasyGlobalImageManager = class(TEasyOwnedPersistent)
  private
    FGroupExpandButton: TBitmap;
    FGroupCollapseButton: TBitmap;
    FColumnSortUp: TBitmap;
    FColumnSortDown: TBitmap;
    function GetColumnSortDown: TBitmap;
    function GetColumnSortUp: TBitmap;
    function GetGroupCollapseImage: TBitmap;
    function GetGroupExpandImage: TBitmap;
    procedure SetColumnSortDown(Value: TBitmap);
    procedure SetColumnSortUp(Value: TBitmap);
    procedure SetGroupCollapseImage(const Value: TBitmap);
    procedure SetGroupExpandImage(const Value: TBitmap);
  protected
    procedure MakeTransparent(Bits: TBitmap; TransparentColor: TColor);
  public
    constructor Create(AnOwner: TCustomEasyListview); override;
    destructor Destroy; override;
  published
    property GroupExpandButton: TBitmap read GetGroupExpandImage write SetGroupExpandImage;
    property GroupCollapseButton: TBitmap read GetGroupCollapseImage write SetGroupCollapseImage;
    property ColumnSortUp: TBitmap read GetColumnSortUp write SetColumnSortUp;
    property ColumnSortDown: TBitmap read GetColumnSortDown write SetColumnSortDown;
  end;

  // **************************************************************************
  // TEasyGridGroup
  //   Builds the Grid for the Group, by default the grid fits in the OwnerWindow
  // horizontally and wraps down
  // **************************************************************************
  TEasyGridGroup = class(TEasyOwnedPersistent)
  private
    FColumnCount: Integer;        // Number of Columns in the grid
    FLayout: TEasyGridLayout;
    FOwnerGroup: TEasyGroup;      // The group that the grid is attached to
    FRowCount: Integer;           // The number of Rows in the group
    function GetOwnerGroups: TEasyGroups;
    
  protected
    function AdjacentItem(Item: TEasyItem; Direction: TEasyAdjacentCellDir): TEasyItem; virtual;
    function GetCellSize: TEasyCellSize; virtual; abstract;
    function GetMaxColumns(Group: TEasyGroup; WindowWidth: Integer): Integer; virtual;
    function LastItemInNColumn(Group: TEasyGroup; N: Integer): TEasyItem;
    function NextVisibleGroupWithNItems(StartGroup: TEasyGroup; N: Integer): TEasyGroup;
    function PrevVisibleGroupWithNItems(StartGroup: TEasyGroup; N: Integer): TEasyGroup;
    function SearchForHitRight(ColumnPos: Integer; Pt: TPoint): TEasyItem;
    function StaticTopItemMargin: Integer; virtual;
    function StaticTopMargin: Integer; virtual;
    procedure AutoSizeCells; virtual;
    procedure FindLongestCaption(Column: TEasyColumn; var Item: TEasyItem);
    procedure SetCellSize(Value: TEasyCellSize); virtual; abstract;
  public
    constructor Create(AnOwner: TCustomEasyListview; AnOwnerGroup: TEasyGroup); reintroduce; virtual;
    destructor Destroy; override;
    procedure FindInsertPosition(ViewportPoint: TPoint; var Group: TEasyGroup; var Index: Integer); virtual;
    procedure Rebuild(PrevGroup: TEasyGroup; var NextVisibleItemIndex: Integer); virtual;

    property CellSize: TEasyCellSize read GetCellSize write SetCellSize;
    property ColumnCount: Integer read FColumnCount;
    property Layout: TEasyGridLayout read FLayout;
    property OwnerGroup: TEasyGroup read FOwnerGroup;
    property OwnerGroups: TEasyGroups read GetOwnerGroups;
    property RowCount: Integer read FRowCount;
  end;

  // **************************************************************************
  // TEasyGridIconGroup
  //   Builds the Large Icon Grid for the Group
  // **************************************************************************
  TEasyGridIconGroup = class(TEasyGridGroup)
  protected
    function GetCellSize: TEasyCellSize; override;
    procedure AutoSizeCells; override;
    procedure SetCellSize(Value: TEasyCellSize); override;
  public
  end;

  // **************************************************************************
  // TEasyGridSmallIconGroup
  //   Builds the Small Icon Grid for the Group
  // **************************************************************************
  TEasyGridSmallIconGroup = class(TEasyGridGroup)
  protected
    function GetCellSize: TEasyCellSize; override;
    procedure SetCellSize(Value: TEasyCellSize); override;
  public
  end;

  // **************************************************************************
  // TEasyGridListGroup
  //   Builds the List Grid for the Group
  // **************************************************************************
  TEasyGridListGroup = class(TEasyGridGroup)
  protected
    function AdjacentItem(Item: TEasyItem; Direction: TEasyAdjacentCellDir): TEasyItem; override;
    function GetCellSize: TEasyCellSize; override;
    procedure SetCellSize(Value: TEasyCellSize); override;
  public
    constructor Create(AnOwner: TCustomEasyListview; AnOwnerGroup: TEasyGroup); override;
    procedure FindInsertPosition(ViewportPoint: TPoint; var Group: TEasyGroup; var Index: Integer); override;
    procedure Rebuild(PrevGroup: TEasyGroup; var NextVisibleItemIndex: Integer); override;
  end;

  // **************************************************************************
  // TEasyGridReportGroup
  //   Builds the Report Grid for the Group
  // **************************************************************************
  TEasyGridReportGroup = class(TEasyGridGroup)
  protected
    function AdjacentItem(Item: TEasyItem; Direction: TEasyAdjacentCellDir): TEasyItem; override;
    function GetCellSize: TEasyCellSize; override;
    procedure SetCellSize(Value: TEasyCellSize); override;
  public
    constructor Create(AnOwner: TCustomEasyListview; AnOwnerGroup: TEasyGroup); override;
    procedure FindInsertPosition(ViewportPoint: TPoint; var Group: TEasyGroup; var Index: Integer); override;
    procedure Rebuild(PrevGroup: TEasyGroup; var NextVisibleItemIndex: Integer); override;
  end;

  // **************************************************************************
  // TEasyGridThumbnailGroup
  //   Builds the Thumbnail Grid for the Group
  // **************************************************************************
  TEasyGridThumbnailGroup = class(TEasyGridGroup)
  protected
    function GetCellSize: TEasyCellSize; override;
    procedure AutoSizeCells; override;
    procedure SetCellSize(Value: TEasyCellSize); override;
  public
  end;

  // **************************************************************************
  // TEasyGridTileGroup
  //   Builds the Tile Grid for the Group
  // **************************************************************************
  TEasyGridTileGroup = class(TEasyGridGroup)
  protected
    function GetCellSize: TEasyCellSize; override;
    procedure SetCellSize(Value: TEasyCellSize); override;
  public
    procedure FindInsertPosition(ViewportPoint: TPoint; var Group: TEasyGroup; var Index: Integer); override;
  end;

  // **************************************************************************
  // TEasyGridFilmStripGroup
  //   Builds the List Grid for the Group
  // **************************************************************************
  TEasyGridFilmStripGroup = class(TEasyGridListGroup)
  protected
    function GetCellSize: TEasyCellSize; override;
    procedure AutoSizeCells; override;
    procedure SetCellSize(Value: TEasyCellSize); override;
  end;

  // **************************************************************************
  // TEasyGridGridGroup
  //   Builds the Report Grid for the Group
  // **************************************************************************
  TEasyGridGridGroup = class(TEasyGridGroup)
  protected
    function GetCellSize: TEasyCellSize; override;
    procedure SetCellSize(Value: TEasyCellSize); override;
  public
    constructor Create(AnOwner: TCustomEasyListview; AnOwnerGroup: TEasyGroup); override;
    procedure Rebuild(PrevGroup: TEasyGroup; var NextVisibleItemIndex: Integer); override;
  end;

  TEasyGridSingleColumn = class(TEasyGridGroup)
  protected
    function GetCellSize: TEasyCellSize; override;
    function GetMaxColumns(Group: TEasyGroup; WindowWidth: Integer): Integer; override;
    procedure AutoSizeCells; override;
    procedure SetCellSize(Value: TEasyCellSize); override;
  public
    procedure FindInsertPosition(ViewportPoint: TPoint; var Group: TEasyGroup; var Index: Integer); override;
  end;

  // **************************************************************************
  // TGridTaskBandGroup
  //   Builds the Report Grid for the Group in the Taskband control
  // **************************************************************************
  TEasyGridTaskBandGroup = class(TEasyGridSingleColumn)
  private
    FCellSize: TEasyCellSize;
  protected
    function GetCellSize: TEasyCellSize; override;
    function StaticTopItemMargin: Integer; override;
    function StaticTopMargin: Integer; override;
    procedure SetCellSize(Value: TEasyCellSize); override;
    property CellSize: TEasyCellSize read FCellSize write FCellSize;
  public
    constructor Create(AnOwner: TCustomEasyListview; AnOwnerGroup: TEasyGroup); override;
    destructor Destroy; override;
  end;

  // **************************************************************************
  //  Grid for the TEasyTaskPanel component
  // **************************************************************************
  TEasyGridGroupTaskPanel = class(TEasyGridSingleColumn)
  private
    FCellSize: TEasyCellSize;
  protected
    function GetCellSize: TEasyCellSize; override;
    function StaticTopMargin: Integer; override;
    procedure SetCellSize(Value: TEasyCellSize); override;
  public
    constructor Create(AnOwner: TCustomEasyListview; AnOwnerGroup: TEasyGroup); override;
    destructor Destroy; override;
    procedure Rebuild(PrevGroup: TEasyGroup; var NextVisibleItemIndex: Integer); override;
  published
  end;

  // **************************************************************************
  // TEasyGroupBase
  //   Collection Item that represents a single group in the Listview
  // **************************************************************************
  TEasyGroup = class(TEasyCollectionItem)
  private
    FExpanded: Boolean;           // Is the group expanded/collapsed
    FGrid: TEasyGridGroup;        // The UI grid that defines the item positions
    FItems: TEasyItems;           // The List of Items (TEasyItems) in the group
    FKey: LongWord;
    FView: TEasyViewGroup;
    FVisibleItems: TList;
    function GetBandBlended: Boolean;
    function GetBandColor: TColor;
    function GetBandColorFade: TColor;
    function GetBandEnabled: Boolean;
    function GetBandFullWidth: Boolean;
    function GetBandIndent: Integer;
    function GetBandLength: Integer;
    function GetBandMargin: Integer;
    function GetBandRadius: Byte;
    function GetBandThickness: Integer;
    function GetClientRect: TRect;
    function GetExpandable: Boolean;
    function GetExpandImageIndent: Integer;
    function GetGrid: TEasyGridGroup;
    function GetGridClass: TEasyGridGroupClass;
    function GetItem(Index: Integer): TEasyItem;
    function GetItemCount: Integer;
    function GetMarginBottom: TEasyFooterMargin;
    function GetMarginLeft: TEasyMargin;
    function GetMarginRight: TEasyMargin;
    function GetMarginTop: TEasyHeaderMargin;
    function GetOwnerGroups: TEasyGroups;
    function GetOwnerListview: TCustomEasyListview;
    function GetPaintInfo: TEasyPaintInfoBaseGroup;
    function GetView: TEasyViewGroup;
    function GetViewClass: TEasyViewGroupClass;
    function GetVisibleCount: Integer;
    function GetVisibleItem(Index: Integer): TEasyItem;
    procedure SetBandBlended(Value: Boolean);
    procedure SetBandColor(Value: TColor);
    procedure SetBandColorFade(Value: TColor);
    procedure SetBandEnabled(Value: Boolean);
    procedure SetBandFullWidth(Value: Boolean);
    procedure SetBandIndent(Value: Integer);
    procedure SetBandLength(Value: Integer);
    procedure SetBandMargin(Value: Integer);
    procedure SetBandRadius(Value: Byte);
    procedure SetBandThickness(Value: Integer);
    procedure SetExpandable(Value: Boolean);
    procedure SetExpanded(Value: Boolean); virtual;
    procedure SetExpandImageIndent(Value: Integer);
    procedure SetItem(Index: Integer; Value: TEasyItem);
    procedure SetMarginBottom(Value: TEasyFooterMargin);
    procedure SetMarginLeft(Value: TEasyMargin);
    procedure SetMarginRight(Value: TEasyMargin);
    procedure SetMarginTop(Value: TEasyHeaderMargin);
    procedure SetPaintInfo(const Value: TEasyPaintInfoBaseGroup);
  protected
    function CanChangeBold(NewValue: Boolean): Boolean; override;
    function CanChangeCheck(NewValue: Boolean): Boolean; override;
    function CanChangeEnable(NewValue: Boolean): Boolean; override;
    function CanChangeFocus(NewValue: Boolean): Boolean; override;
    function CanChangeHotTracking(NewValue: Boolean): Boolean; override;
    function CanChangeSelection(NewValue: Boolean): Boolean; override;
    function CanChangeVisibility(NewValue: Boolean): Boolean; override;
    function DefaultImageList(ImageSize: TEasyImageSize): TCustomImageList; override;
    function GetDefaultGridClass: TEasyGridGroupClass; virtual;
    function GetDefaultViewClass: TEasyViewGroupClass; virtual;
    function LocalPaintInfo: TEasyPaintInfoBasic; override;
    procedure Freeing; override;
    procedure GainingBold; override;
    procedure GainingCheck; override;
    procedure GainingEnable; override;
    procedure GainingFocus; override;
    procedure GainingGhosted; override;
    procedure GainingHilight; override;
    procedure GainingHotTracking(MousePos: TPoint); override;
    procedure GainingSelection; override;
    procedure GainingVisibility; override;
    procedure Initialize; override;
    procedure LosingBold; override;
    procedure LosingCheck; override;
    procedure LosingEnable; override;
    procedure LosingFocus; override;
    procedure LosingGhosted; override;
    procedure LosingHilight; override;
    procedure LosingHotTracking; override;
    procedure LosingSelection; override;
    procedure LosingVisibility; override;
    property Alignment;
    property BandBlended: Boolean read GetBandBlended write SetBandBlended default True;
    property BandColor: TColor read GetBandColor write SetBandColor default clBlue;
    property BandColorFade: TColor read GetBandColorFade write SetBandColorFade default clWindow;
    property BandEnabled: Boolean read GetBandEnabled write SetBandEnabled default True;
    property BandFullWidth: Boolean read GetBandFullWidth write SetBandFullWidth default False;
    property BandIndent: Integer read GetBandIndent write SetBandIndent default 0;
    property BandLength: Integer read GetBandLength write SetBandLength default 300;
    property BandMargin: Integer read GetBandMargin write SetBandMargin default 0;
    property BandRadius: Byte read GetBandRadius write SetBandRadius default 4;
    property BandThickness: Integer read GetBandThickness write SetBandThickness default 3;
    property CaptionIndent;
    property CheckFlat;
    property CheckIndent;
    property CheckSize;
    property CheckType;
    property Expandable: Boolean read GetExpandable write SetExpandable default True;
    property ExpandImageIndent: Integer read GetExpandImageIndent write SetExpandImageIndent default 4;
    property ImageIndent;
    property Key: LongWord read FKey write FKey;
    property MarginBottom: TEasyFooterMargin read GetMarginBottom write SetMarginBottom;
    property MarginLeft: TEasyMargin read GetMarginLeft write SetMarginLeft;
    property MarginRight: TEasyMargin read GetMarginRight write SetMarginRight;
    property MarginTop: TEasyHeaderMargin read GetMarginTop write SetMarginTop;
    property OwnsPaintInfo;
    property PaintInfo: TEasyPaintInfoBaseGroup read GetPaintInfo write SetPaintInfo;
    property VAlignment default cvaCenter;
    property VisibleItem[Index: Integer]: TEasyItem read GetVisibleItem;
    property VisibleItems: TList read FVisibleItems write FVisibleItems;
  public
    constructor Create(ACollection: TEasyCollection); override;
    destructor Destroy; override;

    function BoundsRectBkGnd: TRect;
    function BoundsRectBottomMargin: TRect;
    function BoundsRectLeftMargin: TRect;
    function BoundsRectRightMargin: TRect;
    function BoundsRectTopMargin: TRect;
    function EditAreaHitPt(ViewportPoint: TPoint): Boolean; override;
    function HitTestAt(ViewportPoint: TPoint; var HitInfo: TEasyGroupHitTestInfoSet): Boolean;
    function ItemByPoint(ViewportPoint: TPoint): TEasyItem;
    procedure LoadFromStream(S: TStream; var Version: Integer); override;
    procedure Paint(MarginEdge: TEasyGroupMarginEdge; ObjRect: TRect; ACanvas: TCanvas);
    procedure Rebuild(PrevGroup: TEasyGroup; var NextVisibleItemIndex: Integer);
    function SelectionHit(SelectViewportRect: TRect; SelectType: TEasySelectHitType): Boolean; override;
    function SelectionHitPt(ViewportPoint: TPoint; SelectType: TEasySelectHitType): Boolean; override;
    procedure SaveToStream(S: TStream; Version: Integer = EASYLISTVIEW_STREAM_VERSION); override;
    property Bold;
    property Caption;
    property Checked;
    property ClientRect: TRect read GetClientRect;
    property Cut;
    property Enabled;
    property Expanded: Boolean read FExpanded write SetExpanded default True;
    property Focused;
    property Grid: TEasyGridGroup read GetGrid;
    property GridClass: TEasyGridGroupClass read GetGridClass;
    property ImageIndex;
    property ImageOverlayIndex;
    property Item[Index: Integer]: TEasyItem read GetItem write SetItem; default;
    property ItemCount: Integer read GetItemCount;
    property Items: TEasyItems read FItems write FItems;
    property OwnerListview: TCustomEasyListview read GetOwnerListview;
    property OwnerGroups: TEasyGroups read GetOwnerGroups;
    property View: TEasyViewGroup read GetView;
    property ViewClass: TEasyViewGroupClass read GetViewClass;
    property Visible;
    property VisibleCount: Integer read GetVisibleCount;
  end;

  TEasyGroupInterfaced = class(TEasyGroup)
  protected
    function GetCaptions(Line: Integer): Widestring; override;
    function GetDetailCount: Integer; override;
    function GetDetails(Line: Integer): Integer; override;
    function GetImageIndexes(Column: Integer): TCommonImageIndexInteger; override;
    function GetImageList(Column: Integer; IconSize: TEasyImageSize): TCustomImageList; override;
    function GetImageOverlayIndexes(Column: Integer): TCommonImageIndexInteger; override;
    procedure ImageDraw(Column: TEasyColumn; ACanvas: TCanvas; const RectArray: TEasyRectArrayObject; AlphaBlender: TEasyAlphaBlender); override;
    procedure ImageDrawGetSize(Column: TEasyColumn; var ImageW: Integer; var ImageH: Integer); override;
    procedure ImageDrawIsCustom(Column: TEasyColumn; var IsCustom: Boolean); override;
    procedure SetCaptions(Column: Integer; Value: Widestring); override;
    procedure SetDetailCount(Value: Integer); override;
    procedure SetDetails(Line: Integer; Value: Integer); override;
    procedure SetImageIndexes(Column: Integer; Value: TCommonImageIndexInteger); override;
    procedure SetImageOverlayIndexes(Column: Integer; Value: TCommonImageIndexInteger); override;
    procedure ThumbnailDraw(ACanvas: TCanvas; ARect: TRect; AlphaBlender: TEasyAlphaBlender; var DoDefault: Boolean); override;
  public
    property DataInf;
  end;

  TEasyBaseGroupStored = class(TEasyGroup)
  private
    FDataHelper: TEasyDynamicDataHelper;
  protected
    function GetCaptions(Line: Integer): Widestring; override;
    function GetDetailCount: Integer; override;
    function GetDetails(Line: Integer): Integer; override;
    function GetImageIndexes(Column: Integer): TCommonImageIndexInteger; override;
    function GetImageList(Column: Integer; IconSize: TEasyImageSize): TCustomImageList; override;
    function GetImageOverlayIndexes(Column: Integer): TCommonImageIndexInteger ; override;
    procedure ImageDraw(Column: TEasyColumn; ACanvas: TCanvas; const RectArray: TEasyRectArrayObject; AlphaBlender: TEasyAlphaBlender); override;
    procedure ImageDrawGetSize(Column: TEasyColumn; var ImageW: Integer; var ImageH: Integer); override;
    procedure ImageDrawIsCustom(Column: TEasyColumn; var IsCustom: Boolean); override;
    procedure SetCaptions(Column: Integer; Value: Widestring); override;
    procedure SetDetailCount(Value: Integer); override;
    procedure SetDetails(Line: Integer; Value: Integer); override;
    procedure SetImageIndexes(Column: Integer; Value: TCommonImageIndexInteger); override;
    procedure SetImageOverlayIndexes(Column: Integer; Value: TCommonImageIndexInteger); override;
    procedure ThumbnailDraw(ACanvas: TCanvas; ARect: TRect; AlphaBlender: TEasyAlphaBlender; var DoDefault: Boolean); override;
    property DataHelper: TEasyDynamicDataHelper read FDataHelper write FDataHelper;
  public
    constructor Create(ACollection: TEasyCollection); override;
    destructor Destroy; override;
    procedure LoadFromStream(S: TStream; var Version: Integer); override;
    procedure SaveToStream(S: TStream; Version: Integer = EASYLISTVIEW_STREAM_VERSION); override;
  end;

  TEasyGroupStored = class(TEasyBaseGroupStored)
  published
    property Caption;
    property Bold;
    property Checked;
    property Cut;
    property Enabled;
    property Expanded;
    property ImageIndex;
    property ImageOverlayIndex;
    property Items;
    property Tag;
    property Visible;
  end;

  //
  // Group for the TEasyTaskPanel component
  //
  TEasyGroupTaskPanel = class(TEasyBaseGroupStored)
  private
    FHeight: Integer;
    FTaskPanel: TEasyTaskPanelForm;
    function GetTaskWindowCreated: Boolean;
    procedure SetExpanded(Value: Boolean); override;
    procedure SetHeight(const Value: Integer);
    procedure SetInitialized(Value: Boolean); override;
  protected
    procedure CreateTaskWindow;
  public
    constructor Create(ACollection: TEasyCollection); override;
    destructor Destroy; override;
    procedure LoadFromStream(S: TStream; var Version: Integer); override;
    procedure SaveToStream(S: TStream; Version: Integer = EASYLISTVIEW_STREAM_VERSION); override;
    property TaskPanel: TEasyTaskPanelForm read FTaskPanel;
  published
    property Caption;
    property Bold;
    property Checked;
    property Cut;
    property Enabled;
    property Expanded;
    property Height: Integer read FHeight write SetHeight default 100;
    property ImageIndex;
    property ImageOverlayIndex;
    property Tag;
    property TaskWindowCreated: Boolean read GetTaskWindowCreated;
    property Visible;
  end;

  TEasyGroupVirtual = class(TEasyGroup)
  protected
    function GetCaptions(Line: Integer): Widestring; override;
    function GetDetailCount: Integer; override;
    function GetDetails(Line: Integer): Integer; override;
    function GetImageIndexes(Column: Integer): TCommonImageIndexInteger; override;
    function GetImageList(Column: Integer; IconSize: TEasyImageSize): TCustomImageList; override;
    function GetImageOverlayIndexes(Column: Integer): TCommonImageIndexInteger; override;
    procedure ImageDraw(Column: TEasyColumn; ACanvas: TCanvas; const RectArray: TEasyRectArrayObject; AlphaBlender: TEasyAlphaBlender); override;
    procedure ImageDrawGetSize(Column: TEasyColumn; var ImageW: Integer; var ImageH: Integer); override;
    procedure ImageDrawIsCustom(Column: TEasyColumn; var IsCustom: Boolean); override;
    procedure SetCaptions(Column: Integer; Value: Widestring); override;
    procedure SetDetailCount(Value: Integer); override;
    procedure SetDetails(Line: Integer; Value: Integer); override;
    procedure SetImageIndexes(Column: Integer; Value: TCommonImageIndexInteger); override;
    procedure SetImageOverlayIndexes(Column: Integer; Value: TCommonImageIndexInteger); override;
    procedure ThumbnailDraw(ACanvas: TCanvas; ARect: TRect; AlphaBlender: TEasyAlphaBlender; var DoDefault: Boolean); override;
  end;

  // **************************************************************************
  // TEasyViewGroup
  //    Implements the basis for the UI (drawing, and mouse interaction) for a EasyGroup
  // **************************************************************************
  TEasyViewGroup = class(TEasyOwnedPersistentGroupItem)
  protected
    function CustomExpandImages: Boolean;
    procedure GetCollapseExpandImages(var Expand, Collapse: TBitmap);
  public
    function EditAreaHitPt(Group: TEasyGroup; ViewportPoint: TPoint): Boolean; virtual;
    function GetImageList(Group: TEasyGroup): TCustomImageList;
    procedure GetExpandImageSize(Group: TEasyGroup; var ImageW, ImageH: Integer); virtual;
    procedure GetImageSize(Group: TEasyGroup; var ImageW, ImageH: Integer); virtual;
    procedure GroupRectArray(Group: TEasyGroup; MarginEdge: TEasyGroupMarginEdge; ObjRect: TRect; var RectArray: TEasyRectArrayObject); virtual;
    procedure LoadTextFont(Group: TEasyGroup; ACanvas: TCanvas); virtual;
    procedure Paint(Group: TEasyGroup; MarginEdge: TEasyGroupMarginEdge; ObjRect: TRect; ACanvas: TCanvas); virtual;
    procedure PaintAfter(Group: TEasyGroup; ACanvas: TCanvas; MarginEdge: TEasyGroupMarginEdge; ObjRect: TRect; RectArray: TEasyRectArrayObject); virtual;
    procedure PaintBackground(Group: TEasyGroup; ACanvas: TCanvas; MarginEdge: TEasyGroupMarginEdge; ObjRect: TRect; RectArray: TEasyRectArrayObject); virtual;
    procedure PaintBand(Group: TEasyGroup; ACanvas: TCanvas; MarginEdge: TEasyGroupMarginEdge; ObjRect: TRect; RectArray: TEasyRectArrayObject); virtual;
    procedure PaintBefore(Group: TEasyGroup; ACanvas: TCanvas; MarginEdge: TEasyGroupMarginEdge; ObjRect: TRect; RectArray: TEasyRectArrayObject); virtual;
    procedure PaintCheckBox(Group: TEasyGroup; ACanvas: TCanvas; RectArray: TEasyRectArrayObject); virtual;
    procedure PaintExpandButton(Group: TEasyGroup; ACanvas: TCanvas; MarginEdge: TEasyGroupMarginEdge; ObjRect: TRect; RectArray: TEasyRectArrayObject); virtual;
    procedure PaintFocusRect(Group: TEasyGroup; ACanvas: TCanvas; MarginEdge: TEasyGroupMarginEdge; ObjRect: TRect; RectArray: TEasyRectArrayObject); virtual;
    procedure PaintImage(Group: TEasyGroup; ACanvas: TCanvas; MarginEdge: TEasyGroupMarginEdge; ObjRect: TRect; RectArray: TEasyRectArrayObject); virtual;
    procedure PaintSelectionRect(Group: TEasyGroup; ACanvas: TCanvas; ObjRect: TRect; RectArray: TEasyRectArrayObject); virtual;
    procedure PaintText(Group: TEasyGroup; MarginEdge: TEasyGroupMarginEdge; ACanvas: TCanvas; ObjRect: TRect; RectArray: TEasyRectArrayObject); virtual;
    function SelectionHit(Group: TEasyGroup; SelectViewportRect: TRect; SelectType: TEasySelectHitType): Boolean; virtual;
    function SelectionHitPt(Group: TEasyGroup; ViewportPoint: TPoint; SelectType: TEasySelectHitType): Boolean; virtual;
  end;

  // **************************************************************************
  // TEasyViewTaskBandGroup
  //    Implements the basis for the UI (drawing, and mouse interaction) for a EasyGroup
  // in the TaskBand control
  // **************************************************************************
  TEasyViewTaskBandGroup = class(TEasyViewGroup)
  protected
    function DrawThemed: Boolean; virtual;
    procedure PaintTextTopThemed(ACanvas: TCanvas; Group: TEasyGroup; ObjRect: TRect; RectArray: TEasyRectArrayObject); virtual;
  public
    procedure GetExpandImageSize(Group: TEasyGroup; var ImageW: Integer; var ImageH: Integer); override;
    procedure GroupRectArray(Group: TEasyGroup; MarginEdge: TEasyGroupMarginEdge; ObjRect: TRect; var RectArray: TEasyRectArrayObject); override;
    procedure LoadTextFont(Group: TEasyGroup; ACanvas: TCanvas); override;
    procedure PaintBackground(Group: TEasyGroup; ACanvas: TCanvas; MarginEdge: TEasyGroupMarginEdge; ObjRect: TRect; RectArray: TEasyRectArrayObject); override;
    procedure PaintBand(Group: TEasyGroup; ACanvas: TCanvas; MarginEdge: TEasyGroupMarginEdge; ObjRect: TRect; RectArray: TEasyRectArrayObject); override;
    procedure PaintExpandButton(Group: TEasyGroup; ACanvas: TCanvas; MarginEdge: TEasyGroupMarginEdge; ObjRect: TRect; RectArray: TEasyRectArrayObject); override;
    procedure PaintText(Group: TEasyGroup; MarginEdge: TEasyGroupMarginEdge; ACanvas: TCanvas; ObjRect: TRect; RectArray: TEasyRectArrayObject); override;
  end;

  // **************************************************************************
  // TEasyViewTaskPanelGroup
  //    Implements the basis for the UI (drawing, and mouse interaction) for a EasyGroup
  // in the TaskPanel control
  // **************************************************************************
  TEasyViewTaskPanelGroup = class(TEasyViewTaskBandGroup)
  protected
  public
    procedure PaintBefore(Group: TEasyGroup; ACanvas: TCanvas; MarginEdge: TEasyGroupMarginEdge; ObjRect: TRect; RectArray: TEasyRectArrayObject); override;
  end;

  // **************************************************************************
  // TEasyGroups
  //   Collection that contains all the Groups in the control
  // **************************************************************************
  TEasyGroups = class(TEasyCollection)
  private
    FGroupsState: TEasyGroupsStates;
    FStreamGroups: Boolean;
    function GetCellHeight: Integer;
    function GetCellWidth: Integer;
    function GetGroup(Index: Integer): TEasyGroup;
    function GetItemCount: Integer;
    function GetViewRect: TRect;
    function GetVisibleGroup(Index: Integer): TEasyGroup;
    procedure SetCellHeight(Value: Integer);
    procedure SetCellWidth(Value: Integer);
    procedure SetGroup(Index: Integer; Value: TEasyGroup);
  protected
    function FirstGroupInternal(VisibleOnly: Boolean): TEasyGroup;
    function FirstInGroupInternal(Group: TEasyGroup; VisibleOnly: Boolean): TEasyItem;
    function FirstItemInternal(NextItemType: TEasyNextItemType): TEasyItem;
    function LastGroupInternal(VisibleOnly: Boolean): TEasyGroup;
    function LastInGroupInternal(Group: TEasyGroup; VisibleOnly: Boolean): TEasyItem;
    function LastItemInternal(NextItemType: TEasyNextItemType): TEasyItem;
    function NavigateGroupInternal(Group: TEasyGroup; VisibleOnly: Boolean; Direction: TEasySearchDirection): TEasyGroup;
    function NavigateInGroupInternal(Group: TEasyGroup; Item: TEasyItem; VisibleOnly: Boolean; Direction: TEasySearchDirection): TEasyItem;
    function NavigateItemInternal(Item: TEasyItem; NextItemType: TEasyNextItemType; Direction: TEasySearchDirection): TEasyItem;
    procedure DoStructureChange; override;
    property GroupsState: TEasyGroupsStates read FGroupsState write FGroupsState;
    property VisibleGroup[Index: Integer]: TEasyGroup read GetVisibleGroup;
  public
    constructor Create(AnOwner: TCustomEasyListview); reintroduce; virtual;
    destructor Destroy; override;

    function Add(Data: TObject = nil): TEasyGroup;
    function AddInterfaced(const DataInf: IUnknown; Data: TObject = nil): TEasyGroupInterfaced;
    function AddVirtual(Data: TObject = nil): TEasyGroupVirtual;
    function AddCustom(CustomGroup: TEasyGroupClass; Data: TObject = nil): TEasyGroup;
    function AdjacentItem(Item: TEasyItem; Direction: TEasyAdjacentCellDir): TEasyItem;
    function CanMoveDown: Boolean;
    function CanMoveUp: Boolean;
    procedure Clear(FreeItems: Boolean = True); override;
    procedure CollapseAll;
    procedure DeleteGroup(Group: TEasyGroup);
    procedure DeleteItem(Item: TEasyItem);
    procedure DeleteItems(ItemArray: TEasyItemArray);
    procedure ExpandAll;
    function FirstGroup: TEasyGroup;
    function FirstGroupInRect(ViewportRect: TRect): TEasyGroup;
    function FirstInGroup(Group: TEasyGroup): TEasyItem;
    function FirstInitializedItem: TEasyItem;
    function FirstItem: TEasyItem;
    function FirstItemInRect(ViewportRect: TRect): TEasyItem;
    function FirstVisibleGroup: TEasyGroup;
    function FirstVisibleInGroup(Group: TEasyGroup): TEasyItem;
    function FirstVisibleItem: TEasyItem;
    function GroupByPoint(ViewportPoint: TPoint): TEasyGroup;
    function Insert(Index: Integer; Data: TObject = nil): TEasyGroup;
    function InsertCustom(Index: Integer; CustomGroup: TEasyGroupClass; Data: TObject = nil): TEasyGroup;
    function InsertInterfaced(Index: Integer; const DataInf: IUnknown; Data: TObject): TEasyGroupInterfaced;
    function InsertVirtual(Index: Integer; Data: TObject = nil): TEasyGroupVirtual;
    procedure InitializeAll;
    procedure InvalidateItem(Item: TEasyCollectionItem; ImmediateUpdate: Boolean);
    function LastGroup: TEasyGroup;
    function LastInGroup(Group: TEasyGroup): TEasyItem;
    function LastInitializedItem: TEasyItem;
    function LastItem: TEasyItem;
    function LastVisibleGroup: TEasyGroup;
    function LastVisibleInGroup(Group: TEasyGroup): TEasyItem;
    function LastVisibleItem: TEasyItem;
    function ItemByPoint(ViewportPoint: TPoint): TEasyItem;
    function MoveDown: Boolean;
    function MoveUp: Boolean;
    function NextEditableItem(Item: TEasyItem): TEasyItem;
    function NextGroup(Group: TEasyGroup): TEasyGroup;
    function NextGroupInRect(Group: TEasyGroup; ViewportRect: TRect): TEasyGroup;
    function NextInitializedItem(Item: TEasyItem): TEasyItem;
    function NextInGroup(Group: TEasyGroup; Item: TEasyItem): TEasyItem;
    function NextItem(Item: TEasyItem): TEasyItem;
    function NextItemInRect(Item: TEasyItem; ViewportRect: TRect): TEasyItem;
    function NextVisibleGroup(Group: TEasyGroup): TEasyGroup;
    function NextVisibleGroupWithVisibleItems(Group: TEasyGroup): TEasyGroup;
    function NextVisibleInGroup(Group: TEasyGroup; Item: TEasyItem): TEasyItem;
    function NextVisibleItem(Item: TEasyItem): TEasyItem;
    function PrevEditableItem(Item: TEasyItem): TEasyItem;
    function PrevGroup(Group: TEasyGroup): TEasyGroup;
    function PrevInGroup(Group: TEasyGroup; Item: TEasyItem): TEasyItem;
    function PrevInitializedItem(Item: TEasyItem): TEasyItem;
    function PrevItem(Item: TEasyItem): TEasyItem;
    function PrevVisibleGroup(Group: TEasyGroup): TEasyGroup;
    function PrevVisibleGroupWithVisibleItems(Group: TEasyGroup): TEasyGroup;
    function PrevVisibleInGroup(Group: TEasyGroup; Item: TEasyItem): TEasyItem;
    function PrevVisibleItem(Item: TEasyItem): TEasyItem;
    procedure LoadFromStream(S: TStream; Version: Integer = EASYLISTVIEW_STREAM_VERSION); override;
    procedure MakeAllVisible; override;
    procedure Move(Item: TEasyItem; NewGroup: TEasyGroup);
    procedure Rebuild(Force: Boolean = False); virtual;
    procedure SaveToStream(S: TStream; Version: Integer = EASYLISTVIEW_STREAM_VERSION); override;
    procedure UnInitializeAll;
    property CellHeight: Integer read GetCellHeight write SetCellHeight;
    property CellWidth: Integer read GetCellWidth write SetCellWidth;
    property Groups[Index: Integer]: TEasyGroup read GetGroup write SetGroup; default;
    property ItemCount: Integer read GetItemCount;
    property StreamGroups: Boolean read FStreamGroups write FStreamGroups default True;
    property ViewRect: TRect read GetViewRect;
  end;

  // Specical Groups container and Groups for the TEasyTaskPanel component
  TEasyGroupsTaskPanel = class(TEasyGroups)
  private
    function GetGroup(Index: Integer): TEasyGroupTaskPanel;
    procedure SetGroup(Index: Integer; Value: TEasyGroupTaskPanel);
  public
    constructor Create(AnOwner: TCustomEasyListview); override;
    property Groups[Index: Integer]: TEasyGroupTaskPanel read GetGroup write SetGroup; default;
  end;

  // **************************************************************************
  // TEasyCellSize
  //   Maintains the default sizes for the Cells a single view (grid)
  // **************************************************************************
  TEasyCellSize = class(TEasyOwnedPersistent)
  private
    FAutoSize: Boolean;
    FAutoSizeHeight: Integer;  // Dynamic size that is reset with each rebuid of the Groups if the Grid is AutoSized
    FAutoSizeWidth: Integer;   // Dynamic size that is reset with each rebuid of the Groups if the Grid is AutoSized
    FHeight: Integer;          // Fixed size that remains constant with each rebuid of the Groups if the Grid is not AutoSized
    FWidth: Integer;           // Fixed size that remains constant with each rebuid of the Groups if the Grid is not AutoSized
    function GetHeight: Integer;
    function GetWidth: Integer;
    procedure SetAutoSize(const Value: Boolean);
    procedure SetHeight(Value: Integer);
    procedure SetWidth(Value: Integer);
  protected
    property AutoSizeHeight: Integer read FAutoSizeHeight write FAutoSizeHeight;
    property AutoSizeWidth: Integer read FAutoSizeWidth write FAutoSizeWidth;
  public
    constructor Create(AnOwner: TCustomEasyListview); override;

    procedure Assign(Source: TPersistent); override;
    procedure SetSize(AWidth, AHeight: Integer);
  published
    property AutoSize: Boolean read FAutoSize write SetAutoSize default False;
    property Height: Integer read GetHeight write SetHeight default 75;
    property Width: Integer read GetWidth write SetWidth default 75;
  end;

  // **************************************************************************
  // TEasyCellSizeIcon
  //   Maintains the default sizes for the Cells a Icon view
  // **************************************************************************
  TEasyCellSizeIcon = class(TEasyCellSize)
  end;

  // **************************************************************************
  // TEasyCellSizeSmallIcon
  //   Maintains the default sizes for the Cells a Small Icon view
  // **************************************************************************
  TEasyCellSizeSmallIcon = class(TEasyCellSize)
  public
    constructor Create(AnOwner: TCustomEasyListview); override;
  published
    property Width default 200;
    property Height default 17;
  end;

  // **************************************************************************
  // TEasyCellSizeThumbnail
  //   Maintains the default sizes for the Cells a Thumbnail view
  // **************************************************************************
  TEasyCellSizeThumbnail = class(TEasyCellSize)
  public
    constructor Create(AnOwner: TCustomEasyListview); override;
  published
    property Width default 125;
    property Height default 143;
  end;

  // **************************************************************************
  // TEasyCellSizeTile
  //   Maintains the default sizes for the Cells a Tile view
  // **************************************************************************
  TEasyCellSizeTile = class(TEasyCellSize)
  public
    constructor Create(AnOwner: TCustomEasyListview); override;
  published
    property Width default 218;
    property Height default 58;
  end;

  // **************************************************************************
  // TEasyCellSizeList
  //   Maintains the default sizes for the Cells a List view
  // **************************************************************************
  TEasyCellSizeList = class(TEasyCellSize)
  public
    constructor Create(AnOwner: TCustomEasyListview); override;
  published
    property Width default 200;
    property Height default 17;
  end;

  // **************************************************************************
  // TEasyCellSizeReport
  //   Maintains the default sizes for the Cells a Report view
  // **************************************************************************
  TEasyCellSizeReport = class(TEasyCellSize)
  public
    constructor Create(AnOwner: TCustomEasyListview); override;
  published
    property Width default 75;
    property Height default 17;
  end;

  // **************************************************************************
  // TEasyCellSizeFilmStrip
  //   Maintains the default sizes for the Cells a FilmStrip view
  // **************************************************************************
  TEasyCellSizeFilmStrip = class(TEasyCellSizeThumbnail)
  end;

  // **************************************************************************
  // TEasyCellGrid
  //   Maintains the default sizes for the Cells a FilmStrip view
  // **************************************************************************
  TEasyCellGrid = class(TEasyCellSizeReport)
  end;

  // **************************************************************************
  // TEasyCellSizes
  //   Maintains the default sizes for the Cells in each view
  // **************************************************************************
  TEasyCellSizes = class(TEasyOwnedPersistent)
  private
    FFilmStrip: TEasyCellSize;
    FIcon: TEasyCellSize;
    FList: TEasyCellSize;
    FReport: TEasyCellSize;
    FSmallIcon: TEasyCellSize;
    FGrid: TEasyCellSize;
    FThumbnail: TEasyCellSize;
    FTile: TEasyCellSize;
  public
    constructor Create(AnOwner: TCustomEasyListview); override;
    destructor Destroy; override;

  published
    property FilmStrip: TEasyCellSize read FFilmStrip write FFilmStrip;
    property Icon: TEasyCellSize read FIcon write FIcon;
    property SmallIcon: TEasyCellSize read FSmallIcon write FSmallIcon;
    property Grid: TEasyCellSize read FGrid write FGrid;
    property Thumbnail: TEasyCellSize read FThumbnail write FThumbnail;
    property Tile: TEasyCellSize read FTile write FTile;
    property List: TEasyCellSize read FList write FList;
    property Report: TEasyCellSize read FReport write FReport;
  end;

  TEasyColumnDropDownButtonState = (
    cdbsHovering, // Mouse is hovering over the button
    cdbsDown,     // Mouse is pressing the button
    cdbsClickPending // Button was pressed down and held while on the button, may have been moved off the button
  );
  TEasyColumnDropDownButtonStates = set of TEasyColumnDropDownButtonState;

  TEasyColumnDropDownButton = class(TPersistent)
  private
    FAlwaysShow: Boolean;  // By default the button only shows when the mouse is hovering over the column header
    FEnabled: Boolean;
    FMenu: TPopupMenu;
    FOwner: TEasyColumn;
    FState: TEasyColumnDropDownButtonStates;
    FVisible: Boolean;
    procedure SetAlwaysShow(const Value: Boolean);
    procedure SetEnabled(const Value: Boolean);
    procedure SetVisible(const Value: Boolean);
  public
    property State: TEasyColumnDropDownButtonStates read FState write FState;
  published
    constructor Create(AnOwner: TEasyColumn);
    property AlwaysShow: Boolean read FAlwaysShow write SetAlwaysShow default False;
    property Enabled: Boolean read FEnabled write SetEnabled default True;
    property Menu: TPopupMenu read FMenu write FMenu;
    property Owner: TEasyColumn read FOwner;
    property Visible: Boolean read FVisible write SetVisible default False;
  end;

  // **************************************************************************
  // TEasyColumn
  //   Collection Item that represents a single column in the Listview
  // **************************************************************************
  TEasyColumn = class(TEasyCollectionItem)
  private
    FAlignment: TAlignment;
    FAutoSizeOnDblClk: Boolean; // Autosizes column when double clicked in size area
    FAutoSortOnClick: Boolean;
    FAutoSpring: Boolean;       // Sizes column based on width of window
    FAutoToggleSortGlyph: Boolean;
    FClickable: Boolean;
    FDropDownButton: TEasyColumnDropDownButton;
    FPosition: Integer;
    FSortDirection: TEasySortDirection;
    FSpringRest: Single;
    FStyle: TEasyHeaderButtonStyle;
    FView: TEasyViewColumn;
    FWidth: Integer;
    function GetAlignment: TAlignment;
    function GetColor: TColor;
    function GetHotTrack: Boolean;
    function GetImagePosition: TEasyHeaderImagePosition;
    function GetOwnerColumns: TEasyColumns;
    function GetOwnerHeader: TEasyHeader;
    function GetPaintInfo: TEasyPaintInfoColumn;
    function GetSortGlyphAlign: TEasySortGlyphAlign;
    function GetSortGlyphIndent: Integer;
    function GetStyle: TEasyHeaderButtonStyle;
    function GetView: TEasyViewColumn;
    function GetViewClass: TEasyViewColumnClass;
    procedure SetAlignment(Value: TAlignment);
    procedure SetAutoSpring(const Value: Boolean);
    procedure SetColor(Value: TColor);
    procedure SetHotTrack(Value: Boolean);
    procedure SetImagePosition(Value: TEasyHeaderImagePosition);
    procedure SetPaintInfo(Value: TEasyPaintInfoColumn);
    procedure SetPosition(Value: Integer);
    procedure SetSortDirection(Value: TEasySortDirection);
    procedure SetSortGlpyhAlign(Value: TEasySortGlyphAlign);
    procedure SetSortGlyphIndent(Value: Integer);
    procedure SetStyle(Value: TEasyHeaderButtonStyle);
    procedure SetWidth(Value: Integer);
  protected
    function CanChangeBold(NewValue: Boolean): Boolean; override;
    function CanChangeCheck(NewValue: Boolean): Boolean; override;
    function CanChangeEnable(NewValue: Boolean): Boolean; override;
    function CanChangeFocus(NewValue: Boolean): Boolean; override;
    function CanChangeHotTracking(NewValue: Boolean): Boolean; override;
    function CanChangeSelection(NewValue: Boolean): Boolean; override;
    function CanChangeVisibility(NewValue: Boolean): Boolean; override;
    function DefaultImageList(ImageSize: TEasyImageSize): TCustomImageList; override;
    function GetDefaultViewClass: TEasyViewColumnClass; virtual;
    function LocalPaintInfo: TEasyPaintInfoBasic; override;
    procedure AutoSizeToFit; virtual;
    procedure Freeing; override;
    procedure GainingBold; override;
    procedure GainingCheck; override;
    procedure GainingEnable; override;
    procedure GainingFocus; override;
    procedure GainingGhosted; override;
    procedure GainingHilight; override;
    procedure GainingHotTracking(MousePos: TPoint); override;
    procedure GainingSelection; override;
    procedure GainingVisibility; override;
    procedure Initialize; override;
    procedure LosingBold; override;
    procedure LosingCheck; override;
    procedure LosingEnable; override;
    procedure LosingFocus; override;
    procedure LosingGhosted; override;
    procedure LosingHilight; override;
    procedure LosingHotTracking; override;
    procedure LosingSelection; override;
    procedure LosingVisibility; override;
    property Color: TColor read GetColor write SetColor;
    property HotTrack: Boolean read GetHotTrack write SetHotTrack;
    property ImagePosition: TEasyHeaderImagePosition read GetImagePosition write SetImagePosition;
    property SortGlyphAlign: TEasySortGlyphAlign read GetSortGlyphAlign write SetSortGlpyhAlign;
    property SortGlyphIndent: Integer read GetSortGlyphIndent write SetSortGlyphIndent;
    property SpringRest: Single read FSpringRest write FSpringRest;
    property Style: TEasyHeaderButtonStyle read GetStyle write SetStyle;
  public
    constructor Create(ACollection: TEasyCollection); override;
    destructor Destroy; override;
    function EditAreaHitPt(ViewportPoint: TPoint): Boolean; override;
    function HitTestAt(ViewportPoint: TPoint; var HitInfo: TEasyColumnHitTestInfoSet): Boolean;
    function PaintMouseHovering: Boolean;
    procedure Invalidate(ImmediateUpdate: Boolean); override;
    procedure LoadFromStream(S: TStream; var Version: Integer); override;
    procedure MakeVisible(Position: TEasyMakeVisiblePos); override;
    procedure Paint(ACanvas: TCanvas; HeaderType: TEasyHeaderType);
    function SelectionHit(SelectViewportRect: TRect; SelectType: TEasySelectHitType): Boolean; override;
    function SelectionHitPt(ViewportPoint: TPoint; SelectType: TEasySelectHitType): Boolean; override;
    procedure SaveToStream(S: TStream; Version: Integer = EASYLISTVIEW_STREAM_VERSION); override;
    property Alignment: TAlignment read GetAlignment write SetAlignment default taLeftJustify;
    property AutoSizeOnDblClk: Boolean read FAutoSizeOnDblClk write FAutoSizeOnDblClk default True;
    property AutoSortOnClick: Boolean read FAutoSortOnClick write FAutoSortOnClick default False;
    property AutoSpring: Boolean read FAutoSpring write SetAutoSpring default False;
    property AutoToggleSortGlyph: Boolean read FAutoToggleSortGlyph write FAutoToggleSortGlyph default True;
    property Bold;
    property Caption;
    property Checked;
    property Clickable: Boolean read FClickable write FClickable default True;
    property Clicking;
    property DropDownButton: TEasyColumnDropDownButton read FDropDownButton write FDropDownButton;
    property Enabled;
    property ImageIndex;
    property ImageOverlayIndex;
    property OwnerColumns: TEasyColumns read GetOwnerColumns;
    property OwnerHeader: TEasyHeader read GetOwnerHeader;
    property OwnsPaintInfo;
    property PaintInfo: TEasyPaintInfoColumn read GetPaintInfo write SetPaintInfo;
    property Position: Integer read FPosition write SetPosition;
    property Selected;
    property SortDirection: TEasySortDirection read FSortDirection write SetSortDirection default esdNone;
    property Tag;
    property View: TEasyViewColumn read GetView;
    property ViewClass: TEasyViewColumnClass read GetViewClass;
    property Visible;
    property Width: Integer read FWidth write SetWidth default 50;
  end;

  TEasyColumnInterfaced = class(TEasyColumn)
  protected
    function GetCaptions(Line: Integer): Widestring; override;
    function GetDetailCount: Integer; override;
    function GetDetails(Line: Integer): Integer; override;
    function GetImageIndexes(Column: Integer): TCommonImageIndexInteger; override;
    function GetImageList(Column: Integer; IconSize: TEasyImageSize): TCustomImageList; override;
    function GetImageOverlayIndexes(Column: Integer): TCommonImageIndexInteger; override;
    procedure ImageDraw(Column: TEasyColumn; ACanvas: TCanvas; const RectArray: TEasyRectArrayObject; AlphaBlender: TEasyAlphaBlender); override;
    procedure ImageDrawGetSize(Column: TEasyColumn; var ImageW: Integer; var ImageH: Integer); override;
    procedure ImageDrawIsCustom(Column: TEasyColumn; var IsCustom: Boolean); override;
    procedure SetCaptions(Column: Integer; Value: Widestring); override;
    procedure SetDetailCount(Value: Integer); override;
    procedure SetDetails(Line: Integer; Value: Integer); override;
    procedure SetImageIndexes(Column: Integer; Value: TCommonImageIndexInteger); override;
    procedure SetImageOverlayIndexes(Column: Integer; Value: TCommonImageIndexInteger); override;
    procedure ThumbnailDraw(ACanvas: TCanvas; ARect: TRect; AlphaBlender: TEasyAlphaBlender; var DoDefault: Boolean); override;
  public
    property DataInf;
  end;

  TEasyColumnStored = class(TEasyColumn)
  private
    FDataHelper: TEasyDynamicDataHelper;
  protected
    function GetCaptions(Line: Integer): Widestring; override;
    function GetDetailCount: Integer; override;
    function GetDetails(Line: Integer): Integer; override;
    function GetImageIndexes(Column: Integer): TCommonImageIndexInteger; override;
    function GetImageList(Column: Integer; IconSize: TEasyImageSize): TCustomImageList; override;
    function GetImageOverlayIndexes(Column: Integer): TCommonImageIndexInteger; override;
    procedure ImageDraw(Column: TEasyColumn; ACanvas: TCanvas; const RectArray: TEasyRectArrayObject; AlphaBlender: TEasyAlphaBlender); override;
    procedure ImageDrawGetSize(Column: TEasyColumn; var ImageW: Integer; var ImageH: Integer); override;
    procedure ImageDrawIsCustom(Column: TEasyColumn; var IsCustom: Boolean); override;
    procedure SetCaptions(Column: Integer; Value: Widestring); override;
    procedure SetDetailCount(Value: Integer); override;
    procedure SetDetails(Line: Integer; Value: Integer); override;
    procedure SetImageIndexes(Column: Integer; Value: TCommonImageIndexInteger); override;
    procedure SetImageOverlayIndexes(Column: Integer; Value: TCommonImageIndexInteger); override;
    procedure ThumbnailDraw(ACanvas: TCanvas; ARect: TRect; AlphaBlender: TEasyAlphaBlender; var DoDefault: Boolean); override;
    property DataHelper: TEasyDynamicDataHelper read FDataHelper write FDataHelper;
  public
    constructor Create(ACollection: TEasyCollection); override;
    destructor Destroy; override;
    procedure LoadFromStream(S: TStream; var Version: Integer); override;
    procedure SaveToStream(S: TStream; Version: Integer = EASYLISTVIEW_STREAM_VERSION); override;
  published
    property Alignment;
    property AutoSizeOnDblClk;
    property AutoSortOnClick;
    property AutoSpring;
    property AutoToggleSortGlyph;
    property Bold;
    property Caption;
    property Checked;
    property Clickable;
    property Color;
    property DropDownButton;
    property Enabled;
    property ImageIndex;
    property ImageOverlayIndex;
    property Position;
    property Selected;
    property SortDirection;
    property Width;
    property Visible;
  end;

  TEasyColumnVirtual = class(TEasyColumn)
  protected
    function GetCaptions(Line: Integer): Widestring; override;
    function GetDetailCount: Integer; override;
    function GetDetails(Line: Integer): Integer; override;
    function GetImageIndexes(Column: Integer): TCommonImageIndexInteger; override;
    function GetImageList(Column: Integer; IconSize: TEasyImageSize): TCustomImageList; override;
    function GetImageOverlayIndexes(Column: Integer): TCommonImageIndexInteger; override;
    procedure ImageDraw(Column: TEasyColumn; ACanvas: TCanvas; const RectArray: TEasyRectArrayObject; AlphaBlender: TEasyAlphaBlender); override;
    procedure ImageDrawGetSize(Column: TEasyColumn; var ImageW: Integer; var ImageH: Integer); override;
    procedure ImageDrawIsCustom(Column: TEasyColumn; var IsCustom: Boolean); override;
    procedure SetCaptions(Column: Integer; Value: Widestring); override;
    procedure SetDetailCount(Value: Integer); override;
    procedure SetDetails(Line: Integer; Value: Integer); override;
    procedure SetImageIndexes(Column: Integer; Value: TCommonImageIndexInteger); override;
    procedure SetImageOverlayIndexes(Column: Integer; Value: TCommonImageIndexInteger); override;
    procedure ThumbnailDraw(ACanvas: TCanvas; ARect: TRect;AlphaBlender: TEasyAlphaBlender; var DoDefault: Boolean); override;
  end;

  // **************************************************************************
  // TEasyViewColumn
  //   Implements the basis for the UI (drawing, and mouse interaction) for a EasyColumn
  // **************************************************************************
  TEasyViewColumn = class(TEasyOwnedPersistentView)
  public
    function EditAreaHitPt(Column: TEasyColumn; ViewportPoint: TPoint): Boolean; virtual;
    function GetImageList(Column: TEasyColumn): TCustomImageList;
    procedure CalculateTextRect(Column: TEasyColumn; Canvas: TControlCanvas; var TextR: TRect); virtual;
    procedure GetImageSize(Column: TEasyColumn; var ImageW, ImageH: Integer);
    function ItemRect(Column: TEasyColumn; RectType: TEasyCellRectType): TRect; virtual;
    procedure ItemRectArray(Column: TEasyColumn; var RectArray: TEasyRectArrayObject); virtual;
    procedure LoadTextFont(Column: TEasyColumn; ACanvas: TCanvas); virtual;
    procedure Paint(Column: TEasyColumn; ACanvas: TCanvas; HeaderType: TEasyHeaderType); virtual;
    procedure PaintAfter(Column: TEasyColumn; ACanvas: TCanvas; HeaderType: TEasyHeaderType; RectArray: TEasyRectArrayObject); virtual;
    procedure PaintBefore(Column: TEasyColumn; ACanvas: TCanvas; HeaderType: TEasyHeaderType; RectArray: TEasyRectArrayObject); virtual;
    procedure PaintBkGnd(Column: TEasyColumn; ACanvas: TCanvas; HeaderType: TEasyHeaderType; RectArray: TEasyRectArrayObject); virtual;
    procedure PaintCheckBox(Column: TEasyColumn; ACanvas: TCanvas; HeaderType: TEasyHeaderType; RectArray: TEasyRectArrayObject); virtual;
    procedure PaintDropDownArrow(Column: TEasyColumn; ACanvas: TCanvas; HeaderType: TEasyHeaderType; RectArray: TEasyRectArrayObject); virtual;
    procedure PaintDropGlyph(Column: TEasyColumn; ACanvas: TCanvas; HeaderType: TEasyHeaderType; RectArray: TEasyRectArrayObject); virtual;
    procedure PaintFocusRect(Column: TEasyColumn; ACanvas: TCanvas; HeaderType: TEasyHeaderType; RectArray: TEasyRectArrayObject); virtual;
    procedure PaintImage(Column: TEasyColumn; ACanvas: TCanvas; HeaderType: TEasyHeaderType; RectArray: TEasyRectArrayObject; ImageSize: TEasyImageSize); virtual;
    function PaintImageSize(Column: TEasyColumn; HeaderType: TEasyHeaderType): TEasyImageSize; virtual;
    procedure PaintSortGlyph(Column: TEasyColumn; ACanvas: TCanvas; HeaderType: TEasyHeaderType; RectArray: TEasyRectArrayObject); virtual;
    procedure PaintText(Column: TEasyColumn; ACanvas: TCanvas; HeaderType: TEasyHeaderType; RectArray: TEasyRectArrayObject; LinesToDraw: Integer); virtual;
    procedure ReSizeRectArray(var RectArray: TEasyRectArrayObjectArray); virtual;
    function SelectionHit(Column: TEasyColumn; SelectViewportRect: TRect; SelectType: TEasySelectHitType): Boolean; virtual;
    function SelectionHitPt(Column: TEasyColumn; ViewportPoint: TPoint; SelectType: TEasySelectHitType): Boolean; virtual;
  end;

  // **************************************************************************
  // TEasyGroups
  //   Collection that contains all the Columns in the control
  // **************************************************************************
  TEasyColumns = class(TEasyCollection)
  private
    function GetColumns(Index: Integer): TEasyColumn;
    function GetOwnerHeader: TEasyHeader;
    procedure SetColumns(Index: Integer; Value: TEasyColumn);
  protected
    procedure DoItemAdd(Item: TEasyCollectionItem; Index: Integer); override;
    procedure DoStructureChange; override;
  public
    constructor Create(AnOwner: TCustomEasyListview); reintroduce; virtual;
    destructor Destroy; override;
    function Add(Data: TObject = nil): TEasyColumn;
    function AddInterfaced(const DataInf: IUnknown; Data: TObject): TEasyColumnInterfaced;
    function AddVirtual(Data: TObject = nil): TEasyColumnVirtual;
    function AddCustom(CustomItem: TEasyColumnClass; Data: TObject = nil): TEasyColumn;
    function ColumnByPoint(ViewportPoint: TPoint): TEasyColumn;
    function Insert(Index: Integer; Data: TObject = nil): TEasyColumn;
    function InsertCustom(Index: Integer; CustomColumn: TEasyColumnClass; Data: TObject = nil): TEasyColumn;
    function InsertInterfaced(Index: Integer; const DataInf: IUnknown; Data: TObject = nil): TEasyColumnInterfaced;
    function InsertVirtual(Index: Integer; Data: TObject = nil): TEasyColumnVirtual;
    procedure Clear(FreeItems: Boolean = True); override;
    property Columns[Index: Integer]: TEasyColumn read GetColumns write SetColumns; default;
    property OwnerHeader: TEasyHeader read GetOwnerHeader;
  end;

  TColumnPos = class(TList)
  private
    function Get(Index: Integer): TEasyColumn;
    procedure Put(Index: Integer; Item: TEasyColumn);
  public
    property Items[Index: Integer]: TEasyColumn read Get write Put; default;
  end;
  // **************************************************************************
  // TEasyHeader
  //   Header area of the Listview
  // **************************************************************************
  TEasyHeader = class(TEasyOwnedPersistent)
  private
    FAutoSizeHeight: Boolean;
    FAutoSizeHeightMargin: Integer;
    FCanvasStore: TEasyCanvasStore;
    FColor: TColor;                  // Color of the header
    FColumns: TEasyColumns;          // The columns that define the header
    FDisplayRect: TRect;             // Rectangle occupied in the Client Window of the Listview, clips column not visible due to scrollbar position of LV
    FDragManager: TEasyHeaderDragManager;
    FDropDownHoverColumn: TEasyColumn; // Column that has the DropDownButton and the mouse over it (or button pressed)
    FFixedSingleColumn: Boolean;
    FFont: TFont;                    // The font the header text is drawn in
    FHeight: Integer;                // Height of the header
    FHotTrackedColumn: TEasyColumn;  // the column that is current in a hot tracked state
    FImages: TCustomImageList;             // Images for the header columns
    FLastWidth: Integer;
    FPositions: TColumnPos;
    FPressColumn: TEasyColumn;       // Column that currently pressed
    FResizeColumn: TEasyColumn;      // Column that is currently being resized
    FShowInAllViews: Boolean;
    FSizeable: Boolean;              // The column widths are resizeable by the mouse
    FState: TEasyHeaderStates;       // Current state of the header
    FStreamColumns: Boolean;
    FViewRect: TRect;                // The total size of the header including the column not visible in the current window
    FVisible: Boolean;               // Shows/Hides the header in the control
    function GetCanvasStore: TEasyCanvasStore;
    function GetDisplayRect: TRect;
    function GetDraggable: Boolean;
    function GetFixedSingleColumn: Boolean;
    function GetHeight: Integer;
    function GetMouseCaptured: Boolean;
    function GetRuntimeHeight: Integer;
    procedure SetAutoSizeHeight(const Value: Boolean);
    procedure SetAutoSizeHeightMargin(const Value: Integer);
    procedure SetColor(Value: TColor);
    procedure SetDraggable(Value: Boolean);
    procedure SetDropDownHoverColumn(const Value: TEasyColumn);
    procedure SetFixedSingleColumn(const Value: Boolean);
    procedure SetFont(Value: TFont);
    procedure SetHeight(Value: Integer);
    procedure SetHotTrackedColumn(const Value: TEasyColumn);
    procedure SetImages(Value: TCustomImageList);
    procedure SetShowInAllViews(const Value: Boolean);
    procedure SetVisible(Value: Boolean);
    function GetViewWidth: Integer;
  protected
    function InCheckZone(ViewportPt: TPoint; var Column: TEasyColumn): Boolean;
    function InDropDownButtonZone(ViewportPt: TPoint; var Column: TEasyColumn): Boolean;
    function InHotTrackZone(ViewportPt: TPoint; var Column: TEasyColumn): Boolean;
    function InPressZone(ViewportPt: TPoint; var Column: TEasyColumn): Boolean;
    function InResizeZone(ViewportPt: TPoint; var Column: TEasyColumn): Boolean;
    function IsFontStored: Boolean;
    procedure CaptureMouse;
    procedure ClearStates;
    procedure DoMouseDown(var Message: TWMMouse; Button: TCommonMouseButton; Shift: TShiftState; Column: TEasyColumn);
    procedure DoMouseMove(var Message: TWMMouse; Shift: TShiftState);
    procedure DoMouseUp(var Message: TWMMouse; Button: TCommonMouseButton; Shift: TShiftState; Column: TEasyColumn);
    procedure HandleHotTrack(Msg: TWMMouse; ForceClear: Boolean);
    procedure ReleaseMouse;
    procedure SizeFixedSingleColumn(NewWidth: Integer);
    procedure SpringColumns(NewWidth: Integer);
    procedure WMContextMenu(var Msg: TMessage); message WM_CONTEXTMENU;
    procedure WMLButtonDblClk(var Msg: TWMLButtonDblClk); message WM_LBUTTONDBLCLK;
    procedure WMLButtonDown(var Msg: TWMLButtonDown); message WM_LBUTTONDOWN;
    procedure WMLButtonUp(var Msg: TWMLButtonUp); message WM_LBUTTONUP;
    procedure WMMouseMove(var Msg: TWMMouseMove); message WM_MOUSEMOVE;
    procedure WMRButtonDown(var Msg: TWMRButtonDown); message WM_RBUTTONDOWN;
    procedure WMRButtonUp(var Msg: TWMRButtonUp); message WM_RBUTTONUP;
    procedure WMSize(var Msg: TWMSize); message WM_SIZE;

    property CanvasStore: TEasyCanvasStore read GetCanvasStore write FCanvasStore;
    property DropDownHoverColumn: TEasyColumn read FDropDownHoverColumn write SetDropDownHoverColumn;
    property HotTrackedColumn: TEasyColumn read FHotTrackedColumn write SetHotTrackedColumn;
    property LastWidth: Integer read FLastWidth write FLastWidth;
    property Positions: TColumnPos read FPositions write FPositions;
    property PressColumn: TEasyColumn read FPressColumn write FPressColumn;
    property ResizeColumn: TEasyColumn read FResizeColumn write FResizeColumn;
    property RuntimeHeight: Integer read GetRuntimeHeight;
    property State: TEasyHeaderStates read FState write FState;
  public
    constructor Create(AnOwner: TCustomEasyListview); override;
    destructor Destroy; override;

    function FirstColumn: TEasyColumn;
    function FirstColumnByPosition: TEasyColumn;
    function FirstColumnInRect(ViewportRect: TRect): TEasyColumn;
    function FirstVisibleColumn: TEasyColumn;
    function LastColumn: TEasyColumn;
    function LastVisibleColumn: TEasyColumn;
    function NextColumn(AColumn: TEasyColumn): TEasyColumn;
    function NextColumnByPosition(AColumn: TEasyColumn): TEasyColumn;
    function NextVisibleColumn(Column: TEasyColumn): TEasyColumn;
    function PrevColumn(AColumn: TEasyColumn): TEasyColumn;
    function PrevColumnByPosition(AColumn: TEasyColumn): TEasyColumn;
    function PrevVisibleColumn(Column: TEasyColumn): TEasyColumn;
    procedure ClickColumn(Column: TEasyColumn);
    procedure Invalidate(ImmediateUpdate: Boolean); virtual;
    procedure InvalidateColumn(Item: TEasyColumn; ImmediateUpdate: Boolean); virtual;
    function LastColumnByPosition: TEasyColumn;
    function NextColumnInRect(Column: TEasyColumn; ViewportRect: TRect): TEasyColumn;
    procedure LoadFromStream(S: TStream; Version: Integer = EASYLISTVIEW_STREAM_VERSION); override;
    procedure PaintTo(ACanvas: TCanvas; ARect: TRect); virtual;
    procedure Rebuild(Force: Boolean); virtual;
    procedure SaveToStream(S: TStream; Version: Integer = EASYLISTVIEW_STREAM_VERSION); override;
    property DisplayRect: TRect read GetDisplayRect write FDisplayRect;
    property MouseCaptured: Boolean read GetMouseCaptured;
    property StreamColumns: Boolean read FStreamColumns write FStreamColumns default True;
    property ViewRect: TRect read FViewRect;
    property ViewWidth: Integer read GetViewWidth;
  published
    property AutoSizeHeight: Boolean read FAutoSizeHeight write SetAutoSizeHeight default True;
    property AutoSizeHeightMargin: Integer read FAutoSizeHeightMargin write SetAutoSizeHeightMargin default 8;
    property Color: TColor read FColor write SetColor default clBtnFace;
    property Columns: TEasyColumns read FColumns write FColumns;
    property Draggable: Boolean read GetDraggable write SetDraggable default True;
    property DragManager: TEasyHeaderDragManager read FDragManager write FDragManager;
    property FixedSingleColumn: Boolean read GetFixedSingleColumn write SetFixedSingleColumn default False;
    property Font: TFont read FFont write SetFont stored IsFontStored;
    property Height: Integer read GetHeight write SetHeight default 21;
    property Images: TCustomImageList read FImages write SetImages;
    property ShowInAllViews: Boolean read FShowInAllViews write SetShowInAllViews default False;
    property Sizeable: Boolean read FSizeable write FSizeable default True;
    property Visible: Boolean read FVisible write SetVisible default False;
  end;

  // **************************************************************************
  // TEasyEditManager
  //    EasyEditManager defines the base class to edit an item.  The
  // IEasyCellEditorSink allows the Editor to communicate back to the manager for
  // event dispatching without the need for the Editor interface to have knowledge
  // of the TManagerLink object.
  // **************************************************************************
  TEasyEditManager = class(TEasyOwnedPersistent)
  private
    FAppHooked: Boolean;
    FAutoEditDelayTime: Integer;   // Time (in milliseconds) before the object is edited
    FAutoEditStartClickPt: TPoint; // This is in WINDOW coordinates, the point where the mouse is clicked.  Used to determine if the AutoEdit should begin
    FColor: TColor;
    FEditColumn: TEasyColumn;
    FEditFinished: Boolean;        // Set when the edit is done
    FEditing: Boolean;             // Set when the edit is in progress
    FEditItem: TEasyItem;          // The Item that is being edited
    FEditor: IEasyCellEditor;
    FEnabled: Boolean;             // The Edit Manager is enabled
    FFont: TFont;
    FTabEditColumns: Boolean;
    FTabMoveFocus: Boolean;        // If an item is begin edited a tab move the focus to the next item and puts it in edit
    FTabMoveFocusColumn: TEasyColumn;
    FTabMoveFocusItem: TEasyItem;
    FTimer: TTimer;                // The timer for auto Edit
    FTimerRunning: Boolean;        // True when the auto Edit is timing out

    function GetEditing: Boolean;
    procedure SetEnabled(const Value: Boolean);
    procedure SetFont(const Value: TFont);
  protected
    function MainWindowHook(var Message: TMessage): Boolean;
    procedure StartAutoEditTimer;
    procedure StopAutoEditTimer;
    procedure TimerEvent(Sender: TObject);

    property AppHooked: Boolean read FAppHooked write FAppHooked;
    property AutoEditStartClickPt: TPoint read FAutoEditStartClickPt write FAutoEditStartClickPt;
    property EditColumn: TEasyColumn read FEditColumn write FEditColumn;
    property TabEditColumns: Boolean read FTabEditColumns write FTabEditColumns default False;
    property TabMoveFocusColumn: TEasyColumn read FTabMoveFocusColumn write FTabMoveFocusColumn;
    property TabMoveFocusItem: TEasyItem read FTabMoveFocusItem write FTabMoveFocusItem;
    property Timer: TTimer read FTimer write FTimer;
    property TimerRunning: Boolean read FTimerRunning;

  public
    constructor Create(AnOwner: TCustomEasyListview); override;
    destructor Destroy; override;

    procedure BeginEdit(Item: TEasyItem; Column: TEasyColumn); virtual;
    procedure EndEdit;

    property Editing: Boolean read GetEditing;
    property EditItem: TEasyItem read FEditItem;

    property Editor: IEasyCellEditor read FEditor write FEditor;
  published
    property AutoEditDelayTime: Integer read FAutoEditDelayTime write FAutoEditDelayTime default 300;
    property Color: TColor read FColor write FColor default clWindow;
    property Enabled: Boolean read FEnabled write SetEnabled default False;
    property Font: TFont read FFont write SetFont;
    property TabMoveFocus: Boolean read FTabMoveFocus write FTabMoveFocus default False;
  end;

  // **************************************************************************
  // TEasyEnumFormatEtcManager
  //   The EnumFormatEtc Manager encapsulates the IEnumFormatEtc interface for
  // use with the IDataObject (below) interface for Drag/Drop and Clipboard
  // operations.
  // **************************************************************************
  TEasyEnumFormatEtcManager = class(TEasyInterfacedPersistent, IEnumFormatEtc)
  private
    FInternalIndex: integer;        // Keeps an internal reference to which format is being enumerated
    FFormats: TFormatEtcArray;  // Keeps a list of all formats that are avaialable in the IDataObject
  protected
    { IEnumFormatEtc }
    function Next(celt: Longint; out elt; pceltFetched: PLongint): HResult; stdcall;
    function Skip(celt: Longint): HResult; stdcall;
    function Reset: HResult; stdcall;
    function Clone(out Enum: IEnumFormatEtc): HResult; stdcall;

    property InternalIndex: integer read FInternalIndex write FInternalIndex;
  public
    constructor Create;
    destructor Destroy; override;

    property Formats: TFormatEtcArray read FFormats write FFormats;
  end;

  // **************************************************************************
  // TEasyDataObjectManager
  //   The Data Object Manager encapsulates the IDataObject interface into a
  // Delphi class.  It is used for Drag/Drop and Clipboard operations
  // **************************************************************************
  TEasyDataObjectManager = class(TCommonDataObject, IDataObject, ICommonDataObject, ICommonExtractObj)
  private
    FListview: TCustomEasyListview;
  protected
    procedure DoGetCustomFormats(var Formats: TFormatEtcArray); override;
    procedure DoOnGetData(const FormatEtcIn: TFormatEtc; var Medium: TStgMedium; var Handled: Boolean); override;
    procedure DoOnQueryGetData(const FormatEtcIn: TFormatEtc; var FormatAvailable: Boolean; var Handled: Boolean); override;
  public
    property Listview: TCustomEasyListview read FListview write FListview;
  end;     

  // **************************************************************************
  // TEasyDragManagerBase
  //   Easy Base Drag Manager defines the common actions of the Drag Select
  // and object Drag Manager, including autoscroll
  // **************************************************************************
  TCustomEasyDragManagerBase = class(TEasyOwnedPersistent)
  private
    FAutoScroll: Boolean;
    FAutoScrollDelay: Integer;
    FAutoScrollDelayMet: Boolean;
    FAutoScrollTime: Integer;
    FDataObject: IDataObject;
    FMouseButton: TCommonMouseButtons;   // Defines what mouse buttons are used for a drag
    FRegistered: Boolean;
    FTimer: TTimer;
    FDragState: TEasyDragManagerStates;
    FLastKeyState: TCommonKeyStates;
    FEnabled: Boolean;
    FAutoScrollAccelerator: Byte;
    FAutoScrollMargin: Integer;
    function GetAutoScrolling: Boolean;
    function GetDragging: Boolean;
    function GetTimer: TTimer;
    procedure SetRegistered(Value: Boolean);
  protected
    procedure AutoScrollWindow; virtual;
    procedure DoAfterAutoScroll; virtual;
    procedure DoBeforeAutoScroll; virtual;
    procedure DoAutoScroll(DeltaX, DeltaY: Integer); virtual;
    procedure DoDrag(Canvas: TCanvas; WindowPoint: TPoint; KeyState: TCommonKeyStates; var Effects: TCommonDropEffect); virtual;
    procedure DoDragBegin(WindowPoint: TPoint; KeyState: TCommonKeyStates); virtual;
    procedure DoDragDrop(WindowPoint: TPoint; KeyState: TCommonKeyStates; var Effects: TCommonDropEffect); virtual;
    procedure DoDragEnd(Canvas: TCanvas; WindowPoint: TPoint; KeyState: TCommonKeyStates); virtual;
    procedure DoDragEnter(const DataObject: IDataObject; Canvas: TCanvas; WindowPoint: TPoint; KeyState: TCommonKeyStates; var Effects: TCommonDropEffect); virtual;
    procedure DoGetDragImage(Bitmap: TBitmap; DragStartPt: TPoint; var HotSpot: TPoint; var TransparentColor: TColor; var Handled: Boolean); virtual;
    procedure DoOLEDragEnd(const ADataObject: IDataObject; DragResult: TCommonOLEDragResult; ResultEffect: TCommonDropEffects; KeyStates: TCommonKeyStates); virtual;
    procedure DoOLEDragStart(const ADataObject: IDataObject; var AvailableEffects: TCommonDropEffects; var AllowDrag: Boolean); virtual;
    function DoPtInAutoScrollDownRegion(WindowPoint: TPoint): Boolean; virtual;
    function DoPtInAutoScrollLeftRegion(WindowPoint: TPoint): Boolean; virtual;
    function DoPtInAutoScrollRightRegion(WindowPoint: TPoint): Boolean; virtual;
    function DoPtInAutoScrollUpRegion(WindowPoint: TPoint): Boolean; virtual;
    procedure DoEnable(Enable: Boolean); virtual;
    function PtInAutoScrollDownRegion(WindowPoint: TPoint): Boolean;
    function PtInAutoScrollLeftRegion(WindowPoint: TPoint): Boolean;
    function PtInAutoScrollRegion(WindowPoint: TPoint): Boolean;
    function PtInAutoScrollRightRegion(WindowPoint: TPoint): Boolean;
    function PtInAutoScrollUpRegion(WindowPoint: TPoint): Boolean;
    function ScrollDeltaDown(WindowPoint: TPoint): Integer; virtual;
    function ScrollDeltaLeft(WindowPoint: TPoint): Integer; virtual;
    function ScrollDeltaRight(WindowPoint: TPoint): Integer; virtual;
    function ScrollDeltaUp(WindowPoint: TPoint): Integer; virtual;
    procedure RegisterOLEDragDrop(DoRegister: Boolean);
    procedure SetEnabled(const Value: Boolean); virtual;
    procedure UpdateAfterAutoScroll; virtual;
    procedure VCLDragStart; virtual;
    property DataObject: IDataObject read FDataObject write FDataObject;
    property LastKeyState: TCommonKeyStates read FLastKeyState write FLastKeyState;
  public
    constructor Create(AnOwner: TCustomEasyListview); override;
    destructor Destroy; override;

    procedure BeginDrag(WindowPoint: TPoint; KeyState: TCommonKeyStates); virtual;
    procedure Drag(Canvas: TCanvas; WindowPoint: TPoint; KeyState: TCommonKeyStates; var Effects: TCommonDropEffect);
    procedure DragEnd(Canvas: TCanvas; WindowPoint: TPoint; KeyState: TCommonKeyStates);
    procedure DragEnter(const ADataObject: IDataObject; Canvas: TCanvas; WindowPoint: TPoint; KeyState: TCommonKeyStates; var Effects: TCommonDropEffect);
    procedure DragDrop(WindowPoint: TPoint; KeyState: TCommonKeyStates; var Effects: TCommonDropEffect);
    procedure HandleTimer(Sender: TObject); virtual;
    procedure WMKeyDown(var Msg: TWMKeyDown); virtual;

    property AutoScroll: Boolean read FAutoScroll write FAutoScroll default True;
    property AutoScrollAccelerator: Byte read FAutoScrollAccelerator write FAutoScrollAccelerator default 2;
    property AutoScrollDelay: Integer read FAutoScrollDelay write FAutoScrollDelay default _AUTOSCROLLDELAY;
    property AutoScrollDelayMet: Boolean read FAutoScrollDelayMet;
    property AutoScrolling: Boolean read GetAutoScrolling;
    property AutoScrollMargin: Integer read FAutoScrollMargin write FAutoScrollMargin default 15;
    property AutoScrollTime: Integer read FAutoScrollTime write FAutoScrollTime default _AUTOSCROLLTIME;
    property Dragging: Boolean read GetDragging;
    property DragState: TEasyDragManagerStates read FDragState write FDragState;
    property Enabled: Boolean read FEnabled write SetEnabled;
    property MouseButton: TCommonMouseButtons read FMouseButton write FMouseButton default [cmbLeft];
    property Timer: TTimer read GetTimer write FTimer;
    property Registered: Boolean read FRegistered write SetRegistered;
  published
  end;

  // **************************************************************************
  // TEasyHeaderDragManager
  //   Drag Manager is associated with the Header in the EasyListview for
  // dragging columns around
  // **************************************************************************
  TEasyHeaderDragManager = class(TCustomEasyDragManagerBase)
  private
    FAllowDrop: Boolean;
    FColumn: TEasyColumn;
    FDefaultImageEvent: TGetDragImageEvent;
    FDragImageHeight: Integer;
    FDragImageWidth: Integer;
    FDragMode: TDragMode; // VCL Only
    FDragType: TEasyDragType;
    FEnableDragImage: Boolean;
    FHeader: TEasyHeader;
    FTargetColumn: TEasyColumn;
    function GetDragCursor: TCursor;
    function GetDragMode: TDragMode;
    procedure SetDragCursor(Value: TCursor);
    procedure SetDragMode(Value: TDragMode);
    procedure SetDragType(Value: TEasyDragType);
  protected
    function DoPtInAutoScrollDownRegion(WindowPoint: TPoint): Boolean; override;
    function DoPtInAutoScrollUpRegion(WindowPoint: TPoint): Boolean; override;
    procedure DefaultImage(Sender: TCustomEasyListview; Image: TBitmap; DragStartPt: TPoint; var HotSpot: TPoint; var TransparentColor: TColor; var Handled: Boolean); virtual;
    procedure DoDrag(Canvas: TCanvas; WindowPoint: TPoint; KeyState: TCommonKeyStates; var Effects: TCommonDropEffect); override;
    procedure DoDragBegin(WindowPoint: TPoint; KeyStates: TCommonKeyStates); override;
    procedure DoDragDrop(WindowPoint: TPoint; KeyState: TCommonKeyStates; var Effects: TCommonDropEffect); override;
    procedure DoDragEnd(Canvas: TCanvas; WindowPoint: TPoint; KeyState: TCommonKeyStates); override;
    procedure DoDragEnter(const DataObject: IDataObject; Canvas: TCanvas; WindowPoint: TPoint; KeyState: TCommonKeyStates; var Effects: TCommonDropEffect); override;
    procedure DoOLEDragEnd(const ADataObject: IDataObject; DragResult: TCommonOLEDragResult; ResultEffect: TCommonDropEffects; KeyStates: TCommonKeyStates); override;
    procedure DoOLEDragStart(const ADataObject: IDataObject; var AvailableEffects: TCommonDropEffects; var AllowDrag: Boolean); override;
    function DoPtInAutoScrollLeftRegion(WindowPoint: TPoint): Boolean; override;
    function DoPtInAutoScrollRightRegion(WindowPoint: TPoint): Boolean; override;
    procedure ImageSize(var Width, Height: Integer); virtual;
    property AllowDrop: Boolean read FAllowDrop write FAllowDrop;
    property Column: TEasyColumn read FColumn write FColumn;
    property DefaultImageEvent: TGetDragImageEvent read FDefaultImageEvent write FDefaultImageEvent;
    property DragImageHeight: Integer read FDragImageHeight write FDragImageHeight default 300;
    property DragImageWidth: Integer read FDragImageWidth write FDragImageWidth default 200;
    property DragMode: TDragMode read GetDragMode write SetDragMode default dmManual;
    property DragType: TEasyDragType read FDragType write SetDragType default edtOLE;
    property Header: TEasyHeader read FHeader write FHeader;
    property TargetColumn: TEasyColumn read FTargetColumn write FTargetColumn;
  public
    constructor Create(AnOwner: TCustomEasyListview); override;
  published
    property AutoScroll;
    property AutoScrollAccelerator;
    property AutoScrollDelay;
    property AutoScrollMargin;
    property AutoScrollTime;
    property DragCursor: TCursor read GetDragCursor write SetDragCursor default crDrag;
    property EnableDragImage: Boolean read FEnableDragImage write FEnableDragImage default True;
    property MouseButton;
  end;

  // **************************************************************************
  // TEasyDragManager
  //   Easy Drag Manager is associated with a TWinContol to track the
  // dragging of the cells in a Easy Control
  // **************************************************************************
  TEasyOLEDragManager = class(TEasyHeaderDragManager)
  private
    FDragAnchorPt: TPoint;
    FDragItem: TEasyItem;
    FDragTarget: Boolean;
    FDropTarget: TEasyItem;
    FHilightDropTarget: Boolean;
  protected
    procedure ClearDragItem;
    procedure ClearDropTarget;
    procedure DefaultImage(Sender: TCustomEasyListview; Image: TBitmap; DragStartPt: TPoint; var HotSpot: TPoint; var TransparentColor: TColor; var Handled: Boolean); override;
    procedure DoDrag(Canvas: TCanvas; WindowPoint: TPoint; KeyState: TCommonKeyStates; var Effects: TCommonDropEffect); override;
    procedure DoDragDrop(WindowPoint: TPoint; KeyState: TCommonKeyStates; var Effects: TCommonDropEffect); override;
    procedure DoDragEnd(Canvas: TCanvas; WindowPoint: TPoint; KeyState: TCommonKeyStates); override;
    procedure DoDragEnter(const DataObject: IDataObject; Canvas: TCanvas; WindowPoint: TPoint; KeyState: TCommonKeyStates; var Effects: TCommonDropEffect); override;
    procedure DoGetDragImage(Bitmap: TBitmap; DragStartPt: TPoint; var HotSpot: TPoint; var TransparentColor: TColor; var Handled: Boolean); override;
    procedure DoOLEDragEnd(const ADataObject: IDataObject; DragResult: TCommonOLEDragResult; ResultEffect: TCommonDropEffects; KeyStates: TCommonKeyStates); override;
    procedure DoOLEDragStart(const ADataObject: IDataObject; var AvailableEffects: TCommonDropEffects; var AllowDrag: Boolean); override;
    function DoPtInAutoScrollDownRegion(WindowPoint: TPoint): Boolean; override;
    function DoPtInAutoScrollUpRegion(WindowPoint: TPoint): Boolean; override;
    procedure ImageSize(var Width: Integer; var Height: Integer); override;
    procedure SetEnabled(const Value: Boolean); override;
    procedure VCLDragStart; override;
  public
    constructor Create(AnOwner: TCustomEasyListview); override;
    destructor Destroy; override;

    procedure FinalizeDrag(KeyState: TCommonKeyStates);
    function InitializeDrag(HitItem: TEasyItem; WindowPoint: TPoint;  KeyState: TCommonKeyStates): Boolean;
    property DragAnchorPt: TPoint read FDragAnchorPt write FDragAnchorPt;
    property DragItem: TEasyItem read FDragItem;
    property DragTarget: Boolean read FDragTarget;
    property DropTarget: TEasyItem read FDropTarget;
  published
    property AutoScroll;
    property AutoScrollAccelerator;
    property AutoScrollDelay;
    property AutoScrollMargin;
    property AutoScrollTime;
    property DragImageHeight;
    property DragImageWidth;
    property DragMode;
    property DragType;
    property Enabled default False;
    property HilightDropTarget: Boolean read FHilightDropTarget write FHilightDropTarget default True;
    property MouseButton;
  end;

  // **************************************************************************
  // TEasySelectionManager
  //   Easy Select Manager is associated with a TWinContol to track the
  // selection and focus of the cells in a Easy Control.
  // **************************************************************************
  TEasySelectionManager = class(TEasyOwnedPersistent)
  private
    FAlphaBlend: Boolean;
    FAlphaBlendSelRect: Boolean;
    FBlendAlphaImage: Byte;
    FBlendAlphaSelRect: Byte;
    FBlendAlphaTextRect: Byte;
    FBlendColorIcon: TColor;
    FBlendColorSelRect: TColor;
    FBlendIcon: Boolean;
    FBlurAlphaBkGnd: Boolean;
    FBorderColor: TColor;
    FBorderColorSelRect: TColor;
    FColor: TColor;
    FCount: Integer;
    FFocusedColumn: TEasyColumn;
    FFocusedItem: TEasyItem;
    FAnchorItem: TEasyItem;
    FFocusedGroup: TEasyGroup;
    FEnabled: Boolean;
    FForceDefaultBlend: Boolean;
    FFullCellPaint: Boolean;
    FFullItemPaint: Boolean;
    FFullRowSelect: Boolean;
    FGradient: Boolean;
    FGradientColorBottom: TColor;
    FGradientColorTop: TColor;
    FGroupSelections: Boolean;
    FGroupSelectUpdateCount: Integer;
    FInactiveBorderColor: TColor;
    FInactiveColor: TColor;
    FInactiveTextColor: TColor;
    FItemsToggled: Integer;
    FMouseButton: TCommonMouseButtons;
    FMultiChangeCount: Integer;
    FMultiSelect: Boolean;
    FPopupMode: Boolean;
    FRectSelect: Boolean; // A Click-Shift Select will use the Rectangle of the click and the Anchor Item vs. Selecting all from the Anchor Item Index to the selected Item index
    FResizeGroupOnFocus: Boolean; // If true and a focused caption will overlap next group, the group is resized to fit focused caption
    FRoundRect: Boolean;
    FRoundRectRadius: Byte;
    FTextColor: TColor;
    FUseFocusRect: Boolean;
    function GetAutoScroll: Boolean;
    function GetAutoScrollAccelerator: Byte;
    function GetAutoScrollDelay: Integer;
    function GetAutoScrollMargin: Integer;
    function GetAutoScrollTime: Integer;
    function GetEnableDragSelect: Boolean;
    function GeTCommonMouseButton: TCommonMouseButtons;
    function GetFocusedColumn: TEasyColumn;
    function GetMouseButtonSelRect: TCommonMouseButtons;
    function GetSelecting: Boolean;
    procedure SetAutoScroll(Value: Boolean);
    procedure SetAutoScrollAccelerator(Value: Byte);
    procedure SetAutoScrollDelay(Value: Integer);
    procedure SetAutoScrollMargin(Value: Integer);
    procedure SetAutoScrollTime(Value: Integer);
    procedure SetBlendIcon(Value: Boolean);
    procedure SetBlurAlphaBkGnd(const Value: Boolean);
    procedure SetEnableDragSelect(Value: Boolean);
    procedure SetFocusedColumn(Value: TEasyColumn);
    procedure SetFocusedItem(Value: TEasyItem);
    procedure SetFocusedGroup(const Value: TEasyGroup);
    procedure SetEnabled(const Value: Boolean);
    procedure SeTCommonMouseButton(Value: TCommonMouseButtons);
    procedure SetFullCellPaint(Value: Boolean);
    procedure SetFullItemPaint(Value: Boolean);
    procedure SetGradient(const Value: Boolean);
    procedure SetGradientColorBottom(const Value: TColor);
    procedure SetGradientColorTop(const Value: TColor);
    procedure SetGroupSelections(Value: Boolean);
    procedure SetMouseButtonSelRect(Value: TCommonMouseButtons);
    procedure SetMultiSelect(const Value: Boolean);
    procedure SetAnchorItem(Value: TEasyItem);
  protected
    procedure ActOnAll(SelectType: TEasySelectionType; ExceptItem: TEasyItem);
    procedure BuildSelectionGroupings(Force: Boolean); virtual;
    procedure DragSelect(KeyStates: TCommonKeyStates);
    procedure GainingSelection(Item: TEasyItem);
    procedure LosingSelection(Item: TEasyItem);
    procedure NotifyOwnerListview;
    property ItemsToggled: Integer read FItemsToggled write FItemsToggled;
    property MultiChangeCount: Integer read FMultiChangeCount write FMultiChangeCount;
  public
    constructor Create(AnOwner: TCustomEasyListview); override;
    destructor Destroy; override;

    function SelectedToArray: TEasyItemArray;
    procedure ClearAll;
    procedure ClearAllExcept(Item: TEasyItem);
    function First: TEasyItem;
    function FirstInGroup(Group: TEasyGroup): TEasyItem;
    procedure FocusFirst;
    procedure DecMultiChangeCount;
    procedure DeleteSelected(SelectPrevItem: Boolean = False);
    procedure GroupSelectBeginUpdate;
    procedure GroupSelectEndUpdate;
    procedure IncMultiChangeCount;
    procedure InvalidateVisibleSelected(ValidateWindow: Boolean);
    procedure Invert;
    function Next(Item: TEasyItem): TEasyItem;
    function NextInGroup(Group: TEasyGroup; Item: TEasyItem): TEasyItem;
    procedure SelectAll;
    procedure SelectFirst;
    procedure SelectGroupItems(Group: TEasyGroup; ClearOtherItems: Boolean);
    procedure SelectRange(FromItem, ToItem: TEasyItem; RectSelect: Boolean; ClearFirst: Boolean);
    procedure SelectRect(ViewportSelRect: TRect; ClearFirst: Boolean);

    property AnchorItem: TEasyItem read FAnchorItem write SetAnchorItem;
    property Count: Integer read FCount;
    property FocusedColumn: TEasyColumn read GetFocusedColumn write SetFocusedColumn;
    property FocusedItem: TEasyItem read FFocusedItem write SetFocusedItem;
    property FocusedGroup: TEasyGroup read FFocusedGroup write SetFocusedGroup;
    property Selecting: Boolean read GetSelecting;
  published
    property AlphaBlend: Boolean read FAlphaBlend write FAlphaBlend default False;
    property AlphaBlendSelRect: Boolean read FAlphaBlendSelRect write FAlphaBlendSelRect default False;
    property AutoScroll: Boolean read GetAutoScroll write SetAutoScroll default True;
    property AutoScrollAccelerator: Byte read GetAutoScrollAccelerator write SetAutoScrollAccelerator default 2;
    property AutoScrollDelay: Integer read GetAutoScrollDelay write SetAutoScrollDelay default _AUTOSCROLLDELAY;
    property AutoScrollMargin: Integer read GetAutoScrollMargin write SetAutoScrollMargin default 15;
    property AutoScrollTime: Integer read GetAutoScrollTime write SetAutoScrollTime default _AUTOSCROLLTIME;
    property BlendAlphaImage: Byte read FBlendAlphaImage write FBlendAlphaImage default 128;
    property BlendAlphaSelRect: Byte read FBlendAlphaSelRect write FBlendAlphaSelRect default 70;
    property BlendAlphaTextRect: Byte read FBlendAlphaTextRect write FBlendAlphaTextRect default 128;
    property BlendColorSelRect: TColor read FBlendColorSelRect write FBlendColorSelRect default clHighlight;
    property BlendIcon: Boolean read FBlendIcon write SetBlendIcon default True;
    property BlurAlphaBkGnd: Boolean read FBlurAlphaBkGnd write SetBlurAlphaBkGnd default False;
    property BorderColor: TColor read FBorderColor write FBorderColor default clHighlight;
    property BorderColorSelRect: TColor read FBorderColorSelRect write FBorderColorSelRect default clHighlight;
    property Color: TColor read FColor write FColor default clHighlight;
    property Enabled: Boolean read FEnabled write SetEnabled default True;
    property EnableDragSelect: Boolean read GetEnableDragSelect write SetEnableDragSelect default False;
    property ForceDefaultBlend: Boolean read FForceDefaultBlend write FForceDefaultBlend default False;
    property FullCellPaint: Boolean read FFullCellPaint write SetFullCellPaint default False;
    property FullItemPaint: Boolean read FFullItemPaint write SetFullItemPaint default False;
    property FullRowSelect: Boolean read FFullRowSelect write FFullRowSelect default False;
    property Gradient: Boolean read FGradient write SetGradient default False;
    property GradientColorBottom: TColor read FGradientColorBottom write SetGradientColorBottom default $FCEFD5;
    property GradientColorTop: TColor read FGradientColorTop write SetGradientColorTop default $FDF8F1;
    property GroupSelections: Boolean read FGroupSelections write SetGroupSelections default False;
    property InactiveBorderColor: TColor read FInactiveBorderColor write FInactiveBorderColor default clInactiveBorder;
    property InactiveColor: TColor read FInactiveColor write FInactiveColor default clInactiveBorder;
    property InactiveTextColor: TColor read FInactiveTextColor write FInactiveTextColor default clBlack;
    property MouseButton: TCommonMouseButtons read GeTCommonMouseButton write SeTCommonMouseButton default [cmbLeft];
    property MouseButtonSelRect: TCommonMouseButtons read GetMouseButtonSelRect write SetMouseButtonSelRect default [cmbLeft, cmbRight];
    property MultiSelect: Boolean read FMultiSelect write SetMultiSelect default False;
    property PopupMode: Boolean read FPopupMode write FPopupMode default False;
    property RectSelect: Boolean read FRectSelect write FRectSelect default False;
    property ResizeGroupOnFocus: Boolean read FResizeGroupOnFocus write FResizeGroupOnFocus default False;
    property RoundRect: Boolean read FRoundRect write FRoundRect default False;
    property RoundRectRadius: Byte read FRoundRectRadius write FRoundRectRadius default 4;
    property TextColor: TColor read FTextColor write FTextColor default clHighlightText;
    property UseFocusRect: Boolean read FUseFocusRect write FUseFocusRect default True;
  end;

  // **************************************************************************
  // TEasyCheckManager
  //    Manages the check state of the items.  The item carries the check state
  // of itself but this manager allows manipulation of multiple items
  // **************************************************************************
  TEasyCheckManager = class(TEasyOwnedPersistent)
  private
    FCount: Integer;              // Number of items with checks set
    FPendingObject: TEasyCollectionItem; // When the mouse button is pressed over an item PendingCheckItem is set to that item
    procedure SetPendingObject(Value: TEasyCollectionItem);
  protected
    property PendingObject: TEasyCollectionItem read FPendingObject write SetPendingObject;
  public
    function CheckedToArray: TEasyItemArray;
    procedure CheckAll;
    procedure CheckAllInGroup(Group: TEasyGroup);
    function First: TEasyItem;
    function FirstInGroup(Group: TEasyGroup): TEasyCollectionItem;
    function Next(Item: TEasyItem): TEasyItem;
    function NextInGroup(Item: TEasyItem): TEasyItem;
    procedure DeleteChecked;
    procedure UnCheckAll;
    procedure UnCheckAllInGroup(Group: TEasyGroup);

    property Count: Integer read FCount;
  end;

  TEasyHotTrackManager = class(TEasyOwnedPersistent)
  private
    FColor: TColor;
    FColumnTrack: TEasyHotTrackRectColumns;
    FEnabled: Boolean;
    FCursor: TCursor;
    FGroupTrack: TEasyHotTrackRectGroups;
    FItemTrack: TEasyHotTrackRectItems;
    FOnlyFocused: Boolean;
    FPendingObject: TEasyCollectionItem;
    FPendingObjectCheck: TEasyCollectionItem;
    FUnderline: Boolean;
    function GetPendingObject(MousePos: TPoint): TEasyCollectionItem;
    procedure SetPendingObject(MousePos: TPoint; Value: TEasyCollectionItem);
    procedure SetPendingObjectCheck(const Value: TEasyCollectionItem);
  protected
    property PendingObject[MousePos: TPoint]: TEasyCollectionItem read GetPendingObject write SetPendingObject;
    property PendingObjectCheck: TEasyCollectionItem read FPendingObjectCheck write SetPendingObjectCheck;
  public
    constructor Create(AnOwner: TCustomEasyListview); override;
  published
    property Color: TColor read FColor write FColor default clHighlight;
//    property ColumnTrack: TEasyHotTrackRectColumns read FColumnTrack write FColumnTrack default [htcImage, htcText];
    property Enabled: Boolean read FEnabled write FEnabled default False;
    property Cursor: TCursor read FCursor write FCursor default crHandPoint;
    property GroupTrack: TEasyHotTrackRectGroups read FGroupTrack write FGroupTrack default [htgIcon, htgText];
    property ItemTrack: TEasyHotTrackRectItems read FItemTrack write FItemTrack default [htiIcon, htiText];
    property OnlyFocused: Boolean read FOnlyFocused write FOnlyFocused default False;
    property Underline: Boolean read FUnderline write FUnderline default True;
  end;

  // **************************************************************************
  // TEasyScrollbarManager
  //   ScrollManager is associated with a TWinContol to handle the
  // controls scrollbars
  // **************************************************************************
  TEasyScrollbarManager = class(TEasyOwnedPersistent)
  private
    FHorzEnabled: Boolean;
    FOffsetX: Integer;
    FOffsetY: Integer;
    FRebuilding: Boolean;
    FSnapHorzView: Boolean;  // Causes scroll to snap to column width
    FVertEnabled: Boolean;
    FViewRect: TRect;

    function GetHorzBarVisible: Boolean;
    function GetLine: Integer;
    function GetMaxOffsetX: Integer;
    function GetMaxOffsetY: Integer;
    function GetVertBarVisible: Boolean;
    function GetViewHeight: Integer;
    function GetViewWidth: Integer;
    procedure SetHorzEnabled(const Value: Boolean);
    procedure SetOffsetX(const Value: Integer);
    procedure SetOffsetY(const Value: Integer);
    procedure SetVertEnabled(const Value: Boolean);
  protected
  public
    constructor Create(AnOwner: TCustomEasyListview); override;
    destructor Destroy; override;

    function MapWindowToView(WindowPoint: TPoint; AccountForHeader: Boolean = True): TPoint; overload;
    function MapWindowToView(WindowPoint: TSmallPoint; AccountForHeader: Boolean = True): TPoint; overload;
    function MapWindowRectToViewRect(WindowRect: TRect; AccountForHeader: Boolean = True): TRect;
    function MapViewToWindow(ViewportPoint: TPoint; AccountForHeader: Boolean = True): TPoint; overload;
    function MapViewToWindow(ViewportPoint: TSmallPoint; AccountForHeader: Boolean = True): TPoint; overload;
    function MapViewRectToWindowRect(ViewPortRect: TRect; AccountForHeader: Boolean = True): TRect;
    procedure ReCalculateScrollbars(Redraw: Boolean; Force: Boolean);
    procedure Scroll(DeltaX, DeltaY: Integer);
    procedure SetViewRect(AViewRect: TRect; InvalidateWindow: Boolean);
    procedure ValidateOffsets(var AnOffsetX, AnOffsetY: Integer);
    function ViewableViewportRect: TRect;
    procedure WMHScroll(var Msg: TWMVScroll);
    procedure WMKeyDown(var Msg: TWMKeyDown);
    procedure WMVScroll(var Msg: TWMVScroll);

    property HorzBarVisible: Boolean read GetHorzBarVisible;
    property Line: Integer read GetLine;
    property MaxOffsetX: Integer read GetMaxOffsetX;
    property MaxOffsetY: Integer read GetMaxOffsetY;
    property OffsetX: Integer read FOffsetX write SetOffsetX;
    property OffsetY: Integer read FOffsetY write SetOffsetY;
    property Rebuilding: Boolean read FRebuilding;
    property VertBarVisible: Boolean read GetVertBarVisible;
    property ViewHeight: Integer read GetViewHeight;
    property ViewRect: TRect read FViewRect;
    property ViewWidth: Integer read GetViewWidth;
  published
    property HorzEnabled: Boolean read FHorzEnabled write SetHorzEnabled default True;
    property SnapHorzView: Boolean read FSnapHorzView write FSnapHorzView default True;
    property VertEnabled: Boolean read FVertEnabled write SetVertEnabled default True;
  end;

  // **************************************************************************
  // TEasyBackgroundManager
  //   Easy Background handles the drawing of a background in Easy
  // controls.  It is a stand alone component that can be shared
  // **************************************************************************
  TEasyBackgroundManager = class(TEasyOwnedPersistent)
  private
    FAlphaBlend: Boolean;
    FAlphaBlender: TEasyAlphaBlender;
    FBlendAlpha: Integer;
    FBlendMode: TCommonBlendMode;
    FCaption: WideString;
    FCaptionAlignment: TAlignment;
    FCaptionShow: Boolean;
    FCaptionShowOnlyWhenEmpty: Boolean;
    FCaptionSingleLine: Boolean;
    FCaptionVAlignment: TCommonVAlignment;
    FEnabled: Boolean;
    FImage: TBitmap;
    FOffsetTrack: Boolean;
    FOffsetX: Integer;
    FOffsetY: Integer;
    FTile: Boolean;
    FTransparent: Boolean;
    FAlphaImage: TBitmap;
    procedure SetAlphaBlend(const Value: Boolean);
    procedure SetAlphaImage(const Value: TBitmap);
    procedure SetBlendAlpha(const Value: Integer);
    procedure SetCaption(const Value: WideString);
    procedure SetCaptionAlignment(const Value: TAlignment);
    procedure SetCaptionShow(const Value: Boolean);
    procedure SetCaptionShowOnlyWhenEmpty(const Value: Boolean);
    procedure SetCaptionSingleLine(const Value: Boolean);
    procedure SetCaptionVAlignment(const Value: TCommonVAlignment);
    procedure SetCommonBlendMode(const Value: TCommonBlendMode);
    procedure SetEnabled(const Value: Boolean);
    procedure SetImage(const Value: TBitmap);
    procedure SetOffsetX(const Value: Integer);
    procedure SetOffsetY(const Value: Integer);
    procedure SetTile(const Value: Boolean);
    procedure SetTransparent(const Value: Boolean);
  protected
    procedure ChangeBitmapBits(Sender: TObject);
    property AlphaBlender: TEasyAlphaBlender read FAlphaBlender write FAlphaBlender;
  public
    constructor Create(AnOwner: TCustomEasyListview); override;
    destructor Destroy; override;

    procedure Assign(Source: TPersistent); override;
    procedure AssignTo(Target: TPersistent); override;
    procedure PaintTo(ACanvas: TCanvas; ARect: TRect; PaintDefault: Boolean); virtual;
    procedure WMWindowPosChanging(var Msg: TWMWindowPosChanging); virtual;

  published
    property AlphaBlend: Boolean read FAlphaBlend write SetAlphaBlend default False;
    property AlphaImage: TBitmap read FAlphaImage write SetAlphaImage;
    property BlendAlpha: Integer read FBlendAlpha write SetBlendAlpha default 128;
    property BlendMode: TCommonBlendMode read FBlendMode write SetCommonBlendMode default cbmConstantAlphaAndColor;
    property Caption: WideString read FCaption write SetCaption;
    property CaptionAlignment: TAlignment read FCaptionAlignment write SetCaptionAlignment default taCenter;
    property CaptionShow: Boolean read FCaptionShow write SetCaptionShow default False;
    property CaptionShowOnlyWhenEmpty: Boolean read FCaptionShowOnlyWhenEmpty write SetCaptionShowOnlyWhenEmpty default True;
    property CaptionSingleLine: Boolean read FCaptionSingleLine write SetCaptionSingleLine default True;
    property CaptionVAlignment: TCommonVAlignment read FCaptionVAlignment write SetCaptionVAlignment default cvaCenter;
    property Enabled: Boolean read FEnabled write SetEnabled default False;
    property Image: TBitmap read FImage write SetImage;
    property OffsetTrack: Boolean read FOffsetTrack write FOffsetTrack default False;
    property OffsetX: Integer read FOffsetX write SetOffsetX default 0;
    property OffsetY: Integer read FOffsetY write SetOffsetY default 0;
    property Tile: Boolean read FTile write SetTile default True;
    property Transparent: Boolean read FTransparent write SetTransparent default False;
  end;

  // **************************************************************************
  // TEasyTaskBandBackgroundManager
  //   Easy Background handles the drawing of a background in Easy
  // controls.  It is a stand alone component that can be shared
  // **************************************************************************
  TEasyTaskBandBackgroundManager = class(TEasyBackgroundManager)
  public
    procedure PaintTo(ACanvas: TCanvas; ARect: TRect; PaintDefault: Boolean); override;
  end;

  // **************************************************************************
  // TEasyDropTargetManager
  //   Implements the IDropTarget inteface to allow for the control to become
  // a drag/drop target and accept drops.
  // **************************************************************************
  TEasyDropTargetManager = class(TEasyOwnedInterfacedPersistent, IDropTarget)
  private
    FDragManager: TCustomEasyDragManagerBase;
    FDropTargetHelper: IDropTargetHelper;   // Win2k and up drag image support built in to Windows
    function GetDropTargetHelper: IDropTargetHelper;
  protected
    function DragEnter(const dataObj: IDataObject; grfKeyState: Longint; pt: TPoint; var dwEffect: Longint): HResult; stdcall;
    function DragOver(grfKeyState: Longint; pt: TPoint; var dwEffect: Longint): HResult; stdcall;
    function DragLeave: HResult; stdcall;
    function Drop(const dataObj: IDataObject; grfKeyState: Longint; pt: TPoint; var dwEffect: Longint): HResult; stdcall;

    property DragManager: TCustomEasyDragManagerBase read FDragManager write FDragManager;
    property DropTargetHelper: IDropTargetHelper read GetDropTargetHelper;
  public
  end;

  // **************************************************************************
  // TEasyDropSourceManager
  //   Implements the IDropSource inteface to allow for the control to become
  // a drag/drop source.
  // **************************************************************************
  TEasyDropSourceManager = class(TEasyOwnedInterfacedPersistent, IDropSource)
  protected
    function QueryContinueDrag(fEscapePressed: BOOL; grfKeyState: Longint): HResult; stdcall;
    function GiveFeedback(dwEffect: Longint): HResult; stdcall;     
  end;

  // **************************************************************************
  // Drag TEasyDragRectManager Manager
  //   Easy DragRect Manager is associated with a TWinContol to handle the
  // controls drag selection rectangle
  // **************************************************************************
  TEasyDragRectManager = class(TCustomEasyDragManagerBase)
  private
    FAnchorPoint: TPoint;  // Anchor point in Viewport coordinates
    FDragPoint: TPoint;    // Dragging point in Viewport coordinates
    FOldOffsets: TPoint;
    FPrevRect: TRect;
    function GetSelectionRect: TRect;
    procedure PaintRect(Canvas: TCanvas);
    procedure SetAnchorPoint(ViewportAnchor: TPoint);
    procedure SetDragPoint(const Value: TPoint);
  protected
    procedure DoAfterAutoScroll; override;
    procedure DoAutoScroll(DeltaX, DeltaY: Integer); override;
    procedure DoBeforeAutoScroll; override;
    procedure DoDrag(Canvas: TCanvas; WindowPoint: TPoint; KeyState: TCommonKeyStates; var Effects: TCommonDropEffect); override;
    procedure DoDragBegin(WindowPoint: TPoint; KeyState: TCommonKeyStates); override;
    procedure DoDragEnd(Canvas: TCanvas; WindowPoint: TPoint; KeyState: TCommonKeyStates); override;
    procedure DoDragEnter(const DataObject: IDataObject; Canvas: TCanvas; WindowPoint: TPoint; KeyState: TCommonKeyStates; var Effects: TCommonDropEffect); override;
    function DoPtInAutoScrollDownRegion(WindowPoint: TPoint): Boolean; override;
    function DoPtInAutoScrollLeftRegion(WindowPoint: TPoint): Boolean; override;
    function DoPtInAutoScrollRightRegion(WindowPoint: TPoint): Boolean; override;
    function DoPtInAutoScrollUpRegion(WindowPoint: TPoint): Boolean; override;
    procedure UpdateAfterAutoScroll; override;
    property OldOffsets: TPoint read FOldOffsets write FOldOffsets;
  public
    constructor Create(AnOwner: TCustomEasyListview); override;
    destructor Destroy; override;

    procedure FinalizeDrag(KeyState: TCommonKeyStates);
    function InitializeDrag(WindowPoint: TPoint; KeyState: TCommonKeyStates): Boolean;
    procedure PaintSelectionRect(Canvas: TCanvas);
    function SelRectInWindowCoords: TRect;
    procedure WMKeyDown(var Msg: TWMKeyDown); override;

    property AnchorPoint: TPoint read FAnchorPoint write SetAnchorPoint;
    property DragPoint: TPoint read FDragPoint write SetDragPoint;
    property PrevRect: TRect read FPrevRect;
    property SelectionRect: TRect read GetSelectionRect;
  end;

  // > 0 (positive)  Item1 is less than Item2
  //  0  Item1 is equal to Item2
  // < 0 (negative)  Item1 is greater than Item2
  TEasySortProc = function(Column: TEasyColumn; Item1, Item2: TEasyCollectionItem): Integer;  

  TEasySorter = class
  private
    FOwner: TEasySortManager;
  public
    constructor Create(AnOwner: TEasySortManager); virtual;
    procedure Sort(Column: TEasyColumn; Collection: TEasyCollection; Min, Max: Integer; GroupCompare: TEasyDoGroupCompare; ItemCompare: TEasyDoItemCompare; UseInterfaces: Boolean); virtual; abstract;
    property Owner: TEasySortManager read FOwner write FOwner;
  end;

  TEasyQuickSort = class(TEasySorter)
  public
    procedure Sort(Column: TEasyColumn; Collection: TEasyCollection; Min, Max: Integer; GroupCompare: TEasyDoGroupCompare; ItemCompare: TEasyDoItemCompare; UseInterfaces: Boolean); override;
  end;

  TEasyBubbleSort = class(TEasySorter)
  public
    procedure Sort(Column: TEasyColumn; Collection: TEasyCollection; Min, Max: Integer; GroupCompare: TEasyDoGroupCompare; ItemCompare: TEasyDoItemCompare; UseInterfaces: Boolean); override;
  end;

  TEasyMergeSort = class(TEasySorter)
  private
    FColumn: TEasyColumn;
    FGroupCompareFunc: TEasyDoGroupCompare;
    FItemCompareFunc: TEasyDoItemCompare;
    FOwnerGroup: TEasyGroup;
  protected
    function CompareInterfaces(i1, i2: TEasyCollectionItem): Boolean;
    function CompareGroup(i1, i2: TEasyCollectionItem): Boolean;
    function CompareItem(i1, i2: TEasyCollectionItem): Boolean;
    function CompareDefault(i1, i2: TEasyCollectionItem): Boolean;
    property Column: TEasyColumn read FColumn write FColumn;
    property GroupCompareFunc: TEasyDoGroupCompare read FGroupCompareFunc write FGroupCompareFunc;
    property ItemCompareFunc: TEasyDoItemCompare read FItemCompareFunc write FItemCompareFunc;
    property OwnerGroup: TEasyGroup read FOwnerGroup write FOwnerGroup;
  public
    procedure Sort(Column: TEasyColumn; Collection: TEasyCollection; Min, Max: Integer; GroupCompare: TEasyDoGroupCompare; ItemCompare: TEasyDoItemCompare; UseInterfaces: Boolean); override;
  end;

  TGroupSortInfoRec = record
    Item: TEasyItem;
    Key: LongWord;
  end;
  TGroupSortInfoArray = array of TGroupSortInfoRec;
  // **************************************************************************
  // TEasySortManager
  //   Manages the sorting of the Listview.  Because of the way EasyListview
  // works it is not feasable to try to add an AutoSort property.  EasyListview
  // can access a caption by a callback, storing it in the control, or through
  // an interface.  Due to the wildly varying ways of storing the caption it was
  // decided to not try to find all the possible places it would be necessary to
  // AutoSort.  There are some scenarios, such as the interface access, where it
  // is not possible to detect when an autosort should be carried out.
  // **************************************************************************
  TEasySortManager = class(TEasyOwnedPersistent)
  private
    FAlgorithm: TEasySortAlgorithm; // The algorithm used when sorting
    FAutoRegroup: Boolean;
    FAutoSort: Boolean;             // Items and groups are resorted when any object is added/inserted
    FLockoutSort: Boolean;
    FSortList: TGroupSortInfoArray;
    FSorter: TEasySorter;           // The algorithm engine for the sort
    FUpdateCount: Integer;
    procedure SetAlgorithm(Value: TEasySortAlgorithm);
    procedure SetAutoRegroup(Value: Boolean);
    procedure SetAutoSort(Value: Boolean);
  protected
    function CollectionSupportsInterfaceSorting(Collection: TEasyCollection): Boolean;
    procedure GroupItem(Item: TEasyItem; ColumnIndex: Integer; Key: LongWord);
    property Sorter: TEasySorter read FSorter write FSorter;
    property SortList: TGroupSortInfoArray read FSortList write FSortList;
    property UpdateCount: Integer read FUpdateCount write FUpdateCount;
  public
    constructor Create(AnOwner: TCustomEasyListview); override;
    destructor Destroy; override;
    procedure BeginUpdate;
    procedure EndUpdate;
    procedure ReGroup(Column: TEasyColumn); virtual;
    procedure SortAll(Force: Boolean = False);
    property LockoutSort: Boolean read FLockoutSort write FLockoutSort;
  published
    property Algorithm: TEasySortAlgorithm read FAlgorithm write SetAlgorithm default esaMergeSort;
    property AutoRegroup: Boolean read FAutoRegroup write SetAutoRegroup default False;
    property AutoSort: Boolean read FAutoSort write SetAutoSort default False;
  end;

  // **************************************************************************
  // TEasyHintInfo
  //   Custom hint information for the Unicode Hint Window
  // **************************************************************************
  TEasyHintInfo = class(TEasyOwnedPersistent)
  private
    FBounds: TRect;
    FCanvas: TCanvas;
    FColor: TColor;
    FCursorPos: TPoint;
    FHideTimeout: Integer;
    FHintType: TEasyHintType;
    FMaxWidth: Integer;
    FReshowTimeout: Integer;
    FText: WideString;
    FWindowPos: TPoint;
  public
    property Canvas: TCanvas read FCanvas write FCanvas;
    property Color: TColor read FColor write FColor;
    property CursorPos: TPoint read FCursorPos write FCursorPos;
    property Bounds: TRect read FBounds write FBounds;
    property HideTimeout: Integer read FHideTimeout write FHideTimeout;
    property HintType: TEasyHintType read FHintType write FHintType;
    property MaxWidth: Integer read FMaxWidth write FMaxWidth;
    property ReshowTimeout: Integer read FReshowTimeout write FReshowTimeout;
    property Text: WideString read FText write FText;
    property WindowPos: TPoint read FWindowPos write FWindowPos;
  end;

  // **************************************************************************
  // TEasyHintWindow
  //   Custom hint window that supports Unicode and Custom Draw
  // **************************************************************************
  TEasyHintWindow = class(THintWindow)
  protected
    FEasyHintInfo: TEasyHintInfo;
    FHintInfo: PEasyHintInfoRec;

    procedure Paint; override;

    property EasyHintInfo: TEasyHintInfo read FEasyHintInfo write FEasyHintInfo;
    property HintInfo: PEasyHintInfoRec read FHintInfo write FHintInfo;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    procedure ActivateHint(ARect: TRect; const AHint: string); override;
    procedure ActivateHintData(ARect: TRect; const AHint: string; AData: Pointer); override;
    function CalcHintRect(MaxWidth: Integer; const AHint: string; AData: Pointer): TRect; override;
    function IsHintMsg(var Msg: TMsg): Boolean; override;
  end;
  TEasyHintWindowClass = class of TEasyHintWindow;


  // **************************************************************************
  // TEasyIncrementalSearchManager
  //   Implements the incremental search of the listview
  // **************************************************************************
  TEasyIncrementalSearchManager = class(TEasyOwnedPersistent)
  private
    FDirection: TEasyIncrementalSearchDir;
    FEnabled: Boolean;
    FhTimer: THandle;
    FItemType: TEasyIncrementalSearchItemType;
    FNextSearchItem: TEasyItem;
    FResetTime: Integer;
    FSearchBuffer: WideString;
    FSearchItem: TEasyItem;
    FStart: TCoolIncrementalSearchStart;
    FState: TEasyIncrementalSearchStates;
    FTimerStub: Pointer;
    procedure SetSearchItem(Value: TEasyItem);
  protected
    procedure EndTimer;
    function IsSearching: Boolean;
    procedure HandleWMChar(var Msg: TWMChar); virtual;
    procedure HandleWMKeyDown(var Msg: TWMKeyDown); virtual;
    procedure LoadFutureItem;
    procedure ResetTimer;
    procedure SearchPreloadItem(Next, Prev: TItemNextEnum; First, Last: TItemEnumFirstLast; var SearchItem: TEasyItem);
    procedure StartSearch;
    procedure StartTimer;

    procedure TimerProc(Window: HWnd; uMsg: UINT; idEvent: UINT; dwTime: DWORD); stdcall;

    property hTimer: THandle read FhTimer write FhTimer;
    property NextSearchItem: TEasyItem read FNextSearchItem write FNextSearchItem;
    property SearchBuffer: WideString read FSearchBuffer write FSearchBuffer;
    property SearchItem: TEasyItem read FSearchItem write SetSearchItem;
    property TimerStub: Pointer read FTimerStub write FTimerStub;
  public
    constructor Create(AnOwner: TCustomEasyListview); override;
    destructor Destroy; override; 
    procedure ClearSearch; virtual;
    procedure ResetSearch;
    property State: TEasyIncrementalSearchStates read FState write FState;
  published
    property Direction: TEasyIncrementalSearchDir read FDirection write FDirection default eisdForward;
    property Enabled: Boolean read FEnabled write FEnabled default False;
    property ResetTime: Integer read FResetTime write FResetTime default 2000;
    property StartType: TCoolIncrementalSearchStart read FStart write FStart default eissStartOver;
    property ItemType: TEasyIncrementalSearchItemType read FItemType write FItemType default eisiVisible;
  end;

  // **************************************************************************
  // TEasyGestureManager
  //   Implements the Gesture feature of the listview
  // **************************************************************************
  TEasyGestureManager = class(TEasyOwnedPersistent)
  private
    FButton: TCommonMouseButtons;
    FEnabled: Boolean;
    FPath: WideString;
    FTolerance: Integer;
  protected
    property Path: WideString read FPath write FPath;
  public
    constructor Create(AnOwner: TCustomEasyListview); override;
  published
    property Button: TCommonMouseButtons read FButton write FButton default [cmbRight];
    property Enabled: Boolean read FEnabled write FEnabled default False;
    property Tolerance: Integer read FTolerance write FTolerance default 3;
  end;

  //  Down here due to a bug in C++ BDS2006 hpp generated files 
  TEasyHintInfoRec = record
    HintControl: TControl;
    HintWindowClass: THintWindowClass;
    HintPos: TPoint;
    HintMaxWidth: Integer;
    HintColor: TColor;
    HintType: TEasyHintType;
    CursorRect: TRect;
    CursorPos: TPoint;
    ReshowTimeout: Integer;
    HideTimeout: Integer;
    HintStr: WideString;
    HintData: Pointer;
    Listview: TCustomEasyListview;
    TargetObj: TEasyCollectionItem;  // What the hint is being popped up over (EasyItem, EasyGroup, EasyColumn etc.)
    ToolTipRect: TRect;   // The size of the rect needed for a tool tip hint
  end;

  // **************************************************************************
  // TCustomEasyListview
  // **************************************************************************
  TCustomEasyListview = class(TCommonCanvasControl)
  private
    FAllowInvisibleCheckedItems: Boolean;
    FBackBits: TBitmap;
    FBackGround: TEasyBackgroundManager;
    FCacheDoubleBufferBits: Boolean;
    FCellSizes: TEasyCellSizes;
    FCheckManager: TEasyCheckManager;
    FDisabledBlendAlpha: Byte;
    FDisabledBlendColor: TColor;
    FDragManager: TEasyOLEDragManager;
    FDragRect: TEasyDragRectManager;
    FDropTarget: IDropTarget;
    FEditManager: TEasyEditManager;
    FForcePaint: Boolean;
    FGesture: TEasyGestureManager;
    FGlobalImages: TEasyGlobalImageManager;
    FGroupCollapseButton: TBitmap;
    FGroupExpandButton: TBitmap;
    FGroupFont: TFont;
    FGroupImageGetSize: TGroupImageGetSizeEvent;
    FGroups: TEasyGroups;
    FHeader: TEasyHeader;
    FHintAlignment: TAlignment;
    FHintData: TEasyHintInfoRec;
    FHintInfo: TEasyHintInfo;
    FHotTrack: TEasyHotTrackManager;
    FImagesExLarge: TCustomImageList;
    FImagesGroup: TCustomImageList;
    FImagesLarge: TCustomImageList;
    FImagesSmall: TCustomImageList;
    FImagesState: TCustomImageList;
    FIncrementalSearch: TEasyIncrementalSearchManager;
    FItems: TEasyGlobalItems;
    FLastMousePos: TSmallPoint;
    FNCCanvas: TCanvas;
    FOnAfterPaint: TAfterPaintEvent;
    FOnAutoGroupGetKey: TAutoGroupGetKeyEvent;
    FOnAutoSortGroupCreate: TAutoSortGroupCreateEvent;
    FOnClipboardCopy: TEasyClipboardEvent;
    FOnClipboardCut: TEasyClipboardCutEvent;
    FOnClipboardPaste: TEasyClipboardEvent;
    FOnColumnCheckChange: TColumnCheckChangeEvent;
    FOnColumnCheckChanging: TColumnCheckChangingEvent;
    FOnColumnClick: TColumnClickEvent;
    FOnColumnContextMenu: TColumnContextMenuEvent;
    FOnColumnDblClick: TColumnDblClickEvent;
    FOnColumnDropDownButtonClick: TColumnDropDownButtonClickEvent;
    FOnColumnImageDrawIsCustom: TColumnImageDrawIsCustomEvent;
    FOnColumnEnableChange: TColumnEnableChangeEvent;
    FOnColumnEnableChanging: TColumnEnableChangingEvent;
    FOnColumnFocusChanged: TColumnFocusChangeEvent;
    FOnColumnFocusChanging: TColumnFocusChangingEvent;
    FOnColumnFreeing: TColumnFreeingEvent;
    FOnColumnGetCaption: TColumnGetCaptionEvent;
    FOnColumnGetDetail: TColumnGetDetailEvent;
    FOnColumnGetDetailCount: TColumnGetDetailCountEvent;
    FOnColumnGetImageIndex: TColumnGetImageIndexEvent;
    FOnColumnGetImageList: TColumnGetImageListEvent;
    FOnColumnImageDraw: TColumnImageDrawEvent;
    FOnColumnImageGetSize: TColumnImageGetSizeEvent;
    FOnColumnInitialize: TColumnInitializeEvent;
    FOnColumnLoadFromStream: TEasyColumnLoadFromStreamEvent;
    FOnColumnPaintText: TColumnPaintTextEvent;
    FOnColumnSaveToStream: TEasyColumnSaveToStreamEvent;
    FOnColumnSelectionChanged: TColumnSelectionChangeEvent;
    FOnColumnSelectionChanging: TColumnSelectionChangingEvent;
    FOnColumnSetCaption: TColumnSetCaptionEvent;
    FOnColumnSetDetail: TColumnSetDetailEvent;
    FOnColumnSetImageIndex: TColumnSetImageIndexEvent;
    FOnColumnSizeChanged: TColumnSizeChangedEvent;
    FOnColumnSizeChanging: TColumnSizeChangingEvent;
    FOnColumnStructureChange: TNotifyEvent;
    FOnColumnThumbnailDraw: TColumnThumbnailDrawEvent;
    FOnColumnVisibilityChanged: TColumnVisibilityChangeEvent;
    FOnColumnVisibilityChanging: TColumnVisibilityChangingEvent;
    FOnContextMenu: TContextMenuEvent;
    FOnColumnCustomView: TColumnCustomViewEvent;
    FOnCustomGrid: TCustomGridEvent;
    FOnDblClick: TDblClickEvent;
    FOnMouseGesture: TEasyGestureEvent;
    FOnGetDragImage: TGetDragImageEvent;
    FOnGroupClick: TGroupClickEvent;
    FOnGroupCollapse: TGroupCollapseEvent;
    FOnGroupCollapsing: TGroupCollapsingEvent;
    FOnGroupCompare: TGroupCompareEvent;
    FOnGroupContextMenu: TGroupContextMenuEvent;
    FOnGroupCustomView: TGroupCustomViewEvent;
    FOnGroupDblClick: TGroupDblClickEvent;
    FOnGroupImageDrawIsCustom: TGroupImageDrawIsCustomEvent;
    FOnGroupExpand: TGroupExpandEvent;
    FOnGroupExpanding: TGroupExpandingEvent;
    FOnGroupFocusChanged: TGroupFocusChangeEvent;
    FOnGroupFocusChanging: TGroupFocusChangingEvent;
    FOnGroupFreeing: TGroupFreeingEvent;
    FOnGroupGetCaption: TGroupGetCaptionEvent;
    FOnGroupGetDetailCount: TGroupGetDetailCountEvent;
    FOnGroupGetDetailIndex: TGroupGetDetailEvent;
    FOnGroupGetImageIndex: TGroupGetImageIndexEvent;
    FOnGroupGetImageList: TGroupGetImageListEvent;
    FOnGroupImageDrawEvent: TGroupImageDrawEvent;
    FOnGroupInitialize: TGroupInitializeEvent;
    FOnGroupHotTrack: TGroupHotTrackEvent;
    FOnGroupLoadFromStream: TGroupLoadFromStreamEvent;
    FOnGroupPaintText: TGroupPaintTextEvent;
    FOnGroupSaveToStream: TGroupSaveToStreamEvent;
    FOnGroupSelectionChanged: TGroupSelectionChangeEvent;
    FOnGroupSelectionChanging: TGroupSelectionChangingEvent;
    FOnGroupSetCaption: TGroupSetCaptionEvent;
    FOnGroupSetImageIndex: TGroupSetImageIndexEvent;
    FOnGroupSetDetail: TGroupSetDetailEvent;
    FOnGroupStructureChange: TNotifyEvent;
    FOnGroupThumbnailDraw: TGroupThumbnailDrawEvent;
    FOnGroupVisibilityChanged: TGroupVisibilityChangeEvent;
    FOnGroupVisibilityChanging: TGroupVisibilityChangingEvent;
    FOnHeaderDblClick: THeaderDblClickEvent;
    FOnHeaderMouseDown: THeaderMouseEvent;
    FOnHeaderMouseMove: TMouseMoveEvent;
    FOnHeaderMouseUp: THeaderMouseEvent;
    FOnHintCustomDraw: THintCustomDrawEvent;
    FOnHintCustomInfo: THintCustomizeInfoEvent;
    FOnHintPauseTime: THintPauseTimeEvent;
    FOnHintPopup: THintPopupEvent;
    FOnIncrementalSearch: TIncrementalSearchEvent;
    FOnItemCheckChange: TItemCheckChangeEvent;
    FOnItemCheckChanging: TItemCheckChangingEvent;
    FOnItemClick: TItemClickEvent;
    FOnItemCompare: TItemCompareEvent;
    FOnItemContextMenu: TItemContextMenuEvent;
    FOnItemCreateEditor: TItemCreateEditorEvent;
    FOnItemCustomView: TItemCustomViewEvent;
    FOnItemDblClick: TItemDblClickEvent;
    FOnItemGetEditCaption: TEasyItemGetCaptionEvent;
    FOnItemGetEditMenu: TItemGetEditMenuEvent;
    FOnItemGetStateImageList: TItemGetImageListEvent;
    FOnItemImageDrawIsCustom: TItemImageDrawIsCustomEvent;
    FOnItemEditBegin: TItemEditBegin;
    FOnItemEdited: TItemEditedEvent;
    FOnItemEditEnd: TItemEditEnd;
    FOnItemEnableChange: TItemEnableChangeEvent;
    FOnItemEnableChanging: TItemEnableChangingEvent;
    FOnItemFocusChanged: TItemFocusChangeEvent;
    FOnItemFocusChanging: TItemFocusChangingEvent;
    FOnItemFreeing: TItemFreeingEvent;
    FOnItemGetCaption: TItemGetCaptionEvent;
    FOnItemGetGroupKey: TItemGetGroupKeyEvent;
    FOnItemHotTrack: TItemHotTrackEvent;
    FOnItemGetTileDetailIndex: TItemGetTileDetailEvent;
    FOnItemGetImageIndex: TItemGetImageIndexEvent;
    FOnItemGetImageList: TItemGetImageListEvent;
    FOnItemGetTileDetailCount: TItemGetTileDetailCountEvent;
    FOnItemImageDraw: TItemImageDrawEvent;
    FOnItemImageGetSize: TItemImageGetSizeEvent;
    FOnItemInitialize: TItemInitializeEvent;
    FOnItemLoadFromStream: TItemLoadFromStreamEvent;
    FOnItemMouseDown: TItemMouseDownEvent;
    FOnItemMouseUp: TItemMouseUpEvent;
    FOnItemPaintText: TItemPaintTextEvent;
    FOnItemSaveToStream: TItemSaveToStreamEvent;
    FOnItemSelectionChanged: TItemSelectionChangeEvent;
    FOnItemSelectionChanging: TItemSelectionChangingEvent;
    FOnItemSelectionsChanged: TEasyItemSelectionsChangedEvent;
    FOnItemSetCaption: TItemSetCaptionEvent;
    FOnItemSetGroupKey: TItemSetGroupKeyEvent;
    FOnItemSetImageIndex: TItemSetImageIndexEvent;
    FOnItemSetTileDetail: TItemSetTileDetailEvent;
    FOnItemStructureChange: TNotifyEvent;
    FOnItemThumbnailDraw: TItemThumbnailDrawEvent;
    FOnItemVisibilityChanged: TItemVisibilityChangeEvent;
    FOnItemVisibilityChanging: TItemVisibilityChangingEvent;
    FOnKeyAction: TEasyKeyActionEvent;
    FOnOLEDragDrop: TOLEDropTargetDragDropEvent;
    FOnOLEDragEnd: TOLEDropSourceDragEndEvent;
    FOnOLEDragEnter: TOLEDropTargetDragEnterEvent;
    FOnOLEDragLeave: TOLEDropTargetDragLeaveEvent;
    FOnOLEDragOver: TOLEDropTargetDragOverEvent;
    FOnOLEDragStart: TOLEDropSourceDragStartEvent;
    FOnOLEGetCustomFormats: TOLEGetCustomFormatsEvent;
    FOnOLEGetData: TOLEGetDataEvent;
    FOnOLEGetDataObject: FOLEGetDataObjectEvent;
    FOnOLEGiveFeedback: TOLEDropSourceGiveFeedbackEvent;
    FOnOLEQueryContineDrag: TOLEDropSourceQueryContineDragEvent;
    FOnOLEQueryData: TOLEQueryDataEvent;
    FOnPaintBkGnd: TPaintBkGndEvent;
    FOnPaintHeaderBkGnd: TPaintHeaderBkGndEvent;
    FOnScroll: TEasyScrollEvent;
    FOnViewChange: TViewChangedEvent;
    FPaintInfoColumn: TEasyPaintInfoBaseColumn;
    FPaintInfoGroup: TEasyPaintInfoBaseGroup;
    FPaintInfoItem: TEasyPaintInfoBaseItem;
    FPopupMenuHeader: TPopupMenu;
    FScratchCanvas: TControlCanvas;
    FScrollbars: TEasyScrollbarManager;
    FSelection: TEasySelectionManager;
    FShowGroupMargins: Boolean;
    FShowInactive: Boolean;
    FShowThemedBorder: Boolean;
    FSort: TEasySortManager;
    FStates: TEasyControlStates;
    FView: TEasyListStyle;
    FWheelMouseDefaultScroll: TEasyDefaultWheelScroll;
    FWheelMouseScrollModifierEnabled: Boolean;
    function GetGroupCollapseImage: TBitmap;
    function GetGroupExpandImage: TBitmap;
    function GetHintType: TEasyHintType;
    function GetPaintInfoColumn: TEasyPaintInfoBaseColumn; virtual;
    function GetPaintInfoGroup: TEasyPaintInfoBaseGroup; virtual;
    function GetPaintInfoItem: TEasyPaintInfoBaseItem; virtual;
    function GetScratchCanvas: TControlCanvas;
    function GetTopItem: TEasyItem;
    procedure SetBackGround(const Value: TEasyBackgroundManager);
    procedure SetCacheDoubleBufferBits(const Value: Boolean);
    procedure SetGroupCollapseImage(Value: TBitmap);
    procedure SetGroupExpandImage(Value: TBitmap);
    procedure SetGroupFont(Value: TFont);
    procedure SetHintType(Value: TEasyHintType);
    procedure SetImagesExLarge(Value: TCustomImageList);
    procedure SetImagesGroup(Value: TCustomImageList);
    procedure SetImagesLarge(Value: TCustomImageList);
    procedure SetImagesSmall(Value: TCustomImageList);
    procedure SetImagesState(const Value: TCustomImageList);
    procedure SetPaintInfoColumn(const Value: TEasyPaintInfoBaseColumn); virtual;
    procedure SetPaintInfoGroup(const Value: TEasyPaintInfoBaseGroup); virtual;
    procedure SetPaintInfoItem(const Value: TEasyPaintInfoBaseItem); virtual;
    procedure SetSelection(Value: TEasySelectionManager);
    procedure SetShowInactive(const Value: Boolean);
    procedure SetShowThemedBorder(Value: Boolean);
    procedure SetShowGroupMargins(const Value: Boolean);
  protected
    function CreateColumnPaintInfo: TEasyPaintInfoBaseColumn; virtual;
    function CreateGroupPaintInfo: TEasyPaintInfoBaseGroup; virtual;
    function CreateGroups: TEasyGroups; virtual;
    function CreateItemPaintInfo: TEasyPaintInfoBaseItem; virtual;
    function ExecuteDragDrop(AvailableEffects: TCommonDropEffects; DataObjectInf: IDataObject; DropSource: IDropSource; var dwEffect: Integer): HRESULT; virtual;
    function GetSortColumn: TEasyColumn; virtual;
    function GroupTestExpand(HitInfo: TEasyGroupHitTestInfoSet): Boolean; virtual;
    function ToolTipNeeded(TargetObj: TEasyCollectionItem; var TipCaption: WideString): Boolean;
    function UseInternalDragImage: Boolean; virtual;
    function ViewSupportsHeader: Boolean;
    procedure CancelCut;
    procedure CheckFocus; virtual;
    procedure ClearPendingDrags;
    procedure ClearStates;
    function ClickTestGroup(ViewportPoint: TPoint; KeyStates: TCommonKeyStates; var HitInfo: TEasyGroupHitTestInfoSet): TEasyGroup;
    function ClickTestItem(ViewportPoint: TPoint; Group: TEasyGroup; KeyStates: TCommonKeyStates; var HitInfo: TEasyItemHitTestInfoSet): TEasyItem;
    procedure ClipHeader(ACanvas: TCanvas; ResetClipRgn: Boolean);
    procedure CMDrag(var Msg: TCMDrag); message CM_DRAG;
    procedure CMHintShow(var Message: TCMHintShow); message CM_HINTSHOW;
    procedure CMHintShowPause(var Message: TCMHintShow); message CM_HINTSHOWPAUSE;
    procedure CMMouseWheel(var Msg: TWMMouseWheel); message CM_MOUSEWHEEL;
    procedure CMParentFontChanged(var Msg: TMessage); message CM_PARENTFONTCHANGED;  
    procedure CopyToClipboard; virtual;
    procedure CreateParams(var Params: TCreateParams); override;
    procedure CreateWnd; override;
    procedure CutToClipboard; virtual;
    procedure DoAfterPaint(ACanvas: TCanvas; ClipRect: TRect);
    procedure DoAutoGroupGetKey(Item: TEasyItem; ColumnIndex: Integer; Groups: TEasyGroups; var Key: LongWord); virtual;
    procedure DoAutoSortGroupCreate(Item: TEasyItem; ColumnIndex: Integer; Groups: TEasyGroups; var Group: TEasyGroup; var DoDefaultAction: Boolean); virtual;
    procedure DoClipboardCopy(var Handled: Boolean); virtual;
    procedure DoClipboardCut(var MarkAsCut, Handled: Boolean); virtual;
    procedure DoClipboardPaste(var Handled: Boolean); virtual;
    procedure DoColumnCheckChanged(Column: TEasyColumn); virtual;
    procedure DoColumnCheckChanging(Column: TEasyColumn; var Allow: Boolean); virtual;
    procedure DoColumnClick(Button: TCommonMouseButton; const Column: TEasyColumn); virtual;
    procedure DoColumnContextMenu(HitInfo: TEasyHitInfoColumn; WindowPoint: TPoint; var Menu: TPopupMenu); virtual;
    procedure DoColumnCustomView(Column: TEasyColumn; var ViewClass: TEasyViewColumnClass); virtual;
    procedure DoColumnDblClick(Button: TCommonMouseButton; ShiftState: TShiftState; MousePos: TPoint; Column: TEasyColumn); virtual;
    procedure DoColumnDropDownButtonClick(Column: TEasyColumn; Button: TCommonMouseButton; ShiftState: TShiftState; MousePos: TPoint; var DoDefault: Boolean); virtual;
    procedure DoColumnEnableChanged(Column: TEasyColumn); virtual;
    procedure DoColumnEnableChanging(Column: TEasyColumn; var Allow: Boolean); virtual;
    procedure DoColumnFocusChanged(Column: TEasyColumn); virtual;
    procedure DoColumnFocusChanging(Column: TEasyColumn; var Allow: Boolean); virtual;
    procedure DoColumnFreeing(Column: TEasyColumn); virtual;
    procedure DoColumnGetCaption(Column: TEasyColumn; Line: Integer; var Caption: WideString); virtual;
    procedure DoColumnGetImageIndex(Column: TEasyColumn; ImageKind: TEasyImageKind; var ImageIndex: TCommonImageIndexInteger); virtual;
    procedure DoColumnGetImageList(Column: TEasyColumn; var ImageList: TCustomImageList); virtual;
    procedure DoColumnGetDetail(Column: TEasyColumn; Line: Integer; var Detail: Integer); virtual;
    procedure DoColumnGetDetailCount(Column: TEasyColumn; var Count: Integer); virtual;
    procedure DoColumnImageDraw(Column: TEasyColumn; ACanvas: TCanvas; const RectArray: TEasyRectArrayObject; AlphaBlender: TEasyAlphaBlender); virtual;
    procedure DoColumnImageGetSize(Column: TEasyColumn; var ImageWidth, ImageHeight: Integer); virtual;
    procedure DoColumnImageDrawIsCustom(Column: TEasyColumn; var IsCustom: Boolean); virtual;
    procedure DoColumnInitialize(Column: TEasyColumn); virtual;
    procedure DoColumnLoadFromStream(Column: TEasyColumn; S: TStream; Version: Integer); virtual;
    procedure DoColumnPaintText(Column: TEasyColumn; ACanvas: TCanvas); virtual;
    procedure DoColumnSaveToStream(Column: TEasyColumn; S: TStream; Version: Integer); virtual;
    procedure DoColumnSelectionChanged(Column: TEasyColumn); virtual;
    procedure DoColumnSelectionChanging(Column: TEasyColumn; var Allow: Boolean); virtual;
    procedure DoColumnSetCaption(Column: TEasyColumn; const Caption: WideString); virtual;
    procedure DoColumnSetImageIndex(Column: TEasyColumn; ImageKind: TEasyImageKind; ImageIndex: Integer); virtual;
    procedure DoColumnSetDetail(Column: TEasyColumn; Line: Integer; Detail: Integer); virtual;
    procedure DoColumnSetDetailCount(Column: TEasyColumn; DetailCount: Integer); virtual;
    procedure DoColumnStructureChange;
    procedure DoColumnThumbnailDraw(Column: TEasyColumn; ACanvas: TCanvas; ARect: TRect; var DoDefault: Boolean); virtual;
    procedure DoColumnSizeChanged(Column: TEasyColumn); virtual;
    procedure DoColumnSizeChanging(Column: TEasyColumn; Size, NewSize: Integer; var Allow: Boolean); virtual;
    procedure DoColumnVisibilityChanged(Column: TEasyColumn); virtual;
    procedure DoColumnVisibilityChanging(Column: TEasyColumn; var Allow: Boolean); virtual;
    procedure DoContextMenu(MousePt: TPoint; var Handled: Boolean); virtual;
    procedure DoCustomGrid(Group: TEasyGroup; ViewStyle: TEasyListStyle; var Grid: TEasyGridGroupClass); virtual;
    procedure DoDblClick(Button: TCommonMouseButton; MousePos: TPoint; ShiftState: TShiftState); virtual;
    procedure DoGesture(Gesture: WideString; var Handled: Boolean); virtual;
    procedure DoGetDragImage(Bitmap: TBitmap; DragStartPt: TPoint; var HotSpot: TPoint; var TransparentColor: TColor; var Handled: Boolean); virtual;
    procedure DoGroupClick(Group: TEasyGroup; KeyStates: TCommonKeyStates; HitTest: TEasyGroupHitTestInfoSet); virtual;
    procedure DoGroupCollapse(Group: TEasyGroup); virtual;
    procedure DoGroupCollapsing(Group: TEasyGroup; var Allow: Boolean); virtual;
    function DoGroupCompare(Column: TEasyColumn; Group1, Group2: TEasyGroup): Integer; virtual;
    procedure DoGroupContextMenu(HitInfo: TEasyHitInfoGroup; WindowPoint: TPoint; var Menu: TPopupMenu; var Handled: Boolean); virtual;
    procedure DoGroupCustomView(Group: TEasyGroup; ViewStyle: TEasyListStyle; var View: TEasyViewGroupClass); virtual;
    procedure DoGroupDblClick(Button: TCommonMouseButton; MousePos: TPoint; HitInfo: TEasyHitInfoGroup); virtual;
    procedure DoGroupExpand(Group: TEasyGroup); virtual;
    procedure DoGroupExpanding(Group: TEasyGroup; var Allow: Boolean); virtual;
    procedure DoGroupFreeing(Group: TEasyGroup); virtual;
    procedure DoGroupGetCaption(Group: TEasyGroup; var Caption: WideString); virtual;
    procedure DoGroupGetImageIndex(Group: TEasyGroup; ImageKind: TEasyImageKind; var ImageIndex: TCommonImageIndexInteger); virtual;
    procedure DoGroupGetImageList(Group: TEasyGroup; var ImageList: TCustomImageList); virtual;
    procedure DoGroupGetDetail(Group: TEasyGroup; Line: Integer; var Detail: Integer); virtual;
    procedure DoGroupGetDetailCount(Group: TEasyGroup; var Count: Integer); virtual;
    procedure DoGroupImageDraw(Group: TEasyGroup; ACanvas: TCanvas; const RectArray: TEasyRectArrayObject; AlphaBlender: TEasyAlphaBlender); virtual;
    procedure DoGroupImageGetSize(Group: TEasyGroup; var ImageWidth, ImageHeight: Integer); virtual;
    procedure DoGroupImageDrawIsCustom(Group: TEasyGroup; var IsCustom: Boolean); virtual;
    procedure DoGroupInitialize(Group: TEasyGroup); virtual;
    procedure DoGroupHotTrack(Group: TEasyGroup; State: TEasyHotTrackState; MousePos: TPoint); virtual;
    procedure DoGroupLoadFromStream(Group: TEasyGroup; S: TStream; Version: Integer); virtual;
    procedure DoGroupPaintText(Group: TEasyGroup; ACanvas: TCanvas); virtual;
    procedure DoGroupSaveToStream(Group: TEasyGroup; S: TStream; Version: Integer); virtual;
    procedure DoGroupSelectionChanged(Group: TEasyGroup); virtual;
    procedure DoGroupSelectionChanging(Group: TEasyGroup; var Allow: Boolean); virtual;
    procedure DoGroupSetCaption(Group: TEasyGroup; const Caption: WideString); virtual;
    procedure DoGroupSetImageIndex(Group: TEasyGroup; ImageKind: TEasyImageKind; ImageIndex: Integer); virtual;
    procedure DoGroupSetDetail(Group: TEasyGroup; Line: Integer; Detail: Integer); virtual;
    procedure DoGroupSetDetailCount(Group: TEasyGroup; DetailCount: Integer); virtual;
    procedure DoGroupStructureChange; virtual;
    procedure DoGroupThumbnailDraw(Group: TEasyGroup; ACanvas: TCanvas; ARect: TRect; AlphaBlender: TEasyAlphaBlender; var DoDefault: Boolean); virtual;
    procedure DoGroupVisibilityChanged(Group: TEasyGroup); virtual;
    procedure DoGroupVisibilityChanging(Group: TEasyGroup; var Allow: Boolean); virtual;
    procedure DoHeaderDblClick(Button: TCommonMouseButton; MousePos: TPoint; ShiftState: TShiftState); virtual;
    procedure DoHintCustomInfo(TargetObj: TEasyCollectionItem; const Info: TEasyHintInfo); virtual;
    procedure DoHintCustomDraw(TargetObj: TEasyCollectionItem; const Info: TEasyHintInfo); virtual;
    procedure DoHintPopup(TargetObj: TEasyCollectionItem; HintType: TEasyHintType; MousePos: TPoint; var AText: WideString; var HideTimeout, ReshowTimeout: Integer; var Allow: Boolean); virtual;
    procedure DoHintShowPause(HintShowingNow: Boolean; var PauseTime: Integer);
    procedure DoIncrementalSearch(Item: TEasyItem; const SearchBuffer: WideString; var CompareResult: Integer); virtual;
    procedure DoItemCheckChanged(Item: TEasyItem); virtual;
    procedure DoItemCheckChanging(Item: TEasyItem; var Allow: Boolean); virtual;
    procedure DoItemClick(Item: TEasyItem; KeyStates: TCommonKeyStates; HitInfo: TEasyItemHitTestInfoSet); virtual;
    function DoItemCompare(Column: TEasyColumn; Group: TEasyGroup; Item1, Item2: TEasyItem): Integer; virtual;
    procedure DoItemContextMenu(HitInfo: TEasyHitInfoItem; WindowPoint: TPoint; var Menu: TPopupMenu; var Handled: Boolean); virtual;
    procedure DoItemCreateEditor(Item: TEasyItem; var Editor: IEasyCellEditor); virtual;
    procedure DoItemCustomView(Item: TEasyItem; ViewStyle: TEasyListStyle; var View: TEasyViewItemClass); virtual;
    procedure DoItemDblClick(Button: TCommonMouseButton; MousePos: TPoint; HitInfo: TEasyHitInfoItem); virtual;
    procedure DoItemEditBegin(Item: TEasyItem; var Column: Integer; var Allow: Boolean); virtual;
    procedure DoItemEdited(Item: TEasyItem; var NewValue: Variant; var Accept: Boolean); virtual;
    procedure DoItemEditEnd(Item: TEasyItem); virtual;
    procedure DoItemGetEditMenu(Editor: TEasyBaseEditor; var Menu: TPopupMenu); virtual;
    procedure DoItemEnableChanged(Item: TEasyItem); virtual;
    procedure DoItemEnableChanging(Item: TEasyItem; var Allow: Boolean); virtual;
    procedure DoItemFreeing(Item: TEasyItem); virtual;
    procedure DoItemFocusChanged(Item: TEasyItem); virtual;
    procedure DoItemFocusChanging(Item: TEasyItem; var Allow: Boolean); virtual;
    procedure DoItemGetCaption(Item: TEasyItem; Column: Integer; var ACaption: WideString); virtual;
    procedure DoItemGetEditCaption(Item: TEasyItem; Column: TEasyColumn; var Caption: WideString); virtual;
    procedure DoItemGetGroupKey(Item: TEasyItem; FocusedColumn: Integer; var Key: LongWord); virtual;
    procedure DoItemGetImageIndex(Item: TEasyItem; Column: Integer; ImageKind: TEasyImageKind; var ImageIndex: TCommonImageIndexInteger); virtual;
    procedure DoItemGetImageList(Item: TEasyItem; Column: Integer; var ImageList: TCustomImageList); virtual;
    procedure DoItemGetStateImageList(Item: TEasyItem; Column: Integer; var ImageList: TCustomImageList); virtual;
    procedure DoItemGetTileDetail(Item: TEasyItem; Line: Integer; var Detail: Integer); virtual;
    procedure DoItemGetTileDetailCount(Item: TEasyItem; var Count: Integer); virtual;
    procedure DoItemHotTrack(Item: TEasyItem; State: TEasyHotTrackState; MousePos: TPoint); virtual;
    procedure DoItemImageDraw(Item: TEasyItem; Column: TEasyColumn; ACanvas: TCanvas; const RectArray: TEasyRectArrayObject; AlphaBlender: TEasyAlphaBlender); virtual;
    procedure DoItemImageGetSize(Item: TEasyItem; Column: TEasyColumn; var ImageWidth, ImageHeight: Integer); virtual;
    procedure DoItemImageDrawIsCustom(Column: TEasyColumn; Item: TEasyItem; var IsCustom: Boolean); virtual;
    procedure DoItemInitialize(Item: TEasyItem); virtual;
    procedure DoItemLoadFromStream(Item: TEasyItem; S: TStream; Version: Integer); virtual;
    procedure DoItemMouseDown(Item: TEasyItem; Button: TCommonMouseButton; var DoDefault: Boolean); virtual;
    procedure DoItemMouseUp(Item: TEasyItem; Button: TCommonMouseButton; var DoDefault: Boolean); virtual;
    procedure DoItemPaintText(Item: TEasyItem; Position: Integer; ACanvas: TCanvas); virtual;
    procedure DoItemSaveToStream(Item: TEasyItem; S: TStream; Version: Integer); virtual;
    procedure DoItemSelectionChanged(Item: TEasyItem); virtual;
    procedure DoItemSelectionChanging(Item: TEasyItem; var Allow: Boolean); virtual;
    procedure DoItemSelectionsChanged; virtual;
    procedure DoItemSetCaption(Item: TEasyItem; Column: Integer; const Caption: WideString); virtual;
    procedure DoItemSetGroupKey(Item: TEasyItem; FocusedColumn: Integer; Key: LongWord); virtual;
    procedure DoItemSetImageIndex(Item: TEasyItem; Column: Integer; ImageKind: TEasyImageKind; ImageIndex: Integer); virtual;
    procedure DoItemSetTileDetail(Item: TEasyItem; Line: Integer; Detail: Integer); virtual;
    procedure DoItemSetTileDetailCount(Item: TEasyItem; Detail: Integer); virtual;
    procedure DoItemStructureChange; virtual;
    procedure DoItemThumbnailDraw(Item: TEasyItem; ACanvas: TCanvas; ARect: TRect; AlphaBlender: TEasyAlphaBlender; var DoDefault: Boolean); virtual;
    procedure DoItemVisibilityChanged(Item: TEasyItem); virtual;
    procedure DoItemVisibilityChanging(Item: TEasyItem; var Allow: Boolean); virtual;
    function DoMouseWheel(Shift: TShiftState; WheelDelta: Integer; MousePos: TPoint): Boolean; override;
    function DoMouseWheelDown(Shift: TShiftState; MousePos: TPoint): Boolean; override;
    function DoMouseWheelUp(Shift: TShiftState; MousePos: TPoint): Boolean; override;
    procedure DoKeyAction(var CharCode: Word; var Shift: TShiftState; var DoDefault: Boolean); virtual;
    procedure DoOLEDragEnd(ADataObject: IDataObject; DragResult: TCommonOLEDragResult; ResultEffect: TCommonDropEffects; KeyStates: TCommonKeyStates); virtual;
    procedure DoOLEDragStart(ADataObject: IDataObject; var AvailableEffects: TCommonDropEffects; var AllowDrag: Boolean); virtual;
    procedure DoOLEDropSourceQueryContineDrag(EscapeKeyPressed: Boolean; KeyStates: TCommonKeyStates; var QueryResult: TEasyQueryDragResult); virtual;
    procedure DoOLEDropSourceGiveFeedback(Effect: TCommonDropEffects; var UseDefaultCursors: Boolean); virtual;
    procedure DoOLEDropTargetDragEnter(DataObject: IDataObject; KeyState: TCommonKeyStates; WindowPt: TPoint; AvailableEffects: TCommonDropEffects; var DesiredEffect: TCommonDropEffect); virtual;
    procedure DoOLEDropTargetDragOver(KeyState: TCommonKeyStates; WindowPt: TPoint; AvailableEffects: TCommonDropEffects; var DesiredEffect: TCommonDropEffect); virtual;
    procedure DoOLEDropTargetDragLeave; virtual;
    procedure DoOLEDropTargetDragDrop(DataObject: IDataObject; KeyState: TCommonKeyStates; WindowPt: TPoint; AvailableEffects: TCommonDropEffects; var DesiredEffect: TCommonDropEffect; var Handled: Boolean); virtual;
    procedure DoOLEGetCustomFormats(var Formats: TFormatEtcArray); virtual;
    procedure DoOLEGetData(const FormatEtcIn: TFormatEtc; var Medium: TStgMedium; var Handled: Boolean); virtual;
    procedure DoOLEGetDataObject(var DataObject: IDataObject); virtual;
    procedure DoPaintBkGnd(ACanvas: TCanvas; AWindowRect: TRect; AlphaBlender: TEasyAlphaBlender; var DoDefault: Boolean); virtual;
    procedure DoPaintHeaderBkGnd(ACanvas: TCanvas; ARect: TRect; var Handled: Boolean); virtual;
    procedure DoPaintRect(ACanvas: TCanvas; WindowClipRect: TRect; SelectedOnly: Boolean); virtual;
    procedure DoQueryOLEData(const FormatEtcIn: TFormatEtc; var FormatAvailable: Boolean; var Handled: Boolean); virtual;
    procedure DoResize(DeltaX, DeltaY: Integer); virtual;
    procedure DoScroll(DeltaX, DeltaY: Integer); virtual;
    procedure DoThreadCallback(var Msg: TWMThreadRequest); virtual;
    procedure DoUpdate; override;
    procedure DoViewChange; virtual;

    procedure DestroyWnd; override;
    function DragInitiated: Boolean;
    procedure FinalizeDrag(WindowPoint: TPoint; KeyState: TCommonKeyStates);
    procedure HandleDblClick(Button: TCommonMouseButton; Msg: TWMMouse); virtual;
    procedure HandleKeyDown(Msg: TWMKeyDown); virtual;
    procedure HandleMouseDown(Button: TCommonMouseButton; Msg: TWMMouse); virtual;
    procedure HandleMouseUp(Button: TCommonMouseButton; Msg: TWMMouse); virtual;
    procedure InitializeDragPendings(HitItem: TEasyItem; WindowPoint: TPoint; KeyState: TCommonKeyStates; AllowDrag, AllowDragRect: Boolean);
    function IsFontStored: Boolean;
    function IsHeaderMouseMsg(MousePos: TSmallPoint; ForceTest: Boolean = False): Boolean;
    procedure MarkSelectedCut;
    procedure Notification(AComponent: TComponent; Operation: TOperation); override;
    procedure PasteFromClipboard; virtual;
    procedure ResizeBackBits(NewWidth, NewHeight: Integer);
    procedure SetView(Value: TEasyListStyle); virtual;
    procedure WMChar(var Msg: TWMChar); message WM_CHAR;
    procedure WMClose(var Msg: TWMClose); message WM_CLOSE;
    procedure WMContextMenu(var Msg: TMessage); message WM_CONTEXTMENU;
    procedure WMDestroy(var Msg: TMessage); message WM_DESTROY;
    procedure WMEasyThreadCallback(var Msg: TWMThreadRequest); message WM_COMMONTHREADCALLBACK;
    procedure WMEraseBkGnd(var Msg: TWMEraseBkGnd); message WM_ERASEBKGND;
    procedure WMGetDlgCode(var Msg: TWMGetDlgCode); message WM_GETDLGCODE;
    procedure WMHScroll(var Msg: TWMHScroll); message WM_HSCROLL;
    procedure WMKeyDown(var Msg: TWMKeyDown); message WM_KEYDOWN;
    procedure WMKillFocus(var Msg: TWMKillFocus); message WM_KILLFOCUS;
    procedure WMLButtonDblClk(var Msg: TWMLButtonDblClk); message WM_LBUTTONDBLCLK;
    procedure WMLButtonDown(var Msg: TWMLButtonDown); message WM_LBUTTONDOWN;
    procedure WMLButtonUp(var Msg: TWMLButtonUp); message WM_LBUTTONUP;
    procedure WMMButtonDblClk(var Msg: TWMMButtonDblClk); message WM_MBUTTONDBLCLK;
    procedure WMMButtonDown(var Msg: TWMMButtonDown); message WM_MBUTTONDOWN;
    procedure WMMButtonUp(var Msg: TWMMButtonUp); message WM_MBUTTONUP;
    procedure WMMouseActivate(var Msg: TWMMouseActivate); message WM_MOUSEACTIVATE;
    procedure WMMouseMove(var Msg: TWMMouseMove); message WM_MOUSEMOVE;
    procedure WMPaint(var Msg: TWMPaint); message WM_PAINT;
    procedure WMNCCalcSize(var Msg: TWMNCCalcSize); message WM_NCCALCSIZE;
    procedure WMNCPaint(var Msg: TWMNCPaint); message WM_NCPAINT;
    procedure WMRButtonDblClk(var Msg: TWMRButtonDblClk); message WM_RBUTTONDBLCLK;
    procedure WMRButtonDown(var Msg: TWMRButtonDown); message WM_RBUTTONDOWN;
    procedure WMRButtonUp(var Msg: TWMRButtonUp); message WM_RBUTTONUP;
    procedure WMSetCursor(var Msg: TWMSetCursor); message WM_SETCURSOR;
    procedure WMSetFocus(var Msg: TWMSetFocus); message WM_SETFOCUS;
    procedure WMSize(var Msg: TWMSize); message WM_SIZE;
    procedure WMTabMoveFocus(var Msg: TMessage); message WM_TABMOVEFOCUS;
    procedure WMVScroll(var Msg: TWMVScroll); message WM_VSCROLL;
    procedure WMWindowPosChanged(var Msg: TWMWindowPosChanged); message WM_WINDOWPOSCHANGED;
    procedure WMWindowPosChanging(var Msg: TWMWindowPosChanging); message WM_WINDOWPOSCHANGING;
    property AllowHiddenCheckedItems: Boolean read FAllowInvisibleCheckedItems write FAllowInvisibleCheckedItems default False;
    property BackBits: TBitmap read FBackBits write FBackBits;
    property BackGround: TEasyBackgroundManager read FBackGround write SetBackGround;
    property BevelInner default bvLowered;
    property CacheDoubleBufferBits: Boolean read FCacheDoubleBufferBits write SetCacheDoubleBufferBits;
    property CheckManager: TEasyCheckManager read FCheckManager write FCheckManager;
    property Color default clWindow;
    property CellSizes: TEasyCellSizes read FCellSizes write FCellSizes;
    property DisabledBlendAlpha: Byte read FDisabledBlendAlpha write FDisabledBlendAlpha default 128;
    property DisabledBlendColor: TColor read FDisabledBlendColor write FDisabledBlendColor default clWindow;
    property DragManager: TEasyOLEDragManager read FDragManager write FDragManager;
    property DragRect: TEasyDragRectManager read FDragRect write FDragRect;
    property DropTarget: IDropTarget read FDropTarget write FDropTarget;
    property EditManager: TEasyEditManager read FEditManager write FEditManager;
    property Gesture: TEasyGestureManager read FGesture write FGesture;
    property GlobalImages: TEasyGlobalImageManager read FGlobalImages write FGlobalImages;
    property GroupCollapseButton: TBitmap read GetGroupCollapseImage write SetGroupCollapseImage;
    property GroupExpandButton: TBitmap read GetGroupExpandImage write SetGroupExpandImage;
    property GroupFont: TFont read FGroupFont write SetGroupFont stored IsFontStored;
    property Groups: TEasyGroups read FGroups write FGroups;
    property Header: TEasyHeader read FHeader write FHeader;
    property HintAlignment: TAlignment read FHintAlignment write FHintAlignment;
    property HintData: TEasyHintInfoRec read FHintData write FHintData;
    property HintInfo: TEasyHintInfo read FHintInfo write FHintInfo;
    property HintType: TEasyHintType read GetHintType write SetHintType default ehtText;
    property HotTrack: TEasyHotTrackManager read FHotTrack write FHotTrack;
    property ImagesGroup: TCustomImageList read FImagesGroup write SetImagesGroup;
    property ImagesSmall: TCustomImageList read FImagesSmall write SetImagesSmall;
    property ImagesLarge: TCustomImageList read FImagesLarge write SetImagesLarge;
    property ImagesExLarge: TCustomImageList read FImagesExLarge write SetImagesExLarge;
    property ImagesState: TCustomImageList read FImagesState write SetImagesState;
    property IncrementalSearch: TEasyIncrementalSearchManager read FIncrementalSearch write FIncrementalSearch;
    property Items: TEasyGlobalItems read FItems;
    property LastMousePos: TSmallPoint read FLastMousePos write FLastMousePos;
    property NCCanvas: TCanvas read FNCCanvas write FNCCanvas;
    property OnAfterPaint: TAfterPaintEvent read FOnAfterPaint write FOnAfterPaint;
    property OnAutoGroupGetKey: TAutoGroupGetKeyEvent read FOnAutoGroupGetKey write FOnAutoGroupGetKey;
    property OnAutoSortGroupCreate: TAutoSortGroupCreateEvent read FOnAutoSortGroupCreate write FOnAutoSortGroupCreate;
    property OnClipboardCopy: TEasyClipboardEvent read FOnClipboardCopy write FOnClipboardCopy;
    property OnClipboardCut: TEasyClipboardCutEvent read FOnClipboardCut write FOnClipboardCut;
    property OnClipboardPaste: TEasyClipboardEvent read FOnClipboardPaste write FOnClipboardPaste;
    property OnColumnCheckChanged: TColumnCheckChangeEvent read FOnColumnCheckChange write FOnColumnCheckChange;
    property OnColumnCheckChanging: TColumnCheckChangingEvent read FOnColumnCheckChanging write FOnColumnCheckChanging;
    property OnColumnClick: TColumnClickEvent read FOnColumnClick write FOnColumnClick;
    property OnColumnContextMenu: TColumnContextMenuEvent read FOnColumnContextMenu write FOnColumnContextMenu;
    property OnColumnCustomView: TColumnCustomViewEvent read FOnColumnCustomView write FOnColumnCustomView;
    property OnColumnDblClick: TColumnDblClickEvent read FOnColumnDblClick write FOnColumnDblClick;
    property OnColumnDropDownButtonClick: TColumnDropDownButtonClickEvent read FOnColumnDropDownButtonClick write FOnColumnDropDownButtonClick;
    property OnColumnEnableChanged: TColumnEnableChangeEvent read FOnColumnEnableChange write FOnColumnEnableChange;
    property OnColumnEnableChanging: TColumnEnableChangingEvent read FOnColumnEnableChanging write FOnColumnEnableChanging;
    property OnColumnFocusChanged: TColumnFocusChangeEvent read FOnColumnFocusChanged write FOnColumnFocusChanged;
    property OnColumnFocusChanging: TColumnFocusChangingEvent read FOnColumnFocusChanging write FOnColumnFocusChanging;
    property OnColumnFreeing: TColumnFreeingEvent read FOnColumnFreeing write FOnColumnFreeing;
    property OnColumnGetCaption: TColumnGetCaptionEvent read FOnColumnGetCaption write FOnColumnGetCaption;
    property OnColumnGetImageIndex: TColumnGetImageIndexEvent read FOnColumnGetImageIndex write FOnColumnGetImageIndex;
    property OnColumnGetImageList: TColumnGetImageListEvent read FOnColumnGetImageList write FOnColumnGetImageList;
    property OnColumnGetDetail: TColumnGetDetailEvent read FOnColumnGetDetail write FOnColumnGetDetail;
    property OnColumnGetDetailCount: TColumnGetDetailCountEvent read FOnColumnGetDetailCount write FOnColumnGetDetailCount;
    property OnColumnImageDraw: TColumnImageDrawEvent read FOnColumnImageDraw write FOnColumnImageDraw;
    property OnColumnImageGetSize: TColumnImageGetSizeEvent read FOnColumnImageGetSize write FOnColumnImageGetSize;
    property OnColumnImageDrawIsCustom: TColumnImageDrawIsCustomEvent read FOnColumnImageDrawIsCustom write FOnColumnImageDrawIsCustom;
    property OnColumnInitialize: TColumnInitializeEvent read FOnColumnInitialize write FOnColumnInitialize;
    property OnColumnLoadFromStream: TEasyColumnLoadFromStreamEvent read FOnColumnLoadFromStream write FOnColumnLoadFromStream;
    property OnColumnPaintText: TColumnPaintTextEvent read FOnColumnPaintText write FOnColumnPaintText;
    property OnColumnSaveToStream: TEasyColumnSaveToStreamEvent read FOnColumnSaveToStream write FOnColumnSaveToStream;
    property OnColumnSelectionChanged: TColumnSelectionChangeEvent read FOnColumnSelectionChanged write FOnColumnSelectionChanged;
    property OnColumnSelectionChanging: TColumnSelectionChangingEvent read FOnColumnSelectionChanging write FOnColumnSelectionChanging;
    property OnColumnSetCaption: TColumnSetCaptionEvent read FOnColumnSetCaption write FOnColumnSetCaption;
    property OnColumnSetImageIndex: TColumnSetImageIndexEvent read FOnColumnSetImageIndex write FOnColumnSetImageIndex;
    property OnColumnSetDetail: TColumnSetDetailEvent read FOnColumnSetDetail write FOnColumnSetDetail;
    property OnColumnStructureChange: TNotifyEvent read FOnColumnStructureChange write FOnColumnStructureChange;
    property OnColumnThumbnailDraw: TColumnThumbnailDrawEvent read FOnColumnThumbnailDraw write FOnColumnThumbnailDraw;
    property OnColumnSizeChanged: TColumnSizeChangedEvent read FOnColumnSizeChanged write FOnColumnSizeChanged;
    property OnColumnSizeChanging: TColumnSizeChangingEvent read FOnColumnSizeChanging write FOnColumnSizeChanging;
    property OnColumnVisibilityChanged: TColumnVisibilityChangeEvent read FOnColumnVisibilityChanged write FOnColumnVisibilityChanged;
    property OnColumnVisibilityChanging: TColumnVisibilityChangingEvent read FOnColumnVisibilityChanging write FOnColumnVisibilityChanging;
    property OnContextMenu: TContextMenuEvent read FOnContextMenu write FOnContextMenu;
    property OnCustomGrid: TCustomGridEvent read FOnCustomGrid write FOnCustomGrid;
    property OnDblClick: TDblClickEvent read FOnDblClick write FOnDblClick;
    property OnGetDragImage: TGetDragImageEvent read FOnGetDragImage write FOnGetDragImage;
    property OnGroupClick: TGroupClickEvent read FOnGroupClick write FOnGroupClick;
    property OnGroupCollapse: TGroupCollapseEvent read FOnGroupCollapse write FOnGroupCollapse;
    property OnGroupCollapsing: TGroupCollapsingEvent read FOnGroupCollapsing write FOnGroupCollapsing;
    property OnGroupCompare: TGroupCompareEvent read FOnGroupCompare write FOnGroupCompare;
    property OnGroupContextMenu: TGroupContextMenuEvent read FOnGroupContextMenu write FOnGroupContextMenu;
    property OnGroupCustomView: TGroupCustomViewEvent read FOnGroupCustomView write FOnGroupCustomView;
    property OnGroupDblClick: TGroupDblClickEvent read FOnGroupDblClick write FOnGroupDblClick;
    property OnGroupExpand: TGroupExpandEvent read FOnGroupExpand write FOnGroupExpand;
    property OnGroupExpanding: TGroupExpandingEvent read FOnGroupExpanding write FOnGroupExpanding;
    property OnGroupFocusChanged: TGroupFocusChangeEvent read FOnGroupFocusChanged write FOnGroupFocusChanged;
    property OnGroupFocusChanging: TGroupFocusChangingEvent read FOnGroupFocusChanging write FOnGroupFocusChanging;
    property OnGroupFreeing: TGroupFreeingEvent read FOnGroupFreeing write FOnGroupFreeing;
    property OnGroupGetCaption: TGroupGetCaptionEvent read FOnGroupGetCaption write FOnGroupGetCaption;
    property OnGroupGetImageIndex: TGroupGetImageIndexEvent read FOnGroupGetImageIndex write FOnGroupGetImageIndex;
    property OnGroupGetImageList: TGroupGetImageListEvent read FOnGroupGetImageList write FOnGroupGetImageList;
    property OnGroupGetDetail: TGroupGetDetailEvent read FOnGroupGetDetailIndex write FOnGroupGetDetailIndex;
    property OnGroupGetDetailCount: TGroupGetDetailCountEvent read FOnGroupGetDetailCount write FOnGroupGetDetailCount;
    property OnGroupHotTrack: TGroupHotTrackEvent read FOnGroupHotTrack write FOnGroupHotTrack;
    property OnGroupImageDraw: TGroupImageDrawEvent read FOnGroupImageDrawEvent write FOnGroupImageDrawEvent;
    property OnGroupImageGetSize: TGroupImageGetSizeEvent read FGroupImageGetSize write FGroupImageGetSize;
    property OnGroupImageDrawIsCustom: TGroupImageDrawIsCustomEvent read FOnGroupImageDrawIsCustom write FOnGroupImageDrawIsCustom;
    property OnGroupInitialize: TGroupInitializeEvent read FOnGroupInitialize write FOnGroupInitialize;
    property OnGroupLoadFromStream: TGroupLoadFromStreamEvent read FOnGroupLoadFromStream write FOnGroupLoadFromStream;
    property OnGroupPaintText: TGroupPaintTextEvent read FOnGroupPaintText write FOnGroupPaintText;
    property OnGroupSaveToStream: TGroupSaveToStreamEvent read FOnGroupSaveToStream write FOnGroupSaveToStream;
    property OnGroupSelectionChanged: TGroupSelectionChangeEvent read FOnGroupSelectionChanged write FOnGroupSelectionChanged;
    property OnGroupSelectionChanging: TGroupSelectionChangingEvent read FOnGroupSelectionChanging write FOnGroupSelectionChanging;
    property OnGroupSetCaption: TGroupSetCaptionEvent read FOnGroupSetCaption write FOnGroupSetCaption;
    property OnGroupSetImageIndex: TGroupSetImageIndexEvent read FOnGroupSetImageIndex write FOnGroupSetImageIndex;
    property OnGroupSetDetail: TGroupSetDetailEvent read FOnGroupSetDetail write FOnGroupSetDetail;
    property OnGroupStructureChange: TNotifyEvent read FOnGroupStructureChange write FOnGroupStructureChange;
    property OnGroupThumbnailDraw: TGroupThumbnailDrawEvent read FOnGroupThumbnailDraw write FOnGroupThumbnailDraw;
    property OnGroupVisibilityChanged: TGroupVisibilityChangeEvent read FOnGroupVisibilityChanged write FOnGroupVisibilityChanged;
    property OnGroupVisibilityChanging: TGroupVisibilityChangingEvent read FOnGroupVisibilityChanging write FOnGroupVisibilityChanging;
    property OnHeaderDblClick: THeaderDblClickEvent read FOnHeaderDblClick write FOnHeaderDblClick;
    property OnHeaderMouseDown: THeaderMouseEvent read FOnHeaderMouseDown write FOnHeaderMouseDown;
    property OnHeaderMouseMove: TMouseMoveEvent read FOnHeaderMouseMove write FOnHeaderMouseMove;
    property OnHeaderMouseUp: THeaderMouseEvent read FOnHeaderMouseUp write FOnHeaderMouseUp;
    property OnHintCustomDraw: THintCustomDrawEvent read FOnHintCustomDraw write FOnHintCustomDraw;
    property OnHintCustomInfo: THintCustomizeInfoEvent read FOnHintCustomInfo write FOnHintCustomInfo;
    property OnHintPauseTime: THintPauseTimeEvent read FOnHintPauseTime write FOnHintPauseTime;
    property OnHintPopup: THintPopupEvent read FOnHintPopup write FOnHintPopup;
    property OnIncrementalSearch: TIncrementalSearchEvent read FOnIncrementalSearch write FOnIncrementalSearch;
    property OnItemCheckChange: TItemCheckChangeEvent read FOnItemCheckChange write FOnItemCheckChange;
    property OnItemCheckChanging: TItemCheckChangingEvent read FOnItemCheckChanging write FOnItemCheckChanging;
    property OnItemClick: TItemClickEvent read FOnItemClick write FOnItemClick;
    property OnItemCompare: TItemCompareEvent read FOnItemCompare write FOnItemCompare;
    property OnItemContextMenu: TItemContextMenuEvent read FOnItemContextMenu write FOnItemContextMenu;
    property OnItemCreateEditor: TItemCreateEditorEvent read FOnItemCreateEditor write FOnItemCreateEditor;
    property OnItemCustomView: TItemCustomViewEvent read FOnItemCustomView write FOnItemCustomView;
    property OnItemDblClick: TItemDblClickEvent read FOnItemDblClick write FOnItemDblClick;
    property OnItemEditBegin: TItemEditBegin read FOnItemEditBegin write FOnItemEditBegin;
    property OnItemEdited: TItemEditedEvent read FOnItemEdited write FOnItemEdited;
    property OnItemEditEnd: TItemEditEnd read FOnItemEditEnd write FOnItemEditEnd;
    property OnItemEnableChange: TItemEnableChangeEvent read FOnItemEnableChange write FOnItemEnableChange;
    property OnItemEnableChanging: TItemEnableChangingEvent read FOnItemEnableChanging write FOnItemEnableChanging;
    property OnItemFreeing: TItemFreeingEvent read FOnItemFreeing write FOnItemFreeing;
    property OnItemFocusChanged: TItemFocusChangeEvent read FOnItemFocusChanged write FOnItemFocusChanged;
    property OnItemFocusChanging: TItemFocusChangingEvent read FOnItemFocusChanging write FOnItemFocusChanging;
    property OnItemGetCaption: TItemGetCaptionEvent read FOnItemGetCaption write FOnItemGetCaption;
    property OnItemGetEditCaption: TEasyItemGetCaptionEvent read FOnItemGetEditCaption write FOnItemGetEditCaption;
    property OnItemGetEditMenu: TItemGetEditMenuEvent read FOnItemGetEditMenu write FOnItemGetEditMenu;
    property OnItemGetGroupKey: TItemGetGroupKeyEvent read FOnItemGetGroupKey write FOnItemGetGroupKey;
    property OnItemHotTrack: TItemHotTrackEvent read FOnItemHotTrack write FOnItemHotTrack;
    property OnItemGetImageIndex: TItemGetImageIndexEvent read FOnItemGetImageIndex write FOnItemGetImageIndex;
    property OnItemGetImageList: TItemGetImageListEvent read FOnItemGetImageList write FOnItemGetImageList;
    property OnItemGetStateImageList: TItemGetImageListEvent read FOnItemGetStateImageList write FOnItemGetStateImageList;
    property OnItemGetTileDetail: TItemGetTileDetailEvent read FOnItemGetTileDetailIndex write FOnItemGetTileDetailIndex;
    property OnItemGetTileDetailCount: TItemGetTileDetailCountEvent read FOnItemGetTileDetailCount write FOnItemGetTileDetailCount;
    property OnItemImageDraw: TItemImageDrawEvent read FOnItemImageDraw write FOnItemImageDraw;
    property OnItemImageGetSize: TItemImageGetSizeEvent read FOnItemImageGetSize write FOnItemImageGetSize;
    property OnItemImageDrawIsCustom: TItemImageDrawIsCustomEvent read FOnItemImageDrawIsCustom write FOnItemImageDrawIsCustom;
    property OnItemLoadFromStream: TItemLoadFromStreamEvent read FOnItemLoadFromStream write FOnItemLoadFromStream;
    property OnItemInitialize: TItemInitializeEvent read FOnItemInitialize write FOnItemInitialize;
    property OnItemMouseDown: TItemMouseDownEvent read FOnItemMouseDown write FOnItemMouseDown;
    property OnItemMouseUp: TItemMouseUpEvent read FOnItemMouseUp write FOnItemMouseUp;
    property OnItemPaintText: TItemPaintTextEvent read FOnItemPaintText write FOnItemPaintText;
    property OnItemSaveToStream: TItemSaveToStreamEvent read FOnItemSaveToStream write FOnItemSaveToStream;
    property OnItemSelectionChanged: TItemSelectionChangeEvent read FOnItemSelectionChanged write FOnItemSelectionChanged;
    property OnItemSelectionChanging: TItemSelectionChangingEvent read FOnItemSelectionChanging write FOnItemSelectionChanging;
    property OnItemSelectionsChanged: TEasyItemSelectionsChangedEvent read FOnItemSelectionsChanged write FOnItemSelectionsChanged;
    property OnItemSetCaption: TItemSetCaptionEvent read FOnItemSetCaption write FOnItemSetCaption;
    property OnItemSetGroupKey: TItemSetGroupKeyEvent read FOnItemSetGroupKey write FOnItemSetGroupKey;
    property OnItemSetImageIndex: TItemSetImageIndexEvent read FOnItemSetImageIndex write FOnItemSetImageIndex;
    property OnItemSetTileDetail: TItemSetTileDetailEvent read FOnItemSetTileDetail write FOnItemSetTileDetail;
    property OnItemStructureChange: TNotifyEvent read FOnItemStructureChange write FOnItemStructureChange;
    property OnItemThumbnailDraw: TItemThumbnailDrawEvent read FOnItemThumbnailDraw write FOnItemThumbnailDraw;
    property OnItemVisibilityChanged: TItemVisibilityChangeEvent read FOnItemVisibilityChanged write FOnItemVisibilityChanged;
    property OnItemVisibilityChanging: TItemVisibilityChangingEvent read FOnItemVisibilityChanging write FOnItemVisibilityChanging;
    property OnKeyAction: TEasyKeyActionEvent read FOnKeyAction write FOnKeyAction;
    property OnMouseGesture: TEasyGestureEvent read FOnMouseGesture write FOnMouseGesture;
    property OnOLEDragEnd: TOLEDropSourceDragEndEvent read FOnOLEDragEnd write FOnOLEDragEnd;
    property OnOLEDragStart: TOLEDropSourceDragStartEvent read FOnOLEDragStart write FOnOLEDragStart;
    property OnOLEDragEnter: TOLEDropTargetDragEnterEvent read FOnOLEDragEnter write FOnOLEDragEnter;
    property OnOLEDragOver: TOLEDropTargetDragOverEvent read FOnOLEDragOver write FOnOLEDragOver;
    property OnOLEDragLeave: TOLEDropTargetDragLeaveEvent read FOnOLEDragLeave write FOnOLEDragLeave;
    property OnOLEDragDrop: TOLEDropTargetDragDropEvent read FOnOLEDragDrop write FOnOLEDragDrop;
    property OnOLEGetCustomFormats: TOLEGetCustomFormatsEvent read FOnOLEGetCustomFormats write FOnOLEGetCustomFormats;
    property OnOLEGetData: TOLEGetDataEvent read FOnOLEGetData write FOnOLEGetData;
    property OnOLEGetDataObject: FOLEGetDataObjectEvent read FOnOLEGetDataObject write FOnOLEGetDataObject;
    property OnOLEQueryContineDrag: TOLEDropSourceQueryContineDragEvent read FOnOLEQueryContineDrag write FOnOLEQueryContineDrag;
    property OnOLEGiveFeedback: TOLEDropSourceGiveFeedbackEvent read FOnOLEGiveFeedback write FOnOLEGiveFeedback;
    property OnOLEQueryData: TOLEQueryDataEvent read FOnOLEQueryData write FOnOLEQueryData;
    property OnPaintBkGnd: TPaintBkGndEvent read FOnPaintBkGnd write FOnPaintBkGnd;
    property OnPaintHeaderBkGnd: TPaintHeaderBkGndEvent read FOnPaintHeaderBkGnd write FOnPaintHeaderBkGnd;
    property OnScroll: TEasyScrollEvent read FOnScroll write FOnScroll;
    property OnViewChange: TViewChangedEvent read FOnViewChange write FOnViewChange;
    property PaintInfoColumn: TEasyPaintInfoBaseColumn read GetPaintInfoColumn write SetPaintInfoColumn;
    property PaintInfoGroup: TEasyPaintInfoBaseGroup read GetPaintInfoGroup write SetPaintInfoGroup;
    property PaintInfoItem: TEasyPaintInfoBaseItem read GetPaintInfoItem write SetPaintInfoItem;
    {$IFDEF COMPILER_7_UP}property ParentBackground default False;{$ENDIF COMPILER_7_UP}
    property ParentColor default False;
    property PopupMenuHeader: TPopupMenu read FPopupMenuHeader write FPopupMenuHeader;
    property ScratchCanvas: TControlCanvas read GetScratchCanvas write FScratchCanvas;
    property Scrollbars: TEasyScrollbarManager read FScrollbars write FScrollbars;
    property Selection: TEasySelectionManager read FSelection write SetSelection;
    property ShowGroupMargins: Boolean read FShowGroupMargins write SetShowGroupMargins default False;
    property ShowInactive: Boolean read FShowInactive write SetShowInactive default False;
    property ShowThemedBorder: Boolean read FShowThemedBorder write SetShowThemedBorder default True;
    property Sort: TEasySortManager read FSort write FSort;
    property TabStop default True;
    property View: TEasyListStyle read FView write SetView;
    property WheelMouseDefaultScroll: TEasyDefaultWheelScroll read FWheelMouseDefaultScroll write FWheelMouseDefaultScroll default edwsVert;
    property WheelMouseScrollModifierEnabled: Boolean read FWheelMouseScrollModifierEnabled write FWheelMouseScrollModifierEnabled default True;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;

    function ClientInViewportCoords: TRect;
    function IsGrouped: Boolean; virtual;
    function IsThumbnailView: Boolean;
    function IsVertView: Boolean;
    function ScrollHeaderHorz: Boolean;
    procedure AutoFitAllCells(VisibleOnly: Boolean);
    procedure BeginUpdate; override;
    procedure EndUpdate(Invalidate: Boolean = True); override;
    procedure Loaded; override;
    procedure LoadFromFile(FileName: WideString; Mode: Word);
    procedure LoadFromStream(S: TStream); virtual;
    procedure SaveToFile(FileName: WideString; Mode: Word);
    procedure SaveToStream(S: TStream); virtual;
    property ForcePaint: Boolean read FForcePaint write FForcePaint;
    property States: TEasyControlStates read FStates write FStates;
    property TopItem: TEasyItem read GetTopItem;
  published
  end;

  TEasyBaseEditor = class(TEasyInterfacedPersistent, IEasyCellEditor)
  private
    FEditColumn: TEasyColumn;
    FEditor: TWinControl;
    FItem: TEasyItem;
    FModified: Boolean;
    FOldWndProc: TWndMethod;
    FRectArray: TEasyRectArrayObject;
    function GetEditor: TWinControl; virtual;
    function GetListview: TCustomEasyListview;
    procedure SetEditor(const Value: TWinControl); virtual;
  protected
    function EditText(Item: TEasyItem; Column: TEasyColumn): WideString; virtual;
    function GetEditorColor: TColor;
    procedure CalculateEditorRect(NewText: WideString; var NewRect: TRect); virtual; abstract;
    procedure CreateEditor(var AnEditor: TWinControl; Column: TEasyColumn; Parent: TWinControl); virtual; abstract;
    function GetEditorFont: TFont; virtual; abstract;
    function GetText: Variant; virtual; abstract;
    procedure ResizeEditor;
    property EditColumn: TEasyColumn read FEditColumn write FEditColumn;
    property Editor: TWinControl read GetEditor write SetEditor;
    property Item: TEasyItem read FItem write FItem;
    property Listview: TCustomEasyListview read GetListview;
    property Modified: Boolean read FModified write FModified;
    property OldWndProc: TWndMethod read FOldWndProc write FOldWndProc;
    property RectArray: TEasyRectArrayObject read FRectArray write FRectArray;
  public
    function AcceptEdit: Boolean;                         {IEasyCellEditor}
    procedure ControlWndHookProc(var Message: TMessage);  {IEasyCellEditor}
    function GetHandle: HWnd;                             {IEasyCellEditor}
    function GetModified: Boolean;                        {IEasyCellEditor}
    function PtInEditControl(WindowPt: TPoint): Boolean;  {IEasyCellEditor}
    procedure Finalize;                                   {IEasyCellEditor}
    procedure Hide;                                       {IEasyCellEditor}
    procedure Initialize(AnItem: TEasyItem; Column: TEasyColumn);  {IEasyCellEditor}
    procedure SelectAll; virtual; abstract;
    procedure SetEditorFocus; virtual;                    {IEasyCellEditor}
    procedure Show;                                       {IEasyCellEditor}

    property Handle: HWnd read GetHandle;
  end;

  TEasyStringEditor = class(TEasyBaseEditor)
  protected
    procedure CalculateEditorRect(NewText: WideString; var NewRect: TRect); override;
    procedure CreateEditor(var AnEditor: TWinControl; Column: TEasyColumn; Parent: TWinControl); override;
    function GetEditorFont: TFont; override;
    function GetText: Variant; override;
    procedure DoEditKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState;
      var DoDefault: Boolean); virtual;
    procedure OnEditKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
  public
    procedure SelectAll; override;
    procedure SetEditorFocus; override;
  end;

  TEasyMemoEditor = class(TEasyBaseEditor)
  protected
    procedure CalculateEditorRect(NewText: WideString; var NewRect: TRect); override;
    procedure CreateEditor(var AnEditor: TWinControl; Column: TEasyColumn; Parent: TWinControl); override;
    function GetEditorFont: TFont; override;
    function GetText: Variant; override;
    procedure DoEditKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState; var DoDefault: Boolean); virtual;
    procedure OnEditKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
  public
    procedure SelectAll; override;
    procedure SetEditorFocus; override;
  end;

  TEasyListview = class(TCustomEasyListview)
  private
    function GetPaintInfoColumn: TEasyPaintInfoColumn; reintroduce; virtual;
    function GetPaintInfoGroup: TEasyPaintInfoGroup; reintroduce; virtual;
    function GetPaintInfoItem: TEasyPaintInfoItem; reintroduce; virtual;
    procedure SetPaintInfoColumn(const Value: TEasyPaintInfoColumn); reintroduce; virtual;
    procedure SetPaintInfoGroup(const Value: TEasyPaintInfoGroup); reintroduce; virtual;
    procedure SetPaintInfoItem(const Value: TEasyPaintInfoItem); reintroduce; virtual;
  public
    property CheckManager;
    property GlobalImages;
    property Items;
    property States;
  published
    property Align;
    property AllowHiddenCheckedItems;
    property Anchors;
    property BackGround;
    property BevelKind;
    property BevelInner;
    property BevelOuter;
    property BevelWidth;
    property BiDiMode;
    property BorderWidth;
    property CacheDoubleBufferBits;
    property CellSizes;
    property Color;
    property Constraints;
    property Ctl3D;
    property DisabledBlendAlpha;
    property DisabledBlendColor;
    property EditManager;
    property Gesture;
    property ImagesState;
    property UseDockManager default True;
    property DragKind;
    property DragManager;
    property Font;
    property GroupCollapseButton;
    property GroupExpandButton;
    property GroupFont;
    property Groups;
    property HintAlignment;
    property HintType;
    property Header;
    property HotTrack;
    property IncrementalSearch;
    property ImagesGroup;
    property ImagesSmall;
    property ImagesLarge;
    property ImagesExLarge;
    property PaintInfoColumn: TEasyPaintInfoColumn read GetPaintInfoColumn write SetPaintInfoColumn;
    property PaintInfoGroup: TEasyPaintInfoGroup read GetPaintInfoGroup write SetPaintInfoGroup;
    property PaintInfoItem: TEasyPaintInfoItem read GetPaintInfoItem write SetPaintInfoItem;
    property ParentBiDiMode;
    {$IFDEF COMPILER_7_UP}property ParentBackground;{$ENDIF}
    property ParentColor;
    property ParentCtl3D;
    property ParentFont;
    property ParentShowHint;
    property PopupMenu;
    property PopupMenuHeader;
    property Scrollbars;
    property ShowGroupMargins;
    property ShowInactive;
    property ShowThemedBorder;
    property ShowHint;
    property Selection;
    property Sort;
    property TabOrder;
    property TabStop;
    property Themed;
    property View;
    property Visible;
    property WheelMouseDefaultScroll;
    property WheelMouseScrollModifierEnabled;

    property OnAfterPaint;
    property OnCanResize;
    property OnClick;
    property OnConstrainedResize;
    {$IFDEF COMPILER_5_UP}
    property OnContextPopup;
    {$ENDIF}
    property OnAutoGroupGetKey;
    property OnAutoSortGroupCreate;
    property OnClipboardCopy;
    property OnClipboardCut;
    property OnClipboardPaste;
    property OnColumnCheckChanged;
    property OnColumnCheckChanging;
    property OnColumnClick;
    property OnColumnContextMenu;
    property OnColumnCustomView;
    property OnColumnDblClick;
    property OnColumnDropDownButtonClick;
    property OnColumnEnableChanged;
    property OnColumnEnableChanging;
    property OnColumnFreeing;
    property OnColumnGetCaption;
    property OnColumnGetImageIndex;
    property OnColumnGetImageList;
    property OnColumnGetDetail;
    property OnColumnGetDetailCount;
    property OnColumnImageDraw;
    property OnColumnImageGetSize;
    property OnColumnImageDrawIsCustom;
    property OnColumnInitialize;
    property OnColumnLoadFromStream;
    property OnColumnPaintText;
    property OnColumnSaveToStream;
    property OnColumnSelectionChanged;
    property OnColumnSelectionChanging;
    property OnColumnSetCaption;
    property OnColumnSetImageIndex;
    property OnColumnSetDetail;
    property OnColumnSizeChanged;
    property OnColumnSizeChanging;
    property OnColumnStructureChange;
    property OnColumnVisibilityChanged;
    property OnColumnVisibilityChanging;
    property OnCustomGrid;
    property OnDblClick;
    property OnDragDrop;
    property OnDragOver;
    property OnDockDrop;
    property OnDockOver;
    property OnEndDock;
    property OnEndDrag;
    property OnEndUpdate;
    property OnEnter;
    property OnExit;
    property OnGetDragImage;
    property OnGetSiteInfo;
    property OnGroupClick;
    property OnGroupCollapse;
    property OnGroupCollapsing;
    property OnGroupCompare;
    property OnGroupContextMenu;
    property OnGroupCustomView;
    property OnGroupDblClick;
    property OnGroupExpand;
    property OnGroupExpanding;
    property OnGroupFreeing;
    property OnGroupGetCaption;
    property OnGroupGetImageIndex;
    property OnGroupGetImageList;
    property OnGroupGetDetail;
    property OnGroupGetDetailCount;
    property OnGroupImageDraw;
    property OnGroupImageGetSize;
    property OnGroupImageDrawIsCustom;
    property OnGroupInitialize;
    property OnGroupLoadFromStream;
    property OnGroupPaintText;
    property OnGroupHotTrack;
    property OnGroupSaveToStream;
    property OnGroupSetCaption;
    property OnGroupSetImageIndex;
    property OnGroupSetDetail;
    property OnGroupStructureChange;
    property OnGroupVisibilityChanged;
    property OnGroupVisibilityChanging;
    property OnHeaderDblClick;
    property OnHeaderMouseDown;
    property OnHeaderMouseMove;
    property OnHeaderMouseUp;
    property OnHintCustomInfo;
    property OnHintCustomDraw;
    property OnHintPauseTime;
    property OnHintPopup;
    property OnIncrementalSearch;
    property OnItemCheckChange;
    property OnItemCheckChanging;
    property OnItemClick;
    property OnItemCompare;
    property OnItemContextMenu;
    property OnItemCreateEditor;
    property OnItemCustomView;
    property OnItemDblClick;
    property OnItemEditBegin;
    property OnItemEdited;
    property OnItemEditEnd;
    property OnItemEnableChange;
    property OnItemEnableChanging;
    property OnItemFreeing;
    property OnItemFocusChanged;
    property OnItemFocusChanging;
    property OnItemGetCaption;
    property OnItemGetEditCaption;
    property OnItemGetEditMenu;
    property OnItemGetGroupKey;
    property OnItemGetImageIndex;
    property OnItemGetImageList;
    property OnItemGetTileDetail;
    property OnItemGetTileDetailCount;
    property OnItemHotTrack;
    property OnItemImageDraw;
    property OnItemImageGetSize;
    property OnItemImageDrawIsCustom;
    property OnItemInitialize;
    property OnItemLoadFromStream;
    property OnItemMouseDown;
    property OnItemMouseUp;
    property OnItemPaintText;
    property OnItemSaveToStream;
    property OnItemSelectionChanged;
    property OnItemSelectionChanging;
    property OnItemSelectionsChanged;
    property OnItemSetCaption;
    property OnItemSetGroupKey;
    property OnItemSetImageIndex;
    property OnItemSetTileDetail;
    property OnItemStructureChange;
    property OnItemThumbnailDraw;
    property OnItemVisibilityChanged;
    property OnItemVisibilityChanging;
    property OnKeyAction;
    property OnMouseGesture;
    property OnMouseDown;
    property OnMouseMove;
    property OnMouseUp;
    property OnMouseWheel;
    property OnMouseWheelDown;
    property OnMouseWheelUp;
    property OnOLEDragEnd;
    property OnOLEDragStart;
    property OnOLEDragEnter;
    property OnOLEDragOver;
    property OnOLEDragLeave;
    property OnOLEDragDrop;
    property OnOLEGetCustomFormats;
    property OnOLEGetData;
    property OnOLEGetDataObject;
    property OnOLEQueryContineDrag;
    property OnOLEGiveFeedback;
    property OnOLEQueryData;
    property OnPaintHeaderBkGnd;
    property OnPaintBkGnd;
    property OnResize;
    property OnScroll;
    property OnStartDock;
    property OnStartDrag;
    property OnUnDock;
    property OnViewChange;
  end;

  TEasyBaseTaskBand = class(TCustomEasyListview)
  protected
    function CreateColumnPaintInfo: TEasyPaintInfoBaseColumn; override;
    function CreateGroupPaintInfo: TEasyPaintInfoBaseGroup; override;
    function CreateItemPaintInfo: TEasyPaintInfoBaseItem; override;
    function GetPaintInfoColumn: TEasyPaintInfoTaskBandColumn; reintroduce; virtual;
    function GetPaintInfoGroup: TEasyPaintInfoTaskbandGroup; reintroduce; virtual;
    function GetPaintInfoItem: TEasyPaintInfoTaskBandItem; reintroduce; virtual;
    function GroupTestExpand(HitInfo: TEasyGroupHitTestInfoSet): Boolean; override;
    procedure DoCustomGrid(Group: TEasyGroup; ViewStyle: TEasyListStyle; var Grid: TEasyGridGroupClass); override;
    procedure DoGroupCustomView(Group: TEasyGroup; ViewStyle: TEasyListStyle; var View: TEasyViewGroupClass); override;
    procedure DoItemCustomView(Item: TEasyItem; ViewStyle: TEasyListStyle; var View: TEasyViewItemClass); override;
    procedure SetPaintInfoColumn(const Value: TEasyPaintInfoTaskBandColumn); reintroduce; virtual;
    procedure SetPaintInfoGroup(const Value: TEasyPaintInfoTaskbandGroup); reintroduce; virtual;
    procedure SetPaintInfoItem(const Value: TEasyPaintInfoTaskBandItem); reintroduce; virtual;

    property PaintInfoColumn: TEasyPaintInfoTaskBandColumn read GetPaintInfoColumn write SetPaintInfoColumn;
  public
    constructor Create(AOwner: TComponent); override;
  end;

  TEasyTaskPanelBand = class(TEasyBaseTaskBand)
  private
    FAutoScrollPanels: Boolean;
    FOnGetTaskPanel: TEasyGetTaskPanelEvent;
    function GetGroups: TEasyGroupsTaskPanel;
    procedure SetGroups(const Value: TEasyGroupsTaskPanel);
  protected
    function CreateGroups: TEasyGroups; override;
    procedure CMMouseWheel(var Msg: TCMMouseWheel); message CM_MOUSEWHEEL;
    procedure DoCustomGrid(Group: TEasyGroup; ViewStyle: TEasyListStyle; var Grid: TEasyGridGroupClass); override;
    procedure DoGetTaskPanel(Sender: TEasyGroupTaskPanel; var TaskPanel: TEasyTaskPanelFormClass); virtual;
    procedure DoGroupCustomView(Group: TEasyGroup; ViewStyle: TEasyListStyle; var View: TEasyViewGroupClass); override;
    procedure DoGroupExpand(Group: TEasyGroup); override;
    procedure PositionPanels;
    procedure WMHScroll(var Msg: TWMHScroll); message WM_HSCROLL;
    procedure WMVScroll(var Msg: TWMVScroll); message WM_VSCROLL;
    procedure WMWindowPosChanging(var Msg: TWMWindowPosChanging); message WM_WINDOWPOSCHANGING;
  public
    constructor Create(AOwner: TComponent); override;
  published
    property Align;
    property Anchors;
    property AutoScrollPanels: Boolean read FAutoScrollPanels write FAutoScrollPanels default False;
    property BackGround;
    property BevelInner;
    property BevelOuter;
    property BevelWidth;
    property BiDiMode;
    property BorderWidth;
    property Color;
    property Constraints;
    property Ctl3D;
    property OnGetTaskPanel: TEasyGetTaskPanelEvent read FOnGetTaskPanel write FOnGetTaskPanel;
    property UseDockManager default True;
    property Font;
    property GroupFont;
    property Groups: TEasyGroupsTaskPanel read GetGroups write SetGroups;
    property HintAlignment;
    property HintType;
    property HotTrack;
    property IncrementalSearch;
    property ImagesGroup;
    property ImagesSmall;
    property PaintInfoGroup;
    property ParentBiDiMode;
    {$IFDEF COMPILER_7_UP}property ParentBackground;{$ENDIF}
    property ParentColor;
    property ParentCtl3D;
    property ParentFont;
    property ParentShowHint;
    property PopupMenu;
    property ShowThemedBorder;
    property ShowHint;
    property Selection;
    property Sort;
    property TabOrder;
    property TabStop;
    property Themed;
    property Visible;
    property WheelMouseDefaultScroll;
    property WheelMouseScrollModifierEnabled;

    property OnCanResize;
    property OnClick;
    property OnConstrainedResize;
    {$IFDEF COMPILER_5_UP} property OnContextPopup; {$ENDIF}
    property OnAutoSortGroupCreate;
    property OnDblClick;
    property OnDragDrop;
    property OnDragOver;
    property OnDockDrop;
    property OnDockOver;
    property OnEndDock;
    property OnEndDrag;
    property OnEndUpdate;
    property OnEnter;
    property OnExit;
    property OnGetSiteInfo;
    property OnGroupClick;
    property OnGroupCollapse;
    property OnGroupCollapsing;
    property OnGroupContextMenu;
    property OnGroupCustomView;
    property OnGroupDblClick;
    property OnGroupExpand;
    property OnGroupExpanding;
    property OnGroupFreeing;
    property OnGroupGetCaption;
    property OnGroupGetImageIndex;
    property OnGroupGetImageList;
    property OnGroupImageDraw;
    property OnGroupImageGetSize;
    property OnGroupImageDrawIsCustom;
    property OnGroupInitialize;
    property OnGroupPaintText;
    property OnGroupHotTrack;
    property OnGroupSetCaption;
    property OnGroupSetImageIndex;
    property OnGroupVisibilityChanged;
    property OnGroupVisibilityChanging;
    property OnHintCustomInfo;
    property OnHintCustomDraw;
    property OnHintPauseTime;
    property OnHintPopup;
    property OnKeyAction;
    property OnMouseDown;
    property OnMouseMove;
    property OnMouseUp;
    property OnMouseWheel;
    property OnMouseWheelDown;
    property OnMouseWheelUp;
    property OnPaintBkGnd;
    property OnResize;
    property OnStartDock;
    property OnStartDrag;
    property OnUnDock;
  end;

  TEasyTaskBand = class(TEasyBaseTaskBand)    
  protected
    procedure DoGroupCollapse(Group: TEasyGroup); override;
    procedure DoGroupExpand(Group: TEasyGroup); override;
  public
    property GlobalImages;
    property Items;
    property States;
    property Scrollbars;
  published 
    property Align;
    property Anchors;
    property BevelInner;
    property BevelOuter;
    property BevelWidth;
    property BiDiMode;
    property BorderWidth;
    property CellSizes;
    property Color;
    property Constraints;
    property Ctl3D;
    property EditManager;
    property UseDockManager default True;
    property DragKind;
    property DragManager;
    property Font;
    property GroupFont;
    property Groups;
    property HintAlignment;
    property HintType;
    property HotTrack;
    property IncrementalSearch;
    property ImagesGroup;
    property ImagesSmall;
    property PaintInfoGroup: TEasyPaintInfoTaskbandGroup read GetPaintInfoGroup write SetPaintInfoGroup;
    property PaintInfoItem: TEasyPaintInfoTaskBandItem read GetPaintInfoItem write SetPaintInfoItem;
    property ParentBiDiMode;
    {$IFDEF COMPILER_7_UP}property ParentBackground;{$ENDIF}
    property ParentColor;
    property ParentCtl3D;
    property ParentFont;
    property ParentShowHint;
    property PopupMenu;
    property PopupMenuHeader;
    property ShowGroupMargins;
    property ShowThemedBorder;
    property ShowHint;
    property Selection;
    property Sort;
    property TabOrder;
    property TabStop;
    property Themed;
    property Visible;
    property WheelMouseDefaultScroll;
    property WheelMouseScrollModifierEnabled;

    property OnCanResize;
    property OnClick;
    property OnConstrainedResize;
    {$IFDEF COMPILER_5_UP}
    property OnContextPopup;
    {$ENDIF}
    property OnAutoSortGroupCreate;
    property OnDblClick;
    property OnDragDrop;
    property OnDragOver;
    property OnDockDrop;
    property OnDockOver;
    property OnEndDock;
    property OnEndDrag;
    property OnEndUpdate;
    property OnEnter;
    property OnExit;
    property OnGetDragImage;
    property OnGetSiteInfo;
    property OnGroupClick;
    property OnGroupCollapse;
    property OnGroupCollapsing;
    property OnGroupCompare;
    property OnGroupContextMenu;
    property OnGroupDblClick;
    property OnGroupExpand;
    property OnGroupExpanding;
    property OnGroupFreeing;
    property OnGroupGetCaption;
    property OnGroupGetImageIndex;
    property OnGroupGetImageList;
    property OnGroupImageDraw;
    property OnGroupImageGetSize;
    property OnGroupImageDrawIsCustom;
    property OnGroupInitialize;
    property OnGroupPaintText;
    property OnGroupHotTrack;
    property OnGroupSetCaption;
    property OnGroupSetImageIndex;
    property OnGroupSetDetail;
    property OnGroupVisibilityChanged;
    property OnGroupVisibilityChanging;
    property OnHeaderDblClick;
    property OnHeaderMouseDown;
    property OnHeaderMouseMove;
    property OnHeaderMouseUp;
    property OnHintCustomInfo;
    property OnHintCustomDraw;
    property OnHintPauseTime;
    property OnHintPopup;
    property OnIncrementalSearch;
    property OnItemCheckChange;
    property OnItemCheckChanging;
    property OnItemClick;
    property OnItemCompare;
    property OnItemContextMenu;
    property OnItemCreateEditor;
    property OnItemDblClick;
    property OnItemEditBegin;
    property OnItemEdited;
    property OnItemEditEnd;
    property OnItemEnableChange;
    property OnItemEnableChanging;
    property OnItemFreeing;
    property OnItemFocusChanged;
    property OnItemFocusChanging;
    property OnItemGetCaption;
    property OnItemGetGroupKey;
    property OnItemGetImageIndex;
    property OnItemGetImageList;
    property OnItemHotTrack;
    property OnItemImageDraw;
    property OnItemImageGetSize;
    property OnItemImageDrawIsCustom;
    property OnItemInitialize;
    property OnItemMouseDown;
    property OnItemPaintText;
    property OnItemSelectionChanged;
    property OnItemSelectionChanging;
    property OnItemSetCaption;
    property OnItemSetGroupKey;
    property OnItemSetImageIndex;
    property OnItemVisibilityChanged;
    property OnItemVisibilityChanging;
    property OnKeyAction;
    property OnMouseDown;
    property OnMouseMove;
    property OnMouseUp;
    property OnMouseWheel;
    property OnMouseWheelDown;
    property OnMouseWheelUp;
    property OnOLEDragEnd;
    property OnOLEDragStart;
    property OnOLEDragEnter;
    property OnOLEDragOver;
    property OnOLEDragLeave;
    property OnOLEDragDrop;
    property OnOLEGetCustomFormats;
    property OnOLEGetData;
    property OnOLEQueryContineDrag;
    property OnOLEGiveFeedback;
    property OnOLEQueryData;
    property OnPaintBkGnd;
    property OnResize;
    property OnStartDock;
    property OnStartDrag;
    property OnUnDock;
  end;



const
  EASYLISTSTYLETEXTS: array[TEasyListStyle] of string =
    ('Icon', 'Small Icon', 'List', 'Details', 'Thumbnail', 'Tile', 'FilmStrip', 'Grid');
  EASYSORTALGORITHMS: array[TEasySortAlgorithm] of string =
    ('QuickSort', 'BubbleSort', 'MergeSort');

  HEADERSUPPORTEDVIEWS = [elsReport, elsGrid];
  VERTICALVIEWS = [elsIcon, elsSmallIcon, elsReport, elsThumbnail, elsTile, elsGrid];
  THUMBNAILVIEWS = [elsThumbnail, elsFilmStrip];
  MULTILINEVIEWS = [elsIcon, elsThumbnail, elsFilmStrip, elsTile];

procedure FillStringsWithEasyListStyles(Strings: TStrings);
procedure FillStringsWithEasySortAlgorithms(Strings: TStrings);

var
  AlphaBlender: TEasyAlphaBlender;

implementation

uses
  Math;

const
  PERSISTENTOBJECTSTATES = [esosSelected, esosEnabled, esosVisible, esosChecked, esosBold]; // States that are stored to a stream for persistance
  H_STRINGEDITORMARGIN = 12;  // Margin for String Editors in the Horz direction
  V_STRINGEDITORMARGIN = 2;  // Margin for String Editors in the Vert direction

var
  HeaderClipboardFormat: TClipFormat;

type
  TWinControlHack = class(TWinControl);

type
  PHeaderClipData = ^THeaderClipData;
  THeaderClipData = record
    Thread: THandle;
    Listview: TCustomEasyListview;
    Column: TEasyColumn;
  end;

const
  MaxPixelCount = 65536;

type
  TRGBArray = array[0..MaxPixelCount-1] OF TRGBQuad;
  pRGBArray = ^TRGBArray;


  // Very simple implementation for saving/loading the tree to disk with a Unicode
  // filename in NT. Use TnT Uniocode package for a full blown implementation
  TWideFileStream = class(THandleStream)
  public
    constructor Create(const FileName: WideString; Mode: Word);
    destructor Destroy; override;
  end;

  TWinControlCracker = class(TWinControl) end;

  function WeightPixels (const pixelA, pixelB, pixelWeightA, pixelWeightB: Cardinal):  Byte;
  begin
   Result := Byte((pixelWeightA*pixelA + pixelWeightB*pixelB) DIV (pixelWeightA + pixelWeightB))
  end {WeightPixels};

  procedure BlendBits(Bits1, Bits2: TBitmap; WeightA, WeightB: Integer; BlurBits2: Boolean);
  var
    y, x: Integer;
    RowA, RowB, TargetRow, PrevRow, NextRow:  pRGBArray;
    RGBArrayA, RGBArrayB, PrevRGBArrayB, NextRGBArrayB, PrevRowRGBArrayB, NextRowRGBArrayB: TRGBQuad;
  begin
    if (Bits1.Height > 0) and (Bits1.Width = Bits2.Width) and (Bits1.Height = Bits2.Height) then
    begin
      PrevRow := nil;
      for y := 0 to Bits1.Height - 1 do
      begin
        RowA := Bits1.ScanLine[y];
        RowB := Bits2.ScanLine[y];
        TargetRow := Bits2.ScanLine[y];
        if y + 1 < Bits1.Height then
          NextRow := Bits2.ScanLine[y+1]
        else
          NextRow := nil;
        for x := 0 to Bits1.Width - 1 do
        begin
          RGBArrayA := RowA[x];
          RGBArrayB := RowB[x];
          if BlurBits2 and Assigned(NextRow) and Assigned(PrevRow) and (x > 0) and (x < Bits1.Width - 1) then
          begin
            PrevRGBArrayB := TargetRow[x-1];
            NextRGBArrayB := TargetRow[x+1];
            PrevRowRGBArrayB := PrevRow[x];
            NextRowRGBArrayB := NextRow[x];
            RGBArrayB.rgbBlue := (
                                  PrevRow[x-1].rgbBlue +
                                  PrevRow[x+1].rgbBlue +
                                  NextRow[x-1].rgbBlue +
                                  NextRow[x+1].rgbBlue +
                                  PrevRowRGBArrayB.rgbBlue +
                                  NextRowRGBArrayB.rgbBlue +
                                  TargetRow[x].rgbBlue div 2+
                                  PrevRGBArrayB.rgbBlue +
                                  NextRGBArrayB.rgbBlue) div 9;
            RGBArrayB.rgbGreen := (PrevRow[x-1].rgbGreen +
                                  PrevRow[x+1].rgbGreen +
                                  NextRow[x-1].rgbGreen +
                                  NextRow[x+1].rgbGreen +
                                  PrevRowRGBArrayB.rgbGreen +
                                  NextRowRGBArrayB.rgbGreen +
                                  TargetRow[x].rgbGreen div 2 +
                                  PrevRGBArrayB.rgbGreen +
                                  NextRGBArrayB.rgbGreen) div 9;
            RGBArrayB.rgbRed := ( PrevRow[x-1].rgbRed +
                                  PrevRow[x+1].rgbRed +
                                  NextRow[x-1].rgbRed +
                                  NextRow[x+1].rgbRed +
                                  PrevRowRGBArrayB.rgbRed +
                                  NextRowRGBArrayB.rgbRed +
                                  TargetRow[x].rgbRed div 2 +
                                  PrevRGBArrayB.rgbRed +
                                  NextRGBArrayB.rgbRed) div 9;
          end;
          RGBArrayB.rgbBlue := WeightPixels(RGBArrayA.rgbBlue, RGBArrayB.rgbBlue, WeightA, WeightB);
          RGBArrayB.rgbGreen := WeightPixels(RGBArrayA.rgbGreen, RGBArrayB.rgbGreen, WeightA, WeightB);
          RGBArrayB.rgbRed := WeightPixels(RGBArrayA.rgbRed, RGBArrayB.rgbRed, WeightA, WeightB);
          RowB[x] := RGBArrayB;
        end;
        PrevRow := TargetRow;
      end;
    end;
  end;

function HeaderClipFormat: TFormatEtc;
begin
  Result.cfFormat := HeaderClipboardFormat;
  Result.ptd := nil;
  Result.lindex := -1;
  Result.dwAspect := -1;
  Result.ptd := nil;
  Result.tymed := TYMED_HGLOBAL
end;

function DefaultSort(Column: TEasyColumn; Item1, Item2: TEasyCollectionItem): Integer;
var
  Index: Integer;
begin
  if not Assigned(Column) then
    Index := 0
  else
    Index := Column.Position;

  Result := WideStrIComp(PWideChar(Item1.Captions[Index]), PWideChar( Item2.Captions[Index]));


  if Assigned(Column) and (Column.SortDirection = esdDescending) then
    Result := -Result
end;

procedure FillStringsWithEasyListStyles(Strings: TStrings);
var
  ListStyle: TEasyListStyle;
begin
  Strings.Clear;
  for ListStyle := low(TEasyListStyle) to high(TEasyListStyle) do
    Strings.Add(EASYLISTSTYLETEXTS[ListStyle]);
end;

procedure FillStringsWithEasySortAlgorithms(Strings: TStrings);
var
  ListStyle: TEasySortAlgorithm;
begin
  Strings.Clear;
  for ListStyle := low(TEasySortAlgorithm) to high(TEasySortAlgorithm) do
    Strings.Add(EASYSORTALGORITHMS[ListStyle]);
end;

function WideFileCreate(const FileName: WideString): Integer;
begin
  if Win32Platform = VER_PLATFORM_WIN32_NT then
    Result := Integer(CreateFileW(PWideChar(FileName), GENERIC_READ or GENERIC_WRITE, 0,
      nil, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0))
  else
    Result := Integer(CreateFileA(PAnsiChar(AnsiString(PWideChar(FileName))), GENERIC_READ or GENERIC_WRITE, 0,
      nil, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0));
end;

function WideFileOpen(const FileName: WideString; Mode: LongWord): Integer;
const
  AccessMode: array[0..2] of LongWord = (
    GENERIC_READ,
    GENERIC_WRITE,
    GENERIC_READ or GENERIC_WRITE);
  ShareMode: array[0..4] of LongWord = (
    0,
    0,
    FILE_SHARE_READ,
    FILE_SHARE_WRITE,
    FILE_SHARE_READ or FILE_SHARE_WRITE);
begin
  if Win32Platform = VER_PLATFORM_WIN32_NT then
    Result := Integer(CreateFileW(PWideChar(FileName), AccessMode[Mode and 3], ShareMode[(Mode and $F0) shr 4],
      nil, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0))
  else
    Result := Integer(CreateFileA(PAnsiChar(AnsiString(FileName)), AccessMode[Mode and 3], ShareMode[(Mode and $F0) shr 4],
      nil, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0));
end;

{ TWideFileStream }

constructor TWideFileStream.Create(const FileName: WideString; Mode: Word);
var
  CreateHandle: Integer;
begin
  if Mode = fmCreate then
  begin
    CreateHandle := WideFileCreate(FileName);
    if CreateHandle < 0 then
     {$IFNDEF COMPILER_5_UP}
      raise EFCreateError.Create('Can not create file: ' + FileName);
     {$ELSE}
     raise EFCreateError.CreateResFmt(PResStringRec(@SFCreateError), [FileName]);
     {$ENDIF COMPILER_5_UP}
  end else
  begin
    CreateHandle := WideFileOpen(FileName, Mode);
    if CreateHandle < 0 then
     {$IFNDEF COMPILER_5_UP}
      raise EFCreateError.Create('Can not create file: ' + FileName);
     {$ELSE}
     raise EFCreateError.CreateResFmt(PResStringRec(@SFCreateError), [FileName]);
     {$ENDIF COMPILER_5_UP}
  end;
  inherited Create(CreateHandle);
end;

destructor TWideFileStream.Destroy;
begin
  if Handle >= 0 then FileClose(Handle);
  inherited Destroy;
end;

{ TEasyGroupItemOwnedPersistent }

constructor TEasyOwnedPersistentGroupItem.Create(AnOwner: TEasyGroup);
begin
  inherited Create(AnOwner.OwnerListview);
  FOwnerGroup := AnOwner
end;

{ TEasyOwnedPersistent}
constructor TEasyOwnedPersistent.Create(AnOwner: TCustomEasyListview);
begin
  inherited Create;
  FOwnerListview := AnOwner;
end;

function TEasyOwnedPersistent.GetOwner: TPersistent;
begin
  Result := FOwnerListview;
end;

procedure TEasyOwnedPersistent.LoadFromStream(S: TStream; Version: Integer = EASYLISTVIEW_STREAM_VERSION);
begin

end;

procedure TEasyOwnedPersistent.SaveToStream(S: TStream; Version: Integer = EASYLISTVIEW_STREAM_VERSION);
begin

end;

{ TEasyInterfacedPersistent }

function TEasyInterfacedPersistent.GetObj: TObject;
begin
  Result := Self
end;

function TEasyInterfacedPersistent._AddRef: Integer;
begin
  Result := InterlockedIncrement(FRefCount);
end;

function TEasyInterfacedPersistent._Release: Integer;
begin
  Result := InterlockedDecrement(FRefCount);
  if (Result = 0) then
    Destroy;
end;

function TEasyInterfacedPersistent.QueryInterface(const IID: TGUID; out Obj): HResult;
begin
  if GetInterface(IID, Obj) then
    Result := 0
  else
    Result := E_NOINTERFACE
end;

procedure TEasyInterfacedPersistent.AfterConstruction;
begin
  // Release the constructor's implicit refcount
  InterlockedDecrement(FRefCount);
end;

procedure TEasyInterfacedPersistent.BeforeDestruction;
begin

end;

class function TEasyInterfacedPersistent.NewInstance: TObject;
begin
  // Set an implicit refcount so that refcounting
  // during construction won't destroy the object.
  Result := inherited NewInstance;
  TEasyInterfacedPersistent(Result).FRefCount := 1;
end;

{ TEasyOwnedInterfacedPersistent }

constructor TEasyOwnedInterfacedPersistent.Create(AnOwner: TCustomEasyListview);
begin
  inherited Create;
  FOwner := AnOwner;
end;

{ TEasyMargins }

constructor TEasyMargin.Create(AnOwner: TCustomEasyListview);
begin
  inherited;
  FSize := 30;
end;

destructor TEasyMargin.Destroy;
begin
  inherited Destroy;
end;

function TEasyMargin.RuntimeSize: Integer;
begin
  if not Visible or not FOwnerListview.ShowGroupMargins then
    Result := 0
  else
    Result := FSize
end;

procedure TEasyMargin.Assign(Source: TPersistent);
var
  Temp: TEasyMargin;
begin
  if Source is TEasyMargin then
  begin
    Temp := TEasyMargin(Source);
    FSize := Temp.Size;
    FVisible := Temp.Visible
  end
end;

procedure TEasyMargin.SetSize(Value: Integer);
begin
  if Value <> FSize then
  begin
    if Value < 0 then
      FSize := 0
    else
      FSize := Value;
    OwnerListview.Groups.Rebuild
  end
end;

procedure TEasyMargin.SetVisible(Value: Boolean);
begin
  if Value <> FVisible then
  begin
    FVisible := Value;
    OwnerListview.Groups.Rebuild
  end
end;

{ TEasyHeaderMargin }

constructor TEasyHeaderMargin.Create(AnOwner: TCustomEasyListview);
begin
  inherited Create(AnOwner);
  FVisible := True;
end;

function TEasyItems.Add(Data: TObject = nil): TEasyItem;
begin
  Result := TEasyItem( inherited Add(Data))
end;

function TEasyItems.AddCustom(CustomItem: TEasyItemClass; Data: TObject = nil): TEasyItem;
begin
  Result := nil;
  if Assigned(CustomItem) then
  begin
    Result := CustomItem.Create(Self);
    FList.Add(Result);
    ReIndexItems;
    DoItemAdd(Result, FList.Count - 1);
    Result.Data := Data;
    DoStructureChange
  end
end;

function TEasyItems.AddInterfaced(const DataInf: IUnknown; Data: TObject = nil): TEasyItemInterfaced;
begin
  Result := nil;
  if Assigned(DataInf) then
  begin
    Result := TEasyItemInterfaced.Create(Self);
    FList.Add(Result);
    ReIndexItems;
    DoItemAdd(Result, FList.Count - 1);
    Result.DataInf := DataInf;
    Result.Data := Data;
    DoStructureChange
  end
end;

function TEasyItems.AddVirtual(Data: TObject = nil): TEasyItemVirtual;
begin
  Result := TEasyItemVirtual.Create(Self);
  FList.Add(Result);
  ReIndexItems;
  DoItemAdd(Result, FList.Count - 1);
  Result.Data := Data;
  DoStructureChange
end;

constructor TEasyItems.Create(AnOwner: TCustomEasyListview; AnOwnerGroup: TEasyGroup);
begin
  inherited Create(AnOwner);
  FOwnerGroup := AnOwnerGroup;
  FItemClass := TEasyItemStored;
end;

function TEasyItems.InsertCustom(Index: Integer; CustomItem: TEasyItemClass; Data: TObject = nil): TEasyItem;
begin
  Result := CustomItem.Create(Self);
  FList.Insert(Index, Result);
  ReIndexItems;
  DoItemAdd(Result, Index);
  Result.Data := Data;
  DoStructureChange
end;

function TEasyItems.InsertInterfaced(Index: Integer; const DataInf: IUnknown; Data: TObject = nil): TEasyItemInterfaced;
begin
  Result := nil;
  if Assigned(DataInf) then
  begin
    Result := TEasyItemInterfaced.Create(Self);
    FList.Insert(Index, Result);
    ReIndexItems;
    DoItemAdd(Result, Index);
    Result.DataInf := DataInf;
    Result.Data := Data;
    DoStructureChange
  end
end;

function TEasyItems.InsertVirtual(Index: Integer; Data: TObject = nil): TEasyItemVirtual;
begin
  Result := TEasyItemVirtual.Create(Self);
  FList.Insert(Index, Result);
  ReIndexItems;
  DoItemAdd(Result, Index);
  Result.Data := Data;
  DoStructureChange
end;

procedure TEasyItems.Clear(FreeItems: Boolean = True);
begin
  OwnerListview.Selection.IncMultiChangeCount;
  OwnerListview.Selection.GroupSelectBeginUpdate;
  inherited Clear(FreeItems);
  OwnerListview.Selection.GroupSelectEndUpdate;
  OwnerListview.Selection.DecMultiChangeCount;
end;

procedure TEasyItems.Delete(Index: Integer);
var
  Temp: TEasyItem;
begin
  // Find the next or prev visible item
  Temp := nil;
  if not (csDestroying in OwnerListview.ComponentState) and Items[Index].Focused then
  begin
    Temp := OwnerGroup.OwnerGroups.NextVisibleItem(Items[Index]);
    if not Assigned(Temp) then
      Temp := OwnerGroup.OwnerGroups.PrevVisibleItem(Items[Index]);
  end;

  // Delete the item, it will also unfocus the item
  inherited;
  
  // Focus the next or prev visible item
  if Assigned(Temp) then
    Temp.Focused := True;
end;

destructor TEasyItems.Destroy;
begin
  inherited;
end;

procedure TEasyItems.DoStructureChange;
begin
  OwnerListview.IncrementalSearch.ResetSearch;
  OwnerListview.Groups.Rebuild(False);
  if OwnerListview.Sort.AutoSort then
    OwnerListview.Sort.SortAll;
  OwnerListview.DoItemStructureChange;
end;

procedure TEasyItems.Exchange(Index1, Index2: Integer);
begin
  inherited;
end;

function TEasyItems.GetItem(Index: Integer): TEasyItem;
begin
  // This is a bottle neck for large data sets.  Do this direct instead of inherited
  Result := ( FList.List[Index])
end;

function TEasyItems.Insert(Index: Integer; Data: TObject = nil): TEasyItem;
begin
  Result := TEasyItem( inherited Insert(Index));
end;

procedure TEasyItems.SetItem(Index: Integer; Value: TEasyItem);
begin
  inherited Items[Index] := Value
end;

{ TEasyGlobalItems}

function TEasyGlobalItems.Add(Data: TObject = nil): TEasyItem;
begin
  EnsureFirstGroup;
  Result := TEasyItem(GetLastGroup.Items.Add(Data));
end;

function TEasyGlobalItems.AddCustom(CustomItem: TEasyItemClass; Data: TObject = nil): TEasyItem;
begin
  EnsureFirstGroup;
  Result := GetLastGroup.Items.AddCustom(CustomItem, Data);
end;

function TEasyGlobalItems.AddInterfaced(const DataInf: IUnknown; Data: TObject = nil): TEasyItemInterfaced;
begin
  EnsureFirstGroup;
  Result := GetLastGroup.Items.AddInterfaced(DataInf, Data);
end;

function TEasyGlobalItems.AddVirtual(Data: TObject = nil): TEasyItemVirtual;
begin
  EnsureFirstGroup;
  Result := GetLastGroup.Items.AddVirtual(Data);
end;

procedure TEasyGlobalItems.Clear;
begin
  FOwner.Groups.Clear;
end;

constructor TEasyGlobalItems.Create(AnOwner: TCustomEasyListview);
begin
  inherited Create;
  FOwner := AnOwner;
end;

procedure TEasyGlobalItems.Delete(Index: Integer; ReIndex: Boolean = True);
var
  Item: TEasyItem;
begin
//  OwnerListview.Groups.Delete(Index);
  Item := Items[Index];
  Item.OwnerGroup.Items.Delete(Item.Index);
//  OwnerListview.Groups.ReIndexItems(nil, ReIndex);
end;

procedure TEasyGlobalItems.EnsureFirstGroup;
begin
  if FOwner.Groups.Count = 0 then
    FOwner.Groups.Add.Caption := DEFAULT_GROUP_NAME;
end;

procedure TEasyGlobalItems.Exchange(Index1, Index2: Integer);
var
  Item1, Item2: TEasyItem;
begin
  Item1 := Items[Index1];
  Item2 := Items[Index2];
  if Item1.OwnerItems = Item2.OwnerItems then
    Item1.OwnerItems.Exchange(Item1.Index, Item2.Index)
  else
    raise Exception.Create('exchange of items between different groups is not yet supported'); // TODO
end;

function TEasyGlobalItems.GetCount: Integer;
var
  i: Integer;
begin
  Result := 0;
  for i := FOwner.Groups.Count - 1 downto 0 do
    Inc(Result, FOwner.Groups[i].Items.Count);
end;

function TEasyGlobalItems.GetItem(Index: Integer): TEasyItem;
begin
  Result := GetItemInternal(Index);
  if Result = nil then // index too big
    IndexError(Index);
end;

function TEasyGlobalItems.GetItemInternal(Index: Integer): TEasyItem;
var
  i: Integer;
  ItemCount: Integer;
begin
  // GetItemInternal translates an absolute index into an item. It simply
  // returns nil if the index is too big but raises an exception for negative
  // indexes.
  if Index < 0 then
    IndexError(Index);

  Result := nil;
  for i := 0 to FOwner.Groups.Count - 1 do
  begin
    ItemCount := FOwner.Groups[i].Items.Count;
    if Index < ItemCount then
    begin
      Result := FOwner.Groups[i].Items[Index];
      break;
    end;
    Dec(Index, ItemCount);
  end;
end;

function TEasyGlobalItems.GetLastGroup: TEasyGroup;
begin
  if FOwner.Groups.Count > 0 then
    Result := FOwner.Groups[FOwner.Groups.Count - 1]
  else
    Result := nil;
end;

procedure TEasyGlobalItems.IndexError(Index: Integer);
begin
  {$IFDEF COMPILER_5_UP}
  TList.Error(SListIndexError, Index);
  {$ELSE}
  TList.Error('List index out of bounds (%d)', Index);
  {$ENDIF}
end;

function TEasyGlobalItems.IndexOf(Item: TEasyItem): Integer;
var
  GroupIndex: Integer;
begin
  Result := Item.Index;
  for GroupIndex := Item.OwnerGroup.Index - 1 downto 0 do
    Inc(Result, OwnerListview.Groups[GroupIndex].Items.Count);
end;

function TEasyGlobalItems.Insert(Index: Integer; Data: TObject = nil): TEasyItem;
var
  Item: TEasyItem;
begin
  Item := GetItemInternal(Index);
  if Item = nil then
    Result := Add
  else
    Result := Item.OwnerItems.Insert(Item.Index, Data);
end;

function TEasyGlobalItems.InsertCustom(Index: Integer; CustomItem: TEasyItemClass; Data: TObject = nil): TEasyItem;
var
  Item: TEasyItem;
begin
  Item := GetItemInternal(Index);
  if Item = nil then
    Result := AddCustom(CustomItem, Data)
  else
    Result := Item.OwnerItems.InsertCustom(Item.Index, CustomItem, Data);
end;

function TEasyGlobalItems.InsertInterfaced(Index: Integer; const DataInf: IUnknown; Data: TObject = nil): TEasyItemInterfaced;
var
  Item: TEasyItem;
begin
  Item := GetItemInternal(Index);
  if Item = nil then
    Result := AddInterfaced(DataInf, Data)
  else
    Result := Item.OwnerItems.InsertInterfaced(Item.Index, DataInf, Data);
end;

function TEasyGlobalItems.InsertVirtual(Index: Integer; Data: TObject = nil): TEasyItemVirtual;
var
  Item: TEasyItem;
begin
  Item := GetItemInternal(Index);
  if Item = nil then
    Result := AddVirtual
  else
    Result := Item.OwnerItems.InsertVirtual(Item.Index, Data);
end;

procedure TEasyGlobalItems.SetItem(Index: Integer; const Value: TEasyItem);
var
  Item: TEasyItem;
begin
  Item := Items[Index];
  Item.OwnerItems[Item.Index] := Value;
end;

procedure TEasyGlobalItems.SetReIndexDisable(const Value: Boolean);
begin
  EnsureFirstGroup;
  FOwner.Groups.ReIndexDisable := Value;
end;

{ TEasyGroups }

constructor TEasyGroups.Create(AnOwner: TCustomEasyListview);
begin
  inherited Create(AnOwner);
  FItemClass := TEasyGroupStored;
  FStreamGroups := True
end;

destructor TEasyGroups.Destroy;
begin
  inherited Destroy;
end;

function TEasyGroups.Add(Data: TObject = nil): TEasyGroup;
begin
  Result := TEasyGroup( inherited Add(Data));
end;

function TEasyGroups.AddCustom(CustomGroup: TEasyGroupClass; Data: TObject = nil): TEasyGroup;
begin
  Result := nil;
  if Assigned(CustomGroup) then
  begin
    Result := CustomGroup.Create(Self);
    FList.Add(Result);
    ReIndexItems;
    Result.Data := Data;
    DoItemAdd(Result, FList.Count - 1);
    DoStructureChange
  end
end;

function TEasyGroups.AddInterfaced(const DataInf: IUnknown; Data: TObject = nil): TEasyGroupInterfaced;
begin
  Result := nil;
  if Assigned(DataInf) then
  begin
    Result := TEasyGroupInterfaced.Create(Self);
    FList.Add(Result);
    ReIndexItems;
    Result.DataInf := DataInf;
    Result.Data := Data;
    DoItemAdd(Result, FList.Count - 1);
    DoStructureChange
  end
end;

function TEasyGroups.AddVirtual(Data: TObject = nil): TEasyGroupVirtual;
begin
  Result := TEasyGroupVirtual.Create(Self);
  FList.Add(Result);
  ReIndexItems;
  Result.Data := Data;
  DoItemAdd(Result, FList.Count - 1);
  DoStructureChange
end;

function TEasyGroups.AdjacentItem(Item: TEasyItem;
  Direction: TEasyAdjacentCellDir): TEasyItem;
begin
  Result := nil;
  if Assigned(Item) then
    Result := Item.OwnerGroup.Grid.AdjacentItem(Item, Direction);
end;

function TEasyGroups.CanMoveDown: Boolean;
begin
  Result := not OwnerListview.Sort.AutoSort and (Count > 0) and
    ((LastItem.Index < OwnerListview.Items.Count - 1) or (LastItem.Index - FirstItem.Index > Count - 1));
end;

function TEasyGroups.CanMoveUp: Boolean;
begin
  Result := not OwnerListview.Sort.AutoSort and (Count > 0) and
    ((FirstItem.Index > 0) or (LastItem.Index - FirstItem.Index > Count - 1));
end;

function TEasyGroups.FirstGroup: TEasyGroup;
begin
  Result := FirstGroupInternal(False)
end;

function TEasyGroups.FirstGroupInRect(ViewportRect: TRect): TEasyGroup;
//
//  Find the first group in the passed rectangle.  It implicitly assumes
// Visible Groups
//
var
  i: Integer;
  R: TRect;
begin
  Result := nil;
  i := 0;
  while (i < Count) and not Assigned(Result) do
  begin
    if Groups[i].Visible then
      if IntersectRect(R, Groups[i].DisplayRect, ViewportRect) then
        Result := Groups[i];
    Inc(i)
  end
end;

function TEasyGroups.FirstGroupInternal(VisibleOnly: Boolean): TEasyGroup;
var
  GroupIndex: Integer;
begin
  Result := nil;
  GroupIndex := 0;
  if Count > 0 then
  begin
    while not Assigned(Result) and (GroupIndex < Count) do
    begin
      if VisibleOnly then
      begin
        if Groups[GroupIndex].Visible then
          Result := Groups[GroupIndex]
      end else
        Result := Groups[GroupIndex];
      Inc(GroupIndex)
    end;
  end
end;

function TEasyGroups.FirstInGroup(Group: TEasyGroup): TEasyItem;
begin
  Result := FirstInGroupInternal(Group, False)
end;

function TEasyGroups.FirstInGroupInternal(Group: TEasyGroup; VisibleOnly: Boolean): TEasyItem;
var
  ItemIndex: Integer;
begin
  Result := nil;
  ItemIndex := 0;
  if Assigned(Group) then
  begin
    if Assigned(Group.Items) then
    begin
      if Group.Items.Count > 0 then
      begin
        if VisibleOnly then
        begin
          while not Assigned(Result) and (ItemIndex < Group.Items.Count) do
          begin
            if Group.Items[ItemIndex].Visible then
              Result := Group.Items[ItemIndex];
            Inc(ItemIndex)
          end
        end else
          Result := Group.Items[ItemIndex]
      end
    end
  end
end;

function TEasyGroups.FirstInitializedItem: TEasyItem;
begin
  Result := FirstItemInternal(enitInitialized)
end;

function TEasyGroups.FirstItem: TEasyItem;
begin
  Result := FirstItemInternal(enitAny)
end;

function TEasyGroups.FirstItemInRect(ViewportRect: TRect): TEasyItem;
//
//  Find the first Item in the passed rectangle.  It implicitly assumes
// Visible Items
//
var
  Group: TEasyGroup;
  i: Integer;
  R, FullDisplayRect: TRect;
begin
  Result := nil;
  i := 0;

  if OwnerListview.View = elsReport then
  begin
    Group := FirstGroupInRect(ViewportRect);
    while not Assigned(Result) and Assigned(Group) do
    begin
      if (Group.Items.Count > 0) and Group.Expanded then
      begin
        while not Assigned(Result) and (i < Group.Items.Count) do
        begin
          FullDisplayRect := OwnerListview.Header.ViewRect;
          FullDisplayRect.Top := Group.Items[i].DisplayRect.Top;
          FullDisplayRect.Bottom := Group.Items[i].DisplayRect.Bottom;

          if  Group.Items[i].Visible and IntersectRect(R, FullDisplayRect, ViewportRect) then
            Result := Group.Items[i]
          else
            Inc(i)
        end
      end;
      i := 0;
      Group := NextGroupInRect(Group, ViewportRect);
    end
  end else
  begin
    Group := FirstGroupInRect(ViewportRect);
    while not Assigned(Result) and Assigned(Group) do
    begin
      if (Group.Items.Count > 0) and Group.Expanded then
      begin
        while not Assigned(Result) and (i < Group.Items.Count) do
        begin
          if  Group.Items[i].Visible and IntersectRect(R, Group.Items[i].DisplayRect, ViewportRect) then
            Result := Group.Items[i]
          else
            Inc(i)
        end
      end;
      i := 0;
      Group := NextGroupInRect(Group, ViewportRect);
    end
  end
end;

function TEasyGroups.FirstItemInternal(NextItemType: TEasyNextItemType): TEasyItem;
var
  GroupIndex, ItemIndex: Integer;
begin
  Result := nil;
  GroupIndex := 0;
  ItemIndex := 0;
  if Count > 0 then
  begin
  while not Assigned(Result) and (GroupIndex < Count) do
    begin
      if Assigned(Groups[GroupIndex].Items) then
      begin
        if Groups[GroupIndex].Items.Count > 0 then
        begin
          case NextItemType of
            enitVisible:
              begin
                while not Assigned(Result) and (ItemIndex < Groups[GroupIndex].Items.Count) do
                begin
                  if Groups[GroupIndex].Items[ItemIndex].Visible then
                    Result := Groups[GroupIndex].Items[ItemIndex];
                  Inc(ItemIndex)
                end
              end;
            enitInitialized:
              begin
                while not Assigned(Result) and (ItemIndex < Groups[GroupIndex].Items.Count) do
                begin
                  if Groups[GroupIndex].Items[ItemIndex].Initialized then
                    Result := Groups[GroupIndex].Items[ItemIndex];
                  Inc(ItemIndex)
                end
              end;
          else
            Result := Groups[GroupIndex].Items[0];
          end
        end;
      end;
      ItemIndex := 0;
      Inc(GroupIndex)
    end
  end
end;

function TEasyGroups.FirstVisibleGroup: TEasyGroup;
begin
  Result := FirstGroupInternal(True)
end;

function TEasyGroups.FirstVisibleInGroup(Group: TEasyGroup): TEasyItem;
begin
  Result := FirstInGroupInternal(Group, True)
end;

function TEasyGroups.FirstVisibleItem: TEasyItem;
begin
  Result := FirstItemInternal(enitVisible)
end;

function TEasyGroups.GetCellHeight: Integer;
begin
  if Count > 0 then
    Result := Groups[0].Grid.CellSize.Height
  else
    Result := 0
end;

function TEasyGroups.GetCellWidth: Integer;
begin
  if Count > 0 then
    Result := Groups[0].Grid.CellSize.Width
  else
    Result := 0
end;

function TEasyGroups.GetGroup(Index: Integer): TEasyGroup;
begin
  // Bottleneck, no inherited call
  Result :=  TEasyGroup( FList.List[Index])
end;

function TEasyGroups.GetItemCount: Integer;
var
  i: Integer;
begin
  Result := 0;
  for i := 0 to FList.Count - 1 do
    Result := Result + TEasyGroup(FList[i]).ItemCount
end;

function TEasyGroups.GetViewRect: TRect;
begin
  Result := Rect(0, 0, 0, 0);
  if LastVisibleGroup <> nil then
    Result.BottomRight := LastVisibleGroup.DisplayRect.BottomRight;
end;

function TEasyGroups.GetVisibleGroup(Index: Integer): TEasyGroup;
begin
  Result := nil;
  if (Index > -1) and (Index < VisibleCount) then
    Result := TEasyGroup( VisibleList[Index])
end;

function TEasyGroups.GroupByPoint(ViewportPoint: TPoint): TEasyGroup;
var
  i: Integer;
begin
  Result := nil;
  i := 0;
  while (i < Count) and not Assigned(Result) do
  begin
    if Groups[i].Visible and Groups[i].Enabled then
      if PtInRect(Groups[i].DisplayRect, ViewportPoint) then
        Result := Groups[i];
    Inc(i)
  end
end;

function TEasyGroups.Insert(Index: Integer; Data: TObject = nil): TEasyGroup;
begin
  Result := TEasyGroup( inherited Insert(Index, Data))
end;

function TEasyGroups.InsertCustom(Index: Integer; CustomGroup: TEasyGroupClass; Data: TObject = nil): TEasyGroup;
begin
  Result := nil;
  if Assigned(CustomGroup) then
  begin
    Result := CustomGroup.Create(Self);
    FList.Insert(Index, Result);
    ReIndexItems;
    DoItemAdd(Result, Index);
    Result.Data := Data;
    DoStructureChange
  end
end;

function TEasyGroups.InsertInterfaced(Index: Integer; const DataInf: IUnknown; Data: TObject): TEasyGroupInterfaced;
begin
  Result := nil;
  if Assigned(Data) then
  begin
    Result := TEasyGroupInterfaced.Create(Self);
    FList.Insert(Index, Result);
    ReIndexItems;
    DoItemAdd(Result, Index);
    Result.DataInf := DataInf;
    Result.Data := Data;
    DoStructureChange
  end
end;

function TEasyGroups.InsertVirtual(Index: Integer; Data: TObject = nil): TEasyGroupVirtual;
begin
  Result := TEasyGroupVirtual.Create(Self);
  FList.Insert(Index, Result);
  ReIndexItems;
  DoItemAdd(Result, Index);
  Result.Data := Data;
  DoStructureChange
end;

function TEasyGroups.ItemByPoint(ViewportPoint: TPoint): TEasyItem;
var
  Group: TEasyGroup;
begin
  Result := nil;
  Group := GroupByPoint(ViewportPoint);
  if Assigned(Group) then
    Result := Group.ItemByPoint(ViewportPoint)
end;

function TEasyGroups.LastGroup: TEasyGroup;
begin
  Result := LastGroupInternal(False)
end;

function TEasyGroups.LastGroupInternal(VisibleOnly: Boolean): TEasyGroup;
var
  GroupIndex: Integer;
begin
  Result := nil;
  if Count > 0 then
  begin
    if VisibleOnly then
    begin
      GroupIndex := Count - 1;
      while not Assigned(Result) and (GroupIndex > -1) do
      begin
        if Groups[GroupIndex].Visible then
          Result := Groups[GroupIndex]
        else
          Dec(GroupIndex)
      end
    end else
      Result := Groups[Count - 1]
  end
end;

function TEasyGroups.LastInGroup(Group: TEasyGroup): TEasyItem;
begin
  Result := LastInGroupInternal(Group, False)
end;

function TEasyGroups.LastInGroupInternal(Group: TEasyGroup; VisibleOnly: Boolean): TEasyItem;
var
  ItemIndex: Integer;
begin
  Result := nil;
  if Assigned(Group) then
  begin
    if Assigned(Group.Items) then
    begin
      ItemIndex := Group.Items.Count - 1;
      if VisibleOnly then
      begin
        while not Assigned(Result) and (ItemIndex > -1) do
        begin
          if Group.Items[ItemIndex].Visible then
            Result := Group.Items[ItemIndex]
          else
            Dec(ItemIndex)
        end
      end else
        Result := Group.Items[Group.Items.Count - 1]
    end
  end
end;

function TEasyGroups.LastInitializedItem: TEasyItem;
begin
  Result := LastItemInternal(enitInitialized)
end;

function TEasyGroups.LastItem: TEasyItem;
begin
  Result := LastItemInternal(enitAny)
end;

function TEasyGroups.LastItemInternal(NextItemType: TEasyNextItemType): TEasyItem;
var
  ItemIndex, GroupIndex: Integer;
begin
  Result := nil;
  if Count > 0 then
  begin
    GroupIndex := Count - 1;
    while not Assigned(Result) and (GroupIndex > -1) do
    begin
      if Assigned(Groups[GroupIndex].Items) then
      begin
        ItemIndex := Groups[GroupIndex].Items.Count - 1;
        while not Assigned(Result) and (ItemIndex > -1) do
        begin
          case NextItemType of
            enitVisible:
              begin
                if Groups[GroupIndex].Items[ItemIndex].Visible then
                  Result := Groups[GroupIndex].Items[ItemIndex]
                else
                  Dec(ItemIndex)
              end;
            enitInitialized:
              begin
                if Groups[GroupIndex].Items[ItemIndex].Visible then
                  Result := Groups[GroupIndex].Items[ItemIndex]
                else
                  Dec(ItemIndex)
              end
            else
              Result := Groups[GroupIndex].Items[ItemIndex]
          end
        end
      end;
      Dec(GroupIndex);
    end
  end
end;

function TEasyGroups.LastVisibleGroup: TEasyGroup;
begin
  Result := LastGroupInternal(True)
end;

function TEasyGroups.LastVisibleInGroup(Group: TEasyGroup): TEasyItem;
begin
  Result := LastInGroupInternal(Group, True)
end;

function TEasyGroups.LastVisibleItem: TEasyItem;
begin
  Result := LastItemInternal(enitVisible)
end;

function TEasyGroups.MoveDown: Boolean;
var
  AItem, ANextItem: TEasyItem;
begin
  Result := CanMoveDown;
  if not Result then
    Exit;

  OwnerListview.BeginUpdate;
  try
    AItem := LastItem;
    while Assigned(AItem) do
    begin
      if AItem.Index < OwnerListview.Items.Count - 1 then
      begin
        ANextItem := OwnerListview.Items[AItem.Index + 1];
        if not ANextItem.Selected then
          OwnerListview.Items.Exchange(AItem.Index, ANextItem.Index);
      end;
      AItem := PrevItem(AItem);
    end;
  finally
    OwnerListview.EndUpdate;
  end;
  LastItem.MakeVisible(emvAuto);
  OwnerListview.DoItemSelectionsChanged; 
end;

function TEasyGroups.MoveUp: Boolean;
var
  AItem, APrevItem: TEasyItem;
begin
  Result := CanMoveUp;
  if not Result then
    Exit;

  OwnerListview.BeginUpdate;
  try
    AItem := FirstItem;
    while Assigned(AItem) do
    begin
      if AItem.Index > 0 then
      begin
        APrevItem := OwnerListview.Items[AItem.Index - 1];
        if not APrevItem.Selected then
          OwnerListview.Items.Exchange(AItem.Index, APrevItem.Index);
      end;
      AItem := NextItem(AItem);
    end;
  finally
    OwnerListview.EndUpdate;
  end;
  FirstItem.MakeVisible(emvAuto);
  OwnerListview.DoItemSelectionsChanged;
end;

function TEasyGroups.NextEditableItem(Item: TEasyItem): TEasyItem;
begin
  Result := NavigateItemInternal(Item, enitEditable, esdForward)
end;

function TEasyGroups.NextGroup(Group: TEasyGroup): TEasyGroup;
begin
  Result := NavigateGroupInternal(Group, False, esdForward)
end;

function TEasyGroups.NextGroupInRect(Group: TEasyGroup; ViewportRect: TRect): TEasyGroup;
//
//  Find the next Groups in the passed rectangle.  It implicitly assumes
// Visible Groups
//
var
  i: Integer;
  ScratchR: TRect;
  Done: Boolean;
begin
  Result := nil;
  Done := False;
  if Assigned(Group) then
  begin
    i := Group.Index + 1;
    while not Assigned(Result) and (i < Count) and not Done do
    begin
      if Groups[i].Visible then
      begin
        if IntersectRect(ScratchR, Groups[i].DisplayRect, ViewportRect) then
          Result := Groups[i]
        else
          Done := True
      end;
      Inc(i)
    end
  end
end;

function TEasyGroups.NavigateGroupInternal(Group: TEasyGroup;
  VisibleOnly: Boolean; Direction: TEasySearchDirection): TEasyGroup;
var
  GroupIndex: Integer;
begin
  Result := nil;
  GroupIndex := Group.Index;
  if Direction = esdForward then
    Inc(GroupIndex)
  else
    Dec(GroupIndex);
  while not Assigned(Result) and (GroupIndex < Count) and (GroupIndex > -1) do
  begin
     if VisibleOnly then
     begin
       if Groups[GroupIndex].Expanded and Groups[GroupIndex].Visible then
         Result := Groups[GroupIndex]
       else begin
         if Direction = esdForward then
           Inc(GroupIndex)
         else
           Dec(GroupIndex);
       end
     end else
       Result := Groups[GroupIndex]
  end
end;

function TEasyGroups.NextInitializedItem(Item: TEasyItem): TEasyItem;
begin
   Result := NavigateItemInternal(Item, enitInitialized, esdForward)
end;

function TEasyGroups.NextVisibleGroupWithVisibleItems(Group: TEasyGroup): TEasyGroup;
//
//  Returns the next Visible Group that has at least one Visible Item
//
var
  Done: Boolean;
begin
  Result := nil;
  if Assigned(Group) then
  begin
    Done := False;
    Result := Group;
    while not Done do
    begin
      Result := NavigateGroupInternal(Result, True, esdForward);
      if Assigned(Result) then
        Done := Result.VisibleCount > 0
      else
        Done := True
    end
  end
end;

function TEasyGroups.NextInGroup(Group: TEasyGroup; Item: TEasyItem): TEasyItem;
begin
  Result := NavigateInGroupInternal(Group, Item, False, esdForward)
end;

function TEasyGroups.NavigateInGroupInternal(Group: TEasyGroup; Item: TEasyItem;
  VisibleOnly: Boolean; Direction: TEasySearchDirection): TEasyItem;
var
  ItemIndex: Integer;
  TempItem: TEasyItem;
  Count: Integer;
begin
  Result := nil;
  if Assigned(Group) and Assigned(Item) then
  begin
    if Assigned(Group.Items) then
    begin
      ItemIndex := Item.FIndex; // Direct access for speed
      if Direction = esdForward then
        Inc(ItemIndex)
      else
        Dec(ItemIndex);
      Count := Group.Items.FList.Count;
      while not Assigned(Result) and (ItemIndex < Count) and (ItemIndex > -1) do
      begin
        // Direct Access for speed
        TempItem := Group.Items.FList.List[ItemIndex];
        if VisibleOnly then
        begin
          // Direct access for speed
          if esosVisible in TempItem.State then
            Result := TempItem
          else begin
            if Direction = esdForward then
              Inc(ItemIndex)
            else
              Dec(ItemIndex)
          end
        end else
          Result := TempItem
      end
    end
  end
end;

function TEasyGroups.NextItem(Item: TEasyItem): TEasyItem;
begin
  Result := NavigateItemInternal(Item, enitAny, esdForward)
end;

function TEasyGroups.NextItemInRect(Item: TEasyItem; ViewportRect: TRect): TEasyItem;
//
//  Find the next Item in the passed rectangle.  It implicitly assumes
// Visible Items
//
var
  Group: TEasyGroup;
  i: Integer;
  R, FullDisplayRect: TRect;
  Done: Boolean;
begin
  Result := nil;
  Done := False;
  Group := Item.OwnerGroup;
  i := Item.Index + 1;

  if OwnerListview.View = elsReport then
  begin
    while not Assigned(Result) and Assigned(Group) and not Done do
    begin
      if (i < Group.Items.Count) and Group.Expanded and not Done then
      begin
        FullDisplayRect := OwnerListview.Header.ViewRect;
        FullDisplayRect.Top := Group.Items[i].DisplayRect.Top;
        FullDisplayRect.Bottom := Group.Items[i].DisplayRect.Bottom;

        if Group.Items[i].Visible then
          if IntersectRect(R, FullDisplayRect, ViewportRect) then
            Result := Group.Items[i]
          else
            Done := True
        else
          Inc(i)
      end else
      begin
       i := 0;
       Group := NextGroupInRect(Group, ViewportRect)
      end
    end
  end else
  begin
    while not Assigned(Result) and Assigned(Group) and not Done do
    begin
      if (i < Group.Items.Count) and Group.Expanded and not Done then
      begin
        if Group.Items[i].Visible then
        begin
          if IntersectRect(R, Group.Items[i].DisplayRect, ViewportRect) then
            Result := Group.Items[i]
          else begin
            if OwnerListview.IsVertView then
              Done := Group.Items[i].DisplayRect.Top > ViewportRect.Bottom
            else
              Done := Group.Items[i].DisplayRect.Left > ViewportRect.Right
          end
        end;
        Inc(i)
      end else
      begin
       i := 0;
       Group := NextGroupInRect(Group, ViewportRect)
      end
    end
  end
end;

function TEasyGroups.NavigateItemInternal(Item: TEasyItem; NextItemType: TEasyNextItemType; Direction: TEasySearchDirection): TEasyItem;
var
  GroupIndex, ItemIndex, Column: Integer;
  Allow: Boolean;
begin
  Result := nil;
  if Assigned(Item) then
  begin
    ItemIndex := Item.Index;
    GroupIndex := Item.OwnerGroup.Index;

    if Direction = esdForward then
      Inc(ItemIndex)
    else
      Dec(ItemIndex);

    while not Assigned(Result) and (GroupIndex < Count) and (GroupIndex > -1) do
    begin
      if Groups[GroupIndex].Expanded and Groups[GroupIndex].Enabled then
      begin
        while not Assigned(Result) and (ItemIndex < Groups[GroupIndex].Items.Count) and (ItemIndex > -1) do
        begin
          if NextItemType in [enitVisible, enitEditable] then
          begin
            if Assigned(Groups[GroupIndex].Items) then
            begin
              if Groups[GroupIndex].Items[ItemIndex].Visible then
              begin
                Result := Groups[GroupIndex].Items[ItemIndex];
                if NextItemType = enitEditable then
                begin
                  Allow := True;
                  OwnerListview.DoItemEditBegin(Result, Column, Allow);
                  if not Allow then
                    Result := nil;
                end
              end
            end;
            if Direction = esdForward then
              Inc(ItemIndex)
            else
              Dec(ItemIndex)
          end else
          if NextItemType = enitInitialized then
          begin
            if Assigned(Groups[GroupIndex].Items) then
            begin
              if Groups[GroupIndex].Items[ItemIndex].Initialized then
                Result := Groups[GroupIndex].Items[ItemIndex]
            end;
            if Direction = esdForward then
              Inc(ItemIndex)
            else
              Dec(ItemIndex)
          end else
            Result := Groups[GroupIndex].Items[ItemIndex];
        end;
        if Direction = esdForward then
        begin
          Inc(GroupIndex);
          ItemIndex := 0
        end else
        begin
          Dec(GroupIndex);
          if (GroupIndex > -1) and Assigned(Groups[GroupIndex].Items) then
            ItemIndex := Groups[GroupIndex].Items.Count - 1;
        end
      end else
      begin
        ItemIndex := 0;
        if Direction = esdForward then
          Inc(GroupIndex)
        else
          Dec(GroupIndex);
      end
    end
  end
end;

function TEasyGroups.NextVisibleGroup(Group: TEasyGroup): TEasyGroup;
begin
  Result := NavigateGroupInternal(Group, True, esdForward)
end;

function TEasyGroups.NextVisibleInGroup(Group: TEasyGroup; Item: TEasyItem): TEasyItem;
begin
  Result := NavigateInGroupInternal(Group, Item, True, esdForward)
end;

function TEasyGroups.NextVisibleItem(Item: TEasyItem): TEasyItem;
begin
  Result := NavigateItemInternal(Item, enitVisible, esdForward)
end;

function TEasyGroups.PrevEditableItem(Item: TEasyItem): TEasyItem;
begin
  Result := NavigateItemInternal(Item, enitEditable, esdBackward)
end;

function TEasyGroups.PrevGroup(Group: TEasyGroup): TEasyGroup;
begin
  Result := NavigateGroupInternal(Group, False, esdBackward)
end;

function TEasyGroups.PrevInitializedItem(Item: TEasyItem): TEasyItem;
begin
  Result := NavigateItemInternal(Item, enitInitialized, esdBackward)
end;

function TEasyGroups.PrevVisibleGroupWithVisibleItems(Group: TEasyGroup): TEasyGroup;
//
//  Returns the prev Visible Group that has at least one Visible Item
//
var
  Done: Boolean;
begin
  Result := nil;
  if Assigned(Group) then
  begin
    Done := False;
    Result := Group;
    while not Done do
    begin
      Result := NavigateGroupInternal(Result, True, esdBackward);
      if Assigned(Result) then
        Done := Result.VisibleCount > 0
      else
        Done := True
    end
  end
end;

function TEasyGroups.PrevInGroup(Group: TEasyGroup; Item: TEasyItem): TEasyItem;
begin
  Result := NavigateInGroupInternal(Group, Item, False, esdBackward)
end;

function TEasyGroups.PrevItem(Item: TEasyItem): TEasyItem;
begin
  Result := NavigateItemInternal(Item, enitAny, esdBackward)
end;

function TEasyGroups.PrevVisibleGroup(Group: TEasyGroup): TEasyGroup;
begin
  Result := NavigateGroupInternal(Group, True, esdBackward)
end;

function TEasyGroups.PrevVisibleInGroup(Group: TEasyGroup; Item: TEasyItem): TEasyItem;
begin
  Result := NavigateInGroupInternal(Group, Item, True, esdBackward)
end;

function TEasyGroups.PrevVisibleItem(Item: TEasyItem): TEasyItem;
begin
  Result := NavigateItemInternal(Item, enitVisible, esdBackward)
end;

procedure TEasyGroups.Clear(FreeItems: Boolean = True);
begin
  if Assigned(OwnerListview.Selection) then
  begin
    OwnerListview.Selection.IncMultiChangeCount;
    OwnerListview.Selection.GroupSelectBeginUpdate;
  end;
  inherited Clear(FreeItems);
  if Assigned(OwnerListview.Selection) then
  begin
    OwnerListview.Selection.DecMultiChangeCount;
    OwnerListview.Selection.GroupSelectEndUpdate;
  end;
end;

procedure TEasyGroups.CollapseAll;
var
  i: Integer;
begin
  OwnerListview.BeginUpdate;
  try
    for i := 0 to Count - 1 do
      Groups[i].Expanded := False
  finally
    OwnerListview.EndUpdate
  end
end;

procedure TEasyGroups.DeleteGroup(Group: TEasyGroup);
var
  Done: Boolean;
  TestGroup: TEasyGroup;
begin
  Done := False;
  TestGroup := FirstGroup;
  while Assigned(TestGroup) and not Done do
  begin
    Done := TestGroup = Group;
    if Done then
      Delete(Group.Index)
    else
      TestGroup := NextGroup(TestGroup)
  end
end;

procedure TEasyGroups.DeleteItem(Item: TEasyItem);
var
  Done: Boolean;
  TestItem: TEasyItem;
  Group: TEasyGroup;
begin
  Done := False;
  TestItem := FirstItem;
  while Assigned(TestItem) and not Done do
  begin
    Done := TestItem = Item;
    if Done then
    begin
      Group := TestItem.OwnerGroup;
      Group.Items.Delete(Item.Index);
    end else
      TestItem := NextItem(TestItem)
  end
end;

procedure TEasyGroups.DeleteItems(ItemArray: TEasyItemArray);
//
// Optimized method of deleting multiple items, the items can be in any group
//
var
  i: Integer;
  Temp: TEasyItem;
begin
  OwnerListview.Selection.IncMultiChangeCount;
  OwnerListview.Selection.GroupSelectBeginUpdate;
  try
    for i := 0 to Length(ItemArray) - 1 do
    begin
      Temp := ItemArray[i].OwnerGroup.FItems[ItemArray[i].Index];
      Temp.Focused := False;
      Temp.Selected := False;
      Temp.Visible := False;
    end;

    for i := 0 to Length(ItemArray) - 1 do
    begin
      Temp := ItemArray[i].OwnerGroup.FItems[ItemArray[i].Index];
      ItemArray[i].OwnerGroup.FItems[ItemArray[i].Index] := nil;
      Temp.Free;
    end;
    for i := 0 to Count - 1 do
    begin
      TEasyGroup( List[i]).FItems.FList.Pack;
      TEasyGroup( List[i]).FItems.ReIndexItems
    end
  finally
    OwnerListview.Selection.GroupSelectEndUpdate;
    OwnerListview.Selection.DecMultiChangeCount
  end
end;

procedure TEasyGroups.DoStructureChange;
begin
  inherited DoStructureChange;
  Rebuild(False)
end;

procedure TEasyGroups.ExpandAll;
var
  i: Integer;
begin
  OwnerListview.BeginUpdate;
  try
    for i := 0 to Count - 1 do
      Groups[i].Expanded := True
  finally
    OwnerListview.EndUpdate
  end
end;

procedure TEasyGroups.InitializeAll;
var
  i, j: Integer;
begin
  for i := 0 to Count - 1 do
  begin
    if Assigned(Groups[i].Items) then
    begin
      for j := 0 to Groups[i].Items.Count - 1 do
        Groups[i][j].Initialized := True
    end
  end
end;

procedure TEasyGroups.InvalidateItem(Item: TEasyCollectionItem; ImmediateUpdate: Boolean);
begin
  if Assigned(Item) then
    Item.Invalidate(ImmediateUpdate)
end;

{$IFDEF COMPILER_6_UP}
{$ELSE}
{$ENDIF COMPILER_6_UP}

procedure TEasyGroups.LoadFromStream(S: TStream; Version: Integer = EASYLISTVIEW_STREAM_VERSION);
begin
  inherited LoadFromStream(S, Version);
  if StreamGroups then
    ReadItems(S);
end;

procedure TEasyGroups.MakeAllVisible;
var
  i, j: Integer;
begin
  OwnerListview.BeginUpdate;
  try
    for i := 0 to Count - 1 do
    begin
      Groups[i].Visible := True;
      for j := 0 to Groups[i].ItemCount - 1 do
        Groups[i].Item[j].Visible := True
    end
  finally
    OwnerListview.EndUpdate()
  end
end;

procedure TEasyGroups.Move(Item: TEasyItem; NewGroup: TEasyGroup);
begin
  if Assigned(Item) and Assigned(NewGroup) then
  begin
    Item.OwnerGroup.Items.Delete(Item.Index);
    NewGroup.Items.Add(Item);
    Item.FCollection := NewGroup.Items;
    DoStructureChange;
  end
end;

procedure TEasyGroups.Rebuild(Force: Boolean = False);
var
  i, VisibleGroupIndex, VisibleItemIndex: Integer;
  ViewRect: TRect;
begin
  if ((OwnerListview.UpdateCount = 0) or Force) and not(csLoading in OwnerListview.ComponentState) and (OwnerListview.HandleAllocated) then
  begin
    Include(FGroupsState, egsRebuilding);

    try
      VisibleList.Clear;
      VisibleList.Capacity := Count;
      VisibleGroupIndex := 0;
      VisibleItemIndex := 0;

      OwnerListview.Header.Rebuild(Force);
      SetRect(ViewRect, 0, 0, 0, 0);
      for i := 0 to Count - 1 do
      begin

        if Groups[i].Visible then
        begin
          VisibleList.Add(Groups[i]);
          Groups[i].FVisibleIndex := VisibleGroupIndex;
          Inc(VisibleGroupIndex);
        end else
          Groups[i].FVisibleIndex := -1;

        if i > 0 then
          Groups[i].Rebuild(Groups[i-1], VisibleItemIndex)
        else
          Groups[i].Rebuild(nil, VisibleItemIndex);    

        UnionRect(ViewRect, ViewRect, Groups[i].DisplayRect);
        ViewRect.Left := 0;
        ViewRect.Top := 0;
        Groups[i].Items.ReIndexItems
      end;
      if OwnerListview.Selection.GroupSelections then
        OwnerListview.Selection.BuildSelectionGroupings(False);
      OwnerListview.Scrollbars.SetViewRect(ViewRect, True);
    finally
      Exclude(FGroupsState, egsRebuilding);
    end
  end
end;

procedure TEasyGroups.SaveToStream(S: TStream; Version: Integer = EASYLISTVIEW_STREAM_VERSION);
begin
  inherited SaveToStream(S, Version);
  if StreamGroups then
    WriteItems(S);
end;

procedure TEasyGroups.SetCellHeight(Value: Integer);
var
  i: Integer;
begin
  OwnerListview.BeginUpdate;
  try
    for i := 0 to Count - 1 do
      Groups[i].Grid.CellSize.Height := Value
  finally
    OwnerListview.EndUpdate
  end
end;

procedure TEasyGroups.SetCellWidth(Value: Integer);
var
  i: Integer;
begin
  OwnerListview.BeginUpdate;
  try
    for i := 0 to Count - 1 do
      Groups[i].Grid.CellSize.Width := Value
  finally
    OwnerListview.EndUpdate
  end
end;

procedure TEasyGroups.SetGroup(Index: Integer; Value: TEasyGroup);
begin
  inherited Items[Index] := Value
end;

procedure TEasyGroups.UnInitializeAll;
var
  i, j: Integer;
begin
  for i := 0 to Count - 1 do
  begin
    if Assigned(Groups[i].Items) then
    begin
      for j := 0 to Groups[i].Items.Count - 1 do
        Groups[i][j].Initialized := False
    end
  end
end;

{ TEasyColumns }

constructor TEasyColumns.Create(AnOwner: TCustomEasyListview);
begin
  inherited;
  FItemClass := TEasyColumnStored
end;

destructor TEasyColumns.Destroy;
begin
  inherited;
end;

function TEasyColumns.Add(Data: TObject = nil): TEasyColumn;
begin
  Result := TEasyColumn( inherited Add(Data));
  Result.Color := OwnerListview.Header.Color;
end;

function TEasyColumns.AddCustom(CustomItem: TEasyColumnClass; Data: TObject = nil): TEasyColumn;
begin
  Result := nil;
  if Assigned(CustomItem) then
  begin
    Result := CustomItem.Create(Self);
    FList.Add(Result);
    Result.Color := OwnerListview.Header.Color;
    ReIndexItems;
    Result.Data := Data;
    DoItemAdd(Result, FList.Count - 1);
    DoStructureChange
  end
end;

function TEasyColumns.AddInterfaced(const DataInf: IUnknown; Data: TObject): TEasyColumnInterfaced;
begin
  Result := nil;
  if Assigned(DataInf) then
  begin
    Result := TEasyColumnInterfaced.Create(Self);
    FList.Add(Result);
    Result.Color := OwnerListview.Header.Color;
    ReIndexItems;
    Result.DataInf := DataInf;
    Result.Data := Data;
    DoItemAdd(Result, FList.Count - 1);
    DoStructureChange
  end
end;

function TEasyColumns.AddVirtual(Data: TObject = nil): TEasyColumnVirtual;
begin
  Result := TEasyColumnVirtual.Create(Self);
  FList.Add(Result);
  Result.Color := OwnerListview.Header.Color;
  ReIndexItems;
  Result.Data := Data;
  DoItemAdd(Result, FList.Count - 1);
  DoStructureChange
end;

function TEasyColumns.ColumnByPoint(ViewportPoint: TPoint): TEasyColumn;
var
  i: Integer;
begin
  Result := nil;
  i := 0;
  // Careful with a binary search here are the rectangles increase with Header.Postions not the Columns property
  while not Assigned(Result) and (i < Count) do
  begin
    if PtInRect(Columns[i].DisplayRect, ViewportPoint) then
      Result := Columns[i]
    else
      Inc(i)
  end
end;

function TEasyColumns.GetColumns(Index: Integer): TEasyColumn;
begin
  Result := TEasyColumn( FList.List[Index])
end;

function TEasyColumns.GetOwnerHeader: TEasyHeader;
begin
  Result := OwnerListview.Header
end;

function TEasyColumns.Insert(Index: Integer; Data: TObject = nil): TEasyColumn;
begin
  Result := TEasyColumn( inherited Insert(Index, Data))
end;

function TEasyColumns.InsertCustom(Index: Integer; CustomColumn: TEasyColumnClass; Data: TObject = nil): TEasyColumn;
begin
  Result := nil;
  if Assigned(CustomColumn) then
  begin
    Result := CustomColumn.Create(Self);
    FList.Insert(Index, Result);
    ReIndexItems;
    DoItemAdd(Result, Index);
    Result.Data := Data;
    DoStructureChange
  end
end;

function TEasyColumns.InsertInterfaced(Index: Integer; const DataInf: IUnknown; Data: TObject = nil): TEasyColumnInterfaced;
begin
  Result := nil;
  if Assigned(DataInf) then
  begin
    Result := TEasyColumnInterfaced.Create(Self);
    FList.Insert(Index, Result);
    ReIndexItems;
    DoItemAdd(Result, Index);
    Result.DataInf := DataInf;
    Result.Data := Data;
    DoStructureChange
  end
end;

function TEasyColumns.InsertVirtual(Index: Integer; Data: TObject = nil): TEasyColumnVirtual;
begin
  Result := TEasyColumnVirtual.Create(Self);
  FList.Insert(Index, Result);
  ReIndexItems;
  DoItemAdd(Result, Index);
  Result.Data := Data;
  DoStructureChange
end;

procedure TEasyColumns.Clear(FreeItems: Boolean = True);
begin
  inherited Clear(FreeItems);
  if Assigned(OwnerHeader) and Assigned(OwnerHeader.Positions) then
  begin
    OwnerHeader.Positions.Clear;
    OwnerHeader.Columns.VisibleList.Clear;
  end
end;

procedure TEasyColumns.DoItemAdd(Item: TEasyCollectionItem; Index: Integer);
begin
  inherited DoItemAdd(Item, Index);
end;

procedure TEasyColumns.DoStructureChange;
begin
  inherited DoStructureChange;
  OwnerListview.Header.Rebuild(False)
end;

procedure TEasyColumns.SetColumns(Index: Integer; Value: TEasyColumn);
begin
  inherited Items[Index] := Value
end;

{ TEasyGlobalImageManager }

constructor TEasyGlobalImageManager.Create(AnOwner: TCustomEasyListview);
begin
  inherited;
  FGroupExpandButton := TBitmap.Create;
  FGroupCollapseButton := TBitmap.Create;
  FColumnSortUp := TBitmap.Create;
  FColumnSortDown := TBitmap.Create;
end;

destructor TEasyGlobalImageManager.Destroy;
begin
  inherited;
  FreeAndNil(FGroupExpandButton);
  FreeAndNil(FGroupCollapseButton);
  FreeAndNil(FColumnSortUp);
  FreeAndNil(FColumnSortDown);
end;

function TEasyGlobalImageManager.GetColumnSortDown: TBitmap;
begin
  if FColumnSortDown.Empty then
  begin
    MakeTransparent(FColumnSortDown, clFuchsia);
    FColumnSortDown.LoadFromResourceName(hInstance, BITMAP_SORTARROWDOWN);
  end;
  Result := FColumnSortDown
end;

function TEasyGlobalImageManager.GetColumnSortUp: TBitmap;
begin
  if FColumnSortUp.Empty then
  begin
    MakeTransparent(FColumnSortUp, clFuchsia);
    FColumnSortUp.LoadFromResourceName(hInstance, BITMAP_SORTARROWUP);
  end;
  Result := FColumnSortUp
end;

function TEasyGlobalImageManager.GetGroupCollapseImage: TBitmap;
begin
  if FGroupCollapseButton.Empty then
  begin
    MakeTransparent(FGroupCollapseButton, clFuchsia);
    FGroupCollapseButton.LoadFromResourceName(hInstance, BITMAP_DEFAULTGROUPCOLLAPSED);
  end;
  Result := FGroupCollapseButton
end;

function TEasyGlobalImageManager.GetGroupExpandImage: TBitmap;
begin
  if FGroupExpandButton.Empty then
  begin
    MakeTransparent(FGroupExpandButton, clFuchsia);
    FGroupExpandButton.LoadFromResourceName(hInstance, BITMAP_DEFAULTGROUPEXPANDED);
  end;
  Result := FGroupExpandButton
end;

procedure TEasyGlobalImageManager.MakeTransparent(Bits: TBitmap; TransparentColor: TColor);
begin
  Bits.Transparent := True;
  Bits.TransparentColor := TransparentColor
end;

procedure TEasyGlobalImageManager.SetColumnSortDown(Value: TBitmap);
begin
  FColumnSortDown.Assign(Value)
end;

procedure TEasyGlobalImageManager.SetColumnSortUp(Value: TBitmap);
begin
  FColumnSortUp.Assign(Value)
end;

procedure TEasyGlobalImageManager.SetGroupCollapseImage(const Value: TBitmap);
begin
  FGroupExpandButton.Assign(Value);
end;

procedure TEasyGlobalImageManager.SetGroupExpandImage(const Value: TBitmap);
begin
  GroupCollapseButton.Assign(Value);
end;

{ TEasyGroup }

constructor TEasyGroup.Create(ACollection: TEasyCollection);
begin
  inherited;
  FExpanded := True;
  FItems := TEasyItems.Create(OwnerListview, Self);
  FVisibleItems := TList.Create;
end;

destructor TEasyGroup.Destroy;
begin
  SetDestroyFlags;
  if OwnerListview.Selection.FocusedGroup = Self then
    OwnerListview.Selection.FocusedGroup := nil;
  Selected := False;
  Focused := False;
  FItems.Free;  // don't nil the reference the items may need it as they are destroyed.
  inherited;
  FreeAndNil(FGrid);
  FreeAndNil(FVisibleItems);
  FreeAndNil(FView);
end;

function TEasyGroup.BoundsRectBottomMargin: TRect;
begin
  if MarginBottom.Visible and OwnerListview.ShowGroupMargins then
  begin
    Result := DisplayRect;
    Result.Top := Result.Bottom - MarginBottom.RuntimeSize;
    Result.Right := Result.Right - MarginRight.RuntimeSize;
    Result.Left := Result.Left + MarginLeft.RuntimeSize;
  end else
    Result := Rect(0, 0, 0, 0);
end;

function TEasyGroup.BoundsRectLeftMargin: TRect;
begin
  if MarginLeft.Visible and OwnerListview.ShowGroupMargins then
  begin
    Result := DisplayRect;
    Result.Right := Result.Left + MarginLeft.RuntimeSize
  end else
    Result := Rect(0, 0, 0, 0);
end;

function TEasyGroup.BoundsRectRightMargin: TRect;
begin
  if MarginRight.Visible and OwnerListview.ShowGroupMargins then
  begin
    Result := DisplayRect;
    Result.Left := Result.Right - MarginRight.RuntimeSize
  end else
    Result := Rect(0, 0, 0, 0);
end;

function TEasyGroup.BoundsRectTopMargin: TRect;
begin
  if MarginTop.Visible and OwnerListview.ShowGroupMargins then
  begin
    Result := DisplayRect;
    Result.Bottom := Result.Top + MarginTop.RuntimeSize;
    Result.Right := Result.Right - MarginRight.RuntimeSize;
    Result.Left := Result.Left + MarginLeft.RuntimeSize;
  end else
    Result := Rect(0, 0, 0, 0);
end;

function TEasyGroup.BoundsRectBkGnd: TRect;
begin
  Result := DisplayRect;
  Result.Left := Result.Left + MarginLeft.RuntimeSize;
  Result.Right := Result.Right - MarginRight.RuntimeSize;
  Result.Top := Result.Top + MarginTop.RuntimeSize;
  Result.Bottom := Result.Bottom -  MarginBottom.RuntimeSize;
  if Result.Bottom < Result.Top then
    Result.Bottom := Result.Top;
  if Result.Right < Result.Left then
    Result.Right := Result.Left
end;

function TEasyGroup.CanChangeBold(NewValue: Boolean): Boolean;
begin
  Result := True
end;

function TEasyGroup.CanChangeCheck(NewValue: Boolean): Boolean;
begin
  Result := Enabled;
end;

function TEasyGroup.CanChangeEnable(NewValue: Boolean): Boolean;
begin
  Result := True;
end;

function TEasyGroup.CanChangeFocus(NewValue: Boolean): Boolean;
begin
  // Unsupported
  Result := False;
end;

function TEasyGroup.CanChangeHotTracking(NewValue: Boolean): Boolean;
begin
  // Unsupported
  Result := True;
end;

function TEasyGroup.CanChangeSelection(NewValue: Boolean): Boolean;
begin
  Result := True;
  OwnerListview.DoGroupSelectionChanging(Self, Result)
end;

function TEasyGroup.CanChangeVisibility(NewValue: Boolean): Boolean;
begin
  Result := True;
  OwnerListview.DoGroupVisibilityChanging(Self, Result)
end;

function TEasyGroup.DefaultImageList(ImageSize: TEasyImageSize): TCustomImageList;
begin
  Result:= OwnerListview.ImagesGroup
end;

function TEasyGroup.EditAreaHitPt(ViewportPoint: TPoint): Boolean;
begin
  Result := View.EditAreaHitPt(Self, ViewportPoint)
end;

function TEasyGroup.GetBandBlended: Boolean;
begin
  Result := PaintInfo.BandBlended
end;

function TEasyGroup.GetBandColor: TColor;
begin
  Result := PaintInfo.BandColor
end;

function TEasyGroup.GetBandColorFade: TColor;
begin
  Result := PaintInfo.BandColorFade
end;

function TEasyGroup.GetBandEnabled: Boolean;
begin
  Result := PaintInfo.BandEnabled
end;

function TEasyGroup.GetBandFullWidth: Boolean;
begin
  Result := PaintInfo.BandFullWidth
end;

function TEasyGroup.GetBandIndent: Integer;
begin
  Result := PaintInfo.BandIndent
end;

function TEasyGroup.GetBandLength: Integer;
begin
  Result := PaintInfo.BandLength
end;

function TEasyGroup.GetBandMargin: Integer;
begin
  Result := PaintInfo.BandMargin
end;

function TEasyGroup.GetBandRadius: Byte;
begin
  Result := PaintInfo.BandRadius
end;

function TEasyGroup.GetBandThickness: Integer;
begin
  Result := PaintInfo.BandThickness
end;

function TEasyGroup.GetClientRect: TRect;
begin
  Result := Rect( FDisplayRect.Left + MarginLeft.RuntimeSize,
                  FDisplayRect.Top + MarginTop.RuntimeSize,
                  FDisplayRect.Right - MarginRight.RuntimeSize,
                  FDisplayRect.Bottom - MarginBottom.RuntimeSize)
end;

function TEasyGroup.GetDefaultGridClass: TEasyGridGroupClass;
begin
  Result := nil;
  case OwnerListview.View of
    elsIcon: Result := TEasyGridIconGroup;
    elsSmallIcon: Result := TEasyGridSmallIconGroup;
    elsList: Result := TEasyGridListGroup;
    elsReport: Result := TEasyGridReportGroup;
    elsThumbnail: Result := TEasyGridThumbnailGroup;
    elsTile: Result := TEasyGridTileGroup;
    elsFilmStrip: Result := TEasyGridFilmStripGroup;
    elsGrid: Result := TEasyGridGridGroup;
  end
end;

function TEasyGroup.GetDefaultViewClass: TEasyViewGroupClass;
begin
  Result := TEasyViewGroup
end;

function TEasyGroup.GetExpandable: Boolean;
begin
  Result := PaintInfo.Expandable
end;

function TEasyGroup.GetExpandImageIndent: Integer;
begin
  Result := PaintInfo.ExpandImageIndent
end;

function TEasyGroup.GetGrid: TEasyGridGroup;
begin
  if Assigned(FGrid) then
  begin
    if FGrid.ClassType <> GridClass then
      FreeAndNil(FGrid);
  end;
  if not Assigned(FGrid) then
  begin
    FGrid := GridClass.Create(OwnerListview, Self);
    case OwnerListview.View of
      elsIcon: FGrid.CellSize.Assign(OwnerListview.CellSizes.Icon);
      elsSmallIcon: FGrid.CellSize.Assign(OwnerListview.CellSizes.SmallIcon);
      elsList: FGrid.CellSize.Assign(OwnerListview.CellSizes.List);
      elsReport: FGrid.CellSize.Assign(OwnerListview.CellSizes.Report);
      elsThumbnail: FGrid.CellSize.Assign(OwnerListview.CellSizes.Thumbnail);
      elsTile: FGrid.CellSize.Assign(OwnerListview.CellSizes.Tile);
      elsFilmStrip: FGrid.CellSize.Assign(OwnerListview.CellSizes.FilmStrip);
      elsGrid: FGrid.CellSize.Assign(OwnerListview.CellSizes.Grid);
    end
  end;
  Result := FGrid;
end;

function TEasyGroup.GetGridClass: TEasyGridGroupClass;
begin
  Result := nil;
  OwnerListview.DoCustomGrid(Self, OwnerListview.View, Result);
  if not Assigned(Result) then
    Result := GetDefaultGridClass
end;

function TEasyGroup.GetItem(Index: Integer): TEasyItem;
begin
  Result := Items[Index]
end;

function TEasyGroup.GetItemCount: Integer;
begin
  Result := Items.Count
end;

function TEasyGroup.GetMarginBottom: TEasyFooterMargin;
begin
  Result := PaintInfo.MarginBottom as TEasyFooterMargin
end;

function TEasyGroup.GetMarginLeft: TEasyMargin;
begin
  Result := PaintInfo.MarginLeft
end;

function TEasyGroup.GetMarginRight: TEasyMargin;
begin
  Result := PaintInfo.MarginRight
end;

function TEasyGroup.GetMarginTop: TEasyHeaderMargin;
begin
  Result := PaintInfo.MarginTop
end;

function TEasyGroup.GetOwnerGroups: TEasyGroups;
begin
  Result := TEasyGroups( Collection)
end;

function TEasyGroup.GetOwnerListview: TCustomEasyListview;
begin
  Result := TEasyGroups( Collection).OwnerListview
end;

function TEasyGroup.GetPaintInfo: TEasyPaintInfoBaseGroup;
begin
  Result := inherited PaintInfo as TEasyPaintInfoBaseGroup
end;

function TEasyGroup.GetView: TEasyViewGroup;
begin
  if Assigned(FView) then
  begin
    if FView.ClassType <> ViewClass then
      FreeAndNil(FView)
  end;
  if not Assigned(FView) then
    FView := ViewClass.Create(Self);
  Result := FView
end;

function TEasyGroup.GetViewClass: TEasyViewGroupClass;
begin
  Result := nil;
  if Assigned(OwnerListview) then
    OwnerListview.DoGroupCustomView(Self, OwnerListview.View, Result);
  if not Assigned(Result) then
    Result := GetDefaultViewClass
end;

function TEasyGroup.GetVisibleCount: Integer;
begin
  Result := FVisibleItems.Count
end;

function TEasyGroup.GetVisibleItem(Index: Integer): TEasyItem;
begin
  Result := nil;
  if (Index > -1) and (Index < VisibleItems.Count) then
    Result := TEasyItem( VisibleItems[Index])
end;

function TEasyGroup.HitTestAt(ViewportPoint: TPoint;
  var HitInfo: TEasyGroupHitTestInfoSet): Boolean;
///
// Returns information about what, if anything, was hit at the passed point in
// the group.  Returns true if anything was hit.
//
var
  RectArray: TEasyRectArrayObject;
  R: TRect;
begin
  HitInfo := [];

  // First need to see what Frame of the group was hit, and then the individual
  // rectangle for each element in the Frame is retrieved
  if PtInRect(BoundsRectTopMargin, ViewportPoint) then
  begin
    Include(HitInfo, egtOnHeader);
    View.GroupRectArray(Self, egmeTop, BoundsRectTopMargin, RectArray);
  end else
  if PtInRect(BoundsRectBottomMargin, ViewportPoint) then
  begin
    Include(HitInfo, egtOnFooter);
    View.GroupRectArray(Self, egmeBottom, BoundsRectBottomMargin, RectArray);
  end else
  if PtInRect(BoundsRectLeftMargin, ViewportPoint) then
  begin
    Include(HitInfo, egtOnLeftMargin);
    View.GroupRectArray(Self, egmeLeft, BoundsRectLeftMargin, RectArray);
  end else
  if PtInRect(BoundsRectRightMargin, ViewportPoint) then
  begin
    Include(HitInfo, egtOnRightMargin);
    View.GroupRectArray(Self, egmeRight, BoundsRectRightMargin, RectArray);
  end;

  if HitInfo <> [] then
  begin
    R := RectArray.IconRect;
    // Make the blank area between the image and text part of the image
    R.Right := R.Right + OwnerListview.PaintInfoGroup.CaptionIndent;
    if PtInRect(R, ViewportPoint) then
      Include(HitInfo, egtOnIcon)
    else
    if PtInRect(RectArray.TextRect, ViewportPoint) then
      Include(HitInfo, egtOnText)
    else
    if PtInRect(RectArray.LabelRect, ViewportPoint) then
      Include(HitInfo, egtOnLabel)
    else
    if PtInRect(RectArray.ExpandButtonRect, ViewportPoint) then
      Include(HitInfo, egtOnExpandButton)
    else
    if PtInRect(RectArray.BandRect, ViewportPoint) then
      Include(HitInfo, egtOnBand)
    else
    if PtInRect(RectArray.CheckRect, ViewportPoint) then
      Include(HitInfo, egtOnCheckbox);

  end;
  Result := HitInfo <> []
end;

function TEasyGroup.ItemByPoint(ViewportPoint: TPoint): TEasyItem;
var
  i: Integer;
begin
  Result := nil;
  i := 0;
  while not Assigned(Result) and (i < Items.Count) do
  begin
    if PtInRect(Items[i].DisplayRect, ViewportPoint) then
      Result := Items[i];
    Inc(i)
  end;
end;

function TEasyGroup.LocalPaintInfo: TEasyPaintInfoBasic;
begin
  Result := OwnerListview.PaintInfoGroup
end;

function TEasyGroup.SelectionHit(SelectViewportRect: TRect;
  SelectType: TEasySelectHitType): Boolean;
begin
  Result := View.SelectionHit(Self, SelectViewPortRect, SelectType)
end;

function TEasyGroup.SelectionHitPt(ViewportPoint: TPoint;
  SelectType: TEasySelectHitType): Boolean;
begin
  Result := View.SelectionHitPt(Self, ViewportPoint, SelectType)
end;

procedure TEasyGroup.Freeing;
begin
  OwnerListview.DoGroupFreeing(Self)
end;

procedure TEasyGroup.GainingBold;
begin
  Invalidate(False)
end;

procedure TEasyGroup.GainingCheck;
var
  i: Integer;
begin
  for i := 0 to Items.Count - 1 do
    if Items[i].Visible then
      Items[i].Checked := True;
  if Visible then
    Include(FState, esosChecked);
  Invalidate(False)
end;

procedure TEasyGroup.GainingEnable;
var
  i: Integer;
begin
  for i := 0 to Items.Count - 1 do
    Items[i].Enabled := True;
  Include(FState, esosEnabled);
  Invalidate(False)
end;

procedure TEasyGroup.GainingFocus;
begin
  // Unsupported
end;

procedure TEasyGroup.GainingGhosted;
begin
  Invalidate(False)
end;

procedure TEasyGroup.GainingHilight;
begin
  // Unsupported
end;

procedure TEasyGroup.GainingHotTracking(MousePos: TPoint);
begin
  OwnerListview.DoGroupHotTrack(Self, ehsEnable, MousePos);
  Invalidate(True)
end;

procedure TEasyGroup.GainingSelection;
begin
  OwnerListview.DoGroupSelectionChanged(Self)
end;

procedure TEasyGroup.GainingVisibility;
var
  i: Integer;
begin
  OwnerListview.DoGroupVisibilityChanged(Self);
  for i := 0 to Items.Count - 1 do
    Items[i].Visible := True
end;

procedure TEasyGroup.Initialize;
begin
  OwnerListview.DoGroupInitialize(Self)
end;

procedure TEasyGroup.LoadFromStream(S: TStream; var Version: Integer);
begin
  inherited LoadFromStream(S, Version);
  if not Assigned(Items) then
    Items := TEasyItems.Create(OwnerListview, Self)
  else
    Items.Clear;
  Items.ReadItems(S);
  OwnerListview.DoGroupLoadFromStream(Self, S, Version);

  // For new objects test the stream version first
  // if Version > X then
  // begin
  //   ReadStream....
  // end
end;

procedure TEasyGroup.LosingBold;
begin
  Invalidate(False)
end;

procedure TEasyGroup.LosingCheck;
var
  i: Integer;
begin
  for i := 0 to Items.Count - 1 do
    Items[i].Checked := False;
  Exclude(FState, esosChecked);
  Invalidate(False)
end;

procedure TEasyGroup.LosingEnable;
var
  i: Integer;
begin
  for i := 0 to Items.Count - 1 do
    Items[i].Enabled := False;
  Exclude(FState, esosEnabled);
  Invalidate(False)
end;

procedure TEasyGroup.LosingFocus;
begin
 // Unsupported
end;

procedure TEasyGroup.LosingGhosted;
begin
  Invalidate(False)
end;

procedure TEasyGroup.LosingHilight;
begin
 // Unsupported
end;

procedure TEasyGroup.LosingHotTracking;
begin
  OwnerListview.DoGroupHotTrack(Self, ehsDisable, Point(0, 0));
  Invalidate(True)
end;

procedure TEasyGroup.LosingSelection;
begin
  OwnerListview.DoGroupSelectionChanged(Self)
end;

procedure TEasyGroup.LosingVisibility;
var
  i: Integer;
begin
  for i := 0 to Items.Count - 1 do
    Items[i].Visible := False;
  OwnerListview.DoGroupVisibilityChanged(Self);
end;

procedure TEasyGroup.Paint(MarginEdge: TEasyGroupMarginEdge; ObjRect: TRect; ACanvas: TCanvas);
begin
  View.Paint(Self, MarginEdge, ObjRect, ACanvas)
end;

procedure TEasyGroup.Rebuild(PrevGroup: TEasyGroup; var NextVisibleItemIndex: Integer);
begin
  if Assigned(Grid) then
    Grid.Rebuild(PrevGroup, NextVisibleItemIndex)
end;

procedure TEasyGroup.SaveToStream(S: TStream; Version: Integer = EASYLISTVIEW_STREAM_VERSION);
begin
  inherited SaveToStream(S);
  Items.WriteItems(S);
  OwnerListview.DoGroupSaveToStream(Self, S, Version);

  // For new objects test the stream version first
  // if Version > X then
  // begin
  //   WriteStream....
  // end
end;

procedure TEasyGroup.SetBandBlended(Value: Boolean);
begin
  if Value <> BandBlended then
  begin
    PaintInfo.BandBlended := Value;
    Invalidate(False)
  end
end;

procedure TEasyGroup.SetBandColor(Value: TColor);
begin
  if Value <> BandColor then
  begin
    PaintInfo.BandColor := Value;
    Invalidate(False)
  end
end;

procedure TEasyGroup.SetBandColorFade(Value: TColor);
begin
  if Value <> BandColorFade then
  begin
    PaintInfo.BandColorFade := Value;
    Invalidate(False)
  end
end;

procedure TEasyGroup.SetBandEnabled(Value: Boolean);
begin
  if Value <> BandEnabled then
  begin
    PaintInfo.BandEnabled := Value;
    Invalidate(False)
  end
end;

procedure TEasyGroup.SetBandFullWidth(Value: Boolean);
begin
  if Value <> BandFullWidth then
  begin
    PaintInfo.BandFullWidth := Value;
    Invalidate(False)
  end
end;

procedure TEasyGroup.SetBandIndent(Value: Integer);
begin
  if Value <> BandIndent then
  begin
    PaintInfo.BandIndent := Value;
    Invalidate(False)
  end
end;

procedure TEasyGroup.SetBandLength(Value: Integer);
begin
  if Value <> BandLength then
  begin
    PaintInfo.BandLength := Value;
    Invalidate(False)
  end
end;

procedure TEasyGroup.SetBandMargin(Value: Integer);
begin
  if Value <> BandMargin then
  begin
    PaintInfo.BandMargin := Value;
    Invalidate(False)
  end
end;

procedure TEasyGroup.SetBandRadius(Value: Byte);
begin
  if Value <> BandRadius then
  begin
    PaintInfo.BandRadius:= Value;
    Invalidate(False)
  end
end;

procedure TEasyGroup.SetBandThickness(Value: Integer);
begin
  if Value <> BandThickness then
  begin
    PaintInfo.BandThickness := Value;
    Invalidate(False)
  end
end;

procedure TEasyGroup.SetExpandable(Value: Boolean);
begin
  if Value <> Expandable then
  begin
    PaintInfo.Expandable := Value;
    Invalidate(False)
  end
end;

procedure TEasyGroup.SetExpanded(Value: Boolean);
begin
  if Value <> FExpanded then
  begin
    OwnerListview.BeginUpdate;
    try
      FExpanded := Value;
      if Value then
        OwnerListview.DoGroupExpand(Self)
      else
        OwnerListview.DoGroupCollapse(Self);  
      OwnerListview.Groups.Rebuild(True);
    finally
      OwnerListview.EndUpdate;
    end
  end
end;

procedure TEasyGroup.SetExpandImageIndent(Value: Integer);
begin
  if Value <> ExpandImageIndent then
  begin
    PaintInfo.ExpandImageIndent := Value;
    Invalidate(False)
  end
end;

procedure TEasyGroup.SetItem(Index: Integer; Value: TEasyItem);
begin
  if (Index > -1) and (Index < Items.Count) then
    Items[Index] := Value
end;

procedure TEasyGroup.SetMarginBottom(Value: TEasyFooterMargin);
begin
  if Value <> MarginBottom then
  begin
    PaintInfo.MarginBottom := Value;
    Invalidate(False)
  end
end;

procedure TEasyGroup.SetMarginLeft(Value: TEasyMargin);
begin
  if Value <> MarginLeft then
  begin
    PaintInfo.MarginLeft := Value;
    Invalidate(False)
  end
end;

procedure TEasyGroup.SetMarginRight(Value: TEasyMargin);
begin
  if Value <> MarginRight then
  begin
    PaintInfo.MarginRight := Value;
    Invalidate(False)
  end
end;

procedure TEasyGroup.SetMarginTop(Value: TEasyHeaderMargin);
begin
  if Value <> MarginTop then
  begin
    PaintInfo.MarginTop := Value;
    Invalidate(False)
  end
end;

procedure TEasyGroup.SetPaintInfo(const Value: TEasyPaintInfoBaseGroup);
begin
  inherited PaintInfo := Value
end;

function TEasyViewGroup.CustomExpandImages: Boolean;
begin
  Result := not OwnerListview.GroupCollapseButton.Empty  or not OwnerListview.GroupExpandButton.Empty
end;

function TEasyViewGroup.EditAreaHitPt(Group: TEasyGroup; ViewportPoint: TPoint): Boolean;
begin
  Result := False
end;

function TEasyViewGroup.GetImageList(Group: TEasyGroup): TCustomImageList;
begin
  Result := Group.ImageList[-1, eisSmall]
end;

function TEasyViewGroup.SelectionHit(Group: TEasyGroup;
  SelectViewportRect: TRect; SelectType: TEasySelectHitType): Boolean;
begin
  Result := False
end;

function TEasyViewGroup.SelectionHitPt(Group: TEasyGroup; ViewportPoint: TPoint;
  SelectType: TEasySelectHitType): Boolean;
begin
  Result := False
end;

{ TEasyGroupView }

procedure TEasyViewGroup.GetCollapseExpandImages(var Expand, Collapse: TBitmap);
begin
  Expand := nil;
  Collapse := nil;
  if not OwnerListview.GroupCollapseButton.Empty then
    Collapse := OwnerListview.GroupCollapseButton
  else
    Collapse := OwnerListview.GlobalImages.GroupCollapseButton;

  if not OwnerListview.GroupExpandButton.Empty then
    Expand := OwnerListview.GroupExpandButton
  else
    Expand := OwnerListview.GlobalImages.GroupExpandButton;
end;

procedure TEasyViewGroup.GetExpandImageSize(Group: TEasyGroup; var ImageW, ImageH: Integer);
var
  ExpandImage, CollapseImage: TBitmap;
begin
  ImageW := 0;
  ImageH := 0;
  GetCollapseExpandImages(ExpandImage, CollapseImage);
  if Assigned(ExpandImage) and Assigned(CollapseImage) then
  begin
    ImageW := ExpandImage.Width;
    ImageH := ExpandImage.Height;
    if CollapseImage.Width > ImageW then
      ImageW := CollapseImage.Width;
    if CollapseImage.Height > ImageH then
      ImageH := CollapseImage.Height
  end
end;

procedure TEasyViewGroup.GetImageSize(Group: TEasyGroup; var ImageW, ImageH: Integer);
var
  Images: TCustomImageList;
  IsCustom: Boolean;
begin
  ImageW := 0;
  ImageH := 0;
  Group.ImageDrawIsCustom(nil, IsCustom);
  if IsCustom then
    Group.ImageDrawGetSize(nil, ImageW, ImageH)
  else begin
    Images := GetImageList(Group);
    if (Group.ImageIndexes[0] > -1) and Assigned(Images) then
    begin
        ImageW := Images.Width;
        ImageH := Images.Height
    end
  end;
end;

procedure TEasyViewGroup.GroupRectArray(Group: TEasyGroup; MarginEdge: TEasyGroupMarginEdge;
  ObjRect: TRect; var RectArray: TEasyRectArrayObject);
//
// Grabs all the rectangles for the items within a cell in one call
// If PaintInfo is nil then the information is fetched automaticlly
//
var
  TextSize: TSize;
  HeaderBand, FooterBand: TRect;
  TempRectArray: TEasyRectArrayObject;
  ImageW, ImageH, ExpandImageW, ExpandImageH, BandOffset: Integer;
begin
  Group.Initialized := True;
  
  FillChar(RectArray, SizeOf(RectArray), #0);

  RectArray.GroupRect := ObjRect;
  RectArray.BackGndRect :=  Group.BoundsRectBkGnd;

  GetImageSize(Group, ImageW, ImageH);
  GetExpandImageSize(Group, ExpandImageW, ExpandImageH);
  ExpandImageW := Round( ExpandImageW + GetDeviceCaps(OwnerListview.Canvas.Handle, LOGPIXELSX)/96);

  if MarginEdge in [egmeTop, egmeBottom] then
  begin

    if Group.BandEnabled then
      BandOffset := Group.BandThickness + 2 * Group.BandMargin
    else
      BandOffset := 0;

    // Calculate the Expansion button first for the Header only
    if Group.Expandable and (MarginEdge = egmeTop) then
      RectArray.ExpandButtonRect := Rect(RectArray.GroupRect.Left + Group.ExpandImageIndent,
                      RectArray.GroupRect.Top,
                      RectArray.GroupRect.Left + ExpandImageW + Group.ExpandImageIndent,
                      RectArray.GroupRect.Bottom - BandOffset)
    else   // Make the ExpandButton R a width of 0
      RectArray.ExpandButtonRect := Rect(RectArray.GroupRect.Left,
                      RectArray.GroupRect.Top,
                      RectArray.GroupRect.Left,
                      RectArray.GroupRect.Bottom - BandOffset);

    if (Group.CheckType <> ectNone) and (MarginEdge in [egmeTop]) then
    begin
      RectArray.CheckRect := Checks.Bound[Group.CheckSize];
      case Group.VAlignment of
        cvaTop: OffsetRect(RectArray.CheckRect, RectArray.ExpandButtonRect.Right + Group.CheckIndent, ObjRect.Top + 2);
        cvaBottom: OffsetRect(RectArray.CheckRect, RectArray.ExpandButtonRect.Right + Group.CheckIndent, ObjRect.Bottom - BandOffset - RectHeight(RectArray.CheckRect) - 2);
        cvaCenter: OffsetRect(RectArray.CheckRect, RectArray.ExpandButtonRect.Right + Group.CheckIndent,
           (ObjRect.Top + ((RectHeight(ObjRect) - BandOffset) - RectHeight(RectArray.CheckRect)) div 2));
      end
    end else
    begin
      // CheckRect is a 0 width
      RectArray.CheckRect := ObjRect;
      RectArray.CheckRect.Left := RectArray.ExpandButtonRect.Right;
      RectArray.CheckRect.Right := RectArray.ExpandButtonRect.Right;
    end;


    // Now Calculate the image for the header or the footer
    if Group.ImageIndex > -1 then
      RectArray.IconRect := Rect(RectArray.CheckRect.Right + Group.ImageIndent,
                    RectArray.GroupRect.Top,
                    RectArray.CheckRect.Right + ImageW + Group.ImageIndent,
                    RectArray.GroupRect.Bottom - BandOffset)
    else   // Make the IconR a width of 0
      RectArray.IconRect := Rect(RectArray.CheckRect.Right,
                    RectArray.CheckRect.Top,
                    RectArray.CheckRect.Right,
                    RectArray.CheckRect.Bottom - BandOffset);

    // Now the Label rect may be calculated for the header or footer
    RectArray.LabelRect := Rect(RectArray.IconRect.Right + Group.CaptionIndent,
                   RectArray.ExpandButtonRect.Top,
                   RectArray.GroupRect.Right,
                   RectArray.ExpandButtonRect.Bottom);


    // Calculate the text size for the text based on the above font
    if Assigned(OwnerListview.ScratchCanvas) then
    begin
      LoadTextFont(Group, OwnerListview.ScratchCanvas);
      TextSize := TextExtentW(Group.Caption, OwnerListview.ScratchCanvas.Font);
    end else
    begin
      TextSize.cx := 0;
      TextSize.cy := 0;
    end;

    // Use the calculated label rectangle to position where the text goes
    RectArray.TextRect := Rect(RectArray.LabelRect.Left,
                               RectArray.LabelRect.Top,
                               RectArray.LabelRect.Left + TextSize.cx,
                               RectArray.LabelRect.Top + TextSize.cy);

    if RectArray.TextRect.Right > RectArray.LabelRect.Right then
      RectArray.TextRect.Right := RectArray.LabelRect.Right;
    if RectArray.TextRect.Bottom > RectArray.LabelRect.Bottom then
      RectArray.TextRect.Bottom := RectArray.LabelRect.Bottom;

    case Group.Alignment of
      taLeftJustify:  OffsetRect(RectArray.TextRect, 0, 0);
      taRightJustify: OffsetRect(RectArray.TextRect, RectWidth(RectArray.LabelRect) - (RectWidth(RectArray.TextRect)), 0);
      taCenter: OffsetRect(RectArray.TextRect, (RectWidth(RectArray.LabelRect) - RectWidth(RectArray.TextRect)) div 2, 0);
    end;

    case Group.VAlignment of
      cvaBottom: OffsetRect(RectArray.TextRect, 0, RectHeight(RectArray.GroupRect) - (RectHeight(RectArray.TextRect) + BandOffset));
      cvaCenter: OffsetRect(RectArray.TextRect, 0, ((RectHeight(RectArray.GroupRect) - BandOffset) - RectHeight(RectArray.TextRect)) div 2);
    end;

    if Group.BandEnabled then
    begin
      if Group.BandFullWidth then
        RectArray.BandRect := Rect(RectArray.GroupRect.Left,
                           RectArray.GroupRect.Bottom - Group.BandMargin - Group.BandThickness,
                           RectArray.GroupRect.Right,
                           RectArray.GroupRect.Bottom - Group.BandMargin)
      else
        RectArray.BandRect := Rect(RectArray.GroupRect.Left,
                           RectArray.GroupRect.Bottom - 2 * Group.BandMargin - Group.BandThickness,
                           RectArray.GroupRect.Left + Group.BandLength,
                           RectArray.GroupRect.Bottom - Group.BandMargin);

      OffsetRect(RectArray.BandRect, Group.BandIndent, 0);

    end;

  end else
  begin  // Calculate the margin rectangles

    // Need to send nil so the user attributes are fetched for the header
    GroupRectArray(Group, egmeTop, Group.BoundsRectTopMargin, TempRectArray);
    HeaderBand := TempRectArray.BandRect;

    // Need to send nil so the user attributes are fetched for the footer
    GroupRectArray(Group, egmeBottom, Group.BoundsRectBottomMargin, TempRectArray);
    FooterBand := TempRectArray.BandRect;

    if MarginEdge  = egmeLeft then
      RectArray.BandRect := Rect(RectArray.GroupRect.Left + (RectWidth(RectArray.GroupRect) - Group.BandThickness) div 2,
                            HeaderBand.Top,
                            RectArray.GroupRect.Right,
                            FooterBand.Bottom - 1);
    if MarginEdge  = egmeRight then
      RectArray.BandRect := Rect(RectArray.GroupRect.Left,
                            HeaderBand.Top,
                            RectArray.GroupRect.Right - (RectWidth(RectArray.GroupRect) - Group.BandThickness) div 2,
                            FooterBand.Bottom - 1)
  end;
end;

procedure TEasyViewGroup.LoadTextFont(Group: TEasyGroup; ACanvas: TCanvas);
begin
  if Assigned(ACanvas) then
  begin
    ACanvas.Font.Assign(OwnerListview.GroupFont);
    ACanvas.Brush.Style := bsClear;
    if Group.Focused then
    begin
      if OwnerListview.Focused then
        ACanvas.Font.Color := clHighlightText;
    end;
    if Group.Bold then
      ACanvas.Font.Style := ACanvas.Font.Style + [fsBold];

    OwnerListview.DoGroupPaintText(Group, ACanvas);
  end
end;

procedure TEasyViewGroup.Paint(Group: TEasyGroup; MarginEdge: TEasyGroupMarginEdge; ObjRect: TRect; ACanvas: TCanvas);
//
// Calls the sub Paint methods to do a complete Paint cycle for the Group
//
var
  RectArray: TEasyRectArrayObject;
begin
  if not IsRectEmpty(ObjRect) then
  begin
    CanvasStore.StoreCanvasState(ACanvas);
    try
      GroupRectArray(Group, MarginEdge, ObjRect, RectArray);
      PaintBefore(Group, ACanvas, MarginEdge, ObjRect, RectArray);
      PaintBackground(Group, ACanvas, MarginEdge, ObjRect, RectArray);
      if MarginEdge = egmeTop then
        PaintCheckBox(Group, ACanvas, RectArray);
      PaintSelectionRect(Group, ACanvas, ObjRect, RectArray);
      PaintFocusRect(Group, ACanvas, MarginEdge, ObjRect, RectArray);
      PaintText(Group, MarginEdge, ACanvas, ObjRect, RectArray);
      PaintBand(Group, ACanvas, MarginEdge, ObjRect, RectArray);
      PaintExpandButton(Group, ACanvas, MarginEdge, ObjRect, RectArray);
      PaintImage(Group, ACanvas, MarginEdge, ObjRect, RectArray);
      PaintAfter(Group, ACanvas, MarginEdge, ObjRect, RectArray);
    finally
      CanvasStore.RestoreCanvasState(ACanvas)
    end
  end
end;

procedure TEasyViewGroup.PaintAfter(Group: TEasyGroup; ACanvas: TCanvas;
  MarginEdge: TEasyGroupMarginEdge; ObjRect: TRect; RectArray: TEasyRectArrayObject);
begin

end;

procedure TEasyViewGroup.PaintBackground(Group: TEasyGroup; ACanvas: TCanvas;
  MarginEdge: TEasyGroupMarginEdge;ObjRect: TRect; RectArray: TEasyRectArrayObject);
begin

end;

procedure TEasyViewGroup.PaintBand(Group: TEasyGroup; ACanvas: TCanvas;
   MarginEdge: TEasyGroupMarginEdge; ObjRect: TRect; RectArray: TEasyRectArrayObject);
//
// Paints the banding in none, one, ..., all margins of the Group
var
  i: Integer;
  Red1, Red2, Green1, Green2, Blue1, Blue2, RC, GC, BC, BandTop: Integer;
  RStep, GStep, BStep : Double;
  RGBVal, BlendStop: Longword;
  CellR: TRect;
begin
  if Group.BandEnabled and not IsRectEmpty(RectArray.BandRect) then
  begin
    CellR := ObjRect;
    if MarginEdge in [egmeBottom, egmeTop] then
    begin
      BandTop := RectArray.BandRect.Top + Group.BandMargin;

     // Draw the Banding
      if Group.BandBlended then
      begin
        if (Group.BandLength > 0) then
        begin
          // Calulate the stepping to create the blended banding
          RGBVal := ColorToRGB(Group.BandColor);
          Red1 := GetRValue(RGBVal);
          Green1 := GetGValue(RGBVal);
          Blue1 := GetBValue(RGBVal);

          RGBVal := ColorToRGB(Group.BandColorFade);
          Red2 := GetRValue(RGBVal);
          Green2 := GetGValue(RGBVal);
          Blue2 := GetBValue(RGBVal);


          if Group.BandFullWidth then
            BlendStop := RectWidth(CellR)
          else
            BlendStop := Group.BandLength;

          RStep := (Red2-Red1)/BlendStop;
          GStep := (Green2-Green1)/BlendStop;
          BStep := (Blue2-Blue1)/BlendStop;

          ACanvas.Pen.Color := Group.BandColor;

          // Draw the Banding
          for i := 0 to BlendStop do
          begin
            if Group.BandFullWidth then
            begin
              ACanvas.MoveTo(CellR.Left + i, BandTop);
              ACanvas.LineTo(CellR.Left + i, BandTop + Group.BandThickness);
            end else
            begin
              ACanvas.MoveTo(RectArray.BandRect.Left + i, BandTop);
              ACanvas.LineTo(RectArray.BandRect.Left + i, BandTop + Group.BandThickness);
            end;
            RC:=Round(Red1 + i*RStep);
            GC:=Round(Green1 +i*GStep);
            BC:=Round(Blue1 + i*BStep);
            ACanvas.Pen.Color := RGB(RC, GC, BC);
          end
        end
      end else
      begin
        ACanvas.Pen.Color := Group.BandColor;

        if Group.BandFullWidth then
        begin
          for i := 0 to Group.BandThickness - 1 do
          begin
            ACanvas.MoveTo(CellR.Left, BandTop + i);
            ACanvas.LineTo(CellR.Right, BandTop + i);
          end
        end else
        begin
          for i := 0 to Group.BandThickness - 1 do
          begin
            ACanvas.MoveTo(RectArray.BandRect.Left, BandTop + i);
            ACanvas.LineTo(RectArray.BandRect.Left + Group.BandLength, BandTop + i);
          end
        end
      end
    end else
    begin
      if (MarginEdge in [egmeRight]) and Group.BandBlended then
        ACanvas.Pen.Color := Group.BandColorFade
      else
        ACanvas.Pen.Color := Group.BandColor;

      if MarginEdge = egmeLeft then
      begin
        // Draw the horizontal stubs on the top and bottom
        for i := 0 to Group.BandThickness - 1 do
        begin
          ACanvas.MoveTo(RectArray.BandRect.Left + Group.BandRadius, RectArray.BandRect.Top + i);
          ACanvas.LineTo(RectArray.BandRect.Right + 1, RectArray.BandRect.Top + i)
        end;
         for i := 0 to Group.BandThickness - 1 do
        begin
          ACanvas.MoveTo(RectArray.BandRect.Left + Group.BandRadius, RectArray.BandRect.Bottom - i);
          ACanvas.LineTo(RectArray.BandRect.Right + 1, RectArray.BandRect.Bottom - i)
        end;
        for i := 0 to Group.BandThickness - 1 do
        begin
          ACanvas.MoveTo(RectArray.BandRect.Left + i, RectArray.BandRect.Top + Group.BandRadius);
          ACanvas.LineTo(RectArray.BandRect.Left + i, RectArray.BandRect.Bottom - Group.BandRadius + 1)
        end;
        for i := 0 to Group.BandThickness - 1 do
        begin
          ACanvas.MoveTo(RectArray.BandRect.Left + Group.BandRadius, RectArray.BandRect.Top);
          AngleArc(ACanvas.Handle,
                   RectArray.BandRect.Left + Group.BandRadius,
                   RectArray.BandRect.Top + Group.BandRadius,
                   Group.BandRadius - i,
                   90,
                   90);

          ACanvas.MoveTo(RectArray.BandRect.Left, RectArray.BandRect.Bottom - Group.BandRadius);
          AngleArc(ACanvas.Handle,
                   RectArray.BandRect.Left + Group.BandRadius,
                   RectArray.BandRect.Bottom - Group.BandRadius,
                   Group.BandRadius - i,
                   180,
                   90);
        end
      end else
      begin
        // Draw the horizontal stubs on the top and bottom
        for i := 0 to Group.BandThickness - 1 do
        begin
          ACanvas.MoveTo(RectArray.BandRect.Left, RectArray.BandRect.Top + i);
          ACanvas.LineTo(RectArray.BandRect.Right - Group.BandRadius + 1, RectArray.BandRect.Top + i)
        end;
         for i := 0 to Group.BandThickness - 1 do
        begin
          ACanvas.MoveTo(RectArray.BandRect.Left, RectArray.BandRect.Bottom - i);
          ACanvas.LineTo(RectArray.BandRect.Right - Group.BandRadius + 1, RectArray.BandRect.Bottom - i)
        end;
        for i := 0 to Group.BandThickness - 1 do
        begin
          ACanvas.MoveTo(RectArray.BandRect.Right - i, RectArray.BandRect.Top + Group.BandRadius);
          ACanvas.LineTo(RectArray.BandRect.Right - i, RectArray.BandRect.Bottom - Group.BandRadius + 1)
        end;
        for i := 0 to Group.BandThickness - 1 do
        begin
          ACanvas.MoveTo(RectArray.BandRect.Right, RectArray.BandRect.Top + Group.BandRadius);
          AngleArc(ACanvas.Handle,
                   RectArray.BandRect.Right - Group.BandRadius,
                   RectArray.BandRect.Top + Group.BandRadius,
                   Group.BandRadius - i,
                   0,
                   90);

          ACanvas.MoveTo(RectArray.BandRect.Right - Group.BandRadius, RectArray.BandRect.Bottom);
          AngleArc(ACanvas.Handle,
                   RectArray.BandRect.Right - Group.BandRadius,
                   RectArray.BandRect.Bottom - Group.BandRadius,
                   Group.BandRadius - i,
                   270,
                   90);
        end
      end
    end
  end
end;

procedure TEasyViewGroup.PaintBefore(Group: TEasyGroup; ACanvas: TCanvas;
  MarginEdge: TEasyGroupMarginEdge; ObjRect: TRect; RectArray: TEasyRectArrayObject);
begin
end;

procedure TEasyViewGroup.PaintCheckBox(Group: TEasyGroup; ACanvas: TCanvas;
  RectArray: TEasyRectArrayObject);
begin
  if not ((Group.CheckType = ectNone) or (Group.CheckType = ettNoneWithSpace)) then
    PaintCheckboxCore(Group.CheckType,       // TEasyCheckType
                      OwnerListview,        // TCustomEasyListview
                      ACanvas,              // TCanvas
                      RectArray.CheckRect,  // TRect
                      Group.Enabled,         // IsEnabled
                      Group.Checked,         // IsChecked
                      Group.CheckPending and (ebcsCheckboxClickPending in OwnerListview.States), // IsHot
                      Group.CheckFlat,       // IsFlat
                      Group.CheckHovering,   // IsHovering
                      Group.CheckPending,    // IsPending
                      Group,
                      Group.CheckSize);
end;

procedure TEasyViewGroup.PaintExpandButton(Group: TEasyGroup; ACanvas: TCanvas;
  MarginEdge: TEasyGroupMarginEdge; ObjRect: TRect; RectArray: TEasyRectArrayObject);
//
// Paints the [+] button for collapable groups
//
var
  Image, ExpandImage, CollapseImage: TBitmap;
  {$IFDEF USETHEMES}
  Part, uState: Longword;
  R: TRect;
  {$ENDIF}
begin
  if MarginEdge in [egmeTop] then
  begin
    GetCollapseExpandImages(ExpandImage, CollapseImage);

    {$IFDEF USETHEMES}
    if Group.OwnerListview.DrawWithThemes and not CustomExpandImages then
    begin
      Part := TVP_GLYPH;
      if Group.Expanded then
        uState := GLPS_OPENED
      else
        uState := GLPS_CLOSED;

      R := RectArray.ExpandButtonRect;
      R.Top := R.Top + (RectHeight(RectArray.ExpandButtonRect) - RectWidth(RectArray.ExpandButtonRect)) div 2;
      R.Bottom := R.Top + RectWidth(R);
      DrawThemeBackground(Group.OwnerListview.Themes.TreeviewTheme, ACanvas.Handle,
        Part, uState, R, nil);
      Exit;
    end;
    {$ENDIF}

    // If the border is the header and it is expandable then must draw the
    // "+" expand box
    if Group.Expandable and (MarginEdge = egmeTop) then
    begin
      // Choose correct image
      if Group.Expanded then
        Image := CollapseImage
      else
        Image := ExpandImage;

      // Draw the image
      if Assigned(Image) then
      begin
        case Group.VAlignment of
          cvaTop:    ACanvas.Draw(RectArray.ExpandButtonRect.Left + (RectWidth(RectArray.ExpandButtonRect) - Image.Width) div 2, RectArray.ExpandButtonRect.Top + 2, Image);
          cvaBottom: ACanvas.Draw(RectArray.ExpandButtonRect.Left + (RectWidth(RectArray.ExpandButtonRect) - Image.Width) div 2, RectArray.ExpandButtonRect.Bottom - 2 - Image.Height, Image);
          cvaCenter: ACanvas.Draw(RectArray.ExpandButtonRect.Left + (RectWidth(RectArray.ExpandButtonRect) - Image.Width) div 2,
                     RectArray.ExpandButtonRect.Top + (RectHeight(RectArray.ExpandButtonRect) - Image.Height) div 2, Image);
        end
      end
    end
  end else
  begin
  end
end;

procedure TEasyViewGroup.PaintFocusRect(Group: TEasyGroup; ACanvas: TCanvas;
  MarginEdge: TEasyGroupMarginEdge; ObjRect: TRect; RectArray: TEasyRectArrayObject);
//
// Paint the caption area of a group margin for a focused state
//
var
  R: TRect;
begin
  if Group.Focused and (MarginEdge = egmeForeground) and not IsRectEmpty(RectArray.GroupRect) then
  begin
    R := RectArray.GroupRect;
    InflateRect(R, -4, -4);
    ACanvas.Brush.Color := Group.OwnerListview.Color;
    ACanvas.Font.Color := clBlack;
    DrawFocusRect(ACanvas.Handle, R);
  end
end;

procedure TEasyViewGroup.PaintImage(Group: TEasyGroup; ACanvas: TCanvas;
  MarginEdge: TEasyGroupMarginEdge; ObjRect: TRect; RectArray: TEasyRectArrayObject);
//
// Paints the Icon/Bitmap to the group margin
//
var
  ImageIndex, OverlayIndex, fStyle: Integer;
  Images: TCustomImageList;
  IsCustom: Boolean;
begin
  if MarginEdge in [egmeTop, egmeBottom] then
  begin
    Group.ImageDrawIsCustom(nil, IsCustom);
    if IsCustom then
      Group.ImageDraw(nil, ACanvas, RectArray, AlphaBlender)
    else begin

      Images := GetImageList(Group);

      if MarginEdge = egmeTop then
      begin
        ImageIndex := Group.ImageIndex;
        OverlayIndex := Group.ImageOverlayIndex
      end else
      begin
        ImageIndex := Group.MarginBottom.ImageIndex;
        OverlayIndex := Group.MarginBottom.ImageOverlayIndex
      end;

      // Draw the image in the ImageList if available
      if Assigned(Images) and (Group.ImageIndex > -1) then
      begin
        fStyle := ILD_TRANSPARENT;

        if OverlayIndex > -1 then
        begin
          ImageList_SetOverlayImage(Images.Handle, OverlayIndex, 1);
          fStyle := fStyle or INDEXTOOVERLAYMASK(1)
        end;

        // Get the "normalized" rectangle for the image
        RectArray.IconRect.Left := RectArray.IconRect.Left + (RectWidth(RectArray.IconRect) - Images.Width) div 2;
        

        case Group.VAlignment of
          cvaTop: RectArray.IconRect.Top := RectArray.IconRect.Top + 2;
          cvaBottom: RectArray.IconRect.Top := RectArray.IconRect.Bottom - (Images.Height + 2);
          cvaCenter: RectArray.IconRect.Top := RectArray.IconRect.Top + (RectHeight(RectArray.IconRect) - Images.Height) div 2;
        end;
        
        ImageList_DrawEx(Images.Handle,
          ImageIndex,
          ACanvas.Handle,
          RectArray.IconRect.Left,
          RectArray.IconRect.Top,
          0,
          0,
          CLR_NONE,
          CLR_NONE,
          fStyle);
      end
    end
  end
end;

procedure TEasyViewGroup.PaintSelectionRect(Group: TEasyGroup; ACanvas: TCanvas;
  ObjRect: TRect; RectArray: TEasyRectArrayObject);
//
// Paint the caption area of a group margin for a selected state
//
begin
  if Group.Selected and not IsRectEmpty(RectArray.TextRect) and (Group.Caption <> '') then
  begin
    ACanvas.Brush.Color := clHighlight;
    InflateRect(RectArray.TextRect, 2, 2);
    ACanvas.FillRect(RectArray.TextRect);
    InflateRect(RectArray.TextRect, 2, 2);
  end
end;

procedure TEasyViewGroup.PaintText(Group: TEasyGroup; MarginEdge: TEasyGroupMarginEdge;
  ACanvas: TCanvas; ObjRect: TRect; RectArray: TEasyRectArrayObject);
//
//  Paints the caption within the group margin
//
var
  DrawTextFlags: TCommonDrawTextWFlags;
  Caption: WideString;
  Alignment: TAlignment;
  VAlignment: TCommonVAlignment;
begin
  if not IsRectEmpty(RectArray.TextRect) and (Group.Caption <> '') then
  begin
    LoadTextFont(Group, ACanvas);

    if MarginEdge = egmeTop then
    begin
      Caption := Group.Caption;
      Alignment := Group.Alignment;
      VAlignment := Group.VAlignment;
    end else
    begin
      Caption := Group.MarginBottom.Caption;
      Alignment := Group.MarginBottom.Alignment;
      VAlignment := Group.MarginBottom.VAlignment;
    end;
    DrawTextFlags := [dtEndEllipsis];

 //   if PaintInfo.Caption.RTLReading then
 //     Include(DrawTextFlags, dtRTLReading);

    case Alignment of
      taLeftJustify: Include(DrawTextFlags, dtLeft);
      taRightJustify: Include(DrawTextFlags, dtRight);
      taCenter:  Include(DrawTextFlags, dtCenter);
    end;

    case VAlignment of
      cvaTop: Include(DrawTextFlags, dtTop);
      cvaCenter: Include(DrawTextFlags, dtVCenter);
      cvaBottom:  Include(DrawTextFlags, dtBottom);
    end;

//    if PaintInfo.Caption.RTLReading then
//      Include(DrawTextFlags, dtRTLReading);

    DrawTextFlags := DrawTextFlags + [dtSingleLine];

    DrawTextWEx(ACanvas.Handle, Caption, RectArray.TextRect, DrawTextFlags, 1);
  end
end;

{ TEasyColumn }

constructor TEasyColumn.Create(ACollection: TEasyCollection);
begin
  inherited;
  FDropDownButton := TEasyColumnDropDownButton.Create(Self);
  FSortDirection := esdNone;
  FAutoToggleSortGlyph := True;
  FClickable := True;
  FWidth := 50;
  FPosition := OwnerColumns.Count;
  AutoSizeOnDblClk := True;
end;

destructor TEasyColumn.Destroy;
begin
  SetDestroyFlags;
  if OwnerListview.Selection.FFocusedColumn = Self then
     OwnerListview.Selection.FocusedColumn := nil;
  if OwnerListview.Header.HotTrackedColumn = Self then
    OwnerListview.Header.HotTrackedColumn := nil;
  if OwnerListview.Header.PressColumn = Self then
    OwnerListview.Header.PressColumn := nil;
  if OwnerListview.EditManager.TabMoveFocusColumn = Self then
      OwnerListview.EditManager.TabMoveFocusColumn := nil;
  if OwnerListview.Selection.FocusedColumn = Self then
    OwnerListview.Selection.FocusedColumn := nil;
  if OwnerListview.Header.DropDownHoverColumn = Self then
    OwnerListview.Header.DropDownHoverColumn := nil;
  FreeAndNil(FDropDownButton);
  inherited Destroy;
  FreeAndNil(FView);
end;

function TEasyColumn.CanChangeBold(NewValue: Boolean): Boolean;
begin
  Result := True
end;


function TEasyColumn.CanChangeCheck(NewValue: Boolean): Boolean;
begin
  Result := True;
  OwnerListview.DoColumnCheckChanging(Self, Result)
end;

function TEasyColumn.CanChangeEnable(NewValue: Boolean): Boolean;
begin
  Result := True;
  OwnerListview.DoColumnEnableChanging(Self, Result)
end;

function TEasyColumn.CanChangeFocus(NewValue: Boolean): Boolean;
begin
  Result := True;
  OwnerListview.DoColumnFocusChanging(Self, Result)
end;

function TEasyColumn.CanChangeHotTracking(NewValue: Boolean): Boolean;
begin
  Result := True;
end;

function TEasyColumn.CanChangeSelection(NewValue: Boolean): Boolean;
begin
  Result := True;
  OwnerListview.DoColumnSelectionChanging(Self, Result)
end;

function TEasyColumn.CanChangeVisibility(NewValue: Boolean): Boolean;
begin
  Result := True;
  OwnerListview.DoColumnVisibilityChanging(Self, Result)
end;

function TEasyColumn.DefaultImageList(ImageSize: TEasyImageSize): TCustomImageList;
begin
  Result := OwnerListview.Header.Images
end;

function TEasyColumn.EditAreaHitPt(ViewportPoint: TPoint): Boolean;
begin
  Result := View.EditAreaHitPt(Self, ViewportPoint)
end;

function TEasyColumn.GetAlignment: TAlignment;
begin
  Result := FAlignment
end;

function TEasyColumn.GetColor: TColor;
begin
  Result := PaintInfo.Color
end;

function TEasyColumn.GetDefaultViewClass: TEasyViewColumnClass;
begin
  Result := TEasyViewColumn
end;

function TEasyColumn.GetHotTrack: Boolean;
begin
  Result := PaintInfo.HotTrack
end;

function TEasyColumn.GetImagePosition: TEasyHeaderImagePosition;
begin
  Result := PaintInfo.ImagePosition
end;

function TEasyColumn.GetOwnerColumns: TEasyColumns;
begin
  Result := TEasyColumns(Collection)
end;

function TEasyColumn.GetOwnerHeader: TEasyHeader;
begin
  Result := OwnerListview.Header
end;

function TEasyColumn.GetPaintInfo: TEasyPaintInfoColumn;
begin
  // Dangerous but necessary.  The TaskPanel has its own PaintInfo to not publish
  // any properites.  It will have a class type error using "is".  The two types
  // are "equal" from a memory foot print so this will work.
  Result := TEasyPaintInfoColumn( inherited PaintInfo)
end;

function TEasyColumn.GetSortGlyphAlign: TEasySortGlyphAlign;
begin
  Result := PaintInfo.SortGlyphAlign
end;

function TEasyColumn.GetSortGlyphIndent: Integer;
begin
  Result := PaintInfo.SortGlyphIndent
end;

function TEasyColumn.GetStyle: TEasyHeaderButtonStyle;
begin
  Result := PaintInfo.Style
end;

function TEasyColumn.GetView: TEasyViewColumn;
begin
  if Assigned(FView) then
  begin
    if ViewClass <> FView.ClassType then
      FreeAndNil(FView)
  end;
  if not Assigned(FView) then
    FView := ViewClass.Create(OwnerListview);
  Result := FView;
end;

function TEasyColumn.GetViewClass: TEasyViewColumnClass;
begin
  Result := nil;
  if Assigned(OwnerListview) then
    OwnerListview.DoColumnCustomView(Self, Result);
  if not Assigned(Result) then
    Result := GetDefaultViewClass
end;

function TEasyColumn.LocalPaintInfo: TEasyPaintInfoBasic;
begin
  Result := OwnerListview.PaintInfoColumn
end;

function TEasyColumn.PaintMouseHovering: Boolean;
begin
  Result := (OwnerListview.Header.HotTrackedColumn = Self) and
      HotTrack and not (OwnerListview.DragManager.Dragging or
      OwnerListview.DragRect.Dragging)
end;

function TEasyColumn.SelectionHit(SelectViewportRect: TRect;
  SelectType: TEasySelectHitType): Boolean;
begin
  Result := View.SelectionHit(Self, SelectViewportRect, SelectType)
end;

function TEasyColumn.SelectionHitPt(ViewportPoint: TPoint;
  SelectType: TEasySelectHitType): Boolean;
begin
  Result := View.SelectionHitPt(Self, ViewportPoint, SelectType)
end;

procedure TEasyColumn.AutoSizeToFit;
var
  iIndex: Integer;
  W, i, j: Integer;
  Group: TEasyGroup;
  Canvas: TCanvas;
  Item: TEasyItem;
  Caption: WideString;
  Size: TSize;
  ImageW, ImageH: Integer;
begin
  iIndex := Index;
  Canvas := OwnerListview.Canvas;
  W := 0;
  for i := 0 to OwnerListview.Groups.Count - 1 do
    for j := 0 to OwnerListview.Groups[i].ItemCount - 1 do
    begin
      Group := OwnerListview.Groups[i];
      Item := Group.Items[j];
      Item.View.LoadTextFont(Item, iIndex, Canvas, False);
      OwnerListview.DoItemPaintText(Item, Index, Canvas);
      Caption := Item.Captions[iIndex];
      Size := TextExtentW(Caption, Canvas);
      Size.cx := Size.cx + {2 * LABEL_MARGIN +} 8;

      Item.View.GetImageSize(Item, Self, ImageW, ImageH, eikNormal);
      if ImageW > 0 then
        Size.cx := Size.cx + ImageW + ImageIndent;
      Item.View.GetImageSize(Item, Self, ImageW, ImageH, eikState);
      if ImageW > 0 then
        Size.cx := Size.cx + ImageW;

      if CheckType <> ectNone then
        Size.cx := Size.cx + CheckIndent + RectWidth(Checks.Bound[CheckSize]);
      if Size.cx > W then
        W := Size.cx;
    end;
  OwnerListview.Groups.BeginUpdate(False);
  try
    Width := W;
    if OwnerListview.Selection.FullRowSelect then
      OwnerListview.Groups.Rebuild(True);
  finally
    OwnerListview.Groups.EndUpdate;
  end
end;

procedure TEasyColumn.Freeing;
begin
  OwnerListview.DoColumnFreeing(Self)
end;

procedure TEasyColumn.GainingBold;
begin
  Invalidate(False)
end;

procedure TEasyColumn.GainingCheck;
begin
  OwnerListview.DoColumnCheckChanged(Self)
end;

procedure TEasyColumn.GainingEnable;
begin
  OwnerListview.DoColumnEnableChanged(Self)
end;

procedure TEasyColumn.GainingFocus;
begin
  OwnerListview.DoColumnFocusChanged(Self)
end;

procedure TEasyColumn.GainingGhosted;
begin
  Invalidate(False)
end;

procedure TEasyColumn.GainingHilight;
begin
  // Unsupported
end;

procedure TEasyColumn.GainingHotTracking(MousePos: TPoint);
begin
  // Unsupported
end;

procedure TEasyColumn.GainingSelection;
begin
  OwnerListview.DoColumnSelectionChanged(Self)
end;

procedure TEasyColumn.GainingVisibility;
begin
  OwnerListview.BeginUpdate;
  OwnerListview.DoColumnVisibilityChanged(Self);
  OwnerListview.EndUpdate(False);
end;

function TEasyColumn.HitTestAt(ViewportPoint: TPoint; var HitInfo: TEasyColumnHitTestInfoSet): Boolean;
var
  RectArray: TEasyRectArrayObject;
  R: TRect;
begin
  HitInfo := [];
  if Assigned(View) then
  begin
    View.ItemRectArray(Self, RectArray);
    R := RectArray.IconRect;
    // Make the blank area between the image and text part of the image
    R.Right := R.Right + OwnerListview.PaintInfoColumn.CaptionIndent;
    if PtInRect(R, ViewportPoint) then
      Include(HitInfo, ectOnText);
    if PtInRect(RectArray.IconRect, ViewportPoint) then
      Include(HitInfo, ectOnIcon);
    if PtInRect(RectArray.CheckRect, ViewportPoint) then
      Include(HitInfo, ectOnCheckbox);
    if PtInRect(RectArray.LabelRect, ViewportPoint) then
      Include(HitInfo, ectOnLabel);
  end;
  Result := HitInfo <> [];
end;

procedure TEasyColumn.Initialize;
begin
  OwnerListview.DoColumnInitialize(Self)
end;

procedure TEasyColumn.Invalidate(ImmediateUpdate: Boolean);
var
  R: TRect;
begin
  if OwnerListview.UpdateCount = 0 then
  begin
    if OwnerListview.HandleAllocated then
    begin
      R := Rect(0, 0, OwnerListview.ClientWidth, OwnerHeader.Height);
      OwnerListview.SafeInvalidateRect(@R, ImmediateUpdate);
    end
  end
end;

procedure TEasyColumn.LoadFromStream(S: TStream; var Version: Integer);
begin
  inherited LoadFromStream(S, Version);
  S.ReadBuffer(FAlignment, SizeOf(FAlignment));
  S.ReadBuffer(FAutoSizeOnDblClk, SizeOf(FAutoSizeOnDblClk));
  if Version > 1 then
    S.ReadBuffer(FAutoSortOnClick, SizeOf(FAutoSortOnClick));
  S.ReadBuffer(FAutoSpring, SizeOf(FAutoSpring));
  S.ReadBuffer(FAutoToggleSortGlyph, SizeOf(FAutoToggleSortGlyph));
  S.ReadBuffer(FPosition, SizeOf(FPosition));
  S.ReadBuffer(FSortDirection, SizeOf(FSortDirection));
  S.ReadBuffer(FStyle, SizeOf(FStyle));
  S.ReadBuffer(FClickable, SizeOf(FClickable));
  S.ReadBuffer(FWidth, SizeOf(FWidth));
  OwnerListview.DoColumnLoadFromStream(Self, S, Version);

  // For new objects test the stream version first
  // if Version > X then
  // begin
  //   ReadStream....
  // end
end;

procedure TEasyColumn.LosingBold;
begin
  Invalidate(False)
end;

procedure TEasyColumn.LosingCheck;
begin
  OwnerListview.DoColumnCheckChanged(Self)
end;

procedure TEasyColumn.LosingEnable;
begin
  OwnerListview.DoColumnEnableChanged(Self)
end;

procedure TEasyColumn.LosingFocus;
begin
  OwnerListview.DoColumnFocusChanged(Self)
end;

procedure TEasyColumn.LosingGhosted;
begin
  Invalidate(False)
end;

procedure TEasyColumn.LosingHilight;
begin
  // Unsupported
end;

procedure TEasyColumn.LosingHotTracking;
begin
  // Unsupported
end;

procedure TEasyColumn.LosingSelection;
begin
  OwnerListview.DoColumnSelectionChanged(Self)
end;

procedure TEasyColumn.LosingVisibility;
begin
  OwnerListview.BeginUpdate;
  OwnerListview.DoColumnVisibilityChanged(Self);
  OwnerListview.EndUpdate(False);
end;

procedure TEasyColumn.MakeVisible(Position: TEasyMakeVisiblePos);
var
  RectArray: TEasyRectArrayObject;
  ViewRect: TRect;
  ColumnW, ViewW: Integer;
begin
  if Visible then
  begin
    View.ItemRectArray(Self, RectArray);
    ViewRect := OwnerListview.ClientInViewportCoords;
    ColumnW := RectArray.BoundsRect.Right - RectArray.BoundsRect.Left;
    ViewW := RectWidth(ViewRect);
    if not ((RectArray.BoundsRect.Left >= ViewRect.Left) and ( RectArray.BoundsRect.Right <= ViewRect.Right)) then
    begin
      case Position of
        emvTop:     // or Left
          begin
            if ViewRect.Left < RectArray.BoundsRect.Left then
              OwnerListview.Scrollbars.OffsetX := RectArray.BoundsRect.Left;
          end;
        emvMiddle:
          begin
            if ColumnW < ViewW then
            OwnerListview.Scrollbars.OffsetX := RectArray.BoundsRect.Left - ((ViewW - ColumnW) div 2);
          end;
        emvBottom:  // or right
          begin
            OwnerListview.Scrollbars.OffsetX := RectArray.BoundsRect.Right - ColumnW;
          end;
        emvAuto:
          begin
            if ViewRect.Left < RectArray.BoundsRect.Left then
              OwnerListview.Scrollbars.OffsetX := RectArray.BoundsRect.Left;
          end
      end
    end
  end
end;

procedure TEasyColumn.Paint(ACanvas: TCanvas; HeaderType: TEasyHeaderType);
begin
  View.Paint(Self, ACanvas, HeaderType)
end;

procedure TEasyColumn.SaveToStream(S: TStream; Version: Integer = EASYLISTVIEW_STREAM_VERSION);
begin
  inherited SaveToStream(S);
  S.WriteBuffer(FAlignment, SizeOf(FAlignment));
  S.WriteBuffer(FAutoSizeOnDblClk, SizeOf(FAutoSizeOnDblClk));
  S.WriteBuffer(FAutoSortOnClick, SizeOf(FAutoSortOnClick));
  S.WriteBuffer(FAutoSpring, SizeOf(FAutoSpring));
  S.WriteBuffer(FAutoToggleSortGlyph, SizeOf(FAutoToggleSortGlyph));
  S.WriteBuffer(FPosition, SizeOf(FPosition));
  S.WriteBuffer(FSortDirection, SizeOf(FSortDirection));
  S.WriteBuffer(FStyle, SizeOf(FStyle));
  S.WriteBuffer(FClickable, SizeOf(FClickable));
  S.WriteBuffer(FWidth, SizeOf(FWidth));
  OwnerListview.DoColumnSaveToStream(Self, S, Version);

  // For new objects test the stream version first
  // if Version > X then
  // begin
  //   WriteStream....
  // end
end;

procedure TEasyColumn.SetAlignment(Value: TAlignment);
begin
  if Value <> FAlignment then
  begin
    FAlignment := Value;
    Invalidate(False);
  end
end;

procedure TEasyColumn.SetAutoSpring(const Value: Boolean);
begin
  FAutoSpring := Value;
end;

procedure TEasyColumn.SetColor(Value: TColor);
begin
  PaintInfo.Color := Value
end;

procedure TEasyColumn.SetHotTrack(Value: Boolean);
begin
  PaintInfo.HotTrack := Value
end;

procedure TEasyColumn.SetImagePosition(Value: TEasyHeaderImagePosition);
begin
  PaintInfo.ImagePosition := Value
end;

procedure TEasyColumn.SetPaintInfo(Value: TEasyPaintInfoColumn);
begin
  inherited PaintInfo := Value
end;

procedure TEasyColumn.SetPosition(Value: Integer);
var
  OldPos, i: Integer;
begin
  if Value > OwnerColumns.Count - 1 then
    Value := OwnerColumns.Count - 1
  else
  if Value < 0 then
    Value := 0;

  if OwnerColumns.Count = 1 then
    FPosition := 0
  else begin
    if Value <> FPosition then
    begin
      OldPos := FPosition;
      if Value > OldPos then
      begin
        for i := 0 to OwnerColumns.Count - 1 do
        begin
          if (OwnerColumns[i].FPosition >= OldPos) and (OwnerColumns[i].FPosition <= Value) then
            Dec(OwnerColumns[i].FPosition)
        end
      end else
      begin
        for i := 0 to OwnerColumns.Count - 1 do
        begin
          if (OwnerColumns[i].FPosition >= Value) and (OwnerColumns[i].FPosition <= OldPos) then
            Inc(OwnerColumns[i].FPosition)
        end
      end;     
      FPosition := Value;
    end;
    OwnerHeader.Rebuild(False);
    OwnerHeader.Invalidate(False)
  end
end;

procedure TEasyColumn.SetSortDirection(Value: TEasySortDirection);
begin
  if Value <> FSortDirection then
  begin
    FSortDirection := Value;
    OwnerHeader.Invalidate(False);
    if FSortDirection <> esdNone then
    begin
      OwnerListview.Selection.FocusedColumn := Self;
      if OwnerListview.Sort.AutoSort then
        OwnerListview.Sort.SortAll 
    end
  end
end;

procedure TEasyColumn.SetSortGlpyhAlign(Value: TEasySortGlyphAlign);
begin
  PaintInfo.SortGlyphAlign := Value
end;

procedure TEasyColumn.SetSortGlyphIndent(Value: Integer);
begin
  PaintInfo.SortGlyphIndent := Value
end;

procedure TEasyColumn.SetStyle(Value: TEasyHeaderButtonStyle);
begin
  if FStyle <> Value then
  begin
    FStyle := Value;
    Invalidate(False)
  end
end;

procedure TEasyColumn.SetWidth(Value: Integer);
var
  R: TRect;
begin
  if FWidth <> Value then
  begin
    if Value < 0 then
      Value := 0;
    FWidth := Value;
    OwnerHeader.Rebuild(False);
    if OwnerListview.View = elsReport then
    begin
  //    if OwnerHeader.ViewRect.Right > OwnerListview.ClientWidth then
      begin
        R := OwnerListview.Scrollbars.ViewRect;
        R.Right := OwnerHeader.ViewRect.Right;
        OwnerListview.Scrollbars.SetViewRect(R, True)
      end;
    end
  end
end;

{ TEasyEditManager }

function TEasyEditManager.GetEditing: Boolean;
begin
  Result := FEditing or Assigned(TabMoveFocusItem);
end;

procedure TEasyEditManager.BeginEdit(Item: TEasyItem; Column: TEasyColumn);
//
// Starts an editor within the passed Item
// We create a local message loop and break out of it when any of the normal
// conditions are met to stop the edit, such as: Hitting the Escape key,
// scrolling with the wheel, clicking out side of the editor and switching
// away from the application.
//
var
  Msg: TMSG;
  Pt: TPoint;
  Allow, Dispatch, EditDone: Boolean;
  NCHit: Longword;
  OldFocus: TWinControl;
  iColumn: Integer;

    procedure FinishEdit;
    begin
      if not EditDone then
      begin
        Editor.Hide;
        FEditing := False;
        OwnerListview.DoItemEditEnd(Item);
        Editor.Finalize;
        FEditItem := nil;
        FEditFinished := True;
        EditDone := True
      end
    end;

    function TestAcceptEdit: Boolean;
    begin
      Result := Editor.AcceptEdit;
      if not Result then
        Editor.SetEditorFocus
    end;

begin
  if not Editing and not (csDestroying in OwnerListview.ComponentState) then
  begin
    EditDone := False;
    FEditFinished := False;
    if Assigned(Item) and Enabled then
    begin
      Item.MakeVisible(emvAuto);
      FEditItem := Item;
      Allow := True;
      if Assigned(Column) then
        iColumn := Column.Index
      else
        iColumn := 0;
      OwnerListview.DoItemEditBegin(Item, iColumn, Allow);
      if (OwnerListview.View = elsReport) and (iColumn < OwnerListview.Header.Columns.Count) then
      begin
        OwnerListview.Header.Columns[iColumn].MakeVisible(emvAuto);
      end else
        iColumn := 0;
      if OwnerListview.Header.Columns.Count > iColumn then
        EditColumn := OwnerListview.Header.Columns[iColumn]
      else
        EditColumn := nil;
      if Allow then
      begin
        Application.HookMainWindow(MainWindowHook);
        AppHooked := True;
        Editor := nil;
        OwnerListview.DoItemCreateEditor(Item, FEditor);
        if Assigned(Editor) then
        begin
          OldFocus := Screen.ActiveControl;
          Editor.Initialize(Item, EditColumn);
          FEditing := True;
          Item.Invalidate(True);
          Editor.Show;
          Editor.SetEditorFocus;
          try
            while not FEditFinished do
            begin
              GetMessage(Msg, 0, 0, 0);

              Dispatch := True;

              if Msg.message = WM_SETFOCUS then
                beep;

              if not FEditFinished then
              begin
                if Msg.message = WM_KEYDOWN then
                begin
                  if Msg.wParam = VK_ESCAPE then
                    FinishEdit
                end
              end;

              // Need to pass the tab to the lsitview if TabMoveFocus is enabled
              if Msg.message = WM_KEYDOWN then
              begin
                // This key combo selects all text in the interface objects, don't allow it to beep
                if ((GetKeyState(VK_CONTROL) and $8000 <> 0) and ((Msg.wParam = Ord('a')) or (Msg.wParam = Ord('A')))) then
                begin
                 Editor.SelectAll;
                 Dispatch := False;
                end;


                if (Msg.wParam = VK_TAB) and OwnerListview.EditManager.TabMoveFocus then
                begin
                  if TestAcceptEdit then
                  begin
                    if GetAsyncKeyState(VK_CONTROL) and $8000 <> 0 then
                    begin
                      if OwnerListview.EditManager.TabEditColumns then
                      begin
                        OwnerListview.EditManager.TabMoveFocusColumn := OwnerListview.Header.PrevVisibleColumn(EditColumn);
                        // Stay on the same item if there is another column
                        if Assigned(OwnerListview.EditManager.TabMoveFocusColumn) then
                          OwnerListview.EditManager.TabMoveFocusItem := OwnerListview.EditManager.EditItem
                        else begin
                          OwnerListview.EditManager.TabMoveFocusItem := OwnerListview.Groups.PrevEditableItem(OwnerListview.EditManager.EditItem);
                          OwnerListview.EditManager.TabMoveFocusColumn := OwnerListview.Header.LastColumn;
                        end
                      end else
                        OwnerListview.EditManager.TabMoveFocusItem := OwnerListview.Groups.PrevEditableItem(OwnerListview.EditManager.EditItem);
                    end else
                    begin
                      if OwnerListview.EditManager.TabEditColumns then
                      begin
                        OwnerListview.EditManager.TabMoveFocusColumn := OwnerListview.Header.NextVisibleColumn(EditColumn);
                        // Stay on the same item if there is another column
                        if Assigned(OwnerListview.EditManager.TabMoveFocusColumn) then
                          OwnerListview.EditManager.TabMoveFocusItem := OwnerListview.EditManager.EditItem
                        else
                          OwnerListview.EditManager.TabMoveFocusItem := OwnerListview.Groups.NextEditableItem(OwnerListview.EditManager.EditItem);
                      end else
                        OwnerListview.EditManager.TabMoveFocusItem := OwnerListview.Groups.NextEditableItem(OwnerListview.EditManager.EditItem);
                    end;
                    EndEdit;
                    PostMessage(OwnerListview.Handle, WM_TABMOVEFOCUS, 0, 0);
                    Dispatch := False;
                  end
                end
              end;

              if not FEditFinished then
              if (Msg.message = WM_LBUTTONDOWN) or
                 (Msg.message = WM_MBUTTONDOWN) or
                 (Msg.message = WM_RBUTTONDOWN) then
              begin
                Pt := SmallPointToPoint(TSmallPoint(Msg.lParam));
                ClientToScreen(Msg.hwnd, Pt);
                ScreenToClient(OwnerListview.Handle, Pt);
                if not Editor.PtInEditControl(Pt) then
                begin
                  Dispatch := False;
                  if TestAcceptEdit then
                  begin
                    FinishEdit;
                    // Allow the default click action to occur after canceling the edit.
                    Dispatch := True
                  end
                end;
              end;

              if not FEditFinished then
              begin
                if (Msg.message = WM_MOUSEWHEEL) or
                   (Msg.message = CM_MOUSEWHEEL) then
                begin
                  FinishEdit
                end
              end;

              if not FEditFinished then
              if (Msg.message = WM_NCLBUTTONDOWN) or
                 (Msg.message = WM_NCMBUTTONDOWN) or
                 (Msg.message = WM_NCRBUTTONDOWN) then
              begin
                NCHit := SendMessage(Msg.hWnd, WM_NCHITTEST, Msg.wParam, Msg.lParam);
                if not (NCHit = HTCAPTION) then
                begin
                   FinishEdit;
                end
              end;

              if not FEditFinished then
              begin
                if (Msg.message = WM_HOOKAPPACTIVATE) then
                begin
                  FinishEdit
                end
              end;

              if Dispatch then
              begin
                TranslateMessage(Msg);
                DispatchMessage(Msg);
              end;
            end
          finally
            if AppHooked then
              Application.UnhookMainWindow(MainWindowHook);
            AppHooked := False;
            FinishEdit;
            OldFocus.SetFocus;
            Editor := nil;
            EditColumn := nil;
          end
        end;
      end
    end
  end;
end;

constructor TEasyEditManager.Create(AnOwner: TCustomEasyListview);
begin
  inherited;
  FFont := TFont.Create;
  Color := clWindow;
  Font.Assign(AnOwner.Font);
  AutoEditDelayTime := 300;
end;

destructor TEasyEditManager.Destroy;
begin
  FreeAndNil(FFont);
  inherited;
end;

procedure TEasyEditManager.EndEdit;
//
// Flags the Edit Manager to stop editing.  The local message loop in BeginEdit
// alway polls this flag to quit
//
begin
  StopAutoEditTimer;
  if AppHooked and not (csDestroying in OwnerListview.ComponentState) then
    Application.UnhookMainWindow(MainWindowHook);
  AppHooked := False;
  FEditFinished := True
end;

function TEasyEditManager.MainWindowHook(var Message: TMessage): Boolean;
//
// Need to hook the Main so we can end the edit if the application is switched
// away from
//
begin
  Result := False;
  if Assigned(OwnerListview) then
  begin
    if (Message.Msg = WM_ACTIVATEAPP) and OwnerListview.HandleAllocated then
      PostMessage(OwnerListview.Handle, WM_HOOKAPPACTIVATE, 0, 0)
  end
end;

procedure TEasyEditManager.SetEnabled(const Value: Boolean);
begin
  FEnabled := Value;
  EndEdit;
end;

procedure TEasyEditManager.SetFont(const Value: TFont);
begin
  Font.Assign(Value)
end;

procedure TEasyEditManager.StartAutoEditTimer;
//
// Starts the AutoEditTimer, the timer will be cancelled if the user moves the
// mouse a specified distance before the timer elapses
//
begin
  StopAutoEditTimer;
  if Enabled then
  begin
    Timer := TTimer.Create(nil);
    Timer.OnTimer := TimerEvent;
    Timer.Interval := AutoEditDelayTime;
    Timer.Enabled := True;
    FTimerRunning := True
  end
end;

procedure TEasyEditManager.TimerEvent(Sender: TObject);
//
// The Timer Event method.  If called the Focused Item will be edited.
//
begin
  StopAutoEditTimer;
  if Assigned(OwnerListview.Selection.FocusedItem) then
    BeginEdit(OwnerListview.Selection.FocusedItem as TEasyItem, nil)
end;

procedure TEasyEditManager.StopAutoEditTimer;
//
// Shuts down the AutoEditTimer
//
begin
  if TimerRunning then
  begin
    if Assigned(Timer) then
      FreeAndNil(FTimer);
    FTimerRunning := False
  end
end;

{ TEasyEnumFormatEtcManager }

function TEasyEnumFormatEtcManager.Clone(out Enum: IEnumFormatEtc): HResult;
// Creates a exact copy of the current object.
var
  EnumFormatEtc: TEasyEnumFormatEtcManager;
begin
  Result := S_OK;                              // Think positive
  EnumFormatEtc := TEasyEnumFormatEtcManager.Create;      // Does not increase COM reference
  if Assigned(EnumFormatEtc) then
  begin
    SetLength(EnumFormatEtc.FFormats, Length(Formats));

    // Make copy of Format info
    Move(FFormats[0], EnumFormatEtc.FFormats[0], Length(Formats) * SizeOf(TFormatEtc));

    // Set COM reference to 1
    Enum := EnumFormatEtc as IEnumFormatEtc;
  end else
    Result := E_UNEXPECTED
end;

constructor TEasyEnumFormatEtcManager.Create;
begin
  inherited Create;
  InternalIndex := 0;
end;

destructor TEasyEnumFormatEtcManager.Destroy;
begin
  inherited;
end;

function TEasyEnumFormatEtcManager.Next(celt: Integer; out elt;
  pceltFetched: PLongint): HResult;
// Another EnumXXXX function.  This function returns the number of objects
// requested by the caller in celt.  The return buffer, elt, is a pointer to an}
// array of, in this case, TFormatEtc structures.  The total number of
// structures returned is placed in pceltFetched.  pceltFetched may be nil if
// celt is only asking for one structure at a time.
var
  i: integer;
begin
  if Assigned(Formats) then
  begin
    i := 0;
    while (i < celt) and (InternalIndex < Length(Formats)) do
    begin
      TeltArray( elt)[i] := Formats[InternalIndex];
      inc(i);
      inc(FInternalIndex);
    end; // while
    if assigned(pceltFetched) then
      pceltFetched^ := i;
    if i = celt then
      Result := S_OK
    else
      Result := S_FALSE
  end else
    Result := E_UNEXPECTED
end;

function TEasyEnumFormatEtcManager.Reset: HResult;
begin
  InternalIndex := 0;
  Result := S_OK
end;

function TEasyEnumFormatEtcManager.Skip(celt: Integer): HResult;
// Allows the caller to skip over unwanted TFormatEtc structures.  Simply adds
// celt to the index as long as it does not skip past the last structure in
// the list.
begin
  if Assigned(Formats) then
  begin
    if InternalIndex + celt < Length(Formats) then
    begin
      InternalIndex := InternalIndex + celt;
      Result := S_OK
    end else
      Result := S_FALSE
  end else
    Result := E_UNEXPECTED
end;

procedure TEasyDataObjectManager.DoGetCustomFormats(var Formats: TFormatEtcArray);
begin
  if Assigned(Listview) then
    Listview.DoOLEGetCustomFormats(Formats);
end;

procedure TEasyDataObjectManager.DoOnGetData(const FormatEtcIn: TFormatEtc; var Medium: TStgMedium; var Handled: Boolean);
begin
  if Assigned(Listview) then
    Listview.DoOLEGetData(FormatEtcIn, Medium, Handled);
end;

procedure TEasyDataObjectManager.DoOnQueryGetData(const FormatEtcIn: TFormatEtc; var FormatAvailable: Boolean; var Handled: Boolean);
begin
  if Assigned(Listview) then
    Listview.DoQueryOLEData(FormatEtcIn, FormatAvailable, Handled);
end;

procedure TEasyOLEDragManager.ClearDragItem;
begin
  FDragItem := nil
end;

procedure TEasyOLEDragManager.ClearDropTarget;
begin
  // Unhilight the current Drop Target
  if Assigned(DropTarget) then
  begin
    DropTarget.Hilighted := False;
    OwnerListview.Groups.InvalidateItem(DropTarget, True);
  end;
  FDropTarget := nil;
end;

constructor TEasyOLEDragManager.Create(AnOwner: TCustomEasyListview);
begin
  inherited;
  HilightDropTarget := True
end;

destructor TEasyOLEDragManager.Destroy;
begin
  Registered := False;
  inherited;
end;

procedure TEasyOLEDragManager.DefaultImage(Sender: TCustomEasyListview; Image: TBitmap; DragStartPt: TPoint; var HotSpot: TPoint; var TransparentColor: TColor; var Handled: Boolean);
var
  R: TRect;
  Bits: TBitmap;
begin
  if Assigned(Image) then
  begin
    TransparentColor := OwnerListview.Color;
    R := Rect(DragStartPt.X - (Image.Width div 2),
              DragStartPt.Y - (Image.Height div 2),
              DragStartPt.X + (Image.Width div 2),
              DragStartPt.Y + (Image.Height div 2));

    HotSpot.X := (R.Right - R.Left) div 2;
    HotSpot.Y := (R.Bottom - R.Top) div 2;

    if R.Left < 0 then
      HotSpot.X := HotSpot.X + R.Left;
    if R.Top < 0 then
      HotSpot.Y := HotSpot.Y + R.Top;

    IntersectRect(R, R, OwnerListview.ClientRect);
    Bits := TBitmap.Create;
    try
      Bits.PixelFormat := pf32Bit;
      Bits.Width := Sender.Width;
      Bits.Height := Sender.Height;
      Bits.Canvas.Brush.Color := OwnerListview.Color;
      Bits.Canvas.FillRect(Rect(0, 0, Sender.Width, Sender.Height));
      OwnerListview.DoPaintRect(Bits.Canvas, R, True);
      BitBlt(Image.Canvas.Handle, 0, 0, Image.Width, Image.Height, Bits.Canvas.Handle, R.Left, R.Top, SRCCOPY);
    finally
      Bits.Free;
    end;
    Handled := True;
  end  
end;

procedure TEasyOLEDragManager.DoDrag(Canvas: TCanvas; WindowPoint: TPoint; KeyState: TCommonKeyStates; var Effects: TCommonDropEffect);
var
  ViewPortPoint: TPoint;
  Item: TEasyItem;
begin
  if OwnerListview.IsHeaderMouseMsg(PointToSmallPoint(WindowPoint), True) then
    Effects := cdeNone
  else begin
    ViewPortPoint := OwnerListview.Scrollbars.MapWindowToView(WindowPoint);
    Item := OwnerListview.Groups.ItemByPoint(ViewportPoint);
    if Assigned(Item) then
    begin
      if Item.SelectionHitPt(ViewportPoint, eshtClickSelect) then
      begin
        if Item <> DropTarget then
          ClearDropTarget;
        FDropTarget := Item;
        if Assigned(DropTarget) then
        begin
          if HilightDropTarget then
            Item.Hilighted := True;
          OwnerListview.Groups.InvalidateItem(Item, True)
        end
      end else
        ClearDropTarget
    end else
      ClearDropTarget
  end
end;

procedure TEasyOLEDragManager.DoDragDrop(WindowPoint: TPoint; KeyState: TCommonKeyStates; var Effects: TCommonDropEffect);
begin
  ClearDragItem;
  ClearDropTarget;
  FDragTarget := False;
end;

procedure TEasyOLEDragManager.DoDragEnd(Canvas: TCanvas; WindowPoint: TPoint; KeyState: TCommonKeyStates);
begin
  ClearDropTarget;
  FDragTarget := False;
end;

procedure TEasyOLEDragManager.DoDragEnter(const DataObject: IDataObject; Canvas: TCanvas; WindowPoint: TPoint; KeyState: TCommonKeyStates; var Effects: TCommonDropEffect);
begin
  FDragTarget := True;
end;

procedure TEasyOLEDragManager.DoGetDragImage(Bitmap: TBitmap; DragStartPt: TPoint; var HotSpot: TPoint; var TransparentColor: TColor; var Handled: Boolean);
begin
  OwnerListview.DoGetDragImage(Bitmap, DragStartPt, HotSpot, TransparentColor, Handled);
end;

procedure TEasyOLEDragManager.DoOLEDragEnd(const ADataObject: IDataObject; DragResult: TCommonOLEDragResult; ResultEffect: TCommonDropEffects; KeyStates: TCommonKeyStates);
begin
  OwnerListview.DoOLEDragEnd(ADataObject, DragResult, ResultEffect, KeyStates);
  DataObject := nil;
end;

procedure TEasyOLEDragManager.DoOLEDragStart(const ADataObject: IDataObject; var AvailableEffects: TCommonDropEffects; var AllowDrag: Boolean);
begin
  OwnerListview.DoOLEDragStart(ADataObject, AvailableEffects, AllowDrag);
end;

function TEasyOLEDragManager.DoPtInAutoScrollDownRegion(WindowPoint: TPoint): Boolean;
begin
  Result := WindowPoint.Y > OwnerListview.ClientHeight - AutoScrollMargin
end;

function TEasyOLEDragManager.DoPtInAutoScrollUpRegion(WindowPoint: TPoint): Boolean;
begin
  Result := WindowPoint.Y - OwnerListview.Header.RuntimeHeight < AutoScrollMargin
end;

procedure TEasyOLEDragManager.FinalizeDrag(KeyState: TCommonKeyStates);
// Does not mean the action actually occured it means that InitializeDrag was
// called and this is it matching call.  EndDrag means that the drag actually
// occured
begin
  inherited;
  ClearDragItem;
end;

function TEasyOLEDragManager.InitializeDrag(HitItem: TEasyItem; WindowPoint: TPoint;
  KeyState: TCommonKeyStates): Boolean;
// Does not mean that the action will be a Drag, it just means get ready for one
// just in case.
begin
  Result := False;
  if Enabled then
  begin
    if Assigned(HitItem) and ( ((cksLButton in KeyState) and (cmbLeft in MouseButton)) or
       ((cksMButton in KeyState) and (cmbMiddle in MouseButton)) or
       ((cksRButton in KeyState) and (cmbRight in MouseButton))) then
    begin
      if HitItem.SelectionHitPt(OwnerListview.Scrollbars.MapWindowToView(WindowPoint), eshtClickSelect) then
      begin
        FDragItem := HitItem;
        Result := True
      end
    end else
      FDragItem := nil
  end
end;

procedure TEasyOLEDragManager.ImageSize(var Width: Integer; var Height: Integer);
begin
  Width := DragImageWidth;
  Height := DragImageHeight
end;

procedure TEasyOLEDragManager.SetEnabled(const Value: Boolean);
begin
  inherited;
  Registered := Value;
end;

procedure TEasyOLEDragManager.VCLDragStart;
begin
  FDragItem := OwnerListview.Selection.FocusedItem;
  if DragMode = dmAutomatic then
    OwnerListview.BeginDrag(True);
end;

{ TEasySelectionManager }

function TEasySelectionManager.GetFocusedColumn: TEasyColumn;
begin
  if not Assigned(FFocusedColumn) then
    FFocusedColumn := OwnerListview.Header.FirstColumn;
  Result := FFocusedColumn;
end;

function TEasySelectionManager.SelectedToArray: TEasyItemArray;
var
  Item: TEasyItem;
  i: Integer;
begin
  SetLength(Result, Count);
  Item := First;
  i := 0;
  while Assigned(Item) do
  begin
    Result[i] := Item;
    Inc(i);
    Item := Next(Item)
  end
end;

procedure TEasySelectionManager.ActOnAll(SelectType: TEasySelectionType;
  ExceptItem: TEasyItem);
var
  i, j: Integer;
begin
  if Enabled then
  begin
    IncMultiChangeCount;
    GroupSelectBeginUpdate;
    try
      // If unselecting we can optimize for the more common situation by stopping when
      // there are no more selected items.
      if SelectType = ecstUnSelect then
      begin
        i := 0;
        while (i < OwnerListview.Groups.Count) and (Count > 0) do
        begin
          j := 0;
          while (j < OwnerListview.Groups[i].Items.Count) and (Count > 0) do
          begin
            if ExceptItem <> OwnerListview.Groups[i].Items[j] then
              OwnerListview.Groups[i].Items[j].Selected := False;
            Inc(j)
          end;
          Inc(i)
        end
      end else
      begin
        for i := 0 to OwnerListview.Groups.Count - 1 do
        begin
          case SelectType of
            ecstSelect:
              begin
                j := 0;
                while (j < OwnerListview.Groups[i].Items.Count) do
                begin
                  if ExceptItem <> OwnerListview.Groups[i].Items[j] then
                    OwnerListview.Groups[i].Items[j].Selected := True;
                  Inc(j)
                end;
              end;
            ecstInvert:
              begin
                j := 0;
                while (j < OwnerListview.Groups[i].Items.Count) do
                begin
                  if ExceptItem <> OwnerListview.Groups[i].Items[j] then
                    OwnerListview.Groups[i].Items[j].Selected := not OwnerListview.Groups[i].Items[j].Selected;
                  Inc(j)
                end;
              end;
          end
        end
      end;
      OwnerListview.DragManager.ClearDragItem;
    finally
      GroupSelectEndUpdate;
      DecMultiChangeCount
    end
  end
end;

procedure TEasySelectionManager.BuildSelectionGroupings(Force: Boolean);
//
// Builds the necessary information to group the selections like eMule, called
// for every selection change.  This could get slow with thousands of items.
//
var
  i: Integer;
  GroupList: TEasySelectionGroupList;
  Group: TEasyGroup;
  Item, NextItem: TEasyItem;
begin
  // It is possible that during streaming the items can be created but
  if not (csLoading in OwnerListview.ComponentState) and
    (Force or (GroupSelections and (FGroupSelectUpdateCount = 0){ and (OwnerListview.UpdateCount = 0) won't work with keyboard})) then
  begin
 //   Windows.Beep(5000, 200);
    GroupList := nil;
    for i := 0 to OwnerListview.Groups.Count - 1 do
    begin
      Group := TEasyGroup( OwnerListview.Groups.List[i]);
      Item := OwnerListview.Groups.FirstVisibleInGroup(Group);
      while Assigned(Item) do
      begin
        // Do a ReleaseSelectionGroup directly for speed
        if Assigned(Item) and Assigned(Item.SelectionGroup) then
        begin
          Item.FSelectionGroup.DecRef;
          Item.FSelectionGroup := nil
        end;

        if (esosSelected in Item.State) then
        begin
          NextItem := OwnerListview.Groups.NextVisibleInGroup(Group, Item);

          // Do a ReleaseSelectionGroup directly for speed
          if Assigned(NextItem) and Assigned(NextItem.SelectionGroup) then
          begin
            NextItem.FSelectionGroup.DecRef;
            NextItem.FSelectionGroup := nil
         end;

          // Direct access for speed
          while Assigned(NextItem) and (esosSelected in NextItem.State) do
          begin
            if not Assigned(GroupList) then
            begin
              GroupList := TEasySelectionGroupList.Create;
              GroupList.Add(Item);
              GroupList.IncRef;
              GroupList.DisplayRect := Item.DisplayRect;
              GroupList.FirstItem := Item;
              Item.SelectionGroup := GroupList;
            end;
            GroupList.Add(NextItem);
            UnionRect(Item.SelectionGroup.FDisplayRect, Item.SelectionGroup.DisplayRect, NextItem.DisplayRect);
            GroupList.IncRef;
            NextItem.SelectionGroup := GroupList;
            NextItem := OwnerListview.Groups.NextVisibleInGroup(Group, NextItem);
          end;
          GroupList := nil;
          Item := NextItem;
        end;
        Item := OwnerListview.Groups.NextVisibleInGroup(Group, Item);
      end;
    end
  end
end;

procedure TEasySelectionManager.ClearAll;
begin
  ActOnAll(ecstUnSelect, nil);
end;

procedure TEasySelectionManager.ClearAllExcept(Item: TEasyItem);
begin
  ActOnAll(ecstUnSelect, Item);
  OwnerListview.DragManager.ClearDragItem;
end;

constructor TEasySelectionManager.Create(AnOwner: TCustomEasyListview);
begin
  inherited;
  FEnabled := True;
  FColor := clHighlight;
  FBorderColor := clHighlight;
  FInactiveBorderColor := clInactiveBorder;
  FInactiveColor := clInactiveBorder;
  FInactiveTextColor := clBlack;
  FTextColor := clHighlightText;
  FRoundRectRadius := 4;
  FBlendColorSelRect := clHighlight;
  FBorderColorSelRect := clHighlight;
  FBlendColorIcon := clHighlight;
  BlendAlphaImage := 128;
  BlendAlphaSelRect := 70;
  BlendAlphaTextRect := 128;
  FAlphaBlend := False;
  FUseFocusRect := True;
  FMouseButton := [cmbLeft];
  MouseButtonSelRect := [cmbLeft, cmbRight];
  FGradientColorBottom := $FCEFD5;
  FGradientColorTop := $FDF8F1;
  FBlendIcon := True
end;

destructor TEasySelectionManager.Destroy;
begin
  inherited;
end;

procedure TEasySelectionManager.DecMultiChangeCount;
begin
  Dec(FMultiChangeCount);
  if MultiChangeCount <= 0 then
  begin
    MultiChangeCount := 0;
    if ItemsToggled > 0 then
      OwnerListview.DoItemSelectionsChanged
  end
end;

procedure TEasySelectionManager.DeleteSelected(SelectPrevItem: Boolean = False);
var
  NextToSelect: Integer;
  AItem: TEasyItem;
begin
  if Count <> 0 then
  begin
    NextToSelect := -1;
    OwnerListview.BeginUpdate;
    try
      if SelectPrevItem then
        NextToSelect := First.Index - 1;
      OwnerListview.Groups.DeleteItems(SelectedToArray);
    finally
      OwnerListview.EndUpdate;
      if SelectPrevItem and (OwnerListview.Groups.ItemCount > 0) then
      begin
        if NextToSelect < 0 then
          NextToSelect := 0
        else
        if NextToSelect >= OwnerListview.Groups.ItemCount then
          NextToSelect := OwnerListview.Groups.ItemCount - 1;
        AItem := OwnerListview.Items[NextToSelect];
        AItem.Selected := True;
        AItem.Focused := True;
      end
    end
  end
end;

procedure TEasySelectionManager.DragSelect(KeyStates: TCommonKeyStates);
// Handles the selection of items during a drag select operation
var
  CoverR: TRect;
  Item: TEasyItem;
begin
  if Enabled and MultiSelect then
  begin
    IncMultiChangeCount;
    GroupSelectBeginUpdate;
    try
      // Get the rectangle that covers any area that need to be tested for change
      UnionRect(CoverR, OwnerListview.DragRect.PrevRect, OwnerListview.DragRect.SelectionRect);

      if not IsRectEmpty(CoverR) then
      begin
        Item := OwnerListview.Groups.FirstItemInRect(CoverR);
        while Assigned(Item) do
        begin
          if cksControl in KeyStates then
          begin
            if Item.SelectionHit( OwnerListview.DragRect.SelectionRect, eshtDragSelect) xor
               Item.SelectionHit(OwnerListview.DragRect.PrevRect, eshtDragSelect)
            then
              Item.Selected := not Item.Selected;
          end else
          begin
            // First see if we need to select an item
            if Item.SelectionHit(OwnerListview.DragRect.SelectionRect, eshtDragSelect) then
              Item.Selected := True
            else begin
              // If we did not select the item see if it is in the Previous rectanagle
              // and it needs to be unselected
              if Item.SelectionHit(OwnerListview.DragRect.PrevRect, eshtDragSelect) then
                Item.Selected := False;
            end
          end;
          Item := OwnerListview.Groups.NextItemInRect(Item, CoverR)
        end
      end
    finally
      GroupSelectEndUpdate;
      DecMultiChangeCount
    end
  end
end;

function TEasySelectionManager.First: TEasyItem;
//
// Gets the first Selected item
//
var
  Item: TEasyItem;
begin
  Result := nil;
  Item := OwnerListview.Groups.FirstVisibleItem;
  while not Assigned(Result) and Assigned(Item) do
  begin
    if Item.Selected then
      Result := Item;
    Item := OwnerListview.Groups.NextVisibleItem(Item)
  end
end;

function TEasySelectionManager.FirstInGroup(Group: TEasyGroup): TEasyItem;
//
// Gets the first Selected item in the specified group from UIObject list
//
var
  Item: TEasyItem;
begin
  Result := nil;
  Item := OwnerListview.Groups.FirstInGroup(Group);
  while not Assigned(Result) and Assigned(Item) do
  begin
    if Item.Selected then
      Result := Item;
    Item := OwnerListview.Groups.NextInGroup(Group, Item)
  end
end;

procedure TEasySelectionManager.FocusFirst;
var
  Item: TEasyItem;
begin
  ClearAll;
  Item := OwnerListview.Groups.FirstItem;
  if Assigned(Item) and Enabled then
    Item.Focused := True;
end;

procedure TEasySelectionManager.GainingSelection(Item: TEasyItem);
begin
  if not MultiSelect then
    ClearAllExcept(Item);
  Inc(OwnerListview.Selection.FCount);
  if not MultiSelect then
    FocusedItem := Item;
  Inc(FItemsToggled);
  NotifyOwnerListview
end;

function TEasySelectionManager.GetAutoScroll: Boolean;
begin
  Result := OwnerListview.DragRect.AutoScroll
end;

function TEasySelectionManager.GetAutoScrollAccelerator: Byte;
begin
  Result := OwnerListview.DragRect.AutoScrollAccelerator
end;

function TEasySelectionManager.GetAutoScrollDelay: Integer;
begin
  Result := OwnerListview.DragRect.AutoScrollDelay
end;

function TEasySelectionManager.GetAutoScrollMargin: Integer;
begin
  Result := OwnerListview.DragRect.AutoScrollMargin
end;

function TEasySelectionManager.GetAutoScrollTime: Integer;
begin
  Result := OwnerListview.DragRect.AutoScrollTime
end;

function TEasySelectionManager.GetEnableDragSelect: Boolean;
begin
  Result := OwnerListview.DragRect.Enabled
end;

function TEasySelectionManager.GeTCommonMouseButton: TCommonMouseButtons;
begin
  Result := FMouseButton
end;

function TEasySelectionManager.GetMouseButtonSelRect: TCommonMouseButtons;
begin
  Result := OwnerListview.DragRect.MouseButton
end;

function TEasySelectionManager.GetSelecting: Boolean;
begin
  Result := OwnerListview.DragRect.Dragging
end;

procedure TEasySelectionManager.GroupSelectBeginUpdate;
begin
  Inc(FGroupSelectUpdateCount)
end;

procedure TEasySelectionManager.GroupSelectEndUpdate;
begin
  Dec(FGroupSelectUpdateCount);
  if FGroupSelectUpdateCount <= 0 then
  begin
    FGroupSelectUpdateCount := 0;
 //   OwnerListview.Groups.ReIndexItems(nil, True);
    BuildSelectionGroupings(False);     
  end
end;

procedure TEasySelectionManager.IncMultiChangeCount;
begin
  if MultiChangeCount = 0 then
    ItemsToggled := 0;
  Inc(FMultiChangeCount)
end;

procedure TEasySelectionManager.InvalidateVisibleSelected(ValidateWindow: Boolean);
var
  Item: TEasyItem;
begin
  if Enabled then
  begin
    Item := OwnerListview.Groups.FirstItemInRect(OwnerListview.ClientRect);
    while Assigned(Item) do
    begin
      if Item.Selected or Item.Focused then
        Item.Invalidate(False);
      Item := OwnerListview.Groups.NextItemInRect(Item, OwnerListview.ClientRect)
    end
  end
end;

procedure TEasySelectionManager.Invert;
begin
  ActOnAll(ecstInvert, nil);
end;

procedure TEasySelectionManager.LosingSelection(Item: TEasyItem);
begin
  Dec(OwnerListview.Selection.FCount);
  Item.ReleaseSelectionGroup;
  Inc(FItemsToggled);
  NotifyOwnerListview
end;

procedure TEasySelectionManager.NotifyOwnerListview;
begin
  if (MultiChangeCount = 0) and (ItemsToggled > 0) then
  begin
    OwnerListview.DoItemSelectionsChanged;
    ItemsToggled := 0
  end
end;

procedure TEasySelectionManager.SelectAll;
begin
  if MultiSelect then
    ActOnAll(ecstSelect, nil);
end;

function TEasySelectionManager.Next(Item: TEasyItem): TEasyItem;
//
// Gets the next Selected item in the UIObject list
//
begin
  Result := nil;
  Item := OwnerListview.Groups.NextVisibleItem(Item);
  while not Assigned(Result) and Assigned(Item) do
  begin
    if Item.Selected then
      Result := Item;
    Item := OwnerListview.Groups.NextVisibleItem(Item)
  end
end;

function TEasySelectionManager.NextInGroup(Group: TEasyGroup;
  Item: TEasyItem): TEasyItem;
  //
// Gets the next Selected item in the specified group from UIObject list
//
begin
  Result := nil;
  Item := OwnerListview.Groups.NextInGroup(Group, Item);
  while not Assigned(Result) and Assigned(Item) do
  begin
    if Item.Selected then
      Result := Item;
    Item := OwnerListview.Groups.NextInGroup(Group, Item)
  end
end;

procedure TEasySelectionManager.SelectFirst;
var
  Item: TEasyItem;
begin
  ClearAll;
  Item := OwnerListview.Groups.FirstItem;
  if Assigned(Item) and Enabled then
  begin
    Item.Focused := True;
    Item.Selected := True
  end
end;

procedure TEasySelectionManager.SelectGroupItems(Group: TEasyGroup; ClearOtherItems: Boolean);
var
  i, j: Integer;
begin
  if Enabled and MultiSelect and Assigned(Group) then
  begin
    IncMultiChangeCount;
    GroupSelectBeginUpdate;
    try
      if ClearOtherItems then
      begin
        for i := 0 to OwnerListview.Groups.Count - 1 do
        begin
          if OwnerListview.Groups[i] <> Group then
          for j := 0 to OwnerListview.Groups[i].Items.Count - 1 do
            OwnerListview.Groups[i].Items[j].Selected := False;
        end;
      end;

      for i := 0 to Group.Items.Count - 1 do
        Group.Items[i].Selected := True
    finally
      GroupSelectEndUpdate;
      DecMultiChangeCount
    end
  end
end;

procedure TEasySelectionManager.SelectRange(FromItem, ToItem: TEasyItem; RectSelect: Boolean; ClearFirst: Boolean);

    procedure SwapItems(var Item1, Item2: TEasyItem);
    var
      Temp: TEasyItem;
    begin
      Temp := Item1;
      Item1 := Item2;
      Item2 := Temp
    end;

var
  R: TRect;
begin
  if Enabled and MultiSelect then
  begin
    IncMultiChangeCount;
    GroupSelectBeginUpdate;
    try
      if ClearFirst then
        ClearAll;
      if Assigned(FromItem) and Assigned(ToItem) then
      begin
        if RectSelect then
        begin
          UnionRect(R, FromItem.DisplayRect, ToItem.DisplayRect);
          SelectRect(R, False);
        end else
        begin
          if FromItem.OwnerGroup = ToItem.OwnerGroup then
          begin
            if FromItem.Index > ToItem.Index then
              SwapItems(FromItem, ToItem)
          end else
          begin
            if FromItem.OwnerGroup.Index > ToItem.OwnerGroup.Index then
              SwapItems(FromItem, ToItem)
          end;

          while Assigned(FromItem) and (FromItem <> ToItem) do
          begin
            FromItem.Selected := True;
            FromItem := OwnerListview.Groups.NextVisibleItem(FromItem)
          end;
          ToItem.Selected := True;
        end
      end
    finally
      GroupSelectEndUpdate;
      DecMultiChangeCount
    end
  end
end;

procedure TEasySelectionManager.SelectRect(ViewportSelRect: TRect; ClearFirst: Boolean);
var
  Temp: TEasyItem;
begin
  if Enabled and MultiSelect then
  begin
    GroupSelectBeginUpdate;
    IncMultiChangeCount;
    try
      if ClearFirst then
        ClearAll;
      Temp := OwnerListview.Groups.FirstItemInRect(ViewportSelRect);
      while Assigned(Temp) do
      begin
        Temp.Selected := True;
        Temp := OwnerListview.Groups.NextItemInRect(Temp, ViewportSelRect);
      end
    finally
      GroupSelectEndUpdate;
      DecMultiChangeCount
    end
  end
end;

procedure TEasySelectionManager.SetAnchorItem(Value: TEasyItem);
begin
  if Assigned(Value) then
  begin          
    if Value.Visible and Value.Enabled then
      FAnchorItem := Value;
  end else
    FAnchorItem := nil
end;

procedure TEasySelectionManager.SetAutoScroll(Value: Boolean);
begin
  OwnerListview.DragRect.AutoScroll := Value
end;

procedure TEasySelectionManager.SetAutoScrollAccelerator(Value: Byte);
begin
  OwnerListview.DragRect.AutoScrollAccelerator := Value
end;

procedure TEasySelectionManager.SetAutoScrollDelay(Value: Integer);
begin
  OwnerListview.DragRect.AutoScrollDelay := Value
end;

procedure TEasySelectionManager.SetAutoScrollMargin(Value: Integer);
begin
  OwnerListview.DragRect.AutoScrollMargin := Value
end;

procedure TEasySelectionManager.SetAutoScrollTime(Value: Integer);
begin
  OwnerListview.DragRect.AutoScrollTime := Value
end;

procedure TEasySelectionManager.SetBlendIcon(Value: Boolean);
begin
  if FBlendIcon <> Value then
  begin
    FBlendIcon := Value;
    OwnerListview.SafeInvalidateRect(nil, False)
  end
end;

procedure TEasySelectionManager.SetBlurAlphaBkGnd(const Value: Boolean);
begin
  if FBlurAlphaBkGnd <> Value then
  begin
    FBlurAlphaBkGnd := Value;
    OwnerListview.SafeInvalidateRect(nil, False)
  end
end;

procedure TEasySelectionManager.SetEnabled(const Value: Boolean);
begin
  if FEnabled <> Value then
  begin
    if not Value then
      ClearAll;
    FEnabled := Value;
  end;
end;

procedure TEasySelectionManager.SetEnableDragSelect(Value: Boolean);
begin
  OwnerListview.DragRect.Enabled := Value
end;

procedure TEasySelectionManager.SetFocusedColumn(Value: TEasyColumn);
var
  OldFocused: TEasyColumn;
begin
  if FFocusedColumn <> Value then
  begin
    OldFocused := FFocusedColumn;
    FFocusedColumn := Value;
    if Assigned(FFocusedColumn) then
      FFocusedColumn.Invalidate(True);
    if Assigned(OldFocused) then
      OldFocused.Invalidate(True);
  end
end;

procedure TEasySelectionManager.SetFocusedGroup(const Value: TEasyGroup);
//var
//  ChangeFocus: Boolean;
//  OldGroup: TEasyGroup;
begin
//  OldGroup := FFocusedGroup;
(*  if Assigned(Value) then
  begin
    if Value.Visible and Value.Enabled then
    begin
      if FFocusedGroup <> Value then
      begin
        if Assigned(OldGroup) then
          OldGroup.Focused := False;

        ChangeFocus := True;
        // The user may not have allowed the focus to change
        if Assigned(OldGroup) then
          ChangeFocus := not OldGroup.Focused;

        if ChangeFocus then
        begin
          Value.Focused := True;
          FFocusedGroup := Value
        end
      end
    end
  end else
  begin
    FFocusedGroup := nil;
    if Assigned(OldGroup) then
       OldGroup.Focused := False;
  end *)
end;

procedure TEasySelectionManager.SetFocusedItem(Value: TEasyItem);
var
  ChangeFocus: Boolean;
  OldItem: TEasyCollectionItem;
  RectArray: TEasyRectArrayObject;
begin
  OldItem := FFocusedItem;
  if Assigned(Value) then
  begin
    if Value.Visible and Value.Enabled then
    begin
      if FFocusedItem <> Value then
      begin
        if Assigned(OldItem) then
          OldItem.Focused := False;

        ChangeFocus := True;
        // The user may not have allowed the focus to change
        if Assigned(OldItem) then
          ChangeFocus := not OldItem.Focused;

        if ChangeFocus then
        begin
          FFocusedItem := Value;
          // Resize the Groups in case the full focus text was bigger than
          // the group
          Value.Focused := True;
          // During Deletion this can lead to weird recurssion
          if not Value.Destroying then
          begin
            Value.ItemRectArray(nil, OwnerListview.ScratchCanvas, RectArray);
            // Rebuild the grid if the focused text will overlap the bottom of the
            // group
            if RectArray.FullTextRect.Bottom > Value.OwnerGroup.ClientRect.Bottom then
              Value.OwnerListview.Groups.Rebuild(True);
           end
        end
      end
    end
  end else
  begin
    FFocusedItem := nil;
    if Assigned(OldItem) then
    begin
      if Assigned(OldItem) then
      begin
         OldItem.Focused := False;
         // Resize the Groups in case the full focus text was bigger than
         // the group
         // During Deletion this can lead to weird recurssion
         if not OldItem.Destroying then
         begin
  (*        OwnerListview.Grid.CacheGroupInfo;
           OwnerListview.Scrollbars.SetViewRect(OwnerListview.Grid.ViewRect, True); *)
         end
      end
    end
  end
end;

procedure TEasySelectionManager.SeTCommonMouseButton(Value: TCommonMouseButtons);
begin
  FMouseButton := Value
end;

procedure TEasySelectionManager.SetFullCellPaint(Value: Boolean);
begin
  if Value <> FFullCellPaint then
  begin
    FFullCellPaint := Value;
    OwnerListview.SafeInvalidateRect(nil, False)
  end
end;

procedure TEasySelectionManager.SetFullItemPaint(Value: Boolean);
begin
  if Value <> FFullItemPaint then
  begin
    FFullItemPaint := Value;
    OwnerListview.SafeInvalidateRect(nil, False)
  end
end;

procedure TEasySelectionManager.SetGradient(const Value: Boolean);
begin
  if FGradient <> Value then
  begin
    FGradient := Value;
    OwnerListview.SafeInvalidateRect(nil, False)
  end
end;

procedure TEasySelectionManager.SetGradientColorBottom(const Value: TColor);
begin
  if FGradientColorBottom <> Value then
  begin
    FGradientColorBottom := Value;
    OwnerListview.SafeInvalidateRect(nil, False)
  end
end;

procedure TEasySelectionManager.SetGradientColorTop(const Value: TColor);
begin
  if FGradientColorTop <> Value then
  begin
    FGradientColorTop := Value;
    OwnerListview.SafeInvalidateRect(nil, False)
  end
end;

procedure TEasySelectionManager.SetGroupSelections(Value: Boolean);
begin
  if Value <> FGroupSelections then
  begin
    OwnerListview.BeginUpdate;
    try
      FGroupSelections := Value;
      BuildSelectionGroupings(True)
    finally
      OwnerListview.EndUpdate
    end
  end
end;

procedure TEasySelectionManager.SetMouseButtonSelRect(Value: TCommonMouseButtons);
begin
  OwnerListview.DragRect.MouseButton := Value
end;

procedure TEasySelectionManager.SetMultiSelect(const Value: Boolean);
begin
  if FMultiSelect <> Value then
  begin
    FMultiSelect := Value;
    ClearAll
  end
end;

{ TEasyCheckManager }

function TEasyCheckManager.CheckedToArray: TEasyItemArray;
var
  Item: TEasyItem;
  i: Integer;
begin
  SetLength(Result, Count);
  Item := First;
  i := 0;
  while Assigned(Item) do
  begin
    Result[i] := Item;
    Inc(i);
    Item := Next(Item)
  end
end;

procedure TEasyCheckManager.CheckAll;
//
// Checks all the Visible items in the EasyControl
//
var
  i: Integer;
begin
  for i := 0 to OwnerListview.Groups.Count - 1 do
    OwnerListview.Groups.Groups[i].Checked := True
end;

procedure TEasyCheckManager.CheckAllInGroup(Group: TEasyGroup);
//
// Checks all the Visible items in a particular group
//
begin
  Group.Checked := True
end;

function TEasyCheckManager.First: TEasyItem;
//
// Gets the first checked item in the control and prepares for the GetNext iteration
//
//var
//  Found: Boolean;
//  i: Integer;
begin
//  Found := False;
//  i := 0;
(*  while (i < OwnerListview.UIObjects.ItemCount) and not Found do
  begin
    Result := OwnerListview.UIObjects.Items[i];
    Found := Result.Checked;
    if not Found then
      Inc(i)
  end;
  if not Found then  *)
    Result := nil;
end;

function TEasyCheckManager.FirstInGroup(Group: TEasyGroup): TEasyCollectionItem;
//
// Gets the first checked item in a particular grouip and prepares for the GetNextInGroup iteration
//
var
  Found: Boolean;
  i: Integer;
begin
 Result := nil;
 Exit;

  Found := False;
  i := 0;
  while (i < Group.Items.Count) and not Found do
  begin
    Result := Group.Items[i];
    // By definition if the item is checked is must be visible
    Found := Result.Checked;
    if not Found then
      Inc(i)
  end;
  if not Found then
    Result := nil;
end;

function TEasyCheckManager.Next(Item: TEasyItem): TEasyItem;
//
// Gets the next checked item in the control, depends on GetFirst to be called first
//
//var
//  Found: Boolean;
//  i: Integer;
begin
//  Found := False;
(*  i := Item.VisibleIndex;
  Inc(i);
  while (i < OwnerListview.UIObjects.ItemCount) and not Found do
  begin
    Result := OwnerListview.UIObjects.Items[i];
    Found := Result.Checked;
    if not Found then
      Inc(i)
  end;
  if not Found then   *)
    Result := nil;
end;

function TEasyCheckManager.NextInGroup(Item: TEasyItem): TEasyItem;
//
// Gets the next checked item in a particular group, depends on GetFirstInGroup to be called first
//
begin
  Result := nil;
  Assert(True = False, 'TEasyCheckManager.GetNextCheckedInGroup not implemented yet');
  Exit;

end;

procedure TEasyCheckManager.DeleteChecked;
var
  Items: TEasyItemArray;
  i: Integer;
begin
  OwnerListview.BeginUpdate;
  try
    Items := CheckedToArray;
    // Set the item in the list to nil and free the item
    try
      for i := 0 to Length(Items) - 1 do
      begin
        Items[i].OwnerGroup.Items[Items[i].Index] := nil;
        Items[i].Free;
      end;
    finally
      // now pack the group lists to elimiate the nil pointers
      for i := 0 to OwnerListview.Groups.Count - 1 do
        OwnerListview.Groups[i].Items.FList.Pack;
    end;
  finally
    OwnerListview.EndUpdate;
  end
end;

procedure TEasyCheckManager.SetPendingObject(Value: TEasyCollectionItem);
begin
  if Value <> FPendingObject then
  begin
    if Assigned(FPendingObject) then
      FPendingObject.CheckPending := False;
    FPendingObject := Value;
    if Assigned(FPendingObject) then
      FPendingObject.CheckPending := True;
  end
end;

procedure TEasyCheckManager.UnCheckAll;
//
// Unchecks all Visible items in the control
//
var
  i: Integer;
begin
  OwnerListview.BeginUpdate;
  try
    for i := 0 to OwnerListview.Groups.Count - 1 do
      OwnerListview.Groups.Groups[i].Checked := False;
  finally
    OwnerListview.EndUpdate;
  end
end;

procedure TEasyCheckManager.UnCheckAllInGroup(Group: TEasyGroup);
//
// Unchecks all Visible items in a particular group
//
begin
  OwnerListview.BeginUpdate;
  try
    Group.Checked := False
  finally
    OwnerListview.EndUpdate;
  end
end;

{ TEasyScrollbarManager }

constructor TEasyScrollbarManager.Create(AnOwner: TCustomEasyListview);
begin
  inherited;
  FHorzEnabled := True;
  FVertEnabled := True;
  FSnapHorzView := True;
end;

destructor TEasyScrollbarManager.Destroy;
begin
  inherited;
end;

function TEasyScrollbarManager.GetHorzBarVisible: Boolean;
begin
  if Assigned(OwnerListview) and OwnerListview.HandleAllocated then
    Result := GetWindowLong(OwnerListview.Handle, GWL_STYLE) and WS_HSCROLL <> 0
  else
    Result := False;
end;

function TEasyScrollbarManager.GetLine: Integer;
begin
  Result := OwnerListview.Groups.CellHeight
end;

function TEasyScrollbarManager.GetMaxOffsetX: Integer;
var
  ScrollTmp: Integer;
begin
  Result := ViewWidth - OwnerListview.ClientWidth;

  if (not OwnerListview.IsVertView) and (HorzBarVisible) then
  begin
    ScrollTmp := Result - (Result mod OwnerListview.CellSizes.List.Width);
    Result := ScrollTmp + OwnerListview.CellSizes.List.Width;
  end;
  if Result < 0 then
    Result := 0;
end;

function TEasyScrollbarManager.GetMaxOffsetY: Integer;
begin
  Result := ViewHeight -
            OwnerListview.ClientHeight +
            OwnerListview.Header.RuntimeHeight {+
            OwnerListview.Columns.Footer.SizeTotal};

  if Result < 0 then
    Result := 0;
end;

function TEasyScrollbarManager.GetVertBarVisible: Boolean;
begin
  if Assigned(OwnerListview) and OwnerListview.HandleAllocated then
    Result := GetWindowLong(OwnerListview.Handle, GWL_STYLE) and WS_VSCROLL <> 0
  else
    Result := False;
end;

function TEasyScrollbarManager.GetViewHeight: Integer;
begin
  Result := ViewRect.Bottom - ViewRect.Top
end;

function TEasyScrollbarManager.GetViewWidth: Integer;
begin
  Result := ViewRect.Right - ViewRect.Left
end;

function TEasyScrollbarManager.MapViewToWindow(ViewportPoint: TPoint; AccountForHeader: Boolean = True): TPoint;
begin
  Result.X := ViewportPoint.X -
              OffsetX;
  Result.Y := ViewportPoint.Y -
              OffsetY;
  if AccountForHeader then
    Inc(Result.Y, OwnerListview.Header.RuntimeHeight)
end;

function TEasyScrollbarManager.MapViewRectToWindowRect(ViewPortRect: TRect; AccountForHeader: Boolean = True): TRect;
begin
  Result.TopLeft := MapViewToWindow(ViewPortrect.TopLeft, AccountForHeader);
  Result.BottomRight := MapViewToWindow(ViewPortRect.BottomRight, AccountForHeader);
end;

function TEasyScrollbarManager.MapViewToWindow(ViewportPoint: TSmallPoint; AccountForHeader: Boolean = True): TPoint;
begin
  Result := MapViewToWindow(SmallPointToPoint(ViewportPoint), AccountForHeader)
end;

function TEasyScrollbarManager.MapWindowToView(WindowPoint: TPoint; AccountForHeader: Boolean = True): TPoint;
begin
  Result.X := WindowPoint.X + OffsetX;
  Result.Y := WindowPoint.Y + OffsetY;
  if AccountForHeader then
    Dec(Result.Y, OwnerListview.Header.RuntimeHeight)
end;

function TEasyScrollbarManager.MapWindowRectToViewRect(WindowRect: TRect; AccountForHeader: Boolean = True): TRect;
begin
  Result.TopLeft := MapWindowToView(WindowRect.TopLeft, AccountForHeader);
  Result.BottomRight := MapWindowToView(WindowRect.BottomRight, AccountForHeader);
end;

function TEasyScrollbarManager.MapWindowToView(WindowPoint: TSmallPoint; AccountForHeader: Boolean = True): TPoint;
begin
  Result := MapWindowToView(SmallPointToPoint(WindowPoint), AccountForHeader);
end;

procedure TEasyScrollbarManager.ReCalculateScrollbars(Redraw: Boolean;
  Force: Boolean);
const
  ALL_SCROLLFLAGS = SIF_PAGE or SIF_POS or SIF_RANGE;
var
  Info: TScrollInfo;
  HorzWasVisible, VertWasVisible, HorzNeeded, VertNeeded, RebuildNeeded: Boolean;
  R: TRect;
begin
  // Performance hit if not checking for update.
  if not Rebuilding and (Assigned(OwnerListview) and OwnerListview.HandleAllocated) and
    ((OwnerListview.UpdateCount = 0) or Force) then
  begin
    FRebuilding := True;
    HorzWasVisible := False;
    VertWasVisible := False;
    try
      // If the window is resizing our offset may become invalid and we need to
      // "pull" the offset with the resize
      ValidateOffsets(FOffsetX, FOffsetY);

      HorzWasVisible := HorzBarVisible;
      HorzNeeded := (ViewWidth - 1 > OwnerListview.ClientWidth) and (OwnerListview.Height > GetSystemMetrics(SM_CYHSCROLL));
      VertWasVisible := VertBarVisible;
      VertNeeded := (ViewHeight + OwnerListview.Header.RuntimeHeight - 1 > OwnerListview.ClientHeight) and (OwnerListview.Width > GetSystemMetrics(SM_CYVSCROLL));

      GetClientRect(OwnerListview.Handle, R);
      RebuildNeeded := False;
      if HorzEnabled then
      begin
        if HorzWasVisible xor HorzNeeded then
        begin
          ShowScrollBar(OwnerListview.Handle, SB_HORZ, HorzNeeded);
          RebuildNeeded := True;
        end
      end else
        ShowScrollBar(OwnerListview.Handle, SB_HORZ, False);

      if VertEnabled then
      begin
        if VertWasVisible xor VertNeeded then
        begin
          ShowScrollBar(OwnerListview.Handle, SB_VERT, VertNeeded);
          RebuildNeeded := True;
        end;
      end else
        ShowScrollBar(OwnerListview.Handle, SB_VERT, False);;

      if RebuildNeeded then
        OwnerListview.Groups.Rebuild(True);

      FillChar(Info, SizeOf(Info), #0);
      Info.cbSize := SizeOf(Info);
      Info.fMask := ALL_SCROLLFLAGS;
      Info.nMin := 0;
      if VertEnabled and VertBarVisible then
      begin
        Info.nMax := ViewHeight + OwnerListview.Header.RuntimeHeight - 1;
        Info.nPos := Abs(OffsetY);
        if OwnerListview.ClientHeight < 0 then
          Info.nPage := 0
        else
          Info.nPage := OwnerListview.ClientHeight;
      end else
      begin
        Info.nMax := 0;
        Info.nPos := 0;
        Info.nPage := 0;
      end;
      SetScrollInfo(OwnerListview.Handle, SB_VERT, Info, Redraw);

      FillChar(Info, SizeOf(Info), #0);
      Info.cbSize := SizeOf(Info);
      Info.fMask := ALL_SCROLLFLAGS;
      Info.nMin := 0;

      if HorzEnabled and HorzBarVisible then
      begin
        Info.nMax := ViewWidth - 1;
        Info.nPos := Abs(OffsetX);
        if OwnerListview.ClientWidth < 0 then
          Info.nPage := 0
        else
          Info.nPage := OwnerListview.ClientWidth;
      end else
      begin
        Info.nMax := 0;
        Info.nPos := 0;
        Info.nPage := 0;
      end;
      SetScrollInfo(OwnerListview.Handle, SB_HORZ, Info, Redraw);

      // If the window is resizing our offset may become invalid and we need to
      // "pull" the offset with the resize
      ValidateOffsets(FOffsetX, FOffsetY);
    finally
      FRebuilding := False;
      if (not HorzWasVisible and HorzBarVisible) or (not VertWasVisible and VertBarVisible) then
        RedrawWindow(OwnerListview.Handle, nil, 0, RDW_FRAME or RDW_INVALIDATE or RDW_ERASENOW);
    end
  end
end;

procedure TEasyScrollbarManager.SetHorzEnabled(
  const Value: Boolean);
begin
  if FHorzEnabled <> Value then
  begin
    FHorzEnabled := Value;
    ReCalculateScrollbars(True, False);
    OwnerListview.SafeInvalidateRect(nil, False)
  end
end;

procedure TEasyScrollbarManager.SetOffsetX(const Value: Integer);
begin
  if FOffsetX <> Value then
    Scroll(Value - OffsetX, 0);
end;

procedure TEasyScrollbarManager.SetOffsetY(const Value: Integer);
begin
  if FOffsetY <> Value then
    Scroll(0, Value - OffsetY);
end;

procedure TEasyScrollbarManager.SetVertEnabled(
  const Value: Boolean);
begin
  if FVertEnabled <> Value then
  begin
    FVertEnabled := Value;
    ReCalculateScrollbars(True, False);
    OwnerListview.SafeInvalidateRect(nil, False)
  end
end;

procedure TEasyScrollbarManager.SetViewRect(AViewRect: TRect; InvalidateWindow: Boolean);
var
  WasX, WasY: Boolean;
begin
  if not EqualRect(AViewRect, FViewRect) then
  begin
    WasX := HorzBarVisible;
    WasY := VertBarVisible;
    FViewRect := AViewRect;
    ReCalculateScrollbars(True, False);
    if (InvalidateWindow or (WasX and not HorzBarVisible) or (WasY and not VertBarVisible)) then
      OwnerListview.SafeInvalidateRect(nil, False);
  end else
    if InvalidateWindow then
      OwnerListview.SafeInvalidateRect(nil, False);
end;

procedure TEasyScrollbarManager.ValidateOffsets(var AnOffsetX, AnOffsetY: Integer);
// "Pulls" the offset to keep in sync with the client window.  This is especially
// for when the window is resizing and the offset is near the bottom of the page.
// Without this the Scroll Manager could detect that the scrollbars are not needed
// any more and hide them leaving the window scrolled to the last known point!
var
  LastCol: Boolean;
begin
  if Assigned(OwnerListview) then
  begin
    if SnapHorzView and not OwnerListview.IsVertView then
    begin
      LastCol := AnOffsetX > ViewWidth - OwnerListview.CellSizes.List.Width;
      if AnOffsetX mod OwnerListview.CellSizes.List.Width <> 0 then
        AnOffsetX := OwnerListview.CellSizes.List.Width * (AnOffsetX div OwnerListview.CellSizes.List.Width);
      if (AnOffsetX mod OwnerListview.CellSizes.List.Width > OwnerListview.CellSizes.List.Width div 2) or LastCol then
        AnOffsetX := AnOffsetX + OwnerListview.CellSizes.List.Width
    end;

    if AnOffsetY > MaxOffsetY  then
      AnOffsetY := MaxOffsetY;
    if AnOffsetX > MaxOffsetX  then
      AnOffsetX := MaxOffsetX;
    if AnOffsetY < 0 then
      AnOffsetY := 0;
    if AnOffsetX < 0 then
      AnOffsetX := 0;
  end else
  begin
    AnOffsetX := 0;
    AnOffsetY := 0
  end
end;

function TEasyScrollbarManager.ViewableViewportRect: TRect;
//
// Returns, in Viewport Coordinates, the rectangle that is current visible to
// the user in the window.
//
begin
  Result := MapWindowRectToViewRect(OwnerListview.ClientRect)
end;

procedure TEasyScrollbarManager.WMHScroll(var Msg: TWMVScroll);
// Called from the WM_HSCROLL message of the owner window to implement the scroll
var
  Info: TScrollInfo;
  ClientR: TRect;
  DeltaX: Integer;
begin
  if Assigned(OwnerListview) then
  begin
    DeltaX := 0;
    // Get the 32 Bit Position
    FillChar(Info, SizeOf(Info), #0);
    Info.cbSize := SizeOf(Info);
    Info.fMask := SIF_POS or SIF_TRACKPOS;
    GetScrollInfo(OwnerListview.Handle, SB_HORZ, Info);
    ClientR :=OwnerListview.ClientRect;

    if not OwnerListview.IsVertView and SnapHorzView then
    begin
      case Msg.ScrollCode of
        SB_BOTTOM: DeltaX := (ViewRect.Right - ViewRect.Left) - OffsetX;
        SB_ENDSCROLL: DeltaX := 0;
         SB_LINEDOWN: DeltaX := OwnerListview.CellSizes.List.Width;
        SB_LINEUP: DeltaX := -OwnerListview.CellSizes.List.Width;
        SB_PAGEDOWN: DeltaX := OwnerListview.CellSizes.List.Width;
        SB_PAGEUP: DeltaX := -OwnerListview.CellSizes.List.Width;
        SB_THUMBPOSITION:
          begin
            DeltaX := Info.nPos - OffsetX;
            DeltaX := (DeltaX - (DeltaX mod OwnerListview.CellSizes.List.Width));
           end;
        SB_THUMBTRACK:
          begin
            DeltaX := Info.nTrackPos - OffsetX;
            if ((MaxOffsetX - Info.nTrackPos) < OwnerListview.CellSizes.List.Width) then
                DeltaX := MaxOffsetX
            else
              DeltaX := (DeltaX - (DeltaX mod OwnerListview.CellSizes.List.Width));
          end;

        SB_TOP: DeltaX := -(ViewRect.Right - ViewRect.Left) - OffsetX;
      end // case
    end else    
    begin
      case Msg.ScrollCode of
        SB_BOTTOM: DeltaX := (ViewRect.Right - ViewRect.Left) - OffsetX;
        SB_ENDSCROLL: DeltaX := 0;
        SB_LINEDOWN: DeltaX := Line;
        SB_LINEUP: DeltaX := -Line;
        SB_PAGEDOWN: DeltaX := ClientR.Right - ClientR.Left;
        SB_PAGEUP: DeltaX := -(ClientR.Right - ClientR.Left);
        SB_THUMBPOSITION: DeltaX := Info.nTrackPos - OffsetX;
        SB_THUMBTRACK: DeltaX := Info.nTrackPos - OffsetX;
        SB_TOP: DeltaX := -OffsetY
      end;
    end;
    Scroll(DeltaX, 0);
  end else
    OffsetX := 0
end;

procedure TEasyScrollbarManager.WMKeyDown(var Msg: TWMKeyDown);
// Call from the WM_KEYDOWN message of the owner window to check for keys that
// are related to scrolling the window
var
  NewMsg: TWMScroll;
  SkipScroll: Boolean;
begin
  FillChar(NewMsg, SizeOf(NewMsg), #0);
  SkipScroll := False;
  case Msg.CharCode of
    VK_HOME:  NewMsg.ScrollCode := SB_TOP;
    VK_END:   NewMsg.ScrollCode := SB_BOTTOM;
    VK_NEXT:  NewMsg.ScrollCode := SB_PAGEDOWN;
    VK_PRIOR: NewMsg.ScrollCode := SB_PAGEUP;
    VK_UP:    NewMsg.ScrollCode := SB_LINEUP;
    VK_DOWN:  NewMsg.ScrollCode := SB_LINEDOWN;
    VK_LEFT:  NewMsg.ScrollCode := SB_LINEUP;
    VK_RIGHT: NewMsg.ScrollCode := SB_LINEDOWN;
  else
    SkipScroll := True;
  end;
  if not SkipScroll then
  begin
    if Msg.CharCode in [VK_LEFT, VK_RIGHT] then
      WMHScroll(NewMsg)
    else
      WMVScroll(NewMsg)
  end
end;

procedure TEasyScrollbarManager.WMVScroll(var Msg: TWMVScroll);
// Call from the WM_VSCROLL message of the owner window to implement the scroll
var
  Info: TScrollInfo;
  ClientR: TRect;
  DeltaY: Integer;
begin
  if Assigned(OwnerListview) then
  begin
    DeltaY := 0;
    // Get the 32 Bit Position
    FillChar(Info, SizeOf(Info), #0);
    Info.cbSize := SizeOf(Info);
    Info.fMask := SIF_TRACKPOS;
    GetScrollInfo(OwnerListview.Handle, SB_VERT, Info);
    ClientR := OwnerListview.ClientRect;
    case Msg.ScrollCode of
      SB_BOTTOM: DeltaY := (ViewRect.Bottom - ViewRect.Top) - OffsetY;
      SB_ENDSCROLL: DeltaY := 0;
      SB_LINEDOWN: DeltaY := Line;
      SB_LINEUP: DeltaY := -Line;
      SB_PAGEDOWN: DeltaY := (ClientR.Bottom - ClientR.Top);
      SB_PAGEUP: DeltaY := -(ClientR.Bottom - ClientR.Top);
      SB_THUMBPOSITION: DeltaY := Info.nTrackPos - OffsetY;
      SB_THUMBTRACK: DeltaY := Info.nTrackPos - OffsetY;
      SB_TOP: DeltaY := -OffsetY;
   end;
    Scroll(0, DeltaY);
  end else
    OffsetY := 0
end;

procedure TEasyScrollbarManager.Scroll(DeltaX, DeltaY: Integer);
var
  OldOffsetX, OldOffsetY: Integer;
begin
  OldOffsetY := OffsetY;
  OldOffsetX := OffsetX;

  FOffsetX := OffsetX + DeltaX;
  FOffsetY := OffsetY + DeltaY;

  ValidateOffsets(FOffsetX, FOffsetY);

  if (OffsetX <> OldOffsetX) or (OffsetY <> OldOffsetY) then
  begin
    ReCalculateScrollbars(True, True);
    OwnerListview.DoScroll(OldOffsetX - FOffsetX, OldOffsetY - FOffsetY);
    OwnerListview.SafeInvalidateRect(nil, False);
  end
end;

{ TEasyBackgroundManager }

procedure TEasyBackgroundManager.Assign(Source: TPersistent);
var
  ASource: TEasyBackgroundManager;
begin
  if Source is TEasyBackgroundManager then
  begin
    ASource := TEasyBackgroundManager(Source);
    Image.Assign(ASource.Image);
    FEnabled := ASource.Enabled;
    FOffsetX := ASource.OffsetX;
    FOffsetY := ASource.OffsetY;
    FTile := ASource.Tile;
  end
end;

procedure TEasyBackgroundManager.AssignTo(Target: TPersistent);
var
  ATarget: TEasyBackgroundManager;
begin
  if Target is TEasyBackgroundManager then
  begin
    ATarget := TEasyBackgroundManager(Target);
    ATarget.Image.Assign(Image);
    ATarget.FEnabled := Enabled;
    ATarget.FOffsetX := OffsetX;
    ATarget.FOffsetY := OffsetY;
    ATarget.FTile := Tile;
  end
end;

procedure TEasyBackgroundManager.ChangeBitmapBits(Sender: TObject);
begin
  Image.PixelFormat := pf32Bit;
  AlphaImage.PixelFormat := pf32Bit;
  OwnerListview.SafeInvalidateRect(nil, False);
end;

constructor TEasyBackgroundManager.Create(AnOwner: TCustomEasyListview);
begin
  inherited;
  FImage := TBitmap.Create;
  FImage.PixelFormat := pf32Bit;
  FImage.OnChange := ChangeBitmapBits;
  FAlphaImage := TBitmap.Create;
  FAlphaImage.PixelFormat := pf32Bit;
  FAlphaImage.OnChange := ChangeBitmapBits;
  FTile := True;
  FOffsetX := 0;
  FOffsetY := 0;
  FBlendMode := cbmConstantAlphaAndColor;
  FBlendAlpha := 128;
  FAlphaBlend := False;
  FCaptionAlignment := taCenter;
  FCaptionVAlignment := cvaCenter;
  FCaptionSingleLine := True;
  FCaptionShowOnlyWhenEmpty := True;
  FAlphaBlender := TEasyAlphaBlender.Create;
end;

destructor TEasyBackgroundManager.Destroy;
begin
  FImage.OnChange := nil;
  FreeAndNil(FImage);
  FreeAndNil(FAlphaImage);
  FreeAndNil(FAlphaBlender);
  inherited;
end;

procedure TEasyBackgroundManager.PaintTo(ACanvas: TCanvas; ARect: TRect; PaintDefault: Boolean);
var
  Row, Column, X, Y: Integer;
  BkGndR, TempR: TRect;
  OldOrigin: TPoint;
  Bitmap: TBitmap;
  DrawTextFlags: TCommonDrawTextWFlags;
  ShowCaption, DoDefault: Boolean;
begin
  DoDefault := True;
  OwnerListview.DoPaintBkGnd(ACanvas, ARect, AlphaBlender, DoDefault);
  if DoDefault then
  begin
    if Assigned(Image) and not Image.Empty and Enabled then
    begin
      ACanvas.Brush.Color := OwnerListview.Color;
      ACanvas.FillRect(ARect);
      if AlphaBlend and HasMMX then
      begin
        Bitmap := TBitmap.Create;
        Bitmap.Width := Image.Width;
        Bitmap.Height := Image.Height;
        Bitmap.PixelFormat := pf32Bit;
        Bitmap.Assign(Image);

        if BlendMode = cbmConstantAlphaAndColor then
          MPCommonUtilities.AlphaBlend(0, Bitmap.Canvas.Handle,
            Rect(0, 0, Bitmap.Width, Bitmap.Height), Point(0, 0),
            BlendMode, BlendAlpha, ColorToRGB(OwnerListview.Color))
        else
        if (Image.Width = AlphaImage.Width) and (Image.Height = AlphaImage.Height)
        then
          // AlphaBlend it with the background bitmap
          MPCommonUtilities.AlphaBlend(AlphaImage.Canvas.Handle, Bitmap.Canvas.Handle,
            Rect(0, 0, Bitmap.Width, Bitmap.Height), Point(0, 0),
            BlendMode, BlendAlpha, ColorToRGB(OwnerListview.Color));
      end else
        Bitmap := Image;

      Bitmap.Canvas.Lock;
      try
        // Needed to make IntersectRect work correctly.
        InflateRect(ARect, Bitmap.Width, Bitmap.Height);
        if Tile then
        begin
          // Need to keep BitBlt from needing negative numbers.  It will not work with them
          X := OffsetX mod Bitmap.Width;
          if OffsetX > 0 then
            X := -(Bitmap.Width - X);
          Y := OffsetY mod Bitmap.Height;
          if OffsetY > 0 then
            Y := -(Bitmap.Height - Y);

          SetViewportOrgEx(ACanvas.Handle, X, Y, @OldOrigin);
          try
            BkGndR := Rect(0, 0, Bitmap.Width, Bitmap.Height);
            for Row := 0 to (OwnerListview.ClientHeight div Bitmap.Height + 1) do
            begin
              for Column := 0 to (OwnerListview.ClientWidth div Bitmap.Width + 1) do
              begin
                if IntersectRect(TempR, ARect, BkGndR) then
                begin
                  if Transparent then
                    ACanvas.Draw(BkGndR.Left, BkGndR.Top, Bitmap)
                  else
                    BitBlt(ACanvas.Handle, BkGndR.Left, BkGndR.Top, BkGndR.Right, BkGndR.Bottom,
                      Bitmap.Canvas.Handle, 0, 0, SRCCOPY)
                end;
                OffsetRect(BkGndR, Bitmap.Width, 0);
              end;
              OffsetRect(BkGndR, -BkGndR.Left, Bitmap.Height);
            end;
          finally
            SetViewportOrgEx(ACanvas.Handle, OldOrigin.X, OldOrigin.Y, nil);
            InflateRect(ARect, -Bitmap.Width, -Bitmap.Height);
          end
        end else
        begin
          SetViewportOrgEx(ACanvas.Handle, OffsetX, OffsetY, @OldOrigin);
          BkGndR := Rect(0, 0, Bitmap.Width, Bitmap.Height);
          if Transparent then
            ACanvas.Draw(BkGndR.Left, BkGndR.Top, Bitmap)
          else
            BitBlt(ACanvas.Handle, BkGndR.Left, BkGndR.Top, BkGndR.Right, BkGndR.Bottom,
              Bitmap.Canvas.Handle, 0, 0, SRCCOPY);
          SetViewportOrgEx(ACanvas.Handle, OldOrigin.X, OldOrigin.Y, nil);
        end
      finally
        Bitmap.Canvas.UnLock;
        if AlphaBlend then
          Bitmap.Free
      end
    end else
    if PaintDefault then
    begin
      ACanvas.Brush.Color := OwnerListview.Color;
      ACanvas.FillRect(ARect);
    end;
    if CaptionShow then
    begin
      if CaptionShowOnlyWhenEmpty then
        ShowCaption := (OwnerListview.Groups.ItemCount = 0) and (Caption <> '')
      else
        ShowCaption := Caption <> '';

      if ShowCaption then
      begin
        BkGndR := OwnerListview.ClientRect;
        if IntersectRect(TempR, ARect, BkGndR) then
        begin
          InflateRect(BkGndR, -8, -8);
          ACanvas.Font.Assign(OwnerListview.Font);
          ACanvas.Brush.Style := bsClear;

          DrawTextFlags := [dtEndEllipsis];
          case CaptionAlignment of
            taLeftJustify:  Include(DrawTextFlags, dtLeft);
            taRightJustify: Include(DrawTextFlags, dtRight);
            taCenter: Include(DrawTextFlags, dtCenter);
          end;

          case CaptionVAlignment of
            cvaTop: Include(DrawTextFlags, dtTop);
            cvaBottom: Include(DrawTextFlags, dtBottom);
            cvaCenter: Include(DrawTextFlags, dtVCenter);
          end;

          if CaptionSingleLine then
            Include(DrawTextFlags, dtSingleLine);

          DrawTextWEx(ACanvas.Handle, Caption, BkGndR, DrawTextFlags, -1);
        end
      end
    end
  end
end;

procedure TEasyBackgroundManager.SetAlphaBlend(const Value: Boolean);
begin
  if FAlphaBlend <> Value then
  begin
    FAlphaBlend := Value;
    OwnerListview.SafeInvalidateRect(nil, False)
  end
end;

procedure TEasyBackgroundManager.SetAlphaImage(const Value: TBitmap);
begin
  FAlphaImage.Assign(Value);
  OwnerListview.SafeInvalidateRect(nil, False)
end;

procedure TEasyBackgroundManager.SetBlendAlpha(const Value: Integer);
begin
  if FBlendAlpha <> Value then
  begin
    FBlendAlpha := Value;
    OwnerListview.SafeInvalidateRect(nil, False)
  end
end;

procedure TEasyBackgroundManager.SetCaption(const Value: WideString);
begin
  if FCaption <> Value then
  begin
    FCaption := Value;
    OwnerListview.SafeInvalidateRect(nil, False)
  end
end;

procedure TEasyBackgroundManager.SetCaptionAlignment(const Value: TAlignment);
begin
  if FCaptionAlignment <> Value then
  begin
    FCaptionAlignment := Value;
    OwnerListview.SafeInvalidateRect(nil, False)
  end
end;

procedure TEasyBackgroundManager.SetCaptionShow(const Value: Boolean);
begin
  if FCaptionShow <> Value then
  begin
    FCaptionShow := Value;
    OwnerListview.SafeInvalidateRect(nil, False)
  end
end;

procedure TEasyBackgroundManager.SetCaptionShowOnlyWhenEmpty(const Value: Boolean);
begin
  if FCaptionShowOnlyWhenEmpty <> Value then
  begin
    FCaptionShowOnlyWhenEmpty := Value;
    OwnerListview.SafeInvalidateRect(nil, False)
  end
end;

procedure TEasyBackgroundManager.SetCaptionSingleLine(const Value: Boolean);
begin
  if FCaptionSingleLine <> Value then
  begin
    FCaptionSingleLine := Value;
    OwnerListview.SafeInvalidateRect(nil, False)
  end
end;

procedure TEasyBackgroundManager.SetCaptionVAlignment(const Value: TCommonVAlignment);
begin
  if FCaptionVAlignment <> Value then
  begin
    FCaptionVAlignment := Value;
    OwnerListview.SafeInvalidateRect(nil, False)
  end
end;

procedure TEasyBackgroundManager.SetCommonBlendMode(const Value: TCommonBlendMode);
begin
  if FBlendMode <> Value then
  begin
    FBlendMode := Value;
    OwnerListview.SafeInvalidateRect(nil, False)
  end
end;

procedure TEasyBackgroundManager.SetEnabled(const Value: Boolean);
begin
  if FEnabled <> Value then
  begin
    FEnabled := Value;
    OwnerListview.SafeInvalidateRect(nil, False)
  end
end;

procedure TEasyBackgroundManager.SetImage(const Value: TBitmap);
begin
  FImage.Assign(Value);
  OwnerListview.SafeInvalidateRect(nil, False)
end;

procedure TEasyBackgroundManager.SetOffsetX(const Value: Integer);
begin
  if FOffsetX <> Value then
  begin
    FOffsetX := Value;
    OwnerListview.SafeInvalidateRect(nil, False)
  end
end;

procedure TEasyBackgroundManager.SetOffsetY(const Value: Integer);
begin
  if FOffsetY <> Value then
  begin
    FOffsetY := Value;
    OwnerListview.SafeInvalidateRect(nil, False)
  end
end;

procedure TEasyBackgroundManager.SetTile(const Value: Boolean);
begin
  if FTile <> Value then
  begin
    FTile := Value;
    OwnerListview.SafeInvalidateRect(nil, False)
  end
end;

procedure TEasyBackgroundManager.SetTransparent(const Value: Boolean);
begin
  if FTransparent <> Value then
  begin
    FTransparent := Value;
    OwnerListview.SafeInvalidateRect(nil, False);
    Image.Transparent := Value;
    Image.TransparentMode := tmAuto;
  end
end;

procedure TEasyBackgroundManager.WMWindowPosChanging(var Msg: TWMWindowPosChanging);
begin
  if OffsetTrack then
  begin
    // If SWP_NOSIZE is in the flags then the cx, cy params can be garbage
    if Msg.WindowPos.flags and SWP_NOSIZE  = 0 then
    begin
      FOffsetX := FOffsetX + (Msg.WindowPos.cx - OwnerListview.Width);
      FOffsetY := FOffsetY + (Msg.WindowPos.cy - OwnerListview.Height);
    end
  end
end;

function TEasyDropTargetManager.DragEnter(const dataObj: IDataObject;
  grfKeyState: Integer; pt: TPoint; var dwEffect: Integer): HResult;
var
  Effect: TCommonDropEffect;
  Effects: TCommonDropEffects;
  Keys: TCommonKeyStates;
  StgMedium: TStgMedium;
begin
  if Assigned(DropTargetHelper) then
    DropTargetHelper.DragEnter(Owner.Handle, dataObj, Pt, dwEffect);

  if Owner.DragManager.Enabled then
  begin
    Keys := KeyToKeyStates(grfKeyState);
    Effects := DropEffectToDropEffectStates(dwEffect);

    // Get the "Windows Style" effect with the key modifiers
    Effect := KeyStateToDropEffect(Keys);

    Owner.DoOLEDropTargetDragEnter(dataObj, KeyToKeyStates(grfKeyState), pt, DropEffectToDropEffectStates(dwEffect), Effect);

    // Decide if this is a Header Drag and Drop or an Item Drag and Drop
    // Note that if we use a Windows supplied IDataObject it will return TRUE for QueryGetData
    // so we must really try to get the data to be sure
    if Succeeded(dataObj.GetData(HeaderClipFormat, StgMedium)) then
      DragManager := Owner.Header.DragManager
    else
      DragManager := Owner.DragManager;

    if Assigned(DragManager) then
      DragManager.DragEnter(dataObj, nil, Owner.ScreenToClient(pt), Keys, Effect);

    dwEffect := DropEffectStateToDropEffect(Effect);
  end else
    dwEffect := DROPEFFECT_NONE;
  Result := S_OK;
end;

function TEasyDropTargetManager.DragLeave: HResult;
begin
  if Assigned(DropTargetHelper) then
    DropTargetHelper.DragLeave;

  Owner.DoOLEDropTargetDragLeave;

  // Just pass some dummy parameters as they are not important for OLE drag drop
  if Assigned(DragManager) then
    DragManager.DragEnd(nil, Point(0, 0), []);

  Result := S_OK
end;

function TEasyDropTargetManager.DragOver(grfKeyState: Integer; pt: TPoint;
  var dwEffect: Integer): HResult;
var
  Effect: TCommonDropEffect;
  Effects: TCommonDropEffects;
  Keys: TCommonKeyStates;
begin
  if Assigned(DropTargetHelper) then
    DropTargetHelper.DragOver(Pt, dwEffect);

  if Owner.DragManager.Enabled then
  begin
    Keys := KeyToKeyStates(grfKeyState);
    Effects := DropEffectToDropEffectStates(dwEffect);

    // Get the "Windows Style" effect with the key modifiers
    Effect := KeyStateToDropEffect(Keys);
    Owner.DoOLEDropTargetDragOver(KeyToKeyStates(grfKeyState), pt, Effects, Effect);

    if Assigned(DragManager) then
      DragManager.Drag(nil, Owner.ScreenToClient(pt), Keys, Effect);

    dwEffect := DropEffectStateToDropEffect(Effect);
  end else
    dwEffect := DROPEFFECT_NONE;
  Result := S_OK
end;

function TEasyDropTargetManager.Drop(const dataObj: IDataObject;
  grfKeyState: Integer; pt: TPoint; var dwEffect: Integer): HResult;
var
  Effect: TCommonDropEffect;
  Effects: TCommonDropEffects;
  Keys: TCommonKeyStates;
  Handled: Boolean;
begin
  if Assigned(DropTargetHelper) then
    DropTargetHelper.Drop(dataObj, Pt, dwEffect);

  Keys := KeyToKeyStates(grfKeyState);
  Effects := DropEffectToDropEffectStates(dwEffect);

  // Get the "Windows Style" effect with the key modifiers
  Effect := KeyStateToDropEffect(Keys);
  Handled := False;
  Owner.DoOLEDropTargetDragDrop(dataObj, KeyToKeyStates(grfKeyState), pt, DropEffectToDropEffectStates(dwEffect), Effect, Handled);

  if Assigned(DragManager) and not Handled then
    DragManager.DragDrop(Owner.ScreenToClient(pt), Keys, Effect);

  dwEffect := DropEffectStateToDropEffect(Effect);
  Result := S_OK
end;

function TEasyDropTargetManager.GetDropTargetHelper: IDropTargetHelper;
begin
  if not Assigned(FDropTargetHelper) and IsWin2000 then
    if CoCreateInstance(CLSID_DragDropHelper, nil, CLSCTX_INPROC_SERVER or CLSCTX_LOCAL_SERVER, IID_IDropTargetHelper, FDropTargetHelper) <> S_OK then
      FDropTargetHelper := nil;
  Result := FDropTargetHelper
end;

{ TEasyDropSourceManager }

function TEasyDropSourceManager.GiveFeedback(dwEffect: Integer): HResult;
var
  UseDefaultCursors: Boolean;
begin
  Result := DRAGDROP_S_USEDEFAULTCURSORS;
  UseDefaultCursors := True;
  Owner.DoOLEDropSourceGiveFeedback(DropEffectToDropEffectStates(dwEffect), UseDefaultCursors);
  // The application has set the cursor style
  if not UseDefaultCursors then
    Result := S_OK
end;

function TEasyDropSourceManager.QueryContinueDrag(fEscapePressed: BOOL;
  grfKeyState: Integer): HResult;
var
  QueryResult: TEasyQueryDragResult;
  KeyStates: TCommonKeyStates;
begin
  KeyStates := KeyToKeyStates(grfKeyState);
  if fEscapePressed then
    QueryResult := eqdrQuit
  else begin
    if cksButton in KeyStates then
      QueryResult := eqdrContinue
    else
      QueryResult := eqdrQuit
  end;

  // If no buttons are down anymore then the user dropped the objects
  if not(cksButton in KeyStates) then
    QueryResult := eqdrDrop;

  // Allow the application to modify if desired
  Owner.DoOLEDropSourceQueryContineDrag(fEscapePressed, KeyStates, QueryResult);
  if QueryResult = eqdrQuit then
    Result := DRAGDROP_S_CANCEL
  else
  if QueryResult = eqdrContinue then
    Result := S_OK
  else
  if QueryResult = eqdrDrop then
    Result := DRAGDROP_S_DROP
  else
    Result := E_UNEXPECTED
end;

function TCustomEasyDragManagerBase.DoPtInAutoScrollLeftRegion(WindowPoint: TPoint): Boolean;
begin
  Result := False;
end;

function TCustomEasyDragManagerBase.DoPtInAutoScrollRightRegion(WindowPoint: TPoint): Boolean;
begin
  Result := False;
end;

{ TEasyBaseDragManager }

procedure TCustomEasyDragManagerBase.AutoScrollWindow;
// This is called to autoscroll the window
var
  Pt: TPoint;
begin
  if Assigned(OwnerListview.Scrollbars) and OwnerListview.HandleAllocated then
  begin
    // Only scroll after an initial delay is met.  This is defined as the mouse
    // is in constantly the autoscroll area for AutoScrollDelay time
    if AutoScrollDelayMet then
    begin
      // It is just easier to grab the mouse position on the screen to do the
      // autoscroll for various operation (drag drop, drag select, etc.)
      Pt := OwnerListview.ScreenToClient(Mouse.CursorPos);

      if PtInAutoScrollUpRegion(Pt) then
        DoAutoScroll(0, -(ScrollDeltaUp(Pt) * AutoScrollAccelerator))
      else
      if PtInAutoScrollDownRegion(Pt) then
        DoAutoScroll(0, ScrollDeltaDown(Pt) * AutoScrollAccelerator);

      if OwnerListview.Scrollbars.SnapHorzView then
      begin
        if PtInAutoScrollLeftRegion(Pt) then
          DoAutoScroll(-(OwnerListview.CellSizes.List.Width), 0)
        else
        if PtInAutoScrollRightRegion(Pt) then
          DoAutoScroll(OwnerListview.CellSizes.List.Width, 0)
      end else     
      begin
        if PtInAutoScrollLeftRegion(Pt) then
          DoAutoScroll(-(ScrollDeltaLeft(Pt) * AutoScrollAccelerator), 0)
        else
        if PtInAutoScrollRightRegion(Pt) then
          DoAutoScroll(ScrollDeltaRight(Pt) * AutoScrollAccelerator, 0)
      end
    end
  end;
end;

procedure TCustomEasyDragManagerBase.BeginDrag(WindowPoint: TPoint; KeyState: TCommonKeyStates);
begin
  DoDragBegin(WindowPoint, KeyState);
end;

constructor TCustomEasyDragManagerBase.Create(AnOwner: TCustomEasyListview);
begin
  inherited;
  FAutoScrollDelay := _AUTOSCROLLDELAY;
  FAutoScrollTime := _AUTOSCROLLTIME;
  FAutoScroll := True;
  FAutoScrollAccelerator := 2;
  FAutoScrollMargin := 15;
  FMouseButton := [cmbLeft];
end;

destructor TCustomEasyDragManagerBase.Destroy;
begin
  inherited;
end;

procedure TCustomEasyDragManagerBase.DoAfterAutoScroll;
begin

end;

procedure TCustomEasyDragManagerBase.DoAutoScroll(DeltaX, DeltaY: Integer);
var
  Msg: TWMMouse;
// Usually called from AutoScrollWindow which calcualate how to do the scroll.
begin
  // Need to flag the controls paint method to not worry about the selection
  // rect as we take care of it in the auto scroll
  DoBeforeAutoScroll;
  Include(FDragState, edmsAutoScrolling);
  OwnerListview.Scrollbars.Scroll(DeltaX, DeltaY);
  UpdateAfterAutoScroll;
  // Need to fake a mouse move to update any drag selection after a scroll
  Msg.Pos := PointToSmallPoint( OwnerListview.ScreenToClient(Mouse.CursorPos));
  Msg.Keys := 0;
  Msg.Msg := WM_MOUSEMOVE;
  OwnerListview.WMMouseMove(Msg);
  Exclude(FDragState, edmsAutoScrolling);
  DoAfterAutoScroll;
end;

procedure TCustomEasyDragManagerBase.DoBeforeAutoScroll;
begin

end;

procedure TCustomEasyDragManagerBase.DoDrag(Canvas: TCanvas; WindowPoint: TPoint; KeyState: TCommonKeyStates; var Effects: TCommonDropEffect);
begin

end;

procedure TCustomEasyDragManagerBase.DoDragBegin(WindowPoint: TPoint; KeyState: TCommonKeyStates);
begin

end;

procedure TCustomEasyDragManagerBase.DoDragDrop(WindowPoint: TPoint; KeyState: TCommonKeyStates; var Effects: TCommonDropEffect);
begin

end;

procedure TCustomEasyDragManagerBase.DoDragEnd(Canvas: TCanvas; WindowPoint: TPoint; KeyState: TCommonKeyStates);
begin

end;

procedure TCustomEasyDragManagerBase.DoDragEnter(const DataObject: IDataObject; Canvas: TCanvas; WindowPoint: TPoint; KeyState: TCommonKeyStates; var Effects: TCommonDropEffect);
begin

end;

procedure TCustomEasyDragManagerBase.DoEnable(Enable: Boolean);
begin

end;

procedure TCustomEasyDragManagerBase.DoGetDragImage(Bitmap: TBitmap; DragStartPt: TPoint; var HotSpot: TPoint; var TransparentColor: TColor; var Handled: Boolean);
begin

end;

procedure TCustomEasyDragManagerBase.DoOLEDragEnd(const ADataObject: IDataObject; DragResult: TCommonOLEDragResult; ResultEffect: TCommonDropEffects; KeyStates: TCommonKeyStates);
begin
  
end;

procedure TCustomEasyDragManagerBase.DoOLEDragStart(const ADataObject: IDataObject; var AvailableEffects: TCommonDropEffects; var AllowDrag: Boolean);
begin

end;

function TCustomEasyDragManagerBase.DoPtInAutoScrollDownRegion(WindowPoint: TPoint): Boolean;
begin
  Result := False
end;

function TCustomEasyDragManagerBase.DoPtInAutoScrollUpRegion(WindowPoint: TPoint): Boolean;
begin
  Result := False
end;

procedure TCustomEasyDragManagerBase.Drag(Canvas: TCanvas; WindowPoint: TPoint; KeyState: TCommonKeyStates; var Effects: TCommonDropEffect);
begin
  // Watch for an auto scroll
  if AutoScroll then
  begin
    // If the mouse is not in the window start the timer and prepare for autoscroll
    // If is then reset the flags and wait for the next time the mouse leaves the window
    if PtInAutoScrollRegion(WindowPoint) then
      Timer.Enabled := True
    else begin
      FAutoScrollDelayMet := False;
      Timer.Enabled := False
    end;
  end;
  LastKeyState := KeyState;
  DoDrag(Canvas, WindowPoint, KeyState, Effects)
end;

procedure TCustomEasyDragManagerBase.DragDrop(WindowPoint: TPoint; KeyState: TCommonKeyStates; var Effects: TCommonDropEffect);
begin
  FreeAndNil(FTimer);
  LastKeyState := [];
  Exclude(FDragState, edmsDragging);
  DoDragDrop(WindowPoint, KeyState, Effects);
  DataObject := nil;
end;

procedure TCustomEasyDragManagerBase.DragEnd(Canvas: TCanvas; WindowPoint: TPoint; KeyState: TCommonKeyStates);
begin
  FreeAndNil(FTimer);
  LastKeyState := [];
  Exclude(FDragState, edmsDragging);
  DoDragEnd(Canvas, WindowPoint, KeyState);
  DataObject := nil;
end;

procedure TCustomEasyDragManagerBase.DragEnter(const ADataObject: IDataObject; Canvas: TCanvas; WindowPoint: TPoint; KeyState: TCommonKeyStates; var Effects: TCommonDropEffect);
begin   
  Include(FDragState, edmsDragging);
  DataObject := ADataObject;
  Timer.Enabled := False;
  Timer.OnTimer := HandleTimer;
  Timer.Interval := AutoScrollDelay;
  FAutoScrollDelayMet := False;
  DoDragEnter(ADataObject, Canvas, WindowPoint, KeyState, Effects)
end;

function TCustomEasyDragManagerBase.GetAutoScrolling: Boolean;
begin
  Result := edmsAutoScrolling in DragState
end;

function TCustomEasyDragManagerBase.GetDragging: Boolean;
begin
  Result := edmsDragging in DragState
end;

function TCustomEasyDragManagerBase.GetTimer: TTimer;
begin
  if not Assigned(FTimer) then
    FTimer := TTimer.Create(nil);
  Result := FTimer;
end;

procedure TCustomEasyDragManagerBase.HandleTimer(Sender: TObject);
begin    
  if AutoScrollDelayMet then
    AutoScrollWindow
  else begin
    FAutoScrollDelayMet := True;
    Timer.Interval := AutoScrollTime;
  end
end;

function TCustomEasyDragManagerBase.PtInAutoScrollDownRegion(WindowPoint: TPoint): Boolean;
begin
  Result := DoPtInAutoScrollDownRegion(WindowPoint)
end;

function TCustomEasyDragManagerBase.PtInAutoScrollLeftRegion(WindowPoint: TPoint): Boolean;
begin
  Result := DoPtInAutoScrollLeftRegion(WindowPoint)
end;

function TCustomEasyDragManagerBase.PtInAutoScrollRegion(WindowPoint: TPoint): Boolean;
begin
  Result := PtInAutoScrollDownRegion(WindowPoint) or PtInAutoScrollLeftRegion(WindowPoint) or
    PtInAutoScrollRightRegion(WindowPoint) or PtInAutoScrollUpRegion(WindowPoint)
end;

function TCustomEasyDragManagerBase.PtInAutoScrollRightRegion(WindowPoint: TPoint): Boolean;
begin
  Result := DoPtInAutoScrollRightRegion(WindowPoint)
end;

function TCustomEasyDragManagerBase.PtInAutoScrollUpRegion(WindowPoint: TPoint): Boolean;
begin
  Result := DoPtInAutoScrollUpRegion(WindowPoint)
end;

function TCustomEasyDragManagerBase.ScrollDeltaDown(WindowPoint: TPoint): Integer;
begin
  Result := Abs(OwnerListview.ClientHeight -  WindowPoint.Y - AutoScrollMargin)
end;

function TCustomEasyDragManagerBase.ScrollDeltaLeft(WindowPoint: TPoint): Integer;
begin
  Result := Abs(WindowPoint.X - AutoScrollMargin)
end;

function TCustomEasyDragManagerBase.ScrollDeltaRight(WindowPoint: TPoint): Integer;
begin
  Result := Abs(OwnerListview.ClientWidth - WindowPoint.X - AutoScrollMargin )
end;

function TCustomEasyDragManagerBase.ScrollDeltaUp(WindowPoint: TPoint): Integer;
begin
  Result := Abs(WindowPoint.Y - OwnerListview.Header.RuntimeHeight - AutoScrollMargin)
end;

procedure TCustomEasyDragManagerBase.RegisterOLEDragDrop(DoRegister: Boolean);
begin
  if OwnerListview.HandleAllocated then
  begin
    if DoRegister then
    begin
      if not (ebcsOLERegistered in OwnerListview.States) then
      begin
        RegisterDragDrop(OwnerListview.Handle, OwnerListview.DropTarget);
        Include(OwnerListview.FStates, ebcsOLERegistered)
      end
    end else
    begin
      if ebcsOLERegistered in OwnerListview.States then
      begin
        RevokeDragDrop(OwnerListview.Handle);
        Exclude(OwnerListview.FStates, ebcsOLERegistered)
      end
    end;
  end
end;

procedure TCustomEasyDragManagerBase.SetEnabled(const Value: Boolean);
begin
  if FEnabled <> Value then
  begin
    DoEnable(Value);
    FEnabled := Value;
  end
end;

procedure TCustomEasyDragManagerBase.SetRegistered(Value: Boolean);
begin
  if Value <> FRegistered then
  begin
    RegisterOLEDragDrop(Value);
    FRegistered := Value;
  end
end;

procedure TCustomEasyDragManagerBase.UpdateAfterAutoScroll;
begin

end;

procedure TCustomEasyDragManagerBase.VCLDragStart;
begin

end;

procedure TCustomEasyDragManagerBase.WMKeyDown(var Msg: TWMKeyDown);
begin
  case Msg.CharCode of
    VK_HOME:  DoAutoScroll(-OwnerListview.Scrollbars.OffsetX, -OwnerListview.Scrollbars.OffsetY);
    VK_END:   DoAutoScroll(OwnerListview.Scrollbars.ViewWidth-OwnerListview.ClientWidth,
                     OwnerListview.Scrollbars.ViewHeight-OwnerListview.ClientHeight);
    VK_NEXT:  DoAutoScroll(0, OwnerListview.ClientHeight);
    VK_PRIOR: DoAutoScroll(0, -OwnerListview.ClientHeight);
    VK_UP:    DoAutoScroll(0, -OwnerListview.Scrollbars.Line);
    VK_DOWN:  DoAutoScroll(0, OwnerListview.Scrollbars.Line);
    VK_LEFT:  DoAutoScroll(-OwnerListview.Scrollbars.Line, 0);
    VK_RIGHT: DoAutoScroll(OwnerListview.Scrollbars.Line, 0);
  end;
end;

constructor TEasyDragRectManager.Create(AnOwner: TCustomEasyListview);
begin
  inherited;
  FMouseButton := [cmbLeft, cmbRight];
  FEnabled := False;
end;

destructor TEasyDragRectManager.Destroy;
begin
  inherited;
end;

function TEasyDragRectManager.DoPtInAutoScrollDownRegion(WindowPoint: TPoint): Boolean;
begin
  Result := (WindowPoint.Y > OwnerListview.ClientHeight - AutoScrollMargin) and
    (OwnerListview.Scrollbars.OffsetY < OwnerListview.Scrollbars.MaxOffsetY)
end;

function TEasyDragRectManager.DoPtInAutoScrollLeftRegion(WindowPoint: TPoint): Boolean;
begin
  Result := (WindowPoint.X < AutoScrollMargin) and (OwnerListview.Scrollbars.OffsetX > 0)
end;

function TEasyDragRectManager.DoPtInAutoScrollRightRegion(WindowPoint: TPoint): Boolean;
begin
  Result := (WindowPoint.X > (OwnerListview.ClientWidth - AutoScrollMargin)) and
    (OwnerListview.Scrollbars.OffsetX < OwnerListview.Scrollbars.MaxOffsetX)
end;

function TEasyDragRectManager.DoPtInAutoScrollUpRegion(WindowPoint: TPoint): Boolean;
begin
  Result := (WindowPoint.Y - OwnerListview.Header.RuntimeHeight < AutoScrollMargin) and (OwnerListview.Scrollbars.OffsetY > 0)
end;

procedure TEasyDragRectManager.DoAfterAutoScroll;
begin
  inherited;
end;

procedure TEasyDragRectManager.DoAutoScroll(DeltaX, DeltaY: Integer);
// Usually called from AutoScrollWindow which calcualate how to do the scroll.
// This method is usually overridden to perform the scroll in the passed direction
begin
  inherited;
end;

procedure TEasyDragRectManager.DoBeforeAutoScroll;
begin
  inherited;
  // Keep the Dragpoint in sync with the current view
  // Need to map the dragged point to a new point after a scroll
  FOldOffsets.X := OwnerListview.Scrollbars.OffsetX;
  FOldOffsets.Y := OwnerListview.Scrollbars.OffsetY;
end;

procedure TEasyDragRectManager.DoDrag(Canvas: TCanvas; WindowPoint: TPoint; KeyState: TCommonKeyStates; var Effects: TCommonDropEffect);
var
  ARect: TRect;
begin
  // Update the Selection due to this drag
  OwnerListview.Selection.DragSelect(KeyState);

  // Get the last rectangle
  FPrevRect := SelectionRect;
  inherited;

  // Update the drag point
  DragPoint := OwnerListview.Scrollbars.MapWindowToView(WindowPoint);
  // Map it to the Client window coordinates
  ARect := SelRectInWindowCoords;
  // Make sure it is confined in the visible window
  IntersectRect(ARect, ARect, OwnerListview.ClientRect);

  // May need to update the last rectangle to if the drag rect is getting smaller
  UnionRect(ARect, ARect, OwnerListview.Scrollbars.MapViewRectToWindowRect(PrevRect));

  // Reentrant possibility
  if not(edmsAutoScrolling in DragState) then
    AutoScrollWindow;

 // Always have to update if the selection rectangle if it gets smaller or full
 // update if full row select (leaves white bands at the column interfaces)
  if OwnerListview.Selection.FullRowSelect then 
    OwnerListview.SafeInvalidateRect(nil, True) 
  else 
    OwnerListview.SafeInvalidateRect(@ARect, True); 

end;

procedure TEasyDragRectManager.DoDragBegin(WindowPoint: TPoint; KeyState: TCommonKeyStates);
begin
  inherited;
  FPrevRect := SelectionRect;
end;

procedure TEasyDragRectManager.DoDragEnd(Canvas: TCanvas; WindowPoint: TPoint; KeyState: TCommonKeyStates);
var
  ARect: TRect;
begin
  // Erase the selection rectangle, mainly for the XOR FocusRect when not DoubleBuffered
  inherited;
  // Get the last rectangle
  ARect := SelRectInWindowCoords;
  // Make sure it is confined in the visible window
  IntersectRect(ARect, ARect, OwnerListview.ClientRect);

  // Redraw the window
  if OwnerListview.Selection.FullRowSelect then
    OwnerListview.SafeInvalidateRect(nil, True)
  else
    OwnerListview.SafeInvalidateRect(@ARect, True);

  AnchorPoint := Point(0, 0);
  DragPoint := Point(0, 0);
  FPrevRect := Rect(0, 0, 0, 0);
end;

procedure TEasyDragRectManager.DoDragEnter(const DataObject: IDataObject; Canvas: TCanvas; WindowPoint: TPoint; KeyState: TCommonKeyStates; var Effects: TCommonDropEffect);
begin
  inherited DoDragEnter(DataObject, Canvas, WindowPoint, KeyState, Effects);
  FDragPoint := WindowPoint
end;

procedure TEasyDragRectManager.FinalizeDrag(KeyState: TCommonKeyStates);
// Does not mean the action actually occured it means that InitializeDrag was
// called and this is it matching call.  EndDrag means that the drag actually
// occured
begin

end;

function TEasyDragRectManager.GetSelectionRect: TRect;
begin
  Result := ProperRect(Rect(AnchorPoint.X, AnchorPoint.Y, DragPoint.X, DragPoint.Y));
  if Result.Top = Result.Bottom then // added
    Inc(Result.Bottom);
  if Result.Left = Result.Right then // added
    Inc(Result.Right);
end;

function TEasyDragRectManager.InitializeDrag(WindowPoint: TPoint; KeyState: TCommonKeyStates): Boolean;
// Does not mean that the action will be a Selection Drag, it just means get
// ready for one just in case.
begin
  Result := False;
  if Enabled then
  begin
    if ((cksLButton in KeyState) and (cmbLeft in MouseButton)) or
       ((cksMButton in KeyState) and (cmbMiddle in MouseButton)) or
       ((cksRButton in KeyState) and (cmbRight in MouseButton))
    then begin
      AnchorPoint := OwnerListview.Scrollbars.MapWindowToView(WindowPoint);
      DragPoint := AnchorPoint;
      Result := True
    end
  end
end;

procedure TEasyDragRectManager.PaintRect(Canvas: TCanvas);
// Paints the selection rectangle to the canvas:
// NOTE:  DO NOT USE THIS METHOD DIRECTLY use the PaintSelectionRect to draw
// the rectangle
// If not DoubleBuffered it is assumed that the Canvas is a Screen DC else it is
// assumed to be a Bitmap Compatiable Memory DC
var
  SelectRect: TRect;
begin
  SelectRect.TopLeft := OwnerListview.Scrollbars.MapViewToWindow(AnchorPoint);
  SelectRect.BottomRight := OwnerListview.Scrollbars.MapViewToWindow(DragPoint);
  SelectRect := ProperRect(SelectRect);
  IntersectRect(SelectRect, SelectRect, OwnerListview.ClientRect);

  if (OwnerListview.ViewSupportsHeader) and (OwnerListview.Header.Visible) then
  begin
    if SelectRect.Top < OwnerListview.Header.Height then
      SelectRect.Top := OwnerListview.Header.Height;
  end;
  
  if OwnerListview.Selection.AlphaBlendSelRect and HasMMX then
  begin
    Canvas.Brush.Color := OwnerListview.Selection.BorderColorSelRect;
    MPCommonUtilities.AlphaBlend(0, Canvas.Handle, SelectRect, Point(0, 0),
      cbmConstantAlphaAndColor, OwnerListview.Selection.BlendAlphaSelRect,
      ColorToRGB(OwnerListview.Selection.BlendColorSelRect));
    Canvas.FrameRect(SelectRect);
  end else
  begin
    Canvas.Font.Assign(OwnerListview.Font);
    Canvas.Brush.Color := OwnerListview.Color;
    Canvas.Font.Color := clBlack;
    DrawFocusRect(Canvas.Handle, SelectRect);
  end
end;

procedure TEasyDragRectManager.PaintSelectionRect(Canvas: TCanvas);
  // Causes the Selection Rectangle to be drawn or "erased" (erased is just redrawn
  // again in the same place so the XOR mode will clear the rectangle)
  // NOTE:  Use this method and not the PaintRect method directly
begin
  PaintRect(Canvas);
end;

function TEasyDragRectManager.SelRectInWindowCoords: TRect;
// Gets the selection rectangle in the current Window coordinates then trims off
// the pieces that do not lie in the current window rectangle
begin
  Result.TopLeft := OwnerListview.Scrollbars.MapViewToWindow(AnchorPoint);
  Result.BottomRight := OwnerListview.Scrollbars.MapViewToWindow(DragPoint);
  Result := ProperRect(Result);
end;

procedure TEasyDragRectManager.SetAnchorPoint(ViewportAnchor: TPoint);
begin
  FAnchorPoint := ViewportAnchor
end;

procedure TEasyDragRectManager.SetDragPoint(const Value: TPoint);
begin
  FDragPoint := Value
end;

procedure TEasyDragRectManager.UpdateAfterAutoScroll;
begin
  inherited UpdateAfterAutoScroll;
  // Keep the Dragpoint in sync with the current view
  // Need to map the dragged point to a new point after a scroll
  FDragPoint.X := DragPoint.X + (OwnerListview.Scrollbars.OffsetX - OldOffsets.X);
  FDragPoint.Y := DragPoint.Y + (OwnerListview.Scrollbars.OffsetY - OldOffsets.Y);
end;

procedure TEasyDragRectManager.WMKeyDown(var Msg: TWMKeyDown);
begin
  inherited;
end;

{ TCustomEasyListview }
constructor TCustomEasyListview.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  FGesture := TEasyGestureManager.Create(Self);
  NCCanvas := TCanvas.Create;
  FGroupFont := TFont.Create;
  FPaintInfoGroup := CreateGroupPaintInfo;
  FPaintInfoItem := CreateItemPaintInfo;
  FPaintInfoColumn := CreateColumnPaintInfo;
  PaintInfoGroup.FMarginBottom.FPaintInfo := TEasyPaintInfoGroup.Create(Self);
  FHotTrack := TEasyHotTrackManager.Create(Self);
  Canvas.Control := Self;
  ParentColor := False;
  Brush.Color := clWindow;
  Color := clWindow;
  BevelInner := bvLowered;
  BevelKind := bkTile;
  ControlStyle := ControlStyle - [csCaptureMouse, csOpaque, csAcceptsControls]; // We will do this ourselves
  FBackGround := TEasyBackgroundManager.Create(Self);
  FScrollbars := TEasyScrollbarManager.Create(Self);
  FDropTarget := TEasyDropTargetManager.Create(Self);
  FDragRect := TEasyDragRectManager.Create(Self);
  FDragManager := TEasyOLEDragManager.Create(Self);
  FCheckManager := TEasyCheckManager.Create(Self);
  FSelection := TEasySelectionManager.Create(Self);
  FGroups := CreateGroups;
  FHeader := TEasyHeader.Create(Self);
  FEditManager := TEasyEditManager.Create(Self);
  FGlobalImages := TEasyGlobalImageManager.Create(Self);
  HintInfo := TEasyHintInfo.Create(Self);
  FCellSizes := TEasyCellSizes.Create(Self);
  FSort := TEasySortManager.Create(Self);
  TabStop := True;
  FWheelMouseDefaultScroll := edwsVert;
  FWheelMouseScrollModifierEnabled := True;
  FGroupCollapseButton := TBitmap.Create;
  FGroupExpandButton := TBitmap.Create;
  FItems := TEasyGlobalItems.Create(Self);
  FShowGroupMargins := False;
  FShowThemedBorder := True;
  DisabledBlendAlpha := 128;
  DisabledBlendColor := clWindow;
  FIncrementalSearch := TEasyIncrementalSearchManager.Create(Self);
  FScratchCanvas := TControlCanvas.Create;
  FScratchCanvas.Control := Self;
  {$IFNDEF COMPILER_6_UP}
  Width := 100;
  Height := 200;
  {$ENDIF COMPILER_6_UP}
  if IsUnicode then
  begin
//    GroupFont.Name := 'MS Shell Dlg 2';
 //   Font.Name := 'MS Shell Dlg 2';
 //   Header.Font.Name := 'MS Shell Dlg 2';
  end
end;

destructor TCustomEasyListview.Destroy;
begin
  Groups.Clear; // Clear the items first so there is no chance of trying to draw them after the window is destroyed
  Header.Columns.Clear; // Clear the columns first so there is no chance of trying to draw them after the window is destroyed
  inherited Destroy;
  DropTarget := nil;
  // Don't destroy these objects until the Window is destroyed
  GroupExpandButton.Canvas.Unlock;
  GroupCollapseButton.Canvas.Unlock;
  FreeAndNil(FGesture);
  FreeAndNil(FItems);
  FreeAndNil(FNCCanvas);
  FreeAndNil(FGroupExpandButton);
  FreeAndNil(FGroupCollapseButton);
  FreeAndNil(FBackGround);
  FreeAndNil(FBackBits);
  FreeAndNil(FScrollbars);
  FreeAndNil(FDragRect);
  FreeAndNil(FDragManager);
  FreeAndNil(FCheckManager);
  FreeAndNil(FSelection);
  FreeAndNil(FHeader);
  FreeAndNil(FEditManager);
  FreeAndNil(FGlobalImages);
  FreeAndNil(FCellSizes);
  {$IFDEF COMPILER_5_UP}
  FreeAndNil(FGroupFont);   // Bug in D4
  {$ENDIF COMPILER_5_UP}
  FreeAndNil(FPaintInfoGroup);
  FreeAndNil(FPaintInfoItem);
  FreeAndNil(FPaintInfoColumn);
  FreeAndNil(FHotTrack);
  FreeAndNil(FHintInfo);
  FreeAndNil(FSort);
  FreeAndNil(FIncrementalSearch);
  FreeAndNil(FScratchCanvas);
  FreeAndNil(FGroups);  // Always make Last
end;

function TCustomEasyListview.ClickTestGroup(ViewportPoint: TPoint;
  KeyStates: TCommonKeyStates; var HitInfo: TEasyGroupHitTestInfoSet): TEasyGroup;
//
// Handles any default behavior of clicking on a group and returns true if the
// click was on a group object
//
begin
  HitInfo := [];
  Result := Groups.GroupByPoint(ViewportPoint);
  if Assigned(Result) then
    Result.HitTestAt(ViewportPoint, HitInfo);
end;

function TCustomEasyListview.ClickTestItem(ViewportPoint: TPoint;
  Group: TEasyGroup; KeyStates: TCommonKeyStates;
  var HitInfo: TEasyItemHitTestInfoSet): TEasyItem;
begin
  Result := nil;
  HitInfo := [];

  if not Assigned(Group) then
    Group := Groups.GroupByPoint(ViewportPoint);
  if Assigned(Group) then
    Result := Group.ItembyPoint(ViewportPoint);
 //   Result := Groups.ItcmbyPoint(ViewportPoint);

  if Assigned(Result) then
    Result.HitTestAt(ViewportPoint, HitInfo);
end;

function TCustomEasyListview.ClientInViewportCoords: TRect;
begin
  Result := ClientRect;
  Result.Top := Result.Top + Header.RuntimeHeight;
  Result := Scrollbars.MapWindowRectToViewRect(Result)
end;

function TCustomEasyListview.CreateColumnPaintInfo: TEasyPaintInfoBaseColumn;
begin
  Result := TEasyPaintInfoColumn.Create(Self)
end;

function TCustomEasyListview.CreateGroupPaintInfo: TEasyPaintInfoBaseGroup;
begin
  Result := TEasyPaintInfoGroup.Create(Self)
end;

function TCustomEasyListview.CreateGroups: TEasyGroups;
begin
  Result := TEasyGroups.Create(Self);
end;

function TCustomEasyListview.CreateItemPaintInfo: TEasyPaintInfoBaseItem;
begin
  Result := TEasyPaintInfoItem.Create(Self);
end;

function TCustomEasyListview.DoMouseWheel(Shift: TShiftState;
  WheelDelta: Integer; MousePos: TPoint): Boolean;
var
  LocalMouseScroll: TEasyDefaultWheelScroll;
  uiParam, pvParam: UINT;
  IsNeg: Boolean;
begin
  IsNeg := WheelDelta < 0;

  uiParam := 0;
  pvParam := 3;

  if IsWinNT then
    SystemParametersInfo(SPI_GETWHEELSCROLLLINES, uiParam, @pvParam, 0);

  if WheelMouseDefaultScroll = edwsVert then
    WheelDelta := Groups.CellHeight * Integer(pvParam) // Sroll down n Cells
  else
    WheelDelta := Groups.CellWidth {* Integer(pvParam)}; // Scroll sideways is different only one cell

  if IsNeg then
    WheelDelta := -WheelDelta;
    
  Result := inherited DoMouseWheel(Shift, WheelDelta, MousePos);

  LocalMouseScroll := WheelMouseDefaultScroll;

  if WheelMouseScrollModifierEnabled then
  begin
    if (ssShift in Shift) then
    begin
      if WheelMouseDefaultScroll = edwsVert then
        LocalMouseScroll := edwsHorz
      else
        LocalMouseScroll := edwsVert
    end;
    if ssCtrl in Shift then
    begin
      if LocalMouseScroll = edwsVert then
        WheelDelta := ClientHeight - Header.RuntimeHeight
      else
        WheelDelta := ClientWidth;
      if IsNeg then
        WheelDelta := -WheelDelta;
    end
  end;

  if LocalMouseScroll = edwsVert then
    Scrollbars.Scroll(0, -WheelDelta)
  else
  if LocalMouseScroll = edwsHorz then
    Scrollbars.Scroll(-WheelDelta, 0);

  if not Result then
    Result := True;
end;

function TCustomEasyListview.DoMouseWheelDown(Shift: TShiftState;
  MousePos: TPoint): Boolean;
begin
  Result := inherited DoMouseWheelDown(Shift, MousePos);
end;

function TCustomEasyListview.DoMouseWheelUp(Shift: TShiftState;
  MousePos: TPoint): Boolean;
begin
  Result := inherited DoMouseWheelUp(Shift, MousePos);
end;

function TCustomEasyListview.DragInitiated: Boolean;
begin
  Result := [ebcsDragSelecting, ebcsDragging, ebcsDragSelectPending, ebcsDragPending] * States <> []
end;

function TCustomEasyListview.ExecuteDragDrop(AvailableEffects: TCommonDropEffects;
  DataObjectInf: IDataObject; DropSource: IDropSource; var dwEffect: Integer): HRESULT;
begin
  Result := ActiveX.DoDragDrop(DataObjectInf, DropSource, DropEffectStatesToDropEffect(AvailableEffects), dwEffect);
end;

function TCustomEasyListview.GetGroupCollapseImage: TBitmap;
begin
  Result := FGroupCollapseButton
end;

function TCustomEasyListview.GetGroupExpandImage: TBitmap;
begin
  Result := FGroupExpandButton
end;

function TCustomEasyListview.GetHintType: TEasyHintType;
begin
  Result := HintInfo.HintType
end;

function TCustomEasyListview.GetPaintInfoColumn: TEasyPaintInfoBaseColumn;
begin
  Result := FPaintInfoColumn;
end;

function TCustomEasyListview.GetPaintInfoGroup: TEasyPaintInfoBaseGroup;
begin
  Result := FPaintInfoGroup;
end;

function TCustomEasyListview.GetPaintInfoItem: TEasyPaintInfoBaseItem;
begin
  Result := FPaintInfoItem;
end;

function TCustomEasyListview.GetScratchCanvas: TControlCanvas;
begin
  if HandleAllocated then
    Result := FScratchCanvas
  else
    Result := nil
end;

function TCustomEasyListview.GetSortColumn: TEasyColumn;
begin
  Result := Selection.FocusedColumn
end;

function TCustomEasyListview.GetTopItem: TEasyItem;
begin
  Result := Groups.FirstItemInRect( Scrollbars.MapWindowRectToViewRect(ClientRect, False))
end;

function TCustomEasyListview.GroupTestExpand(HitInfo: TEasyGroupHitTestInfoSet): Boolean;
begin
  Result := egtOnExpandButton in HitInfo
end;

function TCustomEasyListview.IsFontStored: Boolean;
begin
  Result := not ParentFont and not DesktopFont;
end;

function TCustomEasyListview.IsHeaderMouseMsg(MousePos: TSmallPoint; ForceTest: Boolean = False): Boolean;
var
  R: TRect;
begin
  Result := False;
  if not (DragRect.Dragging or DragManager.Dragging or Header.DragManager.Dragging) or ForceTest then
  begin
    R := Header.DisplayRect;
    R.Bottom := Header.RuntimeHeight;
    Result := (ViewSupportsHeader and PtInRect(R, SmallPointToPoint(MousePos))) or
      (ebcsHeaderCapture in States) or Assigned(Header.HotTrackedColumn)
  end
end;

function TCustomEasyListview.IsThumbnailView: Boolean;
begin
  Result := View in THUMBNAILVIEWS
end;

function TCustomEasyListview.IsVertView: Boolean;
begin
  Result := View in VERTICALVIEWS
end;

function TCustomEasyListview.ScrollHeaderHorz: Boolean;
begin
  Result := not (Header.ShowInAllViews and (View = elsList))
end;

function TCustomEasyListview.ToolTipNeeded(TargetObj: TEasyCollectionItem;
  var TipCaption: WideString): Boolean;
// Calcuates if the text is being truncated in the current view of the object
// If so the result is true.
var
  TextFlags: TCommonDrawTextWFlags;
  RectArray: TEasyRectArrayObject;
  R: TRect;
  LineCount: Integer;
  Item: TEasyItem;
begin
  Result := False;
  TipCaption := '';
  if TargetObj is TEasyItem then
  begin
    Item := TEasyItem( TargetObj);
    if not Item.Focused then
    begin
      Item.ItemRectArray(nil, Canvas, RectArray);
      TextFlags := [dtLeft, dtCalcRect, dtCalcRectAdjR];

      R := RectArray.LabelRect;
      LineCount := Item.View.PaintTextLineCount(Item, nil);
      Item.View.LoadTextFont(Item, 0, Canvas, False);
      DrawTextWEx(Canvas.Handle, Item.Caption, R, TextFlags, LineCount);


      if (RectWidth(RectArray.TextRect) < RectWidth(R)) or
        (RectHeight(RectArray.TextRect) < RectHeight(R)) then
      begin
        TipCaption := Item.Caption;
        Result := True;
      end
    end
  end
end;

function TCustomEasyListview.UseInternalDragImage: Boolean;
begin
  Result := True
end;

function TCustomEasyListview.ViewSupportsHeader: Boolean;
begin
  Result := (View in HEADERSUPPORTEDVIEWS) or Header.ShowInAllViews
end;

procedure TCustomEasyListview.AutoFitAllCells(VisibleOnly: Boolean);
var
  i: Integer;
begin
  case View of
    elsReport:
      begin
        BeginUpdate;
        try
          for i := 0 to Header.Columns.Count - 1 do
          begin
            if VisibleOnly then
            begin
              if Header.Columns[i].Visible then
                Header.Columns[i].AutoSizeToFit
            end else
              Header.Columns[i].AutoSizeToFit
          end
        finally
          EndUpdate
        end
      end;
    elsSmallIcon:
      begin
        BeginUpdate;
        try
          CellSizes.SmallIcon.AutoSize := True;
        finally
          EndUpdate
        end
      end;
    elsList:
      begin
        BeginUpdate;
        try
          CellSizes.List.AutoSize := True;
        finally
          EndUpdate
        end
      end;
    elsGrid:
      begin
        BeginUpdate;
        try
          CellSizes.Grid.AutoSize := True;
        finally
          EndUpdate
        end
      end;
  end
end;

procedure TCustomEasyListview.BeginUpdate;
begin
  inherited BeginUpdate;
//  Sort.BeginUpdate;
end;

procedure TCustomEasyListview.CancelCut;
var
  Item: TEasyItem;
begin
  Item := Groups.FirstItem;
  while Assigned(Item) do
  begin
    Item.Cut := False;
    Item := Groups.NextItem(Item)
  end
end;

procedure TCustomEasyListview.CheckFocus;
begin
  if not Focused then
  begin
    if CanFocus and not (csDesigning in ComponentState) and HandleAllocated then
      SetFocus;
  end;
end;

procedure TCustomEasyListview.ClearPendingDrags;
begin
  States := States - [ebcsDragPending, ebcsDragSelectPending];
  DragManager.ClearDragItem;
end;

procedure TCustomEasyListview.ClearStates;
begin
  Exclude(FStates, ebcsLButtonDown);
  Exclude(FStates, ebcsMButtonDown);
  Exclude(FStates, ebcsRButtonDown);
  Exclude(FStates, ebcsDragSelecting);
  Exclude(FStates, ebcsDragSelectPending);
  Exclude(FStates, ebcsDragSelecting);
  Exclude(FStates, ebcsDragPending);
  Exclude(FStates, ebcsDragging);
  Exclude(FStates, ebcsScrollButtonDown);
  Exclude(FStates, ebcsVCLDrag);
  Exclude(FStates, ebcsCheckboxClickPending);
  Exclude(FStates, ebcsHeaderCapture);
  Exclude(FStates, ebcsGroupExpandPending);
  if not Themed then
    CheckManager.PendingObject := nil;
end;

procedure TCustomEasyListview.ClipHeader(ACanvas: TCanvas; ResetClipRgn: Boolean);
var
  OldOrg: TPoint;
begin
  if ResetClipRgn then
    SelectClipRgn(ACanvas.Handle, 0);
  if Header.RuntimeHeight > 0 then
  begin
    SetWindowOrgEx(ACanvas.Handle, 0, 0, @OldOrg);
    if ViewSupportsHeader and (Header.Visible) then
      ExcludeClipRect(ACanvas.Handle, 0, 0, ClientWidth, Header.RuntimeHeight);
    SetWindowOrgEx(ACanvas.Handle, OldOrg.X, OldOrg.Y, nil);
  end
end;

procedure TCustomEasyListview.CMDrag(var Msg: TCMDrag);
// Called during a VCL drag and drop operation
var
  Keys: TCommonKeyStates;
  P: TPoint;
  Effects: TCommonDropEffect;

  {$IFDEF LOG_VCL_CMDRAG}
  F: TFileStream;
  S: string;
  Buffer: array[0..MAX_PATH] of char;
  {$ENDIF}
begin

  {$IFDEF LOG_VCL_CMDRAG}
  FillChar(Buffer, SizeOf(Buffer), #0);
  GetModuleFileName(hInstance, Buffer, MAX_PATH);
  S := ExtractFilePath(Buffer) + 'VCL Drag.log';
  try
    F := TFileStream.Create(S, fmOpenReadWrite or fmShareExclusive);
  except
    F := TFileStream.Create(S, fmCreate   or fmShareExclusive);
  end;
  F.Seek(0, soFromEnd);

  case Msg.DragMessage of
    dmDragEnter:  S := 'dmDragEnter';
    dmDragLeave:  S := 'dmDragLeave';
    dmDragMove:   S := 'dmDragMove';
    dmDragDrop:   S := 'dmDragDrop';
    dmDragCancel: S := 'dmDragCancel';
    dmFindTarget: S := 'dmFindTarget';
  end;
  S := S + #13+#10;
  F.Write(PChar(S)^, Length(S));
  F.Free;
  {$ENDIF}

  Keys := [];
  Effects := cdeNone;
  case Msg.DragMessage of
    dmDragEnter:
      begin
        DragManager.DragEnter(nil, nil, ScreenToClient(Msg.DragRec^.Pos), Keys, Effects);
        inherited;
      end;
    dmDragLeave:
      begin
        // The VCL DD stupidly sends a Drag Leave before the Drag Drop.  This
        // causes problems since I use DragEnd to reset a lot of the DragManagers
        // flags.  Check if the drop was on this window.  If it was skip the
        // Drag end and wait for the Drag Drop
        GetCursorPos(P);
        if WindowFromPoint(P) <> Handle then
        begin
          DragManager.DragEnd(nil, ScreenToClient(Msg.DragRec^.Pos), Keys);
          ClearStates;
        end;
        inherited;
      end;
    dmDragMove:
    begin
      DragManager.Drag(nil, ScreenToClient(Msg.DragRec^.Pos), Keys, Effects);
      inherited;
    end;
    dmDragDrop:
      begin
        inherited;
        DragManager.DragDrop(ScreenToClient(Msg.DragRec^.Pos), Keys, Effects);
        ClearStates;
      end;
    dmDragCancel:
      begin
        DragManager.DragEnd(nil, ScreenToClient(Msg.DragRec^.Pos), Keys);
        inherited;
        ClearStates;
      end;
  else
    inherited;
  end;
end;

procedure TCustomEasyListview.CMHintShow(var Message: TCMHintShow);
var
  Allow: Boolean;
  TargetObj: TEasyCollectionItem;
  ItemHitInfo: TEasyItemHitTestInfoSet;
  GroupHitInfo: TEasyGroupHitTestInfoSet;
  ViewPt: TPoint;
begin
  {$IFDEF GXDEBUG_HINT}
  SendDebug('CMHintShow');
  {$ENDIF GXDEBUG_HINT}

  ViewPt := Scrollbars.MapWindowToView(Message.HintInfo^.CursorPos);

  // See if we have hit any objects of interest
  TargetObj := Groups.ItembyPoint(ViewPt);
  if Assigned(TargetObj) then
  begin
    TEasyItem( TargetObj).HitTestAt(ViewPt, ItemHitInfo);
    if not(ehtOnClickselectBounds in ItemHitInfo) then
      TargetObj := nil
  end;

  if not Assigned(TargetObj) then
  begin
    TargetObj := Groups.GroupByPoint(ViewPt);
    if Assigned(TargetObj) then
      TEasyGroup( TargetObj).HitTestAt(ViewPt, GroupHitInfo);
  end;

  // Default hint type is Text
  FHintData.HintType := HintInfo.HintType;

  FHintData.HintStr := Hint;
  FHintData.ReshowTimeout := Message.HintInfo^.ReshowTimeout;
  FHintData.HideTimeout := Message.HintInfo^.HideTimeout;

  // Allow the application to modify the HintData
  Allow := False;
  DoHintPopup(TargetObj, FHintData.HintType, Message.HintInfo^.CursorPos, FHintData.HintStr, FHintData.HideTimeout, FHintData.ReshowTimeout, Allow);
  if FHintData.HintStr = '' then
    Allow := False;

  Message.HintInfo^.ReshowTimeout := FHintData.ReshowTimeout;
  Message.HintInfo^.HideTimeout := FHintData.HideTimeout;

  SetRect(FHintData.ToolTipRect, 0, 0, Screen.Width, 0);

  // If the type is ToolTip then calculate if it is necessary
  if (FHintData.HintType = ehtToolTip) then
  begin
    if TargetObj is TEasyItem then
      Allow := ToolTipNeeded(TargetObj, FHintData.HintStr)
    else
      Allow := False;
  end;

  if Allow then
  begin
    Message.HintInfo.HintWindowClass := TEasyHintWindow;

    // Send our HintData to the Hint Window through the supplied Data parameter
    Message.HintInfo.HintData := @FHintData;
    if Message.HintInfo.HintStr = '' then
      Message.HintInfo.HintStr := 'Dummy';

    // Area where the tip is defined, once outside the Hint testing will resume
    if TargetObj is TEasyItem then
      Message.HintInfo^.CursorRect := TargetObj.DisplayRect;

    if TargetObj is TEasyGroup then
    begin
      if egtOnRightMargin in GroupHitInfo then
        Message.HintInfo^.CursorRect := TEasyGroup( TargetObj).BoundsRectRightMargin
      else
      if egtOnLeftMargin in GroupHitInfo then
        Message.HintInfo^.CursorRect := TEasyGroup( TargetObj).BoundsRectLeftMargin
      else
      if egtOnHeader in GroupHitInfo then
        Message.HintInfo^.CursorRect := TEasyGroup( TargetObj).BoundsRectTopMargin
      else
      if egtOnFooter in GroupHitInfo then
        Message.HintInfo^.CursorRect := TEasyGroup( TargetObj).BoundsRectBottomMargin;
    end;

    Message.HintInfo^.CursorRect := Scrollbars.MapViewRectToWindowRect(Message.HintInfo^.CursorRect);

    FHintData.Listview := Self;
    FHintData.HintControl := Message.HintInfo^.HintControl;
    FHintData.HintWindowClass := Message.HintInfo^.HintWindowClass;
    FHintData.HintPos := Message.HintInfo^.HintPos;
    FHintData.HintMaxWidth := Message.HintInfo^.HintMaxWidth;
    FHintData.HintColor := Message.HintInfo^.HintColor;
    FHintData.CursorRect := Message.HintInfo^.CursorRect;
    FHintData.CursorPos := Message.HintInfo^.CursorPos;
    FHintData.HintData := nil;
    FHintData.TargetObj := TargetObj;
    inherited;
  end else
    Message.Result := 1;   // Message is handled, don't show the hint
end;

procedure TCustomEasyListview.CMHintShowPause(var Message: TCMHintShow);
var
  HintShowing: Boolean;
  PauseTime: PInteger;
begin
  {$IFDEF GXDEBUG_HINT}
  SendDebug('CMHintShowPause');
  {$ENDIF GXDEBUG_HINT}
  PauseTime := PInteger(TMessage(Message).LParam);
  HintShowing := Boolean( TMessage(Message).wParam);
  DoHintShowPause(HintShowing, PauseTime^);
  inherited
end;

procedure TCustomEasyListview.CMMouseWheel(var Msg: TWMMouseWheel);
begin
  inherited
end;

procedure TCustomEasyListview.CMParentFontChanged(var Msg: TMessage);
begin
  inherited;
  if ParentFont then
  begin
    if Msg.wParam <> 0 then
    begin
      GroupFont.Assign(TFont(Msg.lParam));
      Header.Font.Assign(TFont(Msg.lParam));
    end else
    begin
      GroupFont.Assign(TWinControlCracker(Parent).Font);
      Header.Font.Assign(TWinControlCracker(Parent).Font);
    end
  end
end;

procedure TCustomEasyListview.CopyToClipboard;
begin

end;

procedure TCustomEasyListview.CreateParams(var Params: TCreateParams);
begin
  inherited;
  Params.WindowClass.Style := Params.WindowClass.Style or CS_DBLCLKS and not (CS_HREDRAW or CS_VREDRAW);
  Params.Style := Params.Style or WS_CLIPCHILDREN  or WS_CLIPSIBLINGS;
  AddBiDiModeExStyle(Params.ExStyle);
end;

procedure TCustomEasyListview.CreateWnd;
begin
  inherited;
  DoUpdate;
  // The Window Handle is now valid
  DragManager.Registered := DragManager.Enabled;
  Header.DragManager.Registered := Header.DragManager.Enabled;
  ResizeBackBits(ClientWidth, ClientHeight);
end;

procedure TCustomEasyListview.CutToClipboard;
var
  Handled, Mark: Boolean;
begin
  Handled := False;
  Mark := True;
  DoClipboardCut(Handled, Mark);
  if Handled and Mark then
    MarkSelectedCut
end;

procedure TCustomEasyListview.DestroyWnd;
begin
  inherited;
end;

procedure TCustomEasyListview.DoAfterPaint(ACanvas: TCanvas; ClipRect: TRect);
begin
  if Assigned(OnAfterPaint) then
    OnAfterPaint(Self, ACanvas, ClipRect)
end;

procedure TCustomEasyListview.DoAutoGroupGetKey(Item: TEasyItem; ColumnIndex: Integer; Groups: TEasyGroups; var Key: LongWord);
begin
  if Assigned(OnAutoGroupGetKey) then
    OnAutoGroupGetKey(Self, Item, ColumnIndex, Groups, Key)
end;

procedure TCustomEasyListview.DoAutoSortGroupCreate(Item: TEasyItem; ColumnIndex: Integer; Groups: TEasyGroups; var Group: TEasyGroup; var DoDefaultAction: Boolean);
begin
  if Assigned(OnAutoSortGroupCreate) then
    OnAutoSortGroupCreate(Self, Item, ColumnIndex, Groups, Group, DoDefaultAction);
  if not Assigned(Group) then
    Group := Groups.Add
end;

procedure TCustomEasyListview.DoClipboardCopy(var Handled: Boolean);
begin
  if Assigned(OnClipboardCopy) then
    OnClipboardCopy(Self, Handled);
end;

procedure TCustomEasyListview.DoClipboardCut(var MarkAsCut, Handled: Boolean);
begin
  if Assigned(OnClipboardCut) then
    OnClipboardCut(Self, MarkAsCut, Handled);
end;

procedure TCustomEasyListview.DoClipboardPaste(var Handled: Boolean);
begin
  if Assigned(OnClipboardPaste) then
    OnClipboardPaste(Self, Handled);
end;

procedure TCustomEasyListview.DoColumnCheckChanged(Column: TEasyColumn);
begin
  if Assigned(OnColumnCheckChanged) and not (csDestroying in ComponentState) then
    OnColumnCheckChanged(Self, Column)
end;

procedure TCustomEasyListview.DoColumnCheckChanging(Column: TEasyColumn; var Allow: Boolean);
begin
  if Assigned(OnColumnCheckChanging) and not (csDestroying in ComponentState) then
    OnColumnCheckChanging(Self, Column, Allow)
end;

procedure TCustomEasyListview.DoColumnClick(Button: TCommonMouseButton;
  const Column: TEasyColumn);
begin
  if Assigned(OnColumnClick) then
    OnColumnClick(Self, Button, Column)
end;

procedure TCustomEasyListview.DoColumnContextMenu(HitInfo: TEasyHitInfoColumn; WindowPoint: TPoint; var Menu: TPopupMenu);
begin
  if Assigned(OnColumnContextMenu) then
    OnColumnContextMenu(Self, HitInfo, WindowPoint, Menu);
end;

procedure TCustomEasyListview.DoColumnDblClick(Button: TCommonMouseButton; ShiftState: TShiftState; MousePos: TPoint; Column: TEasyColumn);
begin
  if Assigned(OnColumnDblClick) then
    OnColumnDblClick(Self, Button, MousePos, Column)
end;

procedure TCustomEasyListview.DoColumnDropDownButtonClick(Column: TEasyColumn; Button: TCommonMouseButton; ShiftState: TShiftState; MousePos: TPoint; var DoDefault: Boolean);
begin
  if Assigned(OnColumnDropDownButtonClick) then
    OnColumnDropDownButtonClick(Self, Column, Button, ShiftState, MousePos, DoDefault)
end;

procedure TCustomEasyListview.DoColumnEnableChanged(Column: TEasyColumn);
begin
  if Assigned(OnColumnEnableChanged) and not (csDestroying in ComponentState) then
    OnColumnEnableChanged(Self, Column)
end;

procedure TCustomEasyListview.DoColumnEnableChanging(Column: TEasyColumn; var Allow: Boolean);
begin
  if Assigned(OnColumnEnableChanging) and not (csDestroying in ComponentState) then
    OnColumnEnableChanging(Self, Column, Allow)
end;

procedure TCustomEasyListview.DoColumnFocusChanged(Column: TEasyColumn);
begin
  if Assigned(OnColumnFocusChanged) and not (csDestroying in ComponentState) then
    OnColumnFocusChanged(Self, Column)
end;

procedure TCustomEasyListview.DoColumnFocusChanging(Column: TEasyColumn; var Allow: Boolean);
begin
  if Assigned(OnColumnFocusChanging) and not (csDestroying in ComponentState) then
    OnColumnFocusChanging(Self, Column, Allow)
end;

procedure TCustomEasyListview.DoColumnFreeing(Column: TEasyColumn);
begin
  if Assigned(OnColumnFreeing) then
    OnColumnFreeing(Self, Column)
end;

procedure TCustomEasyListview.DoColumnGetCaption(Column: TEasyColumn; Line: Integer; var Caption: WideString);
begin
  if Assigned(OnColumnGetCaption) then
   OnColumnGetCaption(Self, Column, Line, Caption)
end;

procedure TCustomEasyListview.DoColumnGetDetail(Column: TEasyColumn; Line: Integer; var Detail: Integer);
begin
  if Assigned(OnColumnGetDetail) then
    OnColumnGetDetail(Self, Column, Line, Detail)
end;

procedure TCustomEasyListview.DoColumnGetDetailCount(Column: TEasyColumn; var Count: Integer);
begin
  if Assigned(OnColumnGetDetailCount) then
    OnColumnGetDetailCount(Self, Column, Count)
end;

procedure TCustomEasyListview.DoColumnGetImageIndex(Column: TEasyColumn; ImageKind: TEasyImageKind; var ImageIndex: TCommonImageIndexInteger);
begin
  if Assigned(OnColumnGetImageIndex) then
    OnColumnGetImageIndex(Self, Column, ImageKind, ImageIndex)
end;

procedure TCustomEasyListview.DoColumnGetImageList(Column: TEasyColumn; var ImageList: TCustomImageList);
begin
  if Assigned(OnColumnGetImageList) then
    OnColumnGetImageList(Self, Column, ImageList)
end;

procedure TCustomEasyListview.DoColumnImageDraw(Column: TEasyColumn; ACanvas: TCanvas; const RectArray: TEasyRectArrayObject; AlphaBlender: TEasyAlphaBlender);
begin
  if Assigned(OnColumnImageDraw) then
    OnColumnImageDraw(Self, Column, ACanvas, RectArray, AlphaBlender)
end;

procedure TCustomEasyListview.DoColumnImageGetSize(Column: TEasyColumn; var ImageWidth, ImageHeight: Integer);
begin
  if Assigned(OnColumnImageGetSize) then
    OnColumnImageGetSize(Self, Column, ImageWidth, ImageHeight)
end;

procedure TCustomEasyListview.DoColumnImageDrawIsCustom(Column: TEasyColumn;
  var IsCustom: Boolean);
begin
  if Assigned(OnColumnImageDrawIsCustom) then
    OnColumnImageDrawIsCustom(Self, Column, IsCustom)
end;

procedure TCustomEasyListview.DoColumnInitialize(Column: TEasyColumn);
begin
  if Assigned(OnColumnInitialize) then
    OnColumnInitialize(Self, Column)
end;

procedure TCustomEasyListview.DoColumnLoadFromStream(Column: TEasyColumn; S: TStream; Version: Integer);
begin
  if Assigned(OnColumnLoadFromStream) then
    OnColumnLoadFromStream(Self, Column, S, Version);
end;

procedure TCustomEasyListview.DoColumnPaintText(Column: TEasyColumn;
  ACanvas: TCanvas);
begin
  if Assigned(OnColumnPaintText) then
    OnColumnPaintText(Self, Column, ACanvas)
end;

procedure TCustomEasyListview.DoColumnSaveToStream(Column: TEasyColumn; S: TStream; Version: Integer);
begin
  if Assigned(OnColumnSaveToStream) then
    OnColumnSaveToStream(Self, Column, S, Version);
end;

procedure TCustomEasyListview.DoColumnSelectionChanged(Column: TEasyColumn);
begin
  if Assigned(OnColumnSelectionChanged) and not (csDestroying in ComponentState) then
    OnColumnSelectionChanged(Self, Column)
end;

procedure TCustomEasyListview.DoColumnSelectionChanging(Column: TEasyColumn; var Allow: Boolean);
begin
  if Assigned(OnColumnSelectionChanging) and not (csDestroying in ComponentState) then
    OnColumnSelectionChanging(Self, Column, Allow)
end;

procedure TCustomEasyListview.DoColumnSetCaption(Column: TEasyColumn; const Caption: WideString);
begin
  if Assigned(OnColumnSetCaption) then
    OnColumnSetCaption(Self, Column, Caption)
end;

procedure TCustomEasyListview.DoColumnSetDetail(Column: TEasyColumn; Line: Integer; Detail: Integer);
begin
  if Assigned(OnColumnSetDetail) then
    OnColumnSetDetail(Self, Column, Line, Detail)
end;

procedure TCustomEasyListview.DoColumnSetDetailCount(Column: TEasyColumn; DetailCount: Integer);
begin
  ///
end;

procedure TCustomEasyListview.DoColumnSetImageIndex(Column: TEasyColumn; ImageKind: TEasyImageKind; ImageIndex: Integer);
begin
  if Assigned(OnColumnSetImageIndex) then
    OnColumnSetImageIndex(Self, Column, ImageKind, ImageIndex)
end;

procedure TCustomEasyListview.DoColumnSizeChanged(Column: TEasyColumn);
begin
  if Assigned(OnColumnSizeChanged) and not (csDestroying in ComponentState) then
    OnColumnSizeChanged(Self, Column)
end;

procedure TCustomEasyListview.DoColumnSizeChanging(Column: TEasyColumn; Size, NewSize: Integer; var Allow: Boolean);
begin
  if Assigned(OnColumnSizeChanging) and not (csDestroying in ComponentState) then
    OnColumnSizeChanging(Self, Column, Size, NewSize, Allow)
end;

procedure TCustomEasyListview.DoColumnStructureChange;
begin
  if Assigned(OnColumnStructureChange) then
    OnColumnStructureChange(Self)
end;

procedure TCustomEasyListview.DoColumnThumbnailDraw(Column: TEasyColumn; ACanvas: TCanvas; ARect: TRect; var DoDefault: Boolean);
begin

end;

procedure TCustomEasyListview.DoColumnVisibilityChanged(Column: TEasyColumn);
begin
  if Assigned(OnColumnVisibilityChanged) then
    OnColumnVisibilityChanged(Self, Column)
end;

procedure TCustomEasyListview.DoColumnVisibilityChanging(Column: TEasyColumn; var Allow: Boolean);
begin
  if Assigned(OnColumnVisibilityChanging) then
    OnColumnVisibilityChanging(Self, Column, Allow)
end;

procedure TCustomEasyListview.DoContextMenu(MousePt: TPoint; var Handled: Boolean);
begin
  if Assigned(OnContextMenu) then
    OnContextMenu(Self, MousePt, Handled)
end;

procedure TCustomEasyListview.DoColumnCustomView(Column: TEasyColumn; var ViewClass: TEasyViewColumnClass);
begin
  if Assigned(OnColumnCustomView) then
    OnColumnCustomView(Self, Column, ViewClass);
end;

procedure TCustomEasyListview.DoCustomGrid(Group: TEasyGroup; ViewStyle: TEasyListStyle; var Grid: TEasyGridGroupClass);
begin
  if Assigned(OnCustomGrid) then
    OnCustomGrid(Self, Group, ViewStyle, Grid)
end;

procedure TCustomEasyListview.DoGesture(Gesture: WideString;
  var Handled: Boolean);
begin
  if Assigned(OnMouseGesture) then
    OnMouseGesture(Self, Gesture, Handled)
end;

procedure TCustomEasyListview.DoGroupCustomView(Group: TEasyGroup; ViewStyle: TEasyListStyle; var View: TEasyViewGroupClass);
begin
  if Assigned(OnGroupCustomView) then
    OnGroupCustomView(Self, Group, ViewStyle, View)
end;

procedure TCustomEasyListview.DoGroupStructureChange;
begin
  if Assigned(OnGroupStructureChange) then
    OnGroupStructureChange(Self)
end;

procedure TCustomEasyListview.DoItemCustomView(Item: TEasyItem; ViewStyle: TEasyListStyle; var View: TEasyViewItemClass);
begin
  if Assigned(OnItemCustomView) then
    OnItemCustomView(Self, Item, ViewStyle, View)
end;

procedure TCustomEasyListview.DoDblClick(Button: TCommonMouseButton; MousePos: TPoint;
  ShiftState: TShiftState);
begin
  if Assigned(OnDblClick) then
    OnDblClick(Self, Button, MousePos, ShiftState)
end;

procedure TCustomEasyListview.DoGetDragImage(Bitmap: TBitmap;
  DragStartPt: TPoint; var HotSpot: TPoint; var TransparentColor: TColor;
  var Handled: Boolean);
begin
  if Assigned(OnGetDragImage) then
    OnGetDragImage(Self, Bitmap, DragStartPt, HotSpot, TransparentColor, Handled);
end;

procedure TCustomEasyListview.DoGroupClick(Group: TEasyGroup; KeyStates: TCommonKeyStates; HitTest: TEasyGroupHitTestInfoSet);
begin
  if Assigned(OnGroupClick) then
    OnGroupClick(Self, Group, KeyStates, HitTest)
end;

procedure TCustomEasyListview.DoGroupCollapse(Group: TEasyGroup);
begin
  if Assigned(OnGroupCollapse) and not (csDestroying in ComponentState) then
    OnGroupCollapse(Self, Group);
end;

procedure TCustomEasyListview.DoGroupCollapsing(Group: TEasyGroup; var Allow: Boolean);
begin
  if Assigned(OnGroupCollapsing) and not (csDestroying in ComponentState) then
    OnGroupCollapsing(Self, Group, Allow);
end;

function TCustomEasyListview.DoGroupCompare(Column: TEasyColumn; Group1,
  Group2: TEasyGroup): Integer;
begin
  if Assigned(OnGroupCompare) then
    Result := OnGroupCompare(Self, Group1, Group2)
  else
    Result := DefaultSort(Column, Group1, Group2)
end;

procedure TCustomEasyListview.DoGroupContextMenu(HitInfo: TEasyHitInfoGroup;
  WindowPoint: TPoint; var Menu: TPopupMenu; var Handled: Boolean);
begin
  if Assigned(OnGroupContextMenu) then
    OnGroupContextMenu(Self, HitInfo, WindowPoint, Menu, Handled);
end;

procedure TCustomEasyListview.DoGroupDblClick(Button: TCommonMouseButton;
  MousePos: TPoint; HitInfo: TEasyHitInfoGroup);
begin
  if Assigned(OnGroupDblClick) then
    OnGroupDblClick(Self, Button, MousePos, HitInfo)
end;

procedure TCustomEasyListview.DoGroupExpand(Group: TEasyGroup);
begin
  Groups.Rebuild;
  if Assigned(OnGroupExpand) then
    OnGroupExpand(Self, Group)
end;

procedure TCustomEasyListview.DoGroupExpanding(Group: TEasyGroup; var Allow: Boolean);
begin
  if Assigned(OnGroupExpanding) then
    OnGroupExpanding(Self, Group, Allow)
end;

procedure TCustomEasyListview.DoGroupHotTrack(Group: TEasyGroup; State: TEasyHotTrackstate; MousePos: TPoint);
begin
  if Assigned(OnGroupHotTrack) then
    OnGroupHotTrack(Self, Group, State, MousePos)
end;

procedure TCustomEasyListview.DoGroupFreeing(Group: TEasyGroup);
begin
  if Assigned(OnGroupFreeing) then
    OnGroupFreeing(Self, Group)
end;

procedure TCustomEasyListview.DoGroupGetCaption(Group: TEasyGroup; var Caption: WideString);
begin
  if Assigned(OnGroupGetCaption) then
   OnGroupGetCaption(Self, Group, Caption)
end;

procedure TCustomEasyListview.DoGroupGetDetail(Group: TEasyGroup; Line: Integer; var Detail: Integer);
begin
  if Assigned(OnGroupGetDetail) then
   OnGroupGetDetail(Self, Group, Line, Detail)
end;

procedure TCustomEasyListview.DoGroupGetDetailCount(Group: TEasyGroup; var Count: Integer);
begin
  if Assigned(OnGroupGetDetailCount) then
   OnGroupGetDetailCount(Self, Group, Count)
end;

procedure TCustomEasyListview.DoGroupGetImageIndex(Group: TEasyGroup; ImageKind: TEasyImageKind; var ImageIndex: TCommonImageIndexInteger);
begin
  if Assigned(OnGroupGetImageIndex) then
    OnGroupGetImageIndex(Self, Group, ImageKind, ImageIndex)
end;

procedure TCustomEasyListview.DoGroupGetImageList(Group: TEasyGroup; var ImageList: TCustomImageList);
begin
  if Assigned(OnGroupGetImageList) then
    OnGroupGetImageList(Self, Group, ImageList)
end;

procedure TCustomEasyListview.DoGroupImageDraw(Group: TEasyGroup; ACanvas: TCanvas; const RectArray: TEasyRectArrayObject; AlphaBlender: TEasyAlphaBlender);
begin
  if Assigned(OnGroupImageDraw) then
    OnGroupImageDraw(Self, Group, ACanvas, RectArray, AlphaBlender)
end;

procedure TCustomEasyListview.DoGroupImageGetSize(Group: TEasyGroup; var ImageWidth, ImageHeight: Integer);
begin
  if Assigned(OnGroupImageGetSize) then
    OnGroupImageGetSize(Self, Group, ImageWidth, ImageHeight)
end;

procedure TCustomEasyListview.DoGroupImageDrawIsCustom(Group: TEasyGroup;
  var IsCustom: Boolean);
begin
  if Assigned(OnGroupImageDrawIsCustom) then
    OnGroupImageDrawIsCustom(Self, Group, IsCustom)
end;

procedure TCustomEasyListview.DoGroupInitialize(Group: TEasyGroup);
begin
  if Assigned(OnGroupInitialize) then
    OnGroupInitialize(Self, Group)
end;

procedure TCustomEasyListview.DoGroupLoadFromStream(Group: TEasyGroup; S: TStream; Version: Integer);
begin
  if Assigned(OnGroupLoadFromStream) then
    OnGroupLoadFromStream(Self, Group, S, Version)
end;

procedure TCustomEasyListview.DoGroupPaintText(Group: TEasyGroup;
  ACanvas: TCanvas);
begin
  if Assigned(OnGroupPaintText) then
    OnGroupPaintText(Self, Group, ACanvas)
end;

procedure TCustomEasyListview.DoGroupSaveToStream(Group: TEasyGroup; S: TStream; Version: Integer);
begin
  if Assigned(OnGroupSaveToStream) then
    OnGroupSaveToStream(Self, Group, S, Version)
end;

procedure TCustomEasyListview.DoGroupSelectionChanged(Group: TEasyGroup);
begin
  if Assigned(OnGroupSelectionChanged) then
    OnGroupSelectionChanged(Self, Group)
end;

procedure TCustomEasyListview.DoGroupSelectionChanging(Group: TEasyGroup; var Allow: Boolean);
begin
  if Assigned(OnGroupSelectionChanging) then
    OnGroupSelectionChanging(Self, Group, Allow)
end;

procedure TCustomEasyListview.DoGroupSetCaption(Group: TEasyGroup; const Caption: WideString);
begin
  if Assigned(OnGroupSetCaption) then
    OnGroupSetCaption(Self, Group, Caption)
end;

procedure TCustomEasyListview.DoGroupSetDetailCount(Group: TEasyGroup; DetailCount: Integer);
begin
  ///
end;

procedure TCustomEasyListview.DoGroupSetImageIndex(Group: TEasyGroup; ImageKind: TEasyImageKind; ImageIndex: Integer);
begin
  if Assigned(OnGroupSetImageIndex) then
    OnGroupSetImageIndex(Self, Group, ImageKind, ImageIndex)
end;

procedure TCustomEasyListview.DoGroupSetDetail(Group: TEasyGroup; Line: Integer; Detail: Integer);
begin
  if Assigned(OnGroupSetDetail) then
    OnGroupSetDetail(Self, Group, Line, Detail)
end;

procedure TCustomEasyListview.DoGroupThumbnailDraw(Group: TEasyGroup; ACanvas: TCanvas; ARect: TRect; AlphaBlender: TEasyAlphaBlender; var DoDefault: Boolean);
begin
// not implemented
end;

procedure TCustomEasyListview.DoGroupVisibilityChanged(Group: TEasyGroup);
begin
  if Assigned(OnGroupVisibilityChanged) and not (csDestroying in ComponentState) then
    OnGroupVisibilityChanged(Self, Group)
end;

procedure TCustomEasyListview.DoGroupVisibilityChanging(Group: TEasyGroup; var Allow: Boolean);
begin
  if Assigned(OnGroupVisibilityChanging) and not (csDestroying in ComponentState) then
    OnGroupVisibilityChanging(Self, Group, Allow)
end;

procedure TCustomEasyListview.DoHeaderDblClick(Button: TCommonMouseButton; MousePos: TPoint;
  ShiftState: TShiftState);
begin
  if Assigned(OnHeaderDblClick) then
    OnHeaderDblClick(Self, Button, MousePos, ShiftState)
end;

procedure TCustomEasyListview.DoHintCustomInfo(TargetObj: TEasyCollectionItem; const Info: TEasyHintInfo);
begin
  if Assigned(OnHintCustomInfo) then
    OnHintCustomInfo(Self, TargetObj, Info);
end;

procedure TCustomEasyListview.DoHintCustomDraw(TargetObj: TEasyCollectionItem; const Info: TEasyHintInfo);
begin
  if Assigned(OnHintCustomDraw) then
    OnHintCustomDraw(Self, TargetObj, Info);
end;

procedure TCustomEasyListview.DoHintPopup(TargetObj: TEasyCollectionItem; HintType: TEasyHintType; MousePos: TPoint; var AText: WideString; var HideTimeout, ReshowTimeout: Integer; var Allow: Boolean);
begin
  if Assigned(OnHintPopup) then
    OnHintPopup(Self, TargetObj, HintType, MousePos, AText, HideTimeout, ReshowTimeout, Allow)
end;

procedure TCustomEasyListview.DoHintShowPause(HintShowingNow: Boolean;
  var PauseTime: Integer);
begin
  if Assigned(OnHintPauseTime) then
    OnHintPauseTime(Self, HintShowingNow, PauseTime)
end;

procedure TCustomEasyListview.DoIncrementalSearch(Item: TEasyItem;
  const SearchBuffer: WideString; var CompareResult: Integer);
var
  Handled: Boolean;
begin
  CompareResult := 0;
  Handled := False;
  if Assigned(OnIncrementalSearch) then
    OnIncrementalSearch(Item, SearchBuffer, Handled, CompareResult);
  if not Handled then
    CompareResult := WideIncrementalSearch(Item.Caption, SearchBuffer)
end;

procedure TCustomEasyListview.DoItemCheckChanged(Item: TEasyItem);
begin
  if Assigned(OnItemCheckChange) and not (csDestroying in ComponentState) then
    OnItemCheckChange(Self, Item)
end;

procedure TCustomEasyListview.DoItemCheckChanging(Item: TEasyItem;
  var Allow: Boolean);
begin
  if Assigned(OnItemCheckChanging) and not (csDestroying in ComponentState) then
    OnItemCheckChanging(Self, Item, Allow);
end;

procedure TCustomEasyListview.DoItemClick(Item: TEasyItem;
  KeyStates: TCommonKeyStates; HitInfo: TEasyItemHitTestInfoSet);
begin
  if Assigned(OnItemClick) then
    OnItemClick(Self, Item, KeyStates, HitInfo)
end;

function TCustomEasyListview.DoItemCompare(Column: TEasyColumn;
  Group: TEasyGroup; Item1, Item2: TEasyItem): Integer;
var
  DoDefault: Boolean;
begin
  Result := 0;
  DoDefault := True;
  if Assigned(OnItemCompare) then
    Result := OnItemCompare(Self, Column, Group, Item1, Item2, DoDefault);
  if DoDefault then
    Result := DefaultSort(Column, Item1, Item2)
end;

procedure TCustomEasyListview.DoItemContextMenu(HitInfo: TEasyHitInfoItem;
  WindowPoint: TPoint; var Menu: TPopupMenu; var Handled: Boolean);
begin
  Menu := nil;
  if Assigned(OnItemContextMenu) then
    OnItemContextMenu(Self, HitInfo, WindowPoint, Menu, Handled);
end;

procedure TCustomEasyListview.DoItemCreateEditor(Item: TEasyItem;
  var Editor: IEasyCellEditor);
begin
  if Assigned(OnItemCreateEditor) then
    OnItemCreateEditor(Self, Item, Editor);
  if not Assigned(Editor) then
  begin
    if View in MULTILINEVIEWS then
      Editor := TEasyMemoEditor.Create
    else
      Editor := TEasyStringEditor.Create;
  end
end;

procedure TCustomEasyListview.DoItemDblClick(Button: TCommonMouseButton;
  MousePos: TPoint; HitInfo: TEasyHitInfoItem);
begin
  if Assigned(OnItemDblClick) then
    OnItemDblClick(Self, Button, MousePos, HitInfo)
end;

procedure TCustomEasyListview.DoItemEditBegin(Item: TEasyItem; var Column: Integer; var Allow: Boolean);
begin
  if Assigned(OnItemEditBegin) then
    OnItemEditBegin(Self, Item, Column, Allow)
end;

procedure TCustomEasyListview.DoItemEdited(Item: TEasyItem;
  var NewValue: Variant; var Accept: Boolean);
begin
  if Assigned(OnItemEdited) then
    OnItemEdited(Self, Item, NewValue, Accept);
end;

procedure TCustomEasyListview.DoItemEditEnd(Item: TEasyItem);
begin
  if Assigned(OnItemEditEnd) then
    OnItemEditEnd(Self, Item);
end;

procedure TCustomEasyListview.DoItemEnableChanged(Item: TEasyItem);
begin
  if Assigned(OnItemEnableChange) then
    OnItemEnableChange(Self, Item)
end;

procedure TCustomEasyListview.DoItemEnableChanging(Item: TEasyItem;
  var Allow: Boolean);
begin
  if Assigned(OnItemEnableChanging) then
    OnItemEnableChanging(Self, Item, Allow)
end;

procedure TCustomEasyListview.DoItemFocusChanged(Item: TEasyItem);
begin
  if Assigned(OnItemFocusChanged) then
    OnItemFocusChanged(Self, Item)
end;

procedure TCustomEasyListview.DoItemFocusChanging(Item: TEasyItem;
  var Allow: Boolean);
begin
  if Assigned(OnItemFocusChanging) then
    OnItemFocusChanging(Self, Item, Allow)
end;

procedure TCustomEasyListview.DoItemFreeing(Item: TEasyItem);
begin
  if Assigned(OnItemFreeing) then
    OnItemFreeing(Self, Item)
end;

procedure TCustomEasyListview.DoItemGetCaption(Item: TEasyItem; Column: Integer; var ACaption: WideString);
begin
  if Assigned(OnItemGetCaption) then
    OnItemGetCaption(Self, Item, Column, ACaption)
end;

procedure TCustomEasyListview.DoItemGetEditCaption(Item: TEasyItem; Column: TEasyColumn; var Caption: WideString);
begin
  if Assigned(OnItemGetEditCaption) then
    OnItemGetEditCaption(Self, Item, Column, Caption)
end;

procedure TCustomEasyListview.DoItemGetEditMenu(Editor: TEasyBaseEditor; var Menu: TPopupMenu);
begin
  if Assigned(OnItemGetEditMenu) then
    OnItemGetEditMenu(Self, Editor, Menu)
end;

procedure TCustomEasyListview.DoItemGetGroupKey(Item: TEasyItem;
  FocusedColumn: Integer; var Key: LongWord);
begin
  if Assigned(OnItemGetGroupKey) then
    OnItemGetGroupKey(Self, Item, FocusedColumn, Key)
end;

procedure TCustomEasyListview.DoItemGetImageIndex(Item: TEasyItem; Column: Integer; ImageKind: TEasyImageKind; var ImageIndex: TCommonImageIndexInteger);
begin
  if Assigned(OnItemGetImageIndex) then
    OnItemGetImageIndex(Self, Item, Column, ImageKind, ImageIndex)
end;

procedure TCustomEasyListview.DoItemGetImageList(Item: TEasyItem; Column: Integer; var ImageList: TCustomImageList);
begin
  if Assigned(OnItemGetImageList) then
    OnItemGetImageList(Self, Item, Column, ImageList)
end;

procedure TCustomEasyListview.DoItemGetStateImageList(Item: TEasyItem; Column: Integer; var ImageList: TCustomImageList);
begin
  if Assigned(OnItemGetStateImageList) then
    OnItemGetStateImageList(Self, Item, Column, ImageList)
end;

procedure TCustomEasyListview.DoItemGetTileDetail(Item: TEasyItem; Line: Integer; var Detail: Integer);
begin
  if Assigned(OnItemGetTileDetail) then
    OnItemGetTileDetail(Self, Item, Line, Detail)
end;

procedure TCustomEasyListview.DoItemGetTileDetailCount(Item: TEasyItem; var Count: Integer);
begin
  if Assigned(OnItemGetTileDetailCount) then
    OnItemGetTileDetailCount(Self, Item, Count)
end;

procedure TCustomEasyListview.DoItemImageDraw(Item: TEasyItem; Column: TEasyColumn; ACanvas: TCanvas; const RectArray: TEasyRectArrayObject; AlphaBlender: TEasyAlphaBlender);
begin
  if Assigned(OnItemImageDraw) then
    OnItemImageDraw(Self, Item, Column, ACanvas, RectArray, AlphaBlender)
end;

procedure TCustomEasyListview.DoItemImageGetSize(Item: TEasyItem; Column: TEasyColumn; var ImageWidth, ImageHeight: Integer);
begin
  if Assigned(OnItemImageGetSize) then
    OnItemImageGetSize(Self, Item, Column, ImageWidth, ImageHeight)
end;

procedure TCustomEasyListview.DoItemImageDrawIsCustom(Column: TEasyColumn;
  Item: TEasyItem; var IsCustom: Boolean);
begin
  if Assigned(OnItemImageDrawIsCustom) then
    OnItemImageDrawIsCustom(Self, Item, Column, IsCustom)
end;

procedure TCustomEasyListview.DoItemHotTrack(Item: TEasyItem; State: TEasyHotTrackstate; MousePos: TPoint);
begin
  if HotTrack.Enabled then
    if Assigned(OnItemHotTrack) then
      OnItemHotTrack(Self, Item, State, MousePos)
end;

procedure TCustomEasyListview.DoItemInitialize(Item: TEasyItem);
begin
  if Assigned(OnItemInitialize) then
    OnItemInitialize(Self, Item)
end;

procedure TCustomEasyListview.DoItemLoadFromStream(Item: TEasyItem; S: TStream; Version: Integer);
begin
  if Assigned(OnItemLoadFromStream) then
    OnItemLoadFromStream(Self, Item, S, Version)
end;

procedure TCustomEasyListview.DoItemMouseDown(Item: TEasyItem; Button: TCommonMouseButton; var DoDefault: Boolean);
begin
  if Assigned(OnItemMouseDown) then
    OnItemMouseDown(Self, Item, Button, DoDefault)
end;

procedure TCustomEasyListview.DoItemMouseUp(Item: TEasyItem; Button: TCommonMouseButton; var DoDefault: Boolean);
begin
  if Assigned(OnItemMouseUp) then
    OnItemMouseUp(Self, Item, Button, DoDefault)
end;

procedure TCustomEasyListview.DoItemPaintText(Item: TEasyItem; Position: Integer; ACanvas: TCanvas);
begin
  if Assigned(OnItemPaintText) then
    OnItemPaintText(Self, Item, Position, ACanvas)
end;

procedure TCustomEasyListview.DoItemSaveToStream(Item: TEasyItem; S: TStream; Version: Integer);
begin
  if Assigned(OnItemSaveToStream) then
    OnItemSaveToStream(Self, Item, S, Version)
end;

procedure TCustomEasyListview.DoItemSelectionChanged(Item: TEasyItem);
begin
  if Assigned(OnItemSelectionChanged) and not (csDestroying in ComponentState) then
    OnItemSelectionChanged(Self, Item)
end;

procedure TCustomEasyListview.DoItemSelectionChanging(Item: TEasyItem;
  var Allow: Boolean);
begin
  if Assigned(OnItemSelectionChanging) and not (csDestroying in ComponentState) then
    OnItemSelectionChanging(Self, Item, Allow)
end;

procedure TCustomEasyListview.DoItemSelectionsChanged;
begin
  if Assigned(OnItemSelectionsChanged) and not (csDestroying in ComponentState) then
    OnItemSelectionsChanged(Self)
end;

procedure TCustomEasyListview.DoItemSetCaption(Item: TEasyItem; Column: Integer; const Caption: WideString);
begin
  if Assigned(OnItemSetCaption) then
    OnItemSetCaption(Self, Item, Column, Caption)
end;

procedure TCustomEasyListview.DoItemSetGroupKey(Item: TEasyItem;
  FocusedColumn: Integer; Key: LongWord);
begin
  if Assigned(OnItemSetGroupKey) then
    OnItemSetGroupKey(Self, Item, FocusedColumn, Key)
end;

procedure TCustomEasyListview.DoItemSetImageIndex(Item: TEasyItem; Column: Integer; ImageKind: TEasyImageKind; ImageIndex: Integer);
begin
  if Assigned(OnItemSetImageIndex) then
    OnItemSetImageIndex(Self, Item, Column, ImageKind, ImageIndex)
end;

procedure TCustomEasyListview.DoItemSetTileDetail(Item: TEasyItem; Line: Integer; Detail: Integer);
begin
  if Assigned(OnItemSetTileDetail) then
    OnItemSetTileDetail(Self, Item, Line, Detail)
end;

procedure TCustomEasyListview.DoItemSetTileDetailCount(Item: TEasyItem; Detail: Integer);
begin
  //
end;

procedure TCustomEasyListview.DoItemStructureChange;
begin
  if Assigned(OnItemStructureChange) then
    OnItemStructureChange(Self)
end;

procedure TCustomEasyListview.DoItemThumbnailDraw(Item: TEasyItem;
  ACanvas: TCanvas; ARect: TRect; AlphaBlender: TEasyAlphaBlender;
  var DoDefault: Boolean);
begin
  if Assigned(OnItemThumbnailDraw) then
    OnItemThumbnailDraw(Self, Item, ACanvas, ARect, AlphaBlender, DoDefault)
end;

procedure TCustomEasyListview.DoItemVisibilityChanged(Item: TEasyItem);
begin
  if Assigned(OnItemVisibilityChanged) and not (csDestroying in ComponentState) then
    OnItemVisibilityChanged(Self, Item)
end;

procedure TCustomEasyListview.DoItemVisibilityChanging(Item: TEasyItem;
  var Allow: Boolean);
begin
  if Assigned(OnItemVisibilityChanging) and not (csDestroying in ComponentState)then
    OnItemVisibilityChanging(Self, Item, Allow)
end;

procedure TCustomEasyListview.DoKeyAction(var CharCode: Word;
  var Shift: TShiftState; var DoDefault: Boolean);
begin
  if Assigned(OnKeyAction) then
    OnKeyAction(Self, CharCode, Shift, DoDefault)
end;

procedure TCustomEasyListview.DoOLEDragEnd(ADataObject: IDataObject; DragResult: TCommonOLEDragResult; ResultEffect: TCommonDropEffects; KeyStates: TCommonKeyStates);
begin
  if Assigned(OnOLEDragEnd) then
    OnOLEDragEnd(Self, ADataObject, DragResult, ResultEffect, KeyStates);
end;

procedure TCustomEasyListview.DoOLEDragStart(ADataObject: IDataObject;
  var AvailableEffects: TCommonDropEffects; var AllowDrag: Boolean);
begin
  if Assigned(OnOLEDragStart) then
    OnOLEDragStart(Self, ADataObject, AvailableEffects, AllowDrag);
end;

procedure TCustomEasyListview.DoOLEDropSourceGiveFeedback(
  Effect: TCommonDropEffects; var UseDefaultCursors: Boolean);
// When the control is the OLE Drag source, this is called when OLE subsystem
// wants to display a different drag cursor (different than the drag image).  By
// default the built in cursors are used but the application may set the cursors
// itself in this event
begin
  if Assigned(OnOLEGiveFeedback) then
    OnOLEGiveFeedback(Self, Effect, UseDefaultCursors)
end;

procedure TCustomEasyListview.DoOLEDropSourceQueryContineDrag(
  EscapeKeyPressed: Boolean; KeyStates: TCommonKeyStates;
  var QueryResult: TEasyQueryDragResult);
// When the control is the OLE Drag source the OLE subsystem calls back to the
// source to query if the source would like to contine the drag, quit the drag,
// or drop the object where it is.
begin
  if Assigned(OnOLEQueryContineDrag) then
    OnOLEQueryContineDrag(Self, EscapeKeyPressed, KeyStates, QueryResult)
end;

procedure TCustomEasyListview.DoOLEDropTargetDragDrop(DataObject: IDataObject;
  KeyState: TCommonKeyStates; WindowPt: TPoint;
  AvailableEffects: TCommonDropEffects; var DesiredEffect: TCommonDropEffect;
  var Handled: Boolean);
// When the control is the OLE Drag target this is called when the data object is
// dropped on the control
begin
  if Assigned(OnOLEDragDrop) then
    OnOLEDragDrop(Self, DataObject, KeyState, WindowPt, AvailableEffects, DesiredEffect, Handled)
end;

procedure TCustomEasyListview.DoOLEDropTargetDragEnter(DataObject: IDataObject;
  KeyState: TCommonKeyStates; WindowPt: TPoint; AvailableEffects: TCommonDropEffects;
  var DesiredEffect: TCommonDropEffect);
// When the control is the OLE Drag target this is called when the drag object
// first enters the controls client window
begin
  if Assigned(OnOLEDragEnter) then
    OnOLEDragEnter(Self, DataObject, KeyState, WindowPt, AvailableEffects, DesiredEffect)
end;

procedure TCustomEasyListview.DoOLEDropTargetDragLeave;
// When the control is the OLE Drag target this is called when the data object
// leaves the controls client window
begin
  if Assigned(OnOLEDragLeave) then
    OnOLEDragLeave(Self)
end;

procedure TCustomEasyListview.DoOLEDropTargetDragOver(KeyState: TCommonKeyStates;
  WindowPt: TPoint; AvailableEffects: TCommonDropEffects;
  var DesiredEffect: TCommonDropEffect);
// When the control is the OLE Drag target this is called when the data object is
// moving over the controls client window
begin
  if Assigned(OnOLEDragOver) then
    OnOLEDragOver(Self, KeyState, WindowPt, AvailableEffects, DesiredEffect)
end;

procedure TCustomEasyListview.DoOLEGetCustomFormats(var Formats: TFormatEtcArray);
begin
  if Assigned(OnOLEGetCustomFormats) then
    OnOLEGetCustomFormats(Self, Formats)
end;

procedure TCustomEasyListview.DoOLEGetData(const FormatEtcIn: TFormatEtc;
  var Medium: TStgMedium; var Handled: Boolean);
// Called from the IDataObject when a target wants the source (us) to give it the
// OLE data
begin
  FillChar(Medium, SizeOf(Medium), #0);
  Handled := False;
  if Assigned(OnOLEGetData) then
    OnOLEGetData(Self, FormatEtcIn, Medium, Handled);
end;

procedure TCustomEasyListview.DoOLEGetDataObject(var DataObject: IDataObject);
begin
  DataObject := nil;
  if Assigned(OnOLEGetDataObject) then
    OnOLEGetDataObject(Self, DataObject)
end;

procedure TCustomEasyListview.DoPaintBkGnd(ACanvas: TCanvas; AWindowRect: TRect; AlphaBlender: TEasyAlphaBlender; var DoDefault: Boolean);
begin
  if Assigned(OnPaintBkGnd) then
    OnPaintBkGnd(Self, ACanvas, AWindowRect, AlphaBlender, DoDefault)
end;

procedure TCustomEasyListview.DoPaintHeaderBkGnd(ACanvas: TCanvas; ARect: TRect; var Handled: Boolean);
begin
  if Assigned(OnPaintHeaderBkGnd) then
    OnPaintHeaderBkGnd(Self, ACanvas, ARect, Handled)
end;

procedure TCustomEasyListview.DoPaintRect(ACanvas: TCanvas; WindowClipRect: TRect;
  SelectedOnly: Boolean);
// Paints the control defined by Rect to the passed canvas.  Called from the
// WM_PAINT message or can be called to make a snapshot of the control
// ARect is in Window coordinates.

    procedure PaintAndGetNextColumn(var Column: TEasyColumn; Item: TEasyItem; ItemRect, ViewClipRect: TRect; OrgPt: TPoint; Clip, ForceSelectionRectDraw: Boolean);
    begin
      ClipHeader(ACanvas, True);
      SetWindowOrgEx(ACanvas.Handle, Scrollbars.OffsetX, Scrollbars.OffsetY - Header.RuntimeHeight, nil);
      if Clip then
      begin
        IntersectRect(ItemRect, ItemRect, ViewClipRect);
        IntersectClipRect(ACanvas.Handle, ItemRect.Left, ItemRect.Top, ItemRect.Right, ItemRect.Bottom);
      end;
      Item.Paint(ACanvas, ViewClipRect, Column, ForceSelectionRectDraw);

      Column := Header.NextColumnInRect(Column, ViewClipRect);
    end;

    procedure PaintReportView(Item: TEasyItem; ViewClipRect: TRect; OrgPt: TPoint);
    var
      R: TRect;
      Column: TEasyColumn;
      RectArray: TEasyRectArrayObject;
    begin
      Column := Header.FirstColumnInRect(ViewClipRect);

      if Item.Selected or Item.Hilighted then
      begin
        if Selection.GroupSelections then
          PaintAndGetNextColumn(Column, Item, Item.DisplayRect, ViewClipRect, OrgPt, False, True)
        else
        if Selection.FullRowSelect then
          PaintAndGetNextColumn(Column, Item, Item.DisplayRect, ViewClipRect, OrgPt, True, True);
      end;

      while Assigned(Column) do
        PaintAndGetNextColumn(Column, Item, Item.DisplayRect, ViewClipRect, OrgPt, False, False);

      // Special processing for Full Row Select
      if Selection.FullRowSelect and Focused and Selection.UseFocusRect and Item.Focused and not SelectedOnly then
      begin
        // Draw a Focus Rectangle around focused item
        ClipHeader(ACanvas, True);
        ACanvas.Brush.Color := Color;
        ACanvas.Font.Color := clBlack;
        Item.ItemRectArray(Header.FirstColumnByPosition, ACanvas, RectArray);
        R := Selection.FocusedItem.DisplayRect;
        if not Selection.FullCellPaint then
          if not Selection.FullItemPaint then
            R.Left := RectArray.SelectionRect.Left;
        DrawFocusRect(ACanvas.Handle, R);
      end
    end;

var
  Group, FirstVisibleGroup: TEasyGroup;
  Item: TEasyItem;
  Column: TEasyColumn;
  OrgPt: TPoint;
  ViewClipRect, R: TRect;
begin
  GroupCollapseButton.Canvas.Lock;
  GroupExpandButton.Canvas.Lock;
  GetWindowOrgEx(Canvas.Handle, OrgPt);
  try
    // Header accounted for in the DC Offset
    ViewClipRect := Scrollbars.MapWindowRectToViewRect(WindowClipRect, True);

    if not SelectedOnly then
    begin
      if Assigned(Background) and not SelectedOnly then
        BackGround.PaintTo(ACanvas, WindowClipRect, False);
      SelectClipRgn(Canvas.Handle, 0);

      if ScrollHeaderHorz then
      begin
        SetWindowOrgEx(ACanvas.Handle, Scrollbars.OffsetX, 0, nil);
        if ViewSupportsHeader and Header.Visible then
          Header.PaintTo(ACanvas, ViewClipRect);
      end else
      begin
        if ViewSupportsHeader and Header.Visible then
          Header.PaintTo(ACanvas, Rect(0, 0, ClientWidth, Header.RuntimeHeight));
      end;

      ClipHeader(ACanvas, True);

      SetWindowOrgEx(ACanvas.Handle, Scrollbars.OffsetX, Scrollbars.OffsetY - Header.RuntimeHeight, nil);
      Group := Groups.FirstGroupInRect(ViewClipRect);
      while Assigned(Group) do
      begin
        Group.Paint(egmeBackground, Group.DisplayRect, ACanvas);
        Group.Paint(egmeTop, Group.BoundsRectTopMargin, ACanvas);
        Group.Paint(egmeBottom, Group.BoundsRectBottomMargin, ACanvas);
        Group.Paint(egmeLeft, Group.BoundsRectLeftMargin, ACanvas);
        Group.Paint(egmeRight, Group.BoundsRectRightMargin, ACanvas);
        Group.Paint(egmeForeground, Group.DisplayRect, ACanvas);
        Group := Groups.NextGroupInRect(Group, ViewClipRect)
      end;
    end;

    SetWindowOrgEx(ACanvas.Handle, Scrollbars.OffsetX, Scrollbars.OffsetY - Header.RuntimeHeight, nil);
    SelectClipRgn(ACanvas.Handle, 0);

    // Paint the colored column background for the focused node 
    if not SelectedOnly and ((View = elsReport) or (View = elsGrid)) then
    begin
      if PaintInfoColumn.HilightFocused and Assigned(Selection.FocusedColumn) then
      begin
        if Selection.FocusedColumn.SortDirection <> esdNone then
        begin
          ACanvas.Brush.Color := PaintInfoColumn.HilightFocusedColor;
          Group := Groups.FirstVisibleGroup;
          while Assigned(Group) do
          begin
            R := Selection.FocusedColumn.DisplayRect;
            R.Top := Scrollbars.OffsetY;
            R.Bottom := Scrollbars.OffsetY + (ClientHeight - Header.RuntimeHeight);
            if IntersectRect(R, ViewClipRect, R) then
              if IntersectRect(R, R, Group.BoundsRectBkGnd) then
                ACanvas.FillRect(R);
            Group := Groups.NextVisibleGroup(Group)
          end
        end
      end
    end;

    Item := Groups.FirstItemInRect(ViewClipRect);
    // If GroupSelection we always need to paint the first item
    if Assigned(Item) and (View = elsReport) and Selection.GroupSelections then
    begin
      // Need to paint the first item in a selection group
      if Assigned(Item.SelectionGroup) then
      begin
        if not SelectedOnly or (SelectedOnly and Item.Selected) then
        begin
          PaintReportView(Item.SelectionGroup.FirstItem, ViewClipRect, OrgPt);
          // Don't Repaint it
          if Item.SelectionGroup.FirstItem = Item then
            Item := Groups.NextItemInRect(Item, ViewClipRect);
        end
      end
    end;
    while Assigned(Item) do
    begin
      ClipHeader(ACanvas, True);
      // Need to paint the focused item last if its text may overlap another cell when focused
      if not Item.Focused or not Item.View.OverlappedFocus then
      begin
        if not SelectedOnly or (SelectedOnly and Item.Selected) then
        begin
          if View = elsReport then
            PaintReportView(Item, ViewClipRect, OrgPt)
          else
            Item.Paint(ACanvas, ViewClipRect, nil, False);
        end
      end;
      Item := Groups.NextItemInRect(Item, ViewClipRect);
    end;

    if Assigned(Selection.FocusedItem) then
      if Selection.FocusedItem.View.OverlappedFocus then
        Selection.FocusedItem.Paint(ACanvas, ViewClipRect, nil, False);

    if not SelectedOnly and ((View = elsReport) or (View = elsGrid)) then
    begin
      // Paint the Grid Lines
      if PaintInfoItem.GridLines then
      begin
        ClipHeader(ACanvas, True);
        FirstVisibleGroup := Groups.FirstVisibleGroup;
        ACanvas.Pen.Color := PaintInfoItem.GridLineColor;
        Column := Header.FirstColumnInRect(ViewClipRect);
        while Assigned(Column) do
        begin
          Group := FirstVisibleGroup;
          while Assigned(Group) do
          begin
            R := Column.DisplayRect;
            ACanvas.MoveTo(R.Right, Group.BoundsRectBkGnd.Top);
            ACanvas.LineTo(R.Right, Group.BoundsRectBkGnd.Bottom);
            Group := Groups.NextVisibleGroup(Group)
          end;
          Column := Header.NextColumnInRect(Column, ViewClipRect);
        end;
        Item := Groups.FirstItemInRect(ViewClipRect);
        while Assigned(Item) do
        begin
          R := Item.DisplayRect;
          ACanvas.MoveTo(Scrollbars.OffsetX, R.Bottom-1);
          ACanvas.LineTo(Scrollbars.OffsetX + ClientWidth, R.Bottom-1);
          Item := Groups.NextItemInRect(Item, ViewClipRect)
        end
      end
    end;
        
  finally
    SetWindowOrgEx(ACanvas.Handle, OrgPt.X, OrgPt.Y, nil);
    GroupCollapseButton.Canvas.UnLock;
    GroupExpandButton.Canvas.UnLock;
  end
end;

procedure TCustomEasyListview.DoQueryOLEData(const FormatEtcIn: TFormatEtc;
  var FormatAvailable: Boolean; var Handled: Boolean);
// Called from the IDataObject when a target wants the source (us) to tell it
// what formats the DataObject supports
begin
  Handled := False;
  FormatAvailable := False;
  if Assigned(OnOLEQueryData) then
    OnOLEQueryData(Self, FormatEtcIn, FormatAvailable, Handled)
end;

procedure TCustomEasyListview.DoResize(DeltaX, DeltaY: Integer);
begin

end;

procedure TCustomEasyListview.DoScroll(DeltaX, DeltaY: Integer);
begin
  if Assigned(OnScroll) then
    OnScroll(Self, DeltaX, DeltaY)
end;

procedure TCustomEasyListview.DoThreadCallback(var Msg: TWMThreadRequest);
begin

end;

procedure TCustomEasyListview.DoUpdate;
begin
  if not(csDestroying in ComponentState) then
  begin
    Groups.Rebuild(True);
    Scrollbars.ReCalculateScrollbars(True, False);
  end;
end;

procedure TCustomEasyListview.DoViewChange;
begin
  if Assigned(OnViewChange) then
    OnViewChange(Self)
end;

procedure TCustomEasyListview.EndUpdate(Invalidate: Boolean = True);
begin
//  Sort.EndUpdate;
  inherited EndUpdate(Invalidate);
end;

procedure TCustomEasyListview.FinalizeDrag(WindowPoint: TPoint;
  KeyState: TCommonKeyStates);
// Called after the mouse is released and a Drag Selection or a D&D operation
// was completed.  It cleans up and resets the flags.
begin
  // Do these after the flags have been reset in case a resulting operation of
  // these calls checks if the dragging is still occuring
  if ebcsDragging in States then
  begin
    Exclude(FStates, ebcsDragging);
    DragManager.DragEnd(Canvas, WindowPoint, KeyState);
  end;
  if ebcsDragSelecting in States then
  begin
    Exclude(FStates, ebcsDragSelecting);
    DragRect.DragEnd(Canvas, WindowPoint, KeyState);
  end;

  ClearStates;
  Mouse.Capture := 0;
end;

procedure TCustomEasyListview.HandleDblClick(Button: TCommonMouseButton; Msg: TWMMouse);
var
  Group: TEasyGroup;
  Keys: TCommonKeyStates;
  GroupHitInfo: TEasyGroupHitTestInfoSet;
  GroupInfo: TEasyHitInfoGroup;
  Item: TEasyItem;
  ItemHitInfo: TEasyItemHitTestInfoSet;
  ItemInfo: TEasyHitInfoItem;
  ViewPt: TPoint;
begin
  Keys := KeyToKeyStates(Msg.Keys);
  ViewPt := Scrollbars.MapWindowToView(Msg.Pos);
  if ViewSupportsHeader and (Header.Visible) and (Msg.YPos < Header.Height) then
    Header.WMLButtonDblClk(Msg)
  else begin
    Item := Groups.ItembyPoint(ViewPt);
    DoDblClick(Button, SmallPointToPoint(Msg.Pos), KeysToShiftState(Msg.Keys));
    if Assigned(Item) then
    begin
      Item.HitTestAt(ViewPt, ItemHitInfo);
      ItemInfo.Group := Item.OwnerGroup;
      ItemInfo.Item := Item;
      if ViewSupportsHeader then
        ItemInfo.Column := Header.Columns.ColumnByPoint(ViewPt)
      else
        ItemInfo.Column := nil;
      ItemInfo.HitInfo := ItemHitInfo;
      DoItemDblClick(Button, SmallPointToPoint(Msg.Pos), ItemInfo)
    end else
    begin
      Group := Groups.GroupByPoint(ViewPt);
      if Assigned(Group) then
      begin
        Group.HitTestAt(ViewPt, GroupHitInfo);
        GroupInfo.Group := Group;
        GroupInfo.HitInfo := GroupHitInfo;
        DoGroupDblClick(Button, SmallPointToPoint(Msg.Pos), GroupInfo)
      end
    end
  end
end;

procedure TCustomEasyListview.HandleKeyDown(Msg: TWMKeyDown);

    procedure MoveFocus(KeyStates: TShiftState; Item: TEasyItem);
    begin
      if Assigned(Item) then
      begin
        // Do this underhanded so we don't cause a grid rebuild but will keep the
        // window from getting WM_PAINT messages until we are done
        Inc(FUpdateCount);
        try
          if Selection.FocusedItem <> Item then
          begin
            if not Selection.Enabled and Selection.UseFocusRect then
              Selection.FocusedItem := Item
            else
            if ssCtrl in KeyStates then
              Selection.FocusedItem := Item
            else
            if ssShift in KeyStates then
            begin
              if Assigned(Selection.AnchorItem) then
              begin
                Selection.SelectRange(Selection.AnchorItem, Item, Selection.RectSelect, True);
                Selection.FocusedItem := Item;
                Selection.FocusedItem.Selected := True;
              end else
              begin
                Selection.ClearAll;
                Selection.FocusedItem := Item;
                Selection.FocusedItem.Selected := True;
              end
            end else
            begin
              Selection.ClearAll;
              Selection.FocusedItem := Item;
              Selection.FocusedItem.Selected := True;
              Selection.AnchorItem := Selection.FocusedItem;
            end;
          end;
        finally
          if Assigned(Selection.FocusedItem) then
          begin
            if Selection.FocusedItem.DisplayRect.Top < ClientInViewportCoords.Top + (RectHeight(ClientInViewportCoords) div 2) then
              Selection.FocusedItem.MakeVisible(emvAuto {emvTop})
            else
              Selection.FocusedItem.MakeVisible(emvAuto{emvBottom});
          end;
          Dec(FUpdateCount);
          UpdateWindow(Handle);
        end
      end
    end;

    function FocusFirst: Boolean;
    var
      Item: TEasyItem;
    begin
      Result := False;
      if not Assigned(Selection.FocusedItem) then
      begin
        Item := Groups.FirstVisibleItem;
        if Assigned(Item) then
        begin
          Selection.FocusedItem := Item;
          Result := True
        end
      end
    end;

var
  Item: TEasyItem;
  KeyStates: TShiftState;
  Handled, Mark: Boolean;
begin
  KeyStates := KeyDataToShiftState(Msg.KeyData);

  Selection.IncMultiChangeCount;
  try
    case Msg.CharCode of
      VK_RIGHT:
        begin
          if Selection.Enabled or Selection.UseFocusRect then
          begin
            if not FocusFirst then
            begin
              Item := Groups.AdjacentItem(Selection.FocusedItem, acdRight);
              MoveFocus(KeyStates, Item);
            end
          end else
            Scrollbars.OffsetX := Scrollbars.OffsetX + 1;
        end;
      VK_LEFT:
        begin
          if Selection.Enabled or Selection.UseFocusRect then
          begin
            if not FocusFirst then
            begin
              Item := Groups.AdjacentItem(Selection.FocusedItem, acdLeft);
              MoveFocus(KeyStates, Item);
            end
          end else
            Scrollbars.OffsetX := Scrollbars.OffsetX - 1;
        end;
      VK_UP:
        begin
          if Selection.Enabled or Selection.UseFocusRect then
          begin
            if not FocusFirst then
            begin
              Item := Groups.AdjacentItem(Selection.FocusedItem, acdUp);
              MoveFocus(KeyStates, Item);
            end
          end else
            Scrollbars.OffsetY := Scrollbars.OffsetY + 1;
        end;
      VK_DOWN:
        begin
          if Selection.Enabled or Selection.UseFocusRect then
          begin
            if not FocusFirst then
            begin
              Item := Groups.AdjacentItem(Selection.FocusedItem, acdDown);
              // Special case with one item
              if not Assigned(Item) and (Selection.Count = 0) and (Groups.ItemCount = 1) then
              begin
                Selection.FocusedItem := nil;
                Item := Groups.FirstItem;
              end;
              MoveFocus(KeyStates, Item);
            end
          end else
            Scrollbars.OffsetY := Scrollbars.OffsetY + 1;
        end;
      VK_HOME:
        begin
          if Selection.Enabled or Selection.UseFocusRect then
          begin
            if not FocusFirst then
            begin
              Item := Groups.FirstVisibleItem;
              MoveFocus(KeyStates, Item);
            end
          end else
          begin
            Scrollbars.OffsetX := 0;
            Scrollbars.OffsetY := 0;
          end
        end;
      VK_END:
        begin
          if Selection.Enabled or Selection.UseFocusRect then
          begin
            if not FocusFirst then
            begin
              Item := Groups.LastVisibleItem;
              MoveFocus(KeyStates, Item);
            end
          end else
          begin
            Scrollbars.OffsetX := Scrollbars.MaxOffsetX;
            Scrollbars.OffsetY := Scrollbars.MaxOffsetY;
          end
        end;
      VK_NEXT:
        begin
          if Selection.Enabled or Selection.UseFocusRect then
          begin
            if not FocusFirst then
            begin
              Item := Groups.AdjacentItem(Selection.FocusedItem, acdPageDown);
              MoveFocus(KeyStates, Item);
            end
          end else
            Scrollbars.OffsetY := Scrollbars.OffsetY + ClientHeight;
        end;
      VK_PRIOR:
        begin
          if Selection.Enabled or Selection.UseFocusRect then
          begin
            if not FocusFirst then
            begin
              Item := Groups.AdjacentItem(Selection.FocusedItem, acdPageUp);
              MoveFocus(KeyStates, Item);
            end
          end else
            Scrollbars.OffsetY := Scrollbars.OffsetY - ClientHeight;
        end;
      VK_F2:
        begin
          if Assigned(Selection.FocusedItem) then
          begin
            EditManager.BeginEdit(Selection.FocusedItem, nil)
          end;
        end;
      VK_SPACE:
        begin
          if Assigned(Selection.FocusedItem) then
            if Selection.FocusedItem.PaintInfo.CheckType in [ectBox, ectRadio] then
              Selection.FocusedItem.Checked := not Selection.FocusedItem.Checked
        end;
      VK_ADD:
      begin
        if (ssCtrl in KeyStates) then
          AutoFitAllCells(True)
      end;
      Ord('A'),
      Ord('a'):
        begin
          if ssCtrl in KeyStates then
            Selection.SelectAll
        end;
      Ord('C'), Ord('c'):   // Ctrl + 'C' Copy
      begin
        if ssCtrl in KeyStates then
        begin
          Handled := False;
          DoClipboardCopy(Handled);
          if not Handled then
            CopyToClipboard;
        end
      end;
      Ord('X'), Ord('x'):  // Ctrl + 'X' Cut
        begin
          if ssCtrl in KeyStates then
          begin
            Handled := False;
            Mark := True;
            DoClipboardCut(Mark, Handled);
            if not Handled then
              CutToClipboard
            else
              if Mark then
                MarkSelectedCut
          end
        end;
      Ord('V'), Ord('v'):   // // Ctrl + 'V' Paste
        begin
          if ssCtrl in KeyStates then
          begin
            Handled := False;
            DoClipboardPaste(Handled);
            if not Handled then
              PasteFromClipboard;
          end
        end;
        VK_ESCAPE:
        begin
          CancelCut;
          Invalidate;
        end;
    end
  finally
    Selection.DecMultiChangeCount
  end
end;

procedure TCustomEasyListview.HandleMouseDown(Button: TCommonMouseButton; Msg: TWMMouse);
var
  WindowPt: TPoint;
  KeyState: TCommonKeyStates;
  Group: TEasyGroup;
  Item: TEasyItem;
  GroupHitInfo: TEasyGroupHitTestInfoSet;
  ItemHitInfo: TEasyItemHitTestInfoSet;
  MouseDown, StartTimer, CtlDown, ShiftDown: Boolean;
  Allow, DoDefaultItemDown: Boolean;
begin
  Item := nil;
  KeyState := KeyToKeyStates(Msg.Keys);
  WindowPt := Scrollbars.MapWindowToView(Msg.Pos);
  MouseDown := KeyState * [cksLButton, cksMButton, cksRButton] <> [];
  CtlDown := cksControl in KeyState;
  ShiftDown := cksShift in KeyState;

  Gesture.Path := '';

  Group := ClickTestGroup(WindowPt, KeyState, GroupHitInfo);
  if Assigned(Group) then
  begin
    // First see if the group expand button or checkbox was hit
    if GroupTestExpand(GroupHitInfo) then
    begin
      // Only the left button can expand
      if Button = cmbLeft then
      begin
        // Deal with the expansion in the mouse down
        Allow := True;
        if Group.Expanded then
          DoGroupCollapsing(Group, Allow)
        else
          DoGroupExpanding(Group, Allow);
        if Allow then
        begin
          BeginUpdate;
          try
            Include(FStates, ebcsGroupExpandPending);
            Group.Expanded := not Group.Expanded;
            // Need to make sure focused item is not in the collapsed group.
            if not Group.Expanded and Assigned(Selection.FocusedItem) then
            begin
              if Selection.FocusedItem.OwnerGroup = Group then
              begin
                Item := Groups.NextVisibleItem(Selection.FocusedItem);
                while Assigned(Item) do
                begin
                  if Item.Enabled then
                    Break
                  else
                    Item := Groups.NextVisibleItem(Item)
                end;
                Selection.FocusedItem := Item;
              end
            end
          finally
            EndUpdate
          end
        end;
      end
    end else
    // Next see if it hit the Group CheckBox
    if egtOnCheckbox in GroupHitInfo then
    begin
       // Only the left button can Check Groups
      if Button = cmbLeft then
      begin
        Include(FStates, ebcsCheckboxClickPending);
        HotTrack.PendingObjectCheck := nil;
        CheckManager.PendingObject := Group;
      end
    end else
    begin
      if Group.Expanded then
      begin
        // Exhausted Group hit tests, move into Item level testing
        Item := ClickTestItem(WindowPt, Group, KeyState, ItemHitInfo);
        if Assigned(Item) then
        begin
          DoDefaultItemDown := True;
          DoItemMouseDown(Item, Button, DoDefaultItemDown);
          if DoDefaultItemDown then
          begin
            // First see it the hit was on the items check box
            if ehtOnCheck in ItemHitInfo then
            begin
               // Only the left button can Check Items
              if Button = cmbLeft then
              begin
                Include(FStates, ebcsCheckboxClickPending);
                HotTrack.PendingObjectCheck := nil;
                CheckManager.PendingObject := Item;
              end
            end else
            // Next see if Selection is enabled and if so handle item selection
            // through direct hit or drag rectangle
            if Selection.Enabled then
            begin
              Selection.IncMultiChangeCount;
              Selection.GroupSelectBeginUpdate;
              try
                if Item.SelectionHitPt(WindowPt, eshtClickselect) then
                begin
                  if MouseDown and (Button in Selection.MouseButton) then
                  begin
                    // See if the user click on the item a second time, if so get ready for
                    // an edit
                    StartTimer := (Button = cmbLeft) and ((Selection.Count = 1) and ((Selection.FocusedItem = Item) and Item.EditAreaHitPt(WindowPt) and Item.Selected)) and Focused;

                    if Selection.MultiSelect then
                    begin
                      // Multi Selection Mode.........
                      if not Item.Selected and not(CtlDown or ShiftDown) then
                        Selection.ClearAll;

                      // Focus the item if the Ctl key is not down.  It will be done in the ButtonUp event
                      if (Selection.FocusedItem <> Item) and not CtlDown then
                        Selection.FocusedItem := Item;

                      if not( CtlDown or ShiftDown) then
                      begin
                        Selection.AnchorItem := Item;
                      end else
                      begin
                        if not ShiftDown then
                          Selection.AnchorItem := Item
                        else
                          Selection.SelectRange(Selection.AnchorItem, Item, Selection.RectSelect, not CtlDown);
                      end
                    end else
                    begin
                      // Single Selection Mode.........
                      // Set the Focus to the hit item
                      if (Selection.FocusedItem <> Item) then
                        Selection.FocusedItem := Item;
                      Selection.AnchorItem := Item;
                      Selection.ClearAll;
                    end;

                    // CtlClick then it will be selected in the Mouse Up
                    if not CtlDown then
                      Item.Selected := True;

                    // This test allows descendent to cancel out of drags incase they do something in the Focus or Selection Changing events
                    if (((Button in DragRect.MouseButton) or (Button in DragManager.MouseButton)) and (States * [ebcsLButtonDown, ebcsRButtonDown, ebcsMButtonDown] <> [])) then
                      InitializeDragPendings(Item, SmallPointToPoint(Msg.Pos), KeyToKeyStates(Msg.Keys), Item.AllowDrag(WindowPt), True);

                    if StartTimer then
                    begin
                      EditManager.AutoEditStartClickPt := WindowPt;
                      EditManager.StartAutoEditTimer;
                    end
                  end else
                  begin
                    if (Button in DragManager.MouseButton) and (States * [ebcsLButtonDown, ebcsRButtonDown, ebcsMButtonDown] <> []) then
                      InitializeDragPendings(Item, SmallPointToPoint(Msg.Pos), KeyToKeyStates(Msg.Keys), True, False);
                  end
                end else
                begin
                  // All mouse button down actions trigger the same events
                  // Since it is unknown what the user is trying to do yet
                  if (Button in DragRect.MouseButton) and (States * [ebcsLButtonDown, ebcsRButtonDown, ebcsMButtonDown] <> []) then
                    InitializeDragPendings(Item, SmallPointToPoint(Msg.Pos), KeyToKeyStates(Msg.Keys), False, True);
                end
              finally
                Selection.GroupSelectEndUpdate;
                Selection.DecMultiChangeCount
              end
            end else
            if Selection.UseFocusRect then
            begin
              if Item.SelectionHitPt(WindowPt, eshtClickselect) then
                Selection.FocusedItem := Item
            end
          end
        end else
        begin
         // All mouse button down actions trigger the same events
         // Since it is unknown what the user is trying to do yet
          if (Button in DragRect.MouseButton) and (States * [ebcsLButtonDown, ebcsRButtonDown, ebcsMButtonDown] <> []) then
            InitializeDragPendings(Item, SmallPointToPoint(Msg.Pos), KeyToKeyStates(Msg.Keys), False, True);
        end
      end else
        // Did not hit an anything so get ready for a drag rectangle
        if (Button in DragRect.MouseButton) and (States * [ebcsLButtonDown, ebcsRButtonDown, ebcsMButtonDown] <> []) then
          InitializeDragPendings(Item, SmallPointToPoint(Msg.Pos), KeyToKeyStates(Msg.Keys), False, True);
    end;
  end else
  begin
    // All mouse button down actions trigger the same events
    // Since it is unknown what the user is trying to do yet
    // Did not hit a group so get ready for a drag rectangle
    if (Button in DragRect.MouseButton) and (States * [ebcsLButtonDown, ebcsRButtonDown, ebcsMButtonDown] <> []) then
      InitializeDragPendings(Item, SmallPointToPoint(Msg.Pos), KeyToKeyStates(Msg.Keys), False, True);
  end;
end;

procedure TCustomEasyListview.HandleMouseUp(Button: TCommonMouseButton; Msg: TWMMouse);
// Called when the Left Mouse button is released
var
  Pt: TPoint;
  KeyState: TCommonKeyStates;
  GroupHitInfo: TEasyGroupHitTestInfoSet;
  ItemHitInfo: TEasyItemHitTestInfoSet;
  Group: TEasyGroup;
  Item: TEasyItem;
  GesturedHandled: Boolean;
  CtlDown, ShiftDown, DoDefaultItemUp: Boolean;
begin
  Group := nil;
  KeyState := KeyToKeyStates(Msg.Keys);
  CtlDown := cksControl in KeyState;
  ShiftDown := cksShift in KeyState;
  Pt := Scrollbars.MapWindowToView(Msg.Pos);

  GesturedHandled := False;

  if Gesture.Enabled and (Gesture.Path <> '') and ([ebcsGroupExpandPending, ebcsCheckboxClickPending, ebcsDragSelecting, ebcsDragging] * States = []) then
    DoGesture(Gesture.Path, GesturedHandled);

  // In some cases we can get a mouse up message without a corresponding mouse
  // down message.  For example if we full expand the application if the mouse
  // is over the Easy window after the expand we get a mouse up message
  if ([ebcsLButtonDown, ebcsRButtonDown, ebcsMButtonDown] * States <> []) then
  begin
    if ebcsGroupExpandPending in States then
    begin
      // Don't do any other processing if the group expand button was clicked
    end else
    if ebcsCheckboxClickPending in States then
    begin
      Group := ClickTestGroup(Pt, KeyState, GroupHitInfo);
      if CheckManager.PendingObject is TEasyGroup then
      begin
        if (egtOnCheckbox in GroupHitInfo) and Assigned(Group) then
          Group.Checked := not Group.Checked;
      end else
      if CheckManager.PendingObject is TEasyItem then
      begin
        Item := ClickTestItem(Pt, Group, KeyState, ItemHitInfo);
        if (ehtOnCheck in ItemHitInfo) and Assigned(Item) then
          Item.Checked := not Item.Checked
      end;
      CheckManager.PendingObject.CheckHovering := False;
      CheckManager.PendingObject.CheckPending := False;
      CheckManager.PendingObject := nil;
    end else
    if ebcsDragSelecting in States then
    begin
      FinalizeDrag(SmallPointToPoint(Msg.Pos), KeyToKeyStates(Msg.Keys));
    end else
    if ebcsDragging in States then
    begin
      // The VCL will send us a fake mouse button up when the drag starts.
      // This screws everything.  We will send a mouse up after the drag ends
      // with the cbcsVCLDrag cleared
      if not(ebcsVCLDrag in States) then
        FinalizeDrag(SmallPointToPoint(Msg.Pos), KeyToKeyStates(Msg.Keys));
    end else
    begin
      // If not dragging or drag selecting then check if it is necessary to unselect
      // the items
      if not GesturedHandled then
      begin
        Item := ClickTestItem(Pt, Group, KeyState, ItemHitInfo);
        if Assigned(Item) then
        begin
          DoDefaultItemUp := True;
          DoItemMouseUp(Item, Button, DoDefaultItemUp);
          if DoDefaultItemUp then
          begin
            DoItemClick(Item, KeyToKeyStates(Msg.Keys), ItemHitInfo);
            if not Item.SelectionHitPt(Pt, eshtClickselect) then
              Selection.ClearAll
            else begin
              // Allow MultiSelect
              if Selection.MultiSelect then
              begin
                if not (ShiftDown or CtlDown) and (not Item.Selected or (Button = cmbLeft)) then
                  Selection.ClearAllExcept(Item)
                else begin
                  if CtlDown then
                  begin
                    EditManager.StopAutoEditTimer;
                    if not ShiftDown then
                      Item.Selected := not Item.Selected;
                    Item.Focused := True;
                  end
                end
              end
            end
          end
        end else
        begin
          Group := ClickTestGroup(Pt, KeyToKeyStates(Msg.Keys), GroupHitInfo);
          if Assigned(Group) then
            DoGroupClick(Group, KeyToKeyStates(Msg.Keys), GroupHitInfo);
          Selection.ClearAll
        end
      end
    end
  end;
end;

procedure TCustomEasyListview.InitializeDragPendings(HitItem: TEasyItem; WindowPoint: TPoint; KeyState: TCommonKeyStates; AllowDrag, AllowDragRect: Boolean);
// Called from the mouse down messages.  It initializes the DragManager and the
// DragSelection Manager to prepare for a possible action.  If the click was not
// on an item then it is interperted as a drag select.  If it hit an item it
// is interperted as a D&D action.
var
  StartSelectDrag, StartDrag: Boolean;
  Pt: TPoint;
begin
  // At least NT4 does not set the focus on a mouse click
  CheckFocus;

  Mouse.Capture := Handle;

  // Initialize both Drag Select and Drag object just in case
  StartSelectDrag := AllowDragRect and DragRect.InitializeDrag(WindowPoint, KeyState);
  StartDrag := AllowDrag and DragManager.InitializeDrag(HitItem, WindowPoint, KeyState);

  Pt := Scrollbars.MapWindowToView(WindowPoint);

  if Assigned(DragManager.DragItem)  and StartDrag then
  begin
     Include(FStates, ebcsDragPending);
     DragRect.FinalizeDrag(KeyState)
  end else
  if StartSelectDrag then
  begin
    Include(FStates, ebcsDragSelectPending);
    DragManager.FinalizeDrag(KeyState)
  end;
end;

function TCustomEasyListview.IsGrouped: Boolean;
begin
  // Default definition that the control is in grouped mode is if the Top Margin is enabled
  Result := PaintInfoGroup.MarginTop.Visible
end;

procedure TCustomEasyListview.Loaded;
begin
  inherited;
  DoUpdate;
end;

procedure TCustomEasyListview.LoadFromFile(FileName: WideString; Mode: Word);
var
  F: TWideFileStream;
begin
  F := TWideFileStream.Create(FileName, Mode);
  try
    LoadFromStream(F)
  finally
    F.Free
  end
end;

procedure TCustomEasyListview.LoadFromStream(S: TStream);
var
  Version: Integer;
  AView: TEasyListStyle;
begin
  BeginUpdate;
  try
    S.Read(Version, SizeOf(Version));
    if Version > 0 then
    begin
      S.Read(AView, SizeOf(AView));
      View := AView;
      Groups.LoadFromStream(S, Version);
      Header.LoadFromStream(S, Version);
      Groups.Rebuild(True);
    end;
    {  if Version > n then
       begin
       end; }
  finally
    EndUpdate
  end
end;

procedure TCustomEasyListview.MarkSelectedCut;
var
  Item: TEasyItem;
begin
  CancelCut;
  Item := Selection.First;
  while Assigned(Item) do
  begin
    Item.Cut := True;
    Item := Selection.Next(Item)
  end
end;

procedure TCustomEasyListview.Notification(AComponent: TComponent;
  Operation: TOperation);
begin
  inherited;
  if Operation = opRemove then
  begin
    if AComponent = FImagesGroup then
      FImagesGroup := nil;
    if AComponent = FImagesExLarge then
      FImagesExLarge := nil;
    if AComponent = FImagesLarge then
      FImagesLarge := nil;
    if AComponent = FImagesSmall then
      FImagesSmall := nil;
    if AComponent = FImagesState then
      FImagesState := nil;
    if AComponent = Header.Images then
      Header.Images := nil;
  end
end;

procedure TCustomEasyListview.PasteFromClipboard;
var
  Handled: Boolean;
begin
  Handled := False;
  DoClipboardPaste(Handled)
end;

procedure TCustomEasyListview.ResizeBackBits(NewWidth, NewHeight: Integer);
// Resizes the Bitmap for the DoubleBuffering.  Called mainly when the window
// is resized.  Note the bitmap only gets larger to maximize speed during dragging
begin
  if CacheDoubleBufferBits and Assigned(BackBits) then
  begin
    // The Backbits grow to the largest window size
    if (NewWidth > BackBits.Width) then
    begin
      if NewWidth > 0 then
        BackBits.Width := NewWidth
      else
        BackBits.Width := 1
    end;
    if NewHeight > BackBits.Height then
    begin
      if NewHeight > 0 then
        BackBits.Height := NewHeight
      else
        BackBits.Height := 1;
    end
  end
end;

procedure TCustomEasyListview.SaveToFile(FileName: WideString; Mode: Word);
var
  F: TWideFileStream;
begin
  F := TWideFileStream.Create(FileName, Mode);
  try
    SaveToStream(F)
  finally
    F.Free
  end  
end;

procedure TCustomEasyListview.SaveToStream(S: TStream);
var
  Version: Integer;
begin
  Version := EASYLISTVIEW_STREAM_VERSION;
  S.Write(Version, SizeOf(Version));
  S.Write(View, SizeOf(View));
  Groups.SaveToStream(S, Version);
  Header.SaveToStream(S, Version)
end;

procedure TCustomEasyListview.SetBackGround(const Value: TEasyBackgroundManager);
begin
  if Assigned(FBackGround) then
    FreeAndNil(FBackGround);
  FBackGround := Value;
end;

procedure TCustomEasyListview.SetCacheDoubleBufferBits(const Value: Boolean);
begin
  if FCacheDoubleBufferBits <> Value then
  begin
    FCacheDoubleBufferBits := Value;
    if Value then
    begin
      if not Assigned(FBackBits) then
      begin
        FBackBits := TBitmap.Create;
        FBackBits.PixelFormat := pf32Bit
      end
    end else
      FreeAndNil(FBackBits)
  end
end;

procedure TCustomEasyListview.SetGroupCollapseImage(Value: TBitmap);
begin
  FGroupCollapseButton.Assign(Value);
  if Assigned(FGroupCollapseButton) then
    FGroupCollapseButton.PixelFormat := pf32Bit
end;

procedure TCustomEasyListview.SetGroupExpandImage(Value: TBitmap);
begin
  FGroupExpandButton.Assign(Value);
  if Assigned(FGroupExpandButton) then
    FGroupExpandButton.PixelFormat := pf32Bit
end;

procedure TCustomEasyListview.SetGroupFont(Value: TFont);
begin
  FGroupFont.Assign(Value)
end;

procedure TCustomEasyListview.SetHintType(Value: TEasyHintType);
begin
  HintInfo.HintType := Value
end;

procedure TCustomEasyListview.SetImagesExLarge(Value: TCustomImageList);
begin
  if Value <> FImagesExLarge then
  begin
    FImagesExLarge := Value;
    SafeInvalidateRect(nil, False);
  end
end;

procedure TCustomEasyListview.SetImagesGroup(Value: TCustomImageList);
begin
  if Value <> FImagesGroup then
  begin
    FImagesGroup := Value;
    SafeInvalidateRect(nil, False);
  end
end;

procedure TCustomEasyListview.SetImagesLarge(Value: TCustomImageList);
begin
  if Value <> FImagesLarge then
  begin
    FImagesLarge := Value;
    SafeInvalidateRect(nil, False);
  end
end;

procedure TCustomEasyListview.SetImagesSmall(Value: TCustomImageList);
begin
  if Value <> FImagesSmall then
  begin
    FImagesSmall := Value;
    SafeInvalidateRect(nil, False);
  end
end;

procedure TCustomEasyListview.SetImagesState(const Value: TCustomImageList);
begin
  if Value <> FImagesState then
  begin
    FImagesState := Value;
    SafeInvalidateRect(nil, False);
  end
end;

procedure TCustomEasyListview.SetPaintInfoColumn(const Value: TEasyPaintInfoBaseColumn);
begin
  if Value <> FPaintInfoColumn then
  begin
    FreeAndNil(FPaintInfoColumn);
    FPaintInfoColumn := Value;
  end
end;

procedure TCustomEasyListview.SetPaintInfoGroup(const Value: TEasyPaintInfoBaseGroup);
begin
   if Value <> FPaintInfoGroup then
  begin
    FreeAndNil(FPaintInfoGroup);
    FPaintInfoGroup := Value;
  end
end;

procedure TCustomEasyListview.SetPaintInfoItem(const Value: TEasyPaintInfoBaseItem);
begin
  if Value <> FPaintInfoItem then
  begin
    FreeAndNil(FPaintInfoItem);
    FPaintInfoItem := Value;
  end
end;

procedure TCustomEasyListview.SetSelection(Value: TEasySelectionManager);
begin
  if Value <> FSelection then
  begin
    FreeAndNil(FSelection);
    FSelection := Value
  end
end;

procedure TCustomEasyListview.SetShowGroupMargins(const Value: Boolean);
begin
  if FShowGroupMargins <> Value then
  begin
    FShowGroupMargins := Value;
    DoUpdate;
  end;
end;

procedure TCustomEasyListview.SetShowInactive(const Value: Boolean);
begin
  if FShowInactive <> Value then
  begin
    FShowInactive := Value;
    SafeInvalidateRect(nil, True)
  end
end;

procedure TCustomEasyListview.SetShowThemedBorder(Value: Boolean);
begin
  if Value <> FShowThemedBorder then
  begin
    FShowThemedBorder := Value;
    // Need to modify the NonClient area
    RecreateWnd;
  end
end;

procedure TCustomEasyListview.SetView(Value: TEasyListStyle);
begin
   if FView <> Value then
   begin
     FView := Value;
     Groups.Rebuild(True);

     if IsVertView then
       WheelMouseDefaultScroll := edwsVert
     else
       WheelMouseDefaultScroll := edwsHorz;
   end
end;

procedure TCustomEasyListview.WMChar(var Msg: TWMChar);
begin
  inherited;
  IncrementalSearch.HandleWMChar(Msg);
end;

procedure TCustomEasyListview.WMClose(var Msg: TWMClose);
begin
  EditManager.EndEdit;
  inherited;
end;

procedure TCustomEasyListview.WMContextMenu(var Msg: TMessage);
var
  Item: TEasyItem;
  Group: TEasyGroup;
  Pt: TPoint;
  HitInfoGroup: TEasyHitInfoGroup;
  HitInfoItem: TEasyHitInfoItem;
  Menu: TPopupMenu;
  Handled, SkipHitTest, MenuKey: Boolean;
begin
  if not EditManager.Editing and not (Gesture.Enabled and (Gesture.Path <> '')) then
  begin
    Handled := False;
    MenuKey := False;
    if not (ebcsCancelContextMenu in States) then
    begin
      SkipHitTest := False;
      // Support Dual monitors with SmallPointToPoint
      Pt:= SmallPointToPoint(SmallPoint(Msg.LParamLo, Msg.LParamHi));
      if ((Pt.X = 65535) and (Pt.Y = 65535)) or ((Pt.X = -1) and (Pt.Y = -1)) then
      begin
        MenuKey := True;
        Pt := ScreenToClient(Mouse.CursorPos);
        if not PtInRect(ClientRect, Pt) or (Selection.Count = 0) then
        begin
          Pt.X := 0;
          Pt.Y := 0;
          SkipHitTest := True;
        end;
        Pt := ClientToScreen(Pt);
      end;

      if MenuKey and (Selection.Count > 0) then
      begin
        HitInfoItem.Item := Selection.First;
        Pt := ClientToScreen(HitInfoItem.Item.DisplayRect.TopLeft);
        Pt.Y := Pt.Y + Header.RuntimeHeight;
        HitInfoItem.Column := nil;
        HitInfoItem.Group := HitInfoItem.Item.OwnerGroup;
        HitInfoItem.HitInfo := [ehtOnLabel, ehtOnIcon];
        DoItemContextMenu(HitInfoItem, Pt, Menu, Handled)
      end else
      if not SkipHitTest then
      begin
        if IsHeaderMouseMsg(PointToSmallPoint( ScreenToClient(Pt))) then
        begin
          Pt := ClientToScreen(Pt);
          Header.WMContextMenu(Msg);
          Handled := True;
        end else
        begin
          Menu := nil;
          Exclude(FStates, ebcsDragSelectPending);
          Exclude(FStates, ebcsDragPending);

          Handled := False;
          Group := Groups.GroupByPoint(Scrollbars.MapWindowToView(ScreenToClient(Pt)));
          if Assigned(Group) then
          begin
            // The hit was in a group so now see if it was in an item
            Item := Group.ItembyPoint(Scrollbars.MapWindowToView( ScreenToClient(Pt)));
            if Assigned(Item) then
            begin
              if Item.HitTestAt(Scrollbars.MapWindowToView( ScreenToClient(Pt)), HitInfoItem.HitInfo) then
              begin
                HitInfoItem.Column := nil;
                HitInfoItem.Group := Group;
                HitInfoItem.Item := Item;
                DoItemContextMenu(HitInfoItem, Pt, Menu, Handled)
              end
            end;
            if not Assigned(Menu) and not Handled then
            begin
              HitInfoGroup.Group := Group;
              Group.HitTestAt(Scrollbars.MapWindowToView(ScreenToClient(Pt)), HitInfoGroup.HitInfo);
              DoGroupContextMenu(HitInfoGroup, Pt, Menu, Handled)
            end
          end
        end
      end;
      if not Handled then
        DoContextMenu(Pt, Handled);

      if Assigned(Menu) and not Handled then
      begin
        Menu.Popup(Msg.LParamLo, Msg.LParamHi);
        Msg.Result := 1
      end else
      if not Handled then
        inherited  // Use the PopupMenu property from TControl
    end;
  end else
  begin
    Msg.Result := 1;
    inherited
  end;
  Exclude(FStates, ebcsCancelContextMenu);
end;

procedure TCustomEasyListview.WMDestroy(var Msg: TMessage);
begin
  EditManager.EndEdit; 
  DragManager.Registered := False;
  Header.DragManager.Registered := False;
  inherited;
end;

procedure TCustomEasyListview.WMEasyThreadCallback(var Msg: TWMThreadRequest);
begin
  DoThreadCallback(Msg)
end;

procedure TCustomEasyListview.WMEraseBkGnd(var Msg: TWMEraseBkGnd);
begin
  Msg.Result := 1;
end;

procedure TCustomEasyListview.WMGetDlgCode(var Msg: TWMGetDlgCode);
// The VCL forms use the Dialog Window Proc so we need to tell windows we want
// the arrow keys for navigation
begin
  Msg.Result := Msg.Result or DLGC_WANTALLKEYS or DLGC_WANTARROWS or DLGC_WANTCHARS;
end;

procedure TCustomEasyListview.WMHScroll(var Msg: TWMHScroll);
// Called to scroll the Window, the Window is responsible for actually performing
// the scroll
begin
  inherited;
  if Msg.ScrollCode <> SB_ENDSCROLL then
    Include(FStates, ebcsScrolling)
  else
    Exclude(FStates, ebcsScrolling);
  ScrollBars.WMHScroll(Msg);
  SafeInvalidateRect(nil, False);
end;

procedure TCustomEasyListview.WMKeyDown(var Msg: TWMKeyDown);
// Called when the user pressed a key on the keyboard.  The Scrollbars need to
// know in case the user is scrolling using the keys.
var
  Shift: TShiftState;
  DoDefault: Boolean;
begin
  inherited;
  if (ebcsDragSelecting in States) then
  begin
    DragRect.WMKeyDown(Msg);
  end else
  begin
    IncrementalSearch.HandleWMKeyDown(Msg);

    Shift := KeyDataToShiftState(Msg.KeyData);
    DoDefault := True;
    DoKeyAction(Msg.CharCode, Shift, DoDefault);
    if DoDefault then
      HandleKeyDown(Msg);
  end;
end;

procedure TCustomEasyListview.WMKillFocus(var Msg: TWMKillFocus);
begin
  inherited;
  if HotTrack.OnlyFocused then
    HotTrack.PendingObject[Point(0, 0)] := nil;
  SafeInvalidateRect(nil, True);
end;

procedure TCustomEasyListview.WMLButtonDblClk(var Msg: TWMLButtonDblClk);
begin
  // Cancel the edit (called in WMLButtonDown)
  EditManager.EndEdit;

  inherited;
  HandleDblClick(cmbLeft, Msg);
end;

procedure TCustomEasyListview.WMLButtonDown(var Msg: TWMLButtonDown);
// Called when the Left Mouse button is pressed
begin
  Include(FStates, ebcsLButtonDown);
  CheckFocus;
  if IsHeaderMouseMsg(Msg.Pos) then
  begin
    // Should this call a Column manager or a Column?
    if Assigned(Header) then
    begin
      Header.CaptureMouse;
      Header.WMLButtonDown(Msg);
    end
  end else
  begin
    inherited;
    HandleMouseDown(cmbLeft, Msg)
  end;
  LastMousePos := Msg.Pos
end;

procedure TCustomEasyListview.WMLButtonUp(var Msg: TWMLButtonUp);
// Called when the Left Mouse button is released
begin
  if IsHeaderMouseMsg(Msg.Pos) then
  begin
   // Should this call a Column manager or a Column?
   if Assigned(Header) then
    begin
      Header.WMLButtonUp(Msg);
      // Need to allow HotTracking to finish up.
      Header.WMMouseMove(Msg);
      Header.ReleaseMouse;
    end
  end else
  begin
    // The VCL D&D will "Perform" a Left Button Up when StartDrag is called.  As such
    // a lot of the State Flags get prematurely reset.
    if not (ebcsVCLDrag in States) then
      ClearPendingDrags;
    inherited;
    HandleMouseUp(cmbLeft, Msg);
    Mouse.Capture := 0;
  end;
  // ClearStates will get called from the VCL end Drag event
  if not (ebcsVCLDrag in States) then
    ClearStates;
end;

procedure TCustomEasyListview.WMMButtonDblClk(var Msg: TWMMButtonDblClk);
begin
  inherited;
  HandleDblClick(cmbMiddle, Msg)
end;

procedure TCustomEasyListview.WMMButtonDown(var Msg: TWMMButtonDown);
begin
  Include(FStates, ebcsMButtonDown);
  CheckFocus;
  inherited;
  HandleMouseDown(cmbMiddle, Msg);
  LastMousePos := Msg.Pos
end;

procedure TCustomEasyListview.WMMButtonUp(var Msg: TWMMButtonUp);
begin
  HandleMouseUp(cmbMiddle, Msg);
  ClearPendingDrags;
  inherited;
  Mouse.Capture := 0;
  ClearStates;
end;

procedure TCustomEasyListview.WMMouseActivate(var Msg: TWMMouseActivate);
// Called when the mouse is clicked in a window and the window does not have
// focus.  By responding MA_ACTIVATE the window should recieve focus but this
// does not seem to happen on NT4.  Necded a SetFocus when the mouse is pressed
begin
  inherited;
  if not EditManager.Editing then
  begin
    CheckFocus;
    Msg.Result := MA_ACTIVATE;
  end else
    Msg.Result := MA_NOACTIVATE;
end;

procedure TCustomEasyListview.WMMouseMove(var Msg: TWMMouseMove);
// Called when the mouse is moved
var
  KeyState: TCommonKeyStates;
  GroupHitInfo: TEasyGroupHitTestInfoSet;
  ItemHitInfo: TEasyItemHitTestInfoSet;
  Effects: TCommonDropEffect;
  Group: TEasyGroup;
  HotTrackCheckObj, HotTrackObj: TEasyCollectionItem;
  Item: TEasyItem;
  Pt: TPoint;
  Dx, Dy: Integer;
  LocalGesture: WideChar;
begin
  KeyState := KeyToKeyStates(Msg.Keys);
  Pt := Scrollbars.MapWindowToView(Msg.Pos);
  HotTrackCheckObj := nil;

  if Gesture.Enabled then
  begin
    Dx := Msg.XPos - LastMousePos.x;
    Dy := Msg.YPos - LastMousePos.y;
    if ((cksLButton in KeyState) and (cmbLeft in Gesture.Button)) or
       ((cksRButton in KeyState) and (cmbRight in Gesture.Button)) or
       ((cksMButton in KeyState) and (cmbMiddle in Gesture.Button)) then
    begin
      LocalGesture := '*';
      if (Dx > Gesture.Tolerance) and (Abs(Dy) < 2 * Gesture.Tolerance) then
        LocalGesture := 'R'
      else if (-Dx > Gesture.Tolerance) and (Abs(Dy) < 2 * Gesture.Tolerance) then
        LocalGesture := 'L'
      else if (Dy > Gesture.Tolerance) and (Abs(Dx) < 2 * Gesture.Tolerance) then
        LocalGesture := 'D'
      else if (-Dy > Gesture.Tolerance) and (Abs(Dx) < 2 * Gesture.Tolerance) then
        LocalGesture := 'U';
      if LocalGesture <> '*' then
      begin
        if Length(Gesture.Path) > 0 then
        begin
          if Gesture.Path[Length(Gesture.Path)] <> LocalGesture then
            Gesture.Path := Gesture.Path + LocalGesture
        end else
         Gesture.Path := LocalGesture
      end
    end
  end;

  if IsHeaderMouseMsg(Msg.Pos) then
  begin
    HotTrack.PendingObject[Point(0, 0)] := nil;
    Header.WMMouseMove(Msg)
  end else
  begin
    if not Assigned(HotTrack.PendingObject[Point(0,0)]) then
      Cursor := crDefault;

    // First see if there is any kind of drag operation pending or occuring
    if DragInitiated then
    begin
      // First check for special cases, such as when the drag selection rectangle
      // is active or an item is being dragged in a D&D operation.  If not see if
      // this is the first move after a mouse press and one of the above cases is
      // pending.  If we then detect a drag start then setup the flags and initialize
      // the action with the appropiate mananger.
      if ebcsDragSelecting in States then
        DragRect.Drag(Canvas, SmallPointToPoint(Msg.Pos), KeyState, Effects)
      else
      if ebcsDragging in States then
        // his only works for VCL drag and drop. The System takes the mouse for OLE
        DragManager.Drag(Canvas, SmallPointToPoint(Msg.Pos), KeyState, Effects)
      else
      // We are not drag selecting; check if we have a drag pending
      if [ebcsDragSelectPending, ebcsDragPending] * States <> [] then
      begin
        if DragDetectPlus(Handle, SmallPointToPoint(Msg.Pos)) then
        begin
          // The decision to if the mouse was a drag or a select was made when the
          // mouse was pressed, based on the area of the control the click occured
          if ebcsDragSelectPending in States then
          begin
            // We are now drag selecting so update the states
            Include(FStates, ebcsDragSelecting);
            Exclude(FStates, ebcsDragSelectPending);
            if KeyState * [cksShift, cksControl] = [] then
              Include(FStates, ebcsDragSelecting);
            Exclude(FStates, ebcsDragSelectPending);
            if KeyState * [cksShift, cksControl] = [] then
              Selection.ClearAll;
            EditManager.StopAutoEditTimer;
            DragRect.BeginDrag(SmallPointToPoint(Msg.Pos), KeyState);
            // Since for a selection rect we have the mouse captured we can just
            // call DragEnter once here and be done with it
            DragRect.DragEnter(nil, Canvas, ScreenToClient(Mouse.CursorPos), KeyState, Effects);
          end else
          if ebcsDragPending in States then
          begin
            Item := Groups.ItembyPoint(Scrollbars.MapWindowToView(SmallPointToPoint(Msg.Pos)));
            if Assigned(Item) then
            begin
              // Since we don't select until a MouseUp on Ctl-Click we must do it here just in case
              if cksControl in KeyState then
              begin
                Item.Selected := True;
                Item.Focused := True;
              end;
              Include(FStates, ebcsDragging);
              if edtVCL = DragManager.DragType then
                Include(FStates, ebcsVCLDrag);
              Exclude(FStates, ebcsDragPending);
              EditManager.StopAutoEditTimer;
              try
                DragManager.BeginDrag(SmallPointToPoint(Msg.Pos), KeyState);
              finally
                // If was an OLE drag then the subsystem has taken the mouse and we
                // will never get a mouse up so fake it, the VCL system fakes it on
                // its own but if the drag/drop is not enabled then it never will so
                // fake it for all cases

                if not(ebcsVCLDrag in States) then
                begin
                  if ebcsLButtonDown in States then
                    Perform(WM_LBUTTONUP, TMessage(Msg).wParam, TMessage(Msg).LParam)
                  else
                  if ebcsRButtonDown in States then
                  begin
                    // Don't want to show the context menu after a drag drop with
                    // the right button
                    Include(FStates, ebcsCancelContextMenu);
                    Perform(WM_RBUTTONUP, TMessage(Msg).wParam, TMessage(Msg).LParam)
                  end else
                  if ebcsMButtonDown in States then
                    Perform(WM_MBUTTONUP, TMessage(Msg).wParam, TMessage(Msg).LParam)
                end
              end
            end else
            begin
              Selection.ClearAll;
              // Switch from a DragDrop Pending to a DragSelect Pending
              Exclude(FStates, ebcsDragPending);
              if DragRect.Enabled then
                Include(FStates, ebcsDragSelectPending);
            end
          end
        end
      end
    end else    // Not DragInitiated
    begin
      HotTrackObj := nil;

      Group := Groups.GroupByPoint(Pt);
      if Assigned(Group) then
      begin
        if Group.HitTestAt(Pt, GroupHitInfo) then
          // See if it is in a Group Check box first
          if egtOnCheckbox in GroupHitInfo then
            HotTrackCheckObj := Group
          else begin
            if (htgAnyWhere in HotTrack.GroupTrack) or
            ((htgIcon in HotTrack.GroupTrack) and (egtOnIcon in GroupHitInfo)) or
            ((htgText in HotTrack.GroupTrack) and (egtOnText in GroupHitInfo)) or
            ((htgTopMargin in HotTrack.GroupTrack) and (egtOnHeader in GroupHitInfo)) or
            ((htgBottomMargin in HotTrack.GroupTrack) and (egtOnFooter in GroupHitInfo)) or
            ((htgLeftMargin in HotTrack.GroupTrack) and (egtOnLeftMargin in GroupHitInfo)) or
            ((htgRightMargin in HotTrack.GroupTrack) and (egtOnRightMargin in GroupHitInfo)) then
              HotTrackObj := Group;
          end
        end;

        // Next See if it is in an Item Check box
        if not (Assigned(HotTrackCheckObj) or Assigned(HotTrackObj)) then
        begin
          Item := Groups.ItembyPoint(Pt);
          if Assigned(Item) then
          begin
            if Item.HitTestAt(Pt, ItemHitInfo) then
            begin
              if ehtOnCheck in ItemHitInfo then
                HotTrackCheckObj := Item
              else begin
                 if (htiAnyWhere in HotTrack.ItemTrack) or
                ((htiIcon in HotTrack.ItemTrack) and (ehtOnIcon in ItemHitInfo)) or
                ((htiText in HotTrack.ItemTrack) and (ehtOnText in ItemHitInfo)) then
                  HotTrackObj := Item
              end
            end
          end
        end;

      if (ebcsCheckboxClickPending in States) then
      begin
        if Assigned(HotTrackCheckObj) then
        begin
          if CheckManager.PendingObject <> HotTrackCheckObj then
          begin
            CheckManager.PendingObject.CheckHovering := True;
            HotTrackCheckObj := nil
          end else
            CheckManager.PendingObject.CheckHovering := False;
        end else
        begin
          CheckManager.PendingObject.CheckHovering := True;
        end;
        if HotTrackCheckObj <> nil then
          HotTrackCheckObj := nil;
      end;

      if not Assigned(CheckManager.PendingObject) and (Self.Focused or not HotTrack.OnlyFocused) and
        PtInRect(ClientRect, SmallPointToPoint(Msg.Pos)) and (Assigned(HotTrackObj) and (HotTrackObj.Enabled)) then
        HotTrack.PendingObject[SmallPointToPoint(Msg.Pos)] := HotTrackObj
      else
        HotTrack.PendingObject[SmallPointToPoint(Msg.Pos)] := nil;

      HotTrack.PendingObjectCheck := HotTrackCheckObj;

      // We are not drag selecting and do not have a drag pending so it is a normal
      // mouse move
      inherited;
    end
  end;
  LastMousePos := Msg.Pos;
end;

procedure TCustomEasyListview.WMNCCalcSize(var Msg: TWMNCCalcSize);
{$IFDEF USETHEMES}
var
  R: TRect;
{$ENDIF USETHEMES}
begin
  if DrawWithThemes then
  begin
    DefaultHandler(Msg);
    {$IFDEF USETHEMES}
    if succeeded(GetThemeBackgroundContentRect(Themes.ListviewTheme, Canvas.Handle, LVP_EMPTYTEXT, LIS_NORMAL, Msg.CalcSize_Params^.rgrc[0], @R)) then
    begin
      InflateRect(R, -1-BorderWidth, -1-BorderWidth);
      Msg.CalcSize_Params^.rgrc[0] := R; 
    end
    {$ENDIF USETHEMES}
  end else
  begin
    inherited;
  end
end;

procedure TCustomEasyListview.WMNCPaint(var Msg: TWMNCPaint);
// The VCL screws this up and draws over the scrollbars making them flicker and
// be covered up by backgound painting when dragging the the window from off the
// screen
const
  InnerStyles: array[TBevelCut] of Integer = (0, BDR_SUNKENINNER, BDR_RAISEDINNER, 0);
  OuterStyles: array[TBevelCut] of Integer = (0, BDR_SUNKENOUTER, BDR_RAISEDOUTER, 0);
  EdgeStyles: array[TBevelKind] of Integer = (0, 0, BF_SOFT, BF_FLAT);
  Ctl3DStyles: array[Boolean] of Integer = (BF_MONO, 0);
var
  ClientR, WindowR, WindowROrig, Filler: TRect;
  DC: HDC;
  Style, StyleEx: Longword;
  Pt: TPoint;
  {$IFDEF USETHEMES}
  R: TRect;
  {$ENDIF USETHEMES}
begin
  // Let Windows paint the scrollbars first
  DefaultHandler(Msg);
  // Always paint the NC area as refreshing it can be tricky and it sometimes
  // is not redrawn on startup
//  if UpdateCount = 0 then
  begin
    DC := GetWindowDC(Handle);
    try
      NCCanvas.Handle := DC;

      if (BevelKind <> bkNone) or (BorderWidth > 0){ or DrawWithThemes} then
      begin
        Windows.GetClientRect(Handle, ClientR);
        Windows.GetWindowRect(Handle, WindowR);
        Pt := WindowR.TopLeft;
        Windows.ScreenToClient(Handle, Pt);
        // Map the screen coordinates of the WindowRect to a 0,0 offset
        OffsetRect(WindowR, -WindowR.Left, -WindowR.Top);
        // Make a copy
        WindowROrig := WindowR;
        // Map the ClientRect to Window coordinates
        OffsetRect(ClientR, -Pt.X, -Pt.Y);
        Style := GetWindowLong(Handle, GWL_STYLE);
        if (Style and WS_VSCROLL) <> 0 then
        begin
          StyleEx := GetWindowLong(Handle, GWL_EXSTYLE);
          if (StyleEx and WS_EX_LEFTSCROLLBAR) <> 0 then
            Dec(ClientR.Left, GetSystemMetrics(SM_CYVSCROLL))
          else
            Inc(ClientR.Right, GetSystemMetrics(SM_CYVSCROLL))
        end;
        if (Style and WS_HSCROLL) <> 0 then
          Inc(ClientR.Bottom, GetSystemMetrics(SM_CYHSCROLL));

        // Paint the little square in the corner made by the scroll bars
        if ((Style and WS_VSCROLL) <> 0) and ((Style and WS_HSCROLL) <> 0) then
        begin
          Filler := ClientR;
          Filler.Left := Filler.Right - GetSystemMetrics(SM_CYVSCROLL);
          Filler.Top := Filler.Bottom - GetSystemMetrics(SM_CYHSCROLL);
          NCCanvas.Brush.Color := clBtnFace;
          NCCanvas.FillRect(Filler);
        end;

   //     if not DrawWithThemes then
          // Punch out the client area and the scroll bar area
          ExcludeClipRect(DC, ClientR.Left, ClientR.Top, ClientR.Right, ClientR.Bottom);

        if DrawWithThemes then
        begin
          {$IFDEF USETHEMES}
          if not ShowThemedBorder then
          begin
            R := Rect(0, 0, 0, 0);
            GetThemeBackgroundExtent(Themes.ListviewTheme, NCCanvas.Handle, LVP_EMPTYTEXT, LIS_NORMAL, ClientRect, R);
            InflateRect(WindowROrig, R.Left - ClientRect.Left, R.Top - ClientRect.Top);
          end;
          DrawThemeBackground(Themes.ListviewTheme, NCCanvas.Handle, LVP_EMPTYTEXT, LIS_NORMAL, WindowROrig, nil);
          {$ENDIF USETHEMES}
        end else
        begin
          Windows.FillRect(DC, WindowR, Brush.Handle);
          DrawEdge(DC, WindowROrig, InnerStyles[BevelInner] or OuterStyles[BevelOuter],
              Byte(BevelEdges) or EdgeStyles[BevelKind] or Ctl3DStyles[Ctl3D] or BF_ADJUST);
        end
      end else
      begin
        Windows.GetWindowRect(Handle, WindowR);
      end;
    finally
      NCCanvas.Handle := 0;
      ReleaseDC(Handle, DC);
    end
  end
end;

procedure TCustomEasyListview.WMPaint(var Msg: TWMPaint);
// The VCL does a poor job at optimizing the paint messages.  It does not look
// to see what rectangle the system actually needs painted.  Sometimes it only
// needs a small slice of the window painted, why paint it all?  This implementation
// also handles DoubleBuffering better
var
  PaintInfo: TPaintStruct;
  ClientR: TRect;
begin
  BeginPaint(Handle, PaintInfo);
  try
    if (UpdateCount = 0) or ForcePaint then
    begin
      try
       if not CacheDoubleBufferBits then
        begin
          BackBits := TBitmap.Create;
          BackBits.PixelFormat := pf32Bit;
          Windows.GetClientRect(Handle, ClientR);
          if ClientWidth > 0 then
            BackBits.Width := ClientWidth
          else
            BackBits.Width := 1;
          if ClientHeight > 0 then
            BackBits.Height := ClientHeight
          else
            BackBits.Height := 1;
        end;
        BackBits.Canvas.Lock;
        try
          if not IsRectEmpty(PaintInfo.rcPaint) and (ClientWidth > 0) and (ClientHeight > 0) then
          begin
            // Assign attributes to the Canvas used
            BackBits.Canvas.Font.Assign(Font);
            BackBits.Canvas.Brush.Color := Color;
            BackBits.Canvas.Brush.Assign(Brush);

            SetWindowOrgEx(BackBits.Canvas.Handle, 0, 0, nil);
            SetViewportOrgEx(BackBits.Canvas.Handle, 0, 0, nil);
            FillRect(BackBits.Canvas.Handle, PaintInfo.rcPaint, Brush.Handle);
            SelectClipRgn(BackBits.Canvas.Handle, 0);  // Remove the clipping region we created

            // Paint the rectangle that is needed
            DoPaintRect(BackBits.Canvas, PaintInfo.rcPaint, False);

            // Remove any clipping regions applied by the views.
            SelectClipRgn(BackBits.Canvas.Handle, 0);
            // Redraw the drag selecting rectangle
            if (ebcsDragSelecting in States) then
              DragRect.PaintSelectionRect(BackBits.Canvas);

            DoAfterPaint(BackBits.Canvas, PaintInfo.rcPaint);

            // Blast the bits to the screen
            BitBlt(PaintInfo.hdc, PaintInfo.rcPaint.Left, PaintInfo.rcPaint.Top,
              PaintInfo.rcPaint.Right - PaintInfo.rcPaint.Left,
              PaintInfo.rcPaint.Bottom - PaintInfo.rcPaint.Top,
              BackBits.Canvas.Handle, PaintInfo.rcPaint.Left, PaintInfo.rcPaint.Top, SRCCOPY);
          end
        finally
          BackBits.Canvas.Unlock;
        end;
      finally
        if not CacheDoubleBufferBits then
          FreeAndNil(FBackBits)
      end;
    end    
  finally
    EndPaint(Handle, PaintInfo);
  end
end;

procedure TCustomEasyListview.WMRButtonDblClk(var Msg: TWMRButtonDblClk);
begin
  inherited;
  HandleDblClick(cmbRight, Msg)
end;

procedure TCustomEasyListview.WMRButtonDown(var Msg: TWMRButtonDown);
begin
  Include(FStates, ebcsRButtonDown);
  CheckFocus;
  if IsHeaderMouseMsg(Msg.Pos) then
  begin
    // Should this call a Column manager or a Column?
    if Assigned(Header) then
    begin
      Header.WMRButtonDown(Msg);
      Header.ReleaseMouse;
    end
  end else
  begin
    inherited;
    HandleMouseDown(cmbRight, Msg)
  end;
  LastMousePos := Msg.Pos
end;

procedure TCustomEasyListview.WMRButtonUp(var Msg: TWMRButtonUp);
begin
  if IsHeaderMouseMsg(Msg.Pos) then
  begin
   // Should this call a Column manager or a Column?
    if Assigned(Header) then
    begin
      inherited;
      // ContextMenu handled it
      if Msg.Result = 0 then
        Header.WMRButtonUp(Msg);
      // Need to allow HotTracking to finish up.
      if Assigned(Header.HotTrackedColumn) then
        Header.WMMouseMove(Msg);
      Header.ReleaseMouse;
    end
  end else
  begin
    if [ebcsDragSelecting, ebcsDragging, ebcsVCLDrag] * States <> [] then
      Include(FStates, ebcsCancelContextMenu);
    if Msg.Result = 0 then
      HandleMouseUp(cmbRight, Msg);
    ClearPendingDrags;
    inherited;  // Handles the context menu
    Mouse.Capture := 0;
  end;
  ClearStates;
end;

procedure TCustomEasyListview.WMSetCursor(var Msg: TWMSetCursor);
begin       
  inherited;
end;

procedure TCustomEasyListview.WMSetFocus(var Msg: TWMSetFocus);
begin
  inherited;
  SafeInvalidateRect(nil, True);
  //Selection.Invalidate(False)VisibleSelected(False);
end;

procedure TCustomEasyListview.WMSize(var Msg: TWMSize);
begin
  {$IFDEF GXDEBUG}
  SendDebug('TCustomEasyListview.WMSize');
  {$ENDIF GXDEBUG}
  Header.WMSize(Msg);  // do it first
  inherited;
end;

procedure TCustomEasyListview.WMTabMoveFocus(var Msg: TMessage);
var
  NextItem: TEasyItem;
  JumpToNextItem: Boolean;
  NextColumn: TEasyColumn;
begin
  JumpToNextItem := True;
  NextItem := EditManager.TabMoveFocusItem;
  if EditManager.TabEditColumns then
  begin
    NextColumn := EditManager.TabMoveFocusColumn;
    if Assigned(NextColumn) then
    begin
      EditManager.TabMoveFocusItem := nil;
      JumpToNextItem := False;
      NextColumn.MakeVisible(emvTop);
      EditManager.BeginEdit(NextItem, NextColumn);
    end
  end;
  if JumpToNextItem then
  begin
    EditManager.TabMoveFocusItem := nil;
    if Assigned(NextItem) then
    begin
      NextItem.Focused := True;
      NextItem.MakeVisible(emvAuto);
      EditManager.BeginEdit(NextItem, nil)
    end;
  end
end;

procedure TCustomEasyListview.WMVScroll(var Msg: TWMVScroll);
// Called to scroll the Window, the Window is responsible for actually performing
// the scroll
begin
  inherited;
  if Msg.ScrollCode <> SB_ENDSCROLL then
    Include(FStates, ebcsScrolling)
  else
    Exclude(FStates, ebcsScrolling);
  ScrollBars.WMVScroll(Msg);
  SafeInvalidateRect(nil, False);
end;

procedure TCustomEasyListview.WMWindowPosChanged(var Msg: TWMWindowPosChanged);
// Called after the window has changed size
var
  YChanged, XChanged: Boolean;
begin
  {$IFDEF GXDEBUG}
  SendDebug('TCustomEasyListview.WMWindowPosChanged');
  {$ENDIF GXDEBUG}
  YChanged := False;
  XChanged := False;
  // Only use cx and cy if NOSIZE is not used
  if Msg.WindowPos^.flags and SWP_NOSIZE = 0 then
  begin
    YChanged := Height <> Msg.WindowPos.cy;
    XChanged := Width <> Msg.WindowPos.cx;
  end;
  inherited;
  if YChanged or XChanged then
  begin
    try
      Groups.Rebuild(True);
      ScrollBars.ReCalculateScrollbars(True, False);
    finally
      ResizeBackBits(Msg.WindowPos.cx, Msg.WindowPos.cy);
    end;     
  end
end;

procedure TCustomEasyListview.WMWindowPosChanging(var Msg: TWMWindowPosChanging);
// Called when the window is changing size
var
  YChanged, XChanged: Boolean;
begin
  {$IFDEF GXDEBUG}
  SendDebug('TCustomEasyListview.WMWindowPosChanging');
  {$ENDIF GXDEBUG}
  YChanged := False;
  XChanged := False;
  // Only use cx and cy if NOSIZE is not used
  if Msg.WindowPos^.flags and SWP_NOSIZE = 0 then
  begin
    YChanged := Height <> Msg.WindowPos.cy;
    XChanged := Width <> Msg.WindowPos.cx;
  end;

  // Track the offset of the background image if desired before the control size is updated
  BackGround.WMWindowPosChanging(Msg);
  inherited;
  if YChanged or XChanged then
    DoResize(Msg.WindowPos.cx - Width, Msg.WindowPos.cy - Height)
end;

{ TEasyItem }

constructor TEasyCollectionItem.Create(ACollection: TEasyCollection);
begin
  inherited Create();
  Collection := ACollection;
  Include(FState, esosVisible);
  Include(FState, esosEnabled);
  FVisibleIndex := -1;
end;

destructor TEasyCollectionItem.Destroy;
begin
  SetDestroyFlags;
  DataInf := nil;
  if Assigned(OwnerListview) then
  begin
    if OwnerListview.HotTrack.PendingObjectCheck = Self then
      OwnerListview.HotTrack.PendingObjectCheck := nil;
    if OwnerListview.HotTrack.FPendingObject = Self then
      OwnerListview.HotTrack.FPendingObject := nil;
  end;
  Freeing;
  inherited;
  if OwnsPaintInfo then
    FreeAndNil(FPaintInfo);
end;

function TEasyCollectionItem.AllowDrag(ViewportPt: TPoint): Boolean;
begin
  Result := True
end;

function TEasyCollectionItem.DefaultImageList(ImageSize: TEasyImageSize): TCustomImageList;
begin
  Result := nil;
  case ImageSize of
   eisSmall: Result := OwnerListview.ImagesSmall;
   eisLarge: Result := OwnerListview.ImagesLarge;
   eisExtraLarge: Result := OwnerListview.ImagesExLarge;
  end
end;

function TEasyCollectionItem.GetAlignment: TAlignment;
begin
  Result := PaintInfo.Alignment
end;

function TEasyCollectionItem.GetBold: Boolean;
begin
  Result := esosBold in State
end;

function TEasyCollectionItem.GetBorder: Integer;
begin
  Result := PaintInfo.Border
end;

function TEasyCollectionItem.GetBorderColor: TColor;
begin
  Result := PaintInfo.BorderColor
end;

function TEasyCollectionItem.GetCaption: WideString;
begin
  Result := GetCaptions(0)
end;

function TEasyCollectionItem.GetCaptionIndent: Integer;
begin
  Result := PaintInfo.CaptionIndent
end;

function TEasyCollectionItem.GetChecked: Boolean;
begin
  Result := esosChecked in State
end;

function TEasyCollectionItem.GetCheckFlat: Boolean;
begin
  Result := PaintInfo.CheckFlat
end;

function TEasyCollectionItem.GetCheckHovering: Boolean;
begin
  Result := esosCheckHover in FState
end;

function TEasyCollectionItem.GetCheckIndent: Integer;
begin
  Result := PaintInfo.CheckIndent
end;

function TEasyCollectionItem.GetCheckPending: Boolean;
begin
  Result := esosCheckPending in FState
end;

function TEasyCollectionItem.GetChecksize: Integer;
begin
  Result := PaintInfo.Checksize
end;

function TEasyCollectionItem.GetCheckType: TEasyCheckType;
begin
  Result := PaintInfo.CheckType
end;

function TEasyCollectionItem.GetClicking: Boolean;
begin
  Result := esosClicking in State
end;

function TEasyCollectionItem.GetCut: Boolean;
begin
  Result := esosCut in State
end;

function TEasyCollectionItem.GetDataInf: IUnknown;
begin
  Result := FDataInf;
end;

function TEasyCollectionItem.GetDestroying: Boolean;
begin
  Result := esosDestroying in State
end;

function TEasyCollectionItem.GetDisplayName: WideString;
begin
  if Caption <> '' then
    Result := Caption
  else
    Result:= ClassName;
end;

function TEasyCollectionItem.GetEnabled: Boolean;
begin
  Result := esosEnabled in State
end;

function TEasyCollectionItem.GetFocused: Boolean;
begin
  Result := esosFocused in State
end;

function TEasyCollectionItem.GetGhosted: Boolean;
begin
  Result := esosGhosted in State
end;

function TEasyCollectionItem.GetHilighted: Boolean;
begin
  Result := esosHilighted in State
end;

function TEasyCollectionItem.GetHotTracking(MousePos: TPoint): Boolean;
begin
  Result := esosHotTracking in State
end;

function TEasyCollectionItem.GetImageIndent: Integer;
begin
  Result := PaintInfo.ImageIndent
end;

function TEasyCollectionItem.GetImageIndex: TCommonImageIndexInteger;
begin
  Result := GetImageIndexes(0)
end;

function TEasyCollectionItem.GetImageOverlayIndex: TCommonImageIndexInteger;
begin
  Result := GetImageOverlayIndexes(0)
end;

function TEasyCollectionItem.GetIndex: Integer;
begin
  Result := Collection.FList.IndexOf(Self)
end;

function TEasyCollectionItem.GetInitialized: Boolean;
begin
  Result := esosInitialized in State
end;

function TEasyCollectionItem.GetOwner: TPersistent;
begin
  Result := FCollection
end;

function TEasyCollectionItem.GetOwnerListview: TCustomEasyListview;
begin
  Result := TEasyCollection( Collection).FOwnerListview
end;

function TEasyCollectionItem.GetPaintInfo: TEasyPaintInfoBasic;
begin
  if Assigned(FPaintInfo) then
    Result := FPaintInfo
  else
    Result := LocalPaintInfo
end;

function TEasyCollectionItem.GetSelected: Boolean;
begin
  Result := esosSelected in State
end;

function TEasyCollectionItem.GetVAlignment: TCommonVAlignment;
begin
  Result := PaintInfo.VAlignment
end;

function TEasyCollectionItem.GetViewRect: TRect;
begin
  Result := DisplayRect
end;

function TEasyCollectionItem.GetVisible: Boolean;
begin
  Result := esosVisible in State
end;

function TEasyCollectionItem.QueryInterface(const IID: TGUID; out Obj): HResult;
begin
  Result := E_NOINTERFACE
end;

function TEasyCollectionItem._AddRef: Integer;
begin
  Result := -1
end;

function TEasyCollectionItem._Release: Integer;
begin
  Result := -1
end;

procedure TEasyCollectionItem.Invalidate(ImmediateUpdate: Boolean);
var
  R: TRect;
begin
  if OwnerListview.UpdateCount = 0 then
  begin
    R := OwnerListview.Scrollbars.MapViewRectToWindowRect(DisplayRect);
    if IntersectRect(R, R, OwnerListview.ClientRect) then
      OwnerListview.SafeInvalidateRect(@R, ImmediateUpdate);
  end
end;

procedure TEasyCollectionItem.InvalidateItem(ImmediateRefresh: Boolean);
begin
  Invalidate(ImmediateRefresh)
end;

procedure TEasyCollectionItem.LoadFromStream(S: TStream; var Version: Integer);
var
  Temp: TEasyStorageObjectStates;
  T: Longword;
begin
  S.Read(T, SizeOf(T));

  // See if we have an old style stream
  if T <> STREAM_ID_KEY then
  begin
    // Old poor way
    S.Seek(-SizeOf(T), soFromCurrent);
    Version := 2 // Use to be Collection.StreamVersion but we can hard code 2 since that appeared in version 2 only
  end else
    S.Read(Version, SizeOf(Version));

  S.Read(Temp, SizeOf(Temp));
  // Add back in the stored persisted states
  Selected := esosSelected in Temp;
  Enabled := esosEnabled in Temp;
  Visible := esosVisible in Temp;
  Checked := esosChecked in Temp;
  Bold := esosBold in Temp;

  // For new objects test the stream version first
  // if Version > X then
  // begin
  //   ReadStream....
  // end
end;

procedure TEasyCollectionItem.MakeVisible(Position: TEasyMakeVisiblePos);
begin

end;

procedure TEasyCollectionItem.SaveToStream(S: TStream; Version: Integer = EASYLISTVIEW_STREAM_VERSION);
var
  Temp: TEasyStorageObjectStates;
  Key: Integer;
begin
  Key := STREAM_ID_KEY;
  S.Write(Key, SizeOf(Key));
  S.Write(Version, SizeOf(Version));
  Temp := State;
  // Only save certin states that should be persistent
  Temp := State * PERSISTENTOBJECTSTATES;
  S.Write(Temp, SizeOf(Temp));

  // For new objects test the stream version first
  // if Version then
  // begin
  //   WriteStream....
  // end
end;

procedure TEasyCollectionItem.SetAlignment(Value: TAlignment);
begin
  if Value <> Alignment then
  begin
    PaintInfo.Alignment := Value;
    Invalidate(False)
  end
end;

procedure TEasyCollectionItem.SetBold(const Value: Boolean);
begin
  if Value xor (esosBold in FState) then
  begin
    if State * [esosDestroying] <> [] then
    begin
      Exclude(FState, esosBold);
      LosingBold
    end else
    begin
      if CanChangeBold(Value) then
      begin
        if Value then
        begin
          Include(FState, esosBold);
          GainingBold
        end else
        begin
          Exclude(FState, esosBold);
          LosingBold
        end
      end
    end
  end
end;

procedure TEasyCollectionItem.SetBorder(Value: Integer);
begin
  if Value <> Border then
  begin
    if Value < 0 then
      PaintInfo.Border := 0
    else
      PaintInfo.Border := Value;
    Invalidate(False)
  end
end;

procedure TEasyCollectionItem.SetBorderColor(Value: TColor);
begin
  if Value <> BorderColor then
  begin
    if Value < 0 then
      PaintInfo.BorderColor := 0
    else
      PaintInfo.BorderColor := Value;
    Invalidate(False)
  end
end;

procedure TEasyCollectionItem.SetCaption(Value: WideString);
begin
  Captions[0] := Value
end;

procedure TEasyCollectionItem.SetCaptionIndent(Value: Integer);
begin
  if Value <> CaptionIndent then
  begin
    if Value < 0 then
      PaintInfo.CaptionIndent := 0
    else
      PaintInfo.CaptionIndent := Value;
    Invalidate(False)
  end
end;

procedure TEasyCollectionItem.SetChecked(Value: Boolean);
begin
  if Value xor (esosChecked in FState) then
  begin
    if State * [esosDestroying] <> [] then
    begin
      Exclude(FState, esosChecked);
      LosingCheck
    end else
    begin
      if CanChangeCheck(Value) then
      begin
        if Value then
        begin
          Include(FState, esosChecked);
          GainingCheck
        end else
        begin
          Exclude(FState, esosChecked);
          LosingCheck
        end
      end
    end
  end
end;

procedure TEasyCollectionItem.SetCheckFlat(Value: Boolean);
begin
  if Value <> CheckFlat then
  begin
    PaintInfo.CheckFlat := Value;
    Invalidate(False)
  end
end;

procedure TEasyCollectionItem.SetCheckHovering(Value: Boolean);
begin
  if Value xor (esosCheckHover in FState) then
  begin
    if Value then
      Include(FState,esosCheckHover)
    else
      Exclude(FState, esosCheckHover);
    Invalidate(False)
  end
end;

procedure TEasyCollectionItem.SetCheckIndent(Value: Integer);
begin
  if Value <> CheckIndent then
  begin
    if Value < 1 then
      PaintInfo.CheckIndent := 0
    else
      PaintInfo.CheckIndent := Value;
    Invalidate(False)
  end;
end;

procedure TEasyCollectionItem.SetCheckPending(Value: Boolean);
begin
  if Value xor (esosCheckPending in FState) then
  begin
    if Value then
      Include(FState,esosCheckPending)
    else
      Exclude(FState, esosCheckPending);
    Invalidate(False)
  end
end;

procedure TEasyCollectionItem.SetChecksize(Value: Integer);
begin
  if Value <> Checksize then
  begin
    if Value < 0 then
      PaintInfo.Checksize := 0
    else
      PaintInfo.Checksize := Value;

    Invalidate(False)
  end
end;

procedure TEasyCollectionItem.SetCheckType(Value: TEasyCheckType);
begin
  if Value <> CheckType then
  begin
    PaintInfo.CheckType := Value;
    Invalidate(False)
  end
end;

procedure TEasyCollectionItem.SetClicking(Value: Boolean);
begin
  if Value then
    Include(FState, esosClicking)
  else
    Exclude(FState, esosClicking)
end;

procedure TEasyCollectionItem.SetCut(Value: Boolean);
begin
  if Value xor (esosCut in State) then
  begin
    if Value then
      Include(FState, esosCut)
    else
      Exclude(FState, esosCut);
    Invalidate(False)
  end
end;

procedure TEasyCollectionItem.SetData(Value: TObject);
begin
  FData := Value
end;

procedure TEasyCollectionItem.SetDataInf(const Value: IUnknown);
var
  Notifier: IEasyNotifier;
begin
  if Value <> FDataInf then
  begin
    if Supports(DataInf, IEasyNotifier, Notifier) then
      Notifier.OnUnRegisterNotify(Self);
    FDataInf := Value;
    if Supports(DataInf, IEasyNotifier, Notifier) then
      Notifier.OnRegisterNotify(Self)
  end
end;

procedure TEasyCollectionItem.SetDestroyFlags;
begin
  Include(FState, esosDestroying);
end;

procedure TEasyCollectionItem.SetEnabled(Value: Boolean);
begin
  if Value xor (esosEnabled in FState) then
  begin
    if State * [esosDestroying] <> [] then
    begin
      // Disabled items can't have the focus or selection
      Focused := False;
      Selected := False;
      Exclude(FState, esosEnabled);
      LosingEnable
    end else
    begin
      if CanChangeEnable(Value) then
      begin
        if Value then
        begin
          Include(FState, esosEnabled);
          GainingEnable
        end else
        begin
          // Disabled items can't have the focus or selection
          Focused := False;
          Selected := False;
          Exclude(FState, esosEnabled);
          LosingEnable
        end
      end
    end
  end
end;

procedure TEasyCollectionItem.SetFocused(Value: Boolean);
begin
  if Visible and Enabled then
  begin
    if Value xor (esosFocused in State) then
    begin
      if State * [esosDestroying] <> [] then
      begin
        Exclude(FState, esosFocused);
        LosingFocus
      end else
      begin
        if CanChangeFocus(Value) then
        begin
          if Value then
          begin
            Include(FState, esosFocused);
            GainingFocus
          end else
          begin
            Exclude(FState, esosFocused);
            LosingFocus
          end
        end
      end
    end
  end
end;

procedure TEasyCollectionItem.SetGhosted(const Value: Boolean);
begin
  if Value xor (esosGhosted in FState) then
  begin
    if State * [esosDestroying] <> [] then
    begin
      Exclude(FState, esosGhosted);
      LosingGhosted
    end else
    begin
      if Value then
      begin
        Include(FState, esosGhosted);
        GainingGhosted
      end else
      begin
        Exclude(FState, esosGhosted);
        LosingGhosted
      end
    end
  end
end;

procedure TEasyCollectionItem.SetHilighted(Value: Boolean);
begin
  if Value xor (esosHilighted in FState) then
  begin
    if State * [esosDestroying] <> [] then
    begin
      // Disabled items can't have the focus or selection
      Focused := False;
      Selected := False;
      Exclude(FState, esosHilighted);
      LosingHilight
    end else
    begin
      if Value then
      begin
        Include(FState, esosHilighted);
        GainingHilight
      end else
      begin
        Exclude(FState, esosHilighted);
        LosingHilight
      end
    end
  end
end;

procedure TEasyCollectionItem.SetHotTracking(MousePos: TPoint; Value: Boolean);
begin
  if Value xor (esosHotTracking in FState) then
  begin
    if State * [esosDestroying] <> [] then
    begin
      Exclude(FState, esosHotTracking);
      LosingHotTracking
    end else
    begin
      if CanChangeHotTracking(Value) then
      begin
        if Value then
        begin
          Include(FState, esosHotTracking);
          GainingHotTracking(MousePos)
        end else
        begin
          Exclude(FState, esosHotTracking);
          LosingHotTracking
        end
      end
    end
  end
end;

procedure TEasyCollectionItem.SetImageIndent(Value: Integer);
begin
  if Value <> ImageIndent then
  begin
    if Value < 0 then
      PaintInfo.ImageIndent := 0
    else
      PaintInfo.ImageIndent := Value;
    Invalidate(False)
  end
end;

procedure TEasyCollectionItem.SetImageIndex(const Value: TCommonImageIndexInteger);
begin
  ImageIndexes[0] := Value
end;

procedure TEasyCollectionItem.SetImageOverlayIndex(const Value: TCommonImageIndexInteger);
begin
  ImageOverlayIndexes[0] := Value
end;

procedure TEasyCollectionItem.SetInitialized(Value: Boolean);
begin
  if Value xor (esosInitialized in State) then
  begin
    if Value then
    begin
      Include(FState, esosInitialized);
      Initialize;
    end else
      Exclude(FState, esosInitialized)
  end
end;

procedure TEasyCollectionItem.SetPaintInfo(Value: TEasyPaintInfoBasic);
begin
  FPaintInfo := Value
end;

procedure TEasyCollectionItem.SetSelected(Value: Boolean);
begin
  if Visible and Enabled then
  begin
    if Value xor (esosSelected in State) then
    begin
      if State * [esosDestroying] <> [] then
      begin
        Exclude(FState, esosSelected);
            LosingSelection
      end else
      begin
        if CanChangeSelection(Value) then
        begin
          if Value then
          begin
            Include(FState, esosSelected);
            GainingSelection
          end else
          begin
            Exclude(FState, esosSelected);
            LosingSelection
          end
        end
      end
    end
  end
end;

procedure TEasyCollectionItem.SetVAlignment(Value: TCommonVAlignment);
begin
  if Value <> VAlignment then
  begin
    PaintInfo.VAlignment := Value;
    Invalidate(False)
  end
end;

procedure TEasyCollectionItem.SetVisible(Value: Boolean);
begin
  if Value xor (esosVisible in State) then
  begin
    if State * [esosDestroying] <> [] then
    begin
      // Invisible Objects can't be focused or selected
      Focused := False;
      Selected := False;
      if not OwnerListview.AllowHiddenCheckedItems then
        Checked := False;
      Exclude(FState, esosVisible);
      LosingVisibility;
    end else
    begin
      if CanChangeVisibility(Value) then
      begin
        if Value then
        begin
          Include(FState, esosVisible);
          GainingVisibility
        end else
        begin
          // Invisible Objects can't be focused or selected or Focused
          Focused := False;
          Selected := False;
          if not OwnerListview.AllowHiddenCheckedItems then
            Checked := False;
          Exclude(FState, esosVisible);
          LosingVisibility;
        end
      end
    end
  end
end;

procedure TEasyCollectionItem.UnRegister;
begin
  FDataInf := nil
end;

{ TEasyCollection }

constructor TEasyCollection.Create(AnOwner: TCustomEasyListview);
begin
  inherited Create(AnOwner);
  FItemClass := TEasyCollectionItem;
  FList := TList.Create;         
  VisibleList := TList.Create;
end;

destructor TEasyCollection.Destroy;
begin
  Clear;
  inherited;
  FreeAndNil(FVisibleList);
  FreeAndNil(FList);
end;

function TEasyCollection.Add(Data: TObject = nil): TEasyCollectionItem;
begin
  Result := ItemClass.Create(Self);
  if Assigned(Result) then
  begin
    FList.Add(Result);
    ReIndexItems;
    DoItemAdd(Result, FList.Count - 1);
    Result.Data := Data;
    DoStructureChange
  end
end;

function TEasyCollection.DoStore: Boolean;
begin
  Result := (FList.Count > 0) and not HideFromDFM
end;

function TEasyCollection.FirstVisible: TEasyColumn;
begin
  if VisibleList.Count > 0 then
    Result := TEasyColumn( VisibleList[0])
  else
    Result := nil
end;

function TEasyCollection.GetCount: Integer;
begin
  Result := FList.Count
end;

function TEasyCollection.GetItem(Index: Integer): TEasyCollectionItem;
begin
  Result := TEasyCollectionItem(FList.List[Index])
end;


function TEasyCollection.GetOwner: TPersistent;
begin
  Result:= FOwnerListview
end;

function TEasyCollection.GetOwnerListview: TCustomEasyListview;
begin
  Result := FOwnerListview
end;

function TEasyCollection.GetReIndexDisable: Boolean;
begin
  Result := FReIndexCount > 0
end;

function TEasyCollection.GetVisibleCount: Integer;
begin
  Result := FVisibleList.Count
end;

function TEasyCollection.Insert(Index: Integer; Data: TObject = nil): TEasyCollectionItem;
begin
  Result := ItemClass.Create(Self);
  if Assigned(Result) then
  begin
    FList.Insert(Index, Result);
    ReIndexItems;
    DoItemAdd(Result, Index);
    Result.Data := Data;
    DoStructureChange
  end
end;

function TEasyCollection.LastVisible: TEasyColumn;
begin
  if VisibleList.Count > 0 then
    Result := TEasyColumn( VisibleList[VisibleList.Count - 1])
  else
    Result := nil
end;

function TEasyCollection.NextVisible(Column: TEasyColumn): TEasyColumn;
var
  i: Integer;
begin
  Result := nil;
  if Assigned(Column) then
  begin
    i := Column.Index + 1;
    while not Assigned(Result) and (i < Count) do
    begin
      if Items[i].Visible then
        Result := TEasyColumn( Items[i]);
      Inc(i)
    end
  end
end;

function TEasyCollection.PrevVisible(Column: TEasyColumn): TEasyColumn;
var
  i: Integer;
begin
  Result := nil;
  if Assigned(Column) then
  begin
    i := Column.Index - 1;
    while not Assigned(Result) and (i < Count) do
    begin
      if Items[i].Visible then
        Result := TEasyColumn( Items[i]);
      Dec(i)
    end
  end
end;

procedure TEasyCollection.Clear(FreeItems: Boolean = True);
var
  i: Integer;
begin
  if FList.Count > 0 then
  begin
    BeginUpdate(False);
    try
      // Need to make sure all items are valid so nothing unexpected happens in the
      // controls events when the item state changes
      for i := 0 to FList.Count - 1 do
      begin
        TEasyCollectionItem(FList[i]).SetDestroyFlags;
        TEasyCollectionItem(FList[i]).Focused := False;
        TEasyCollectionItem(FList[i]).Selected := False;
      end;

      if FreeItems then
        for i := FList.Count - 1 downto 0 do   // Walk backwords
          TEasyCollectionItem(FList[i]).Free;
    finally
      FList.Clear;
      FVisibleList.Clear;
      // The scrollbars do not update correctly on nested BeginUpdate/EndUpdate
      if Assigned(OwnerListview) and OwnerListview.HandleAllocated then
      begin
        OwnerListview.Scrollbars.OffsetX := 0;
        OwnerListview.Scrollbars.OffsetY := 0;
      end;
      EndUpdate;
    end;
  end;
end;

procedure TEasyCollection.DefineProperties(Filer: TFiler);
begin
  inherited DefineProperties(Filer);
  Filer.DefineBinaryProperty('Items', ReadItems, WriteItems, DoStore);
end;

procedure TEasyCollection.Delete(Index: Integer);
var
  Item: TEasyCollectionItem;
begin
  Item := FList[Index];
  Item.SetDestroyFlags;
  Item.Focused := False;
  Item.Selected := False;
  FList.Delete(Index);
  Item.Free;
  ReIndexItems;
  DoStructureChange
end;

procedure TEasyCollection.DoItemAdd(Item: TEasyCollectionItem; Index: Integer);
begin

end;

procedure TEasyCollection.DoStructureChange;
begin

end;

procedure TEasyCollection.EndUpdate(Invalidate: Boolean = True);
begin
  inherited;
  OwnerListview.EndUpdate(Invalidate);
end;

procedure TEasyCollection.BeginUpdate(ReIndex: Boolean);
begin
  inherited;
  OwnerListview.BeginUpdate;
end;

procedure TEasyCollection.Exchange(Index1, Index2: Integer);
var
  Temp: TEasyCollectionItem;
begin
  Temp := TEasyCollectionItem( FList[Index1]);
  FList[Index1] := FList[Index2];
  FList[Index2] := Temp;
  ReIndexItems;
  DoStructureChange
end;

procedure TEasyCollection.MakeAllVisible;
var
  i: Integer;
begin
  for i := 0 to Count - 1 do
    Items[i].Visible := True
end;

procedure TEasyCollection.ReadItems(Stream: TStream);
var
  i, ItemCount: Integer;
  StreamVersion: Integer;
  Cls: TClass;
begin
  Clear;
  StreamVersion := StreamHelper.ReadInteger(Stream);
  ItemCount := StreamHelper.ReadInteger(Stream);
  if StreamVersion > 4 then
  begin
    // Since ELV can store more than one class type of Collection Items the streaming
    // must be more sophisticated (i.e. it can have virtual, custom, and stored items
    // all in the listview at the same time) It must check the class type of every
    // object to make sure it creates the correct item type
    for i := 0 to ItemCount - 1 do
    begin
      Cls := GetClass(StreamHelper.ReadString(Stream));
      Assert(Cls <> nil, 'If using custom item types for Item, Groups or Columns you must register them with the streaming system with RegisterClass(TMyEasyClassItemType)');
      if Assigned(Cls) then
      begin
        FItemClass := TEasyCollectionItemClass( Cls);
        Add.LoadFromStream(Stream, StreamVersion);
      end
    end
  end else
  begin
    // Stream will fill version in on first read
    for i := 0 to ItemCount - 1 do
      Add.LoadFromStream(Stream, StreamVersion);
  end
end;

procedure TEasyCollection.ReIndexItems;
var
  i: Integer;
begin
  if not OwnerListview.Groups.ReIndexDisable then
  begin
    for i := 0 to List.Count - 1 do
      TEasyCollectionItem( List[i]).FIndex := i
  end
end;

procedure TEasyCollection.SetItem(Index: Integer; Value: TEasyCollectionItem);
begin
  FList[Index]  := Value
end;

procedure TEasyCollection.SetReIndexDisable(const Value: Boolean);
begin
  if Value then
  begin
    Inc(FReIndexCount)
  end else
  begin
    Dec(FReIndexCount);
    if ReIndexCount <=0 then
    begin
      ReIndexCount := 0;
      ReIndexItems
    end
  end
end;

procedure TEasyCollection.WriteItems(Stream: TStream);
var
  i: Integer;
begin
  // Write the Current Stream Version to the stream
  StreamHelper.WriteInteger(Stream, EASYLISTVIEW_STREAM_VERSION);
  // Store the number of items we are storing
  StreamHelper.WriteInteger(Stream, FList.Count);
  for i := 0 to FList.Count - 1 do
  begin
    StreamHelper.WriteString(Stream, TEasyCollectionItem( Items[i]).ClassName);
    TEasyCollectionItem( Items[i]).SaveToStream(Stream); // Write custom data to the stream
  end
end;

{ TEasyHeader }

constructor TEasyHeader.Create(AnOwner: TCustomEasyListview);
begin
  inherited;
  FColumns := TEasyColumns.Create(AnOwner);
  FPositions := TColumnPos.Create;
  FDragManager := TEasyHeaderDragManager.Create(AnOwner);
  DragManager.Header := Self;
  FFont := TFont.Create;
  FHeight := 21;
  FColor := clBtnFace;
  FSizeable := True;
  Draggable := True;
  LastWidth := -1;
  FStreamColumns := True;
  FAutoSizeHeight := True;
  FAutoSizeHeightMargin := 8;
end;

destructor TEasyHeader.Destroy;
begin
  Columns.Clear;
  FreeAndNil(FCanvasStore);
  inherited;
  FreeAndNil(FColumns);
  FreeAndNil(FPositions);
  FreeAndNil(FFont);
  FreeAndNil(FDragManager);
end;

function TEasyHeader.FirstColumn: TEasyColumn;
begin
  if Columns.Count > 0 then
    Result := Columns[0]
  else
    Result := nil
end;

function TEasyHeader.FirstColumnByPosition: TEasyColumn;
begin
  if Positions.Count > 0 then
    Result := Positions[0]
  else
    Result := nil
end;

function TEasyHeader.FirstColumnInRect(ViewportRect: TRect): TEasyColumn;
//
// Always assumes by Position as this is a UI function
//
var
  i: Integer;
  ScratchR: TRect;
begin
  Result := nil;
  i := 0;
  OffsetRect(ViewportRect, 0, -ViewportRect.Top);
  while (i < Positions.Count) and not Assigned(Result) do
  begin
    if Positions[i].Visible then
      if IntersectRect(ScratchR, Positions[i].DisplayRect, ViewportRect) then
        Result := Positions[i];
    Inc(i)
  end
end;

function TEasyHeader.FirstVisibleColumn: TEasyColumn;
var
  i: Integer;
  Column: TEasyColumn;
begin
  Result := nil;
  Column := FirstColumn;
  i := 0;
  while not Assigned(Result) and (i < Columns.Count) do
  begin
    if Assigned(Column) then
    begin
      if Column.Visible then
        Result := Column
      else begin
        Column := NextColumn(Column);
        Inc(i)
      end;
    end
  end
end;

function TEasyHeader.GetCanvasStore: TEasyCanvasStore;
begin
  if not Assigned(FCanvasStore) then
    FCanvasStore := TEasyCanvasStore.Create;
  Result := FCanvasStore
end;

function TEasyHeader.GetDisplayRect: TRect;
begin
  Result := Rect(0, 0, OwnerListview.ClientWidth, RuntimeHeight)
end;

function TEasyHeader.GetDraggable: Boolean;
begin
  Result := DragManager.Enabled
end;

function TEasyHeader.GetFixedSingleColumn: Boolean;
begin
  if OwnerListview.HandleAllocated then
  begin
    if Columns.Count = 0 then
      Columns.Add.Width := OwnerListview.ClientWidth - 1
  end;
  Result := FFixedSingleColumn;
end;

function TEasyHeader.GetHeight: Integer;
var
  TextMetrics: TTextMetric;
  DC: HDC;
  OldFont: THandle;
begin
  Result := FHeight;
  if AutoSizeHeight then
  begin
    DC := GetDC(0);
    OldFont := SelectObject(DC, Font.Handle);
    try
      if GetTextMetrics(DC, TextMetrics) then
        if TextMetrics.tmHeight + AutoSizeHeightMargin > FHeight then
          Result := TextMetrics.tmHeight + AutoSizeHeightMargin;
      ReleaseDC(0, DC);
    finally
      if OldFont <> 0 then
        SelectObject(DC, OldFont);
    end
  end
end;

function TEasyHeader.GetMouseCaptured: Boolean;
begin
  Result := (ebcsHeaderCapture in OwnerListview.States) and (ehsMouseCaptured in State)
end;

function TEasyHeader.GetRuntimeHeight: Integer;
begin
  if OwnerListview.ViewSupportsHeader and Visible then
    Result := Height
  else
    Result := 0;
end;

function TEasyHeader.InCheckZone(ViewportPt: TPoint; var Column: TEasyColumn): Boolean;
var
  RectArray: TEasyRectArrayObject;
begin
  Result := False;
  Column := Columns.ColumnByPoint(ViewportPt);
  if Assigned(Column) then
  begin
    Column.View.ItemRectArray(Column, RectArray);
    Result := PtInRect(RectArray.CheckRect, ViewportPt)
  end
end;

function TEasyHeader.InDropDownButtonZone(ViewportPt: TPoint; var Column: TEasyColumn): Boolean;
var
  RectArray: TEasyRectArrayObject;
begin
  Result := False;
  Column := Columns.ColumnByPoint(ViewportPt);
  if Assigned(Column) then
  begin
    Column.View.ItemRectArray(Column, RectArray);
    Result := PtInRect(RectArray.DropDownArrow, ViewportPt)
  end
end;

function TEasyHeader.InHotTrackZone(ViewportPt: TPoint; var Column: TEasyColumn): Boolean;
var
  i: Integer;
  R, ClientR: TRect;
begin
  Result := False;
  if OwnerListview.PaintInfoColumn.HotTrack then
  begin
    Column := nil;
    ClientR := OwnerListview.ClientRect;
    if OwnerListview.ScrollHeaderHorz then
      OffsetRect(ClientR, OwnerListview.Scrollbars.OffsetX, 0);
    i := 0;
    while not Result and (i < OwnerListview.Header.Positions.Count) do
    begin
      R := OwnerListview.Header.Positions[i].DisplayRect;
      // Don't switch hottracking column until done with resize arrow
      Inc(R.Right, RESIZEHITZONEMARGIN + 1);
      IntersectRect(R, R, ClientR);
      if PtInRect(R, ViewportPt) then
      begin
        Column := OwnerListview.Header.Positions[i];
        Result := True
      end;
      Inc(i)
    end;
    if not Result then
      Result := Assigned(HotTrackedColumn);
  end
end;

function TEasyHeader.InPressZone(ViewportPt: TPoint; var Column: TEasyColumn): Boolean;
var
  i: Integer;
  R: TRect;
begin
  Result := False;
  Column := nil;
  i := OwnerListview.Header.Positions.Count - 1;
  while not Result and (i > -1) do
  begin
    R := OwnerListview.Header.Positions[i].DisplayRect;
    if PtInRect(R, ViewportPt) then
    begin
      Column := OwnerListview.Header.Positions[i];
      if Assigned(Column) then
        Result := Column.Clickable
    end;
    Dec(i)
  end
end;

function TEasyHeader.InResizeZone(ViewportPt: TPoint; var Column: TEasyColumn): Boolean;
var
  i: Integer;
  R, ClientR: TRect;
begin
  Result := False;
  if Sizeable then
  begin
    Column := nil;
    i := OwnerListview.Header.Positions.Count - 1;
    ClientR := OwnerListview.ClientRect;
    if OwnerListview.ScrollHeaderHorz then
      OffsetRect(ClientR, OwnerListview.Scrollbars.OffsetX, 0);
    while not Result and (i > -1) do
    begin
      if Positions[i].Visible then
      begin
        R := Positions[i].DisplayRect;
        if (ViewportPt.X <= R.Right + RESIZEHITZONEMARGIN) and (ViewportPt.X > R.Right - RESIZEHITZONEMARGIN)
          and (ViewportPt.Y < Height) and (ViewportPt.Y > 0) and (R.Right <= ClientR.Right) then
        begin
          Column := OwnerListview.Header.Positions[i];
          Result := True
        end;
      end;
      Dec(i)
    end
  end
end;

function TEasyHeader.IsFontStored: Boolean;
begin
  Result := not OwnerListview.ParentFont and not OwnerListview.DesktopFont;
end;

function TEasyHeader.LastColumn: TEasyColumn;
begin
  if Columns.Count > 0 then
    Result := Columns[Columns.Count - 1]
  else
    Result := nil
end;

function TEasyHeader.LastColumnByPosition: TEasyColumn;
begin
  if Positions.Count > 0 then
    Result := Positions[Positions.Count - 1]
  else
    Result := nil
end;

function TEasyHeader.LastVisibleColumn: TEasyColumn;
var
  i: Integer;
  Column: TEasyColumn;
begin
  Result := nil;
  Column := LastColumn;
  i := Columns.Count - 1;
  while not Assigned(Result) and (i > -1 ) do
  begin
    if Assigned(Column) then
    begin
      if Column.Visible then
        Result := Column
      else begin
        Column := PrevColumn(Column);
        Dec(i)
      end;
    end
  end
end;

function TEasyHeader.NextColumn(AColumn: TEasyColumn): TEasyColumn;
begin
  Result := nil;
  if AColumn.Index < Columns.Count - 1 then
    Result := Columns[AColumn.Index + 1]
end;

function TEasyHeader.NextColumnByPosition(AColumn: TEasyColumn): TEasyColumn;
begin
  Result := nil;
  if AColumn.Position < Columns.Count - 1 then
    Result := Positions[AColumn.Position + 1]
end;

function TEasyHeader.NextColumnInRect(Column: TEasyColumn; ViewportRect: TRect): TEasyColumn;
//
// Always assumes by Position as this is a UI function
//
var
  i: Integer;
  ScratchR: TRect;
  Done: Boolean;
begin
  Result := nil;
  Done := False;
  if Assigned(Column) then
  begin
    i := Column.Position + 1;
    OffsetRect(ViewportRect, 0, -ViewportRect.Top);
    while not Assigned(Result) and (i < Positions.Count) and not Done do
    begin
      if Positions[i].Visible and (Positions[i].Width > 0) then
      begin
        if IntersectRect(ScratchR, Positions[i].DisplayRect, ViewportRect) then
          Result := Positions[i]
        else
          Done := True
      end;
      Inc(i)
    end
  end
end;

function TEasyHeader.NextVisibleColumn(Column: TEasyColumn): TEasyColumn;
var
  i: Integer;
begin
  Result := nil;
  Column := NextColumn(Column);
  if Assigned(Column) then
  begin
    i := Column.Index;
    while not Assigned(Result) and (i < Columns.Count) do
    begin
      if Column.Visible then
        Result := Column
      else begin
        Column := NextColumn(Column);
        Inc(i)
      end;
    end
  end
end;

function TEasyHeader.PrevColumn(AColumn: TEasyColumn): TEasyColumn;
begin
  Result := nil;
  if AColumn.Index > 0 then
    Result := Columns[AColumn.Index - 1]
end;

function TEasyHeader.PrevColumnByPosition(AColumn: TEasyColumn): TEasyColumn;
begin
  Result := nil;
  if AColumn.Position > 0 then
    Result := Positions[AColumn.Position - 1]
end;

function TEasyHeader.PrevVisibleColumn(Column: TEasyColumn): TEasyColumn;
var
  i: Integer;
begin
  Result := nil;
  Column := PrevColumn(Column);
  if Assigned(Column) then
  begin
    i := Column.Index;
    while not Assigned(Result) and (i > -1 ) do
    begin
      if Column.Visible then
        Result := Column
      else begin
        Column := PrevColumn(Column);
        Dec(i)
      end
    end
  end
end;

procedure TEasyHeader.CaptureMouse;
begin
  Include(OwnerListview.FStates, ebcsHeaderCapture);
  Include(FState, ehsMouseCaptured);
  SetCapture(OwnerListview.Handle);
end;

procedure TEasyHeader.ClearStates;
begin
  Exclude(FState, ehsMouseCaptured);
  Exclude(FState, ehsResizing);
  Exclude(FState, ehsDragging);
  Exclude(FState, ehsDragPending);
  Exclude(FState, ehsClickPending);
  Exclude(FState, ehsResizePending);
  Exclude(FState, ehsLButtonDown);
  Exclude(FState, ehsRButtonDown);
  Exclude(FState, ehsMButtonDown);
  ReleaseMouse;   
end;

procedure TEasyHeader.ClickColumn(Column: TEasyColumn);
const
  NEXT_SORT_DIRECTION: array[TEasySortDirection] of TEasySortDirection =
    (esdAscending, esdDescending, esdAscending);
var
  PreviousFocusedColumn: TEasyColumn;
begin
  Exclude(Column.FState, esosClicking);
  if Column.AutoToggleSortGlyph then
  begin
    // If sorting takes a bit it is better to "freeze" the painting
    // so the sort arrows don't show the "wrong" way then change when
    // the sort is done.
    OwnerListview.BeginUpdate;
    try
      PreviousFocusedColumn := OwnerListview.Selection.FocusedColumn;
      OwnerListview.Selection.FocusedColumn := Column;

      // If the column has changed the previous column must forget its
      // sort direction. When it is clicked on next time it will start
      // again with SortDirection = esdAscending.
      if PreviousFocusedColumn <> Column then
        PreviousFocusedColumn.SortDirection := esdNone;

      // Now toggle new column's sort direction and regroup / resort
      // if necded.
      Column.SortDirection := NEXT_SORT_DIRECTION[Column.SortDirection];
      if OwnerListview.Sort.AutoReGroup and OwnerListview.IsGrouped then
        OwnerListview.Sort.ReGroup(Column)
      else if (OwnerListview.Sort.AutoSort) or (Column.AutoSortOnClick) then
        OwnerListview.Sort.SortAll(Column.AutoSortOnClick);
    finally
      OwnerListview.EndUpdate
    end;
  end;
end;

procedure TEasyHeader.DoMouseDown(var Message: TWMMouse; Button: TCommonMouseButton;
  Shift: TShiftState; Column: TEasyColumn);
begin
  if Assigned(OwnerListview.OnHeaderMouseDown) then
    OwnerListview.OnHeaderMouseDown(OwnerListview, Button, Shift, Message.XPos, Message.YPos, Column)
end;

procedure TEasyHeader.DoMouseMove(var Message: TWMMouse; Shift: TShiftState);
begin
  if Assigned(OwnerListview.OnHeaderMouseUp) then
    OwnerListview.OnHeaderMouseMove(OwnerListview, Shift, Message.XPos, Message.YPos)
end;

procedure TEasyHeader.DoMouseUp(var Message: TWMMouse; Button: TCommonMouseButton;
  Shift: TShiftState; Column: TEasyColumn);
begin
  if Assigned(OwnerListview.OnHeaderMouseUp) then
    OwnerListview.OnHeaderMouseUp(OwnerListview, Button, Shift, Message.XPos, Message.YPos, Column)
end;

procedure TEasyHeader.HandleHotTrack(Msg: TWMMouse; ForceClear: Boolean);
var
  TempColumn, OldColumn: TEasyColumn;
  DropDownButtonPending: Boolean;
begin
  if ForceClear then
  begin
    if Assigned(HotTrackedColumn) then
    begin
      ReleaseMouse;
      OldColumn := HotTrackedColumn;
      HotTrackedColumn := nil;
      OldColumn.Invalidate(True)
    end;
  end else
  begin
    DropDownButtonPending := False;
    if Assigned(DropDownHoverColumn) then
      DropDownButtonPending := cdbsClickPending in DropDownHoverColumn.DropDownButton.State;

    if not DropDownButtonPending then
    begin
      if OwnerListview.ScrollHeaderHorz then
        Inc(Msg.Pos.x, OwnerListview.Scrollbars.OffsetX);

      if not ([ehsResizing, ehsResizePending, ehsClickPending, ehsDragging, ehsClickPending] * State <> []) {and not DropDownButtonPending}  then
        if InHotTrackZone(SmallPointToPoint(Msg.Pos), TempColumn) or Assigned(HotTrackedColumn) then
        begin
          if TempColumn <> HotTrackedColumn then
          begin
            if Assigned(HotTrackedColumn) then
            begin
              ReleaseMouse;
              OldColumn := HotTrackedColumn;
              HotTrackedColumn := nil;
              OldColumn.Invalidate(True)
            end;
            HotTrackedColumn := TempColumn;
            if Assigned(HotTrackedColumn) then
            begin
              CaptureMouse;
              HotTrackedColumn.Invalidate(True)
            end
          end
       end
     end
  end
end;

procedure TEasyHeader.Invalidate(ImmediateUpdate: Boolean);
begin
  if OwnerListview.UpdateCount = 0 then
    OwnerListview.SafeInvalidateRect(nil, ImmediateUpdate)
end;

procedure TEasyHeader.InvalidateColumn(Item: TEasyColumn;
  ImmediateUpdate: Boolean);
begin

end;

procedure TEasyHeader.LoadFromStream(S: TStream; Version: Integer = EASYLISTVIEW_STREAM_VERSION);
begin
  inherited LoadFromStream(S, Version);
  if StreamColumns then
    Columns.ReadItems(S)
end;

procedure TEasyHeader.PaintTo(ACanvas: TCanvas; ARect: TRect);
var
  Column: TEasyColumn;
  Handled: Boolean;
  {$IFDEF USETHEMES}
  PartID,
  StateID: LongWord;
  {$ENDIF}
begin
  Handled := False;
  CanvasStore.StoreCanvasState(ACanvas);
  OwnerListview.DoPaintHeaderBkGnd(ACanvas, ViewRect, Handled);
  CanvasStore.RestoreCanvasState(ACanvas);
  if not Handled then
  begin
    {$IFDEF USETHEMES}
    if OwnerListview.DrawWithThemes then
    begin
      PartID := HP_HEADERITEM;
      StateID := HIS_NORMAL;
      DrawThemeBackground(OwnerListview.Themes.HeaderTheme, ACanvas.Handle, PartID, StateID, ViewRect, nil);
    end else
    {$ENDIF USETHEMES}
    begin
      ACanvas.Brush.Color := Color;
      ACanvas.FillRect(DisplayRect);
    end;
  end;
  Column := FirstColumnInRect(ARect);
  while Assigned(Column) do
  begin
    // Reset the clipping region
    SelectClipRgn(ACanvas.Handle, 0);
    Column.Paint(ACanvas, ehtHeader);
    Column := NextColumnInRect(Column, ARect);
  end
end;

procedure TEasyHeader.SaveToStream(S: TStream; Version: Integer = EASYLISTVIEW_STREAM_VERSION);
begin
  inherited SaveToStream(S, Version);
  if StreamColumns then
    Columns.WriteItems(S)
end;

procedure TEasyHeader.SetAutoSizeHeight(const Value: Boolean);
begin
  if FAutoSizeHeight <> Value then
  begin
    FAutoSizeHeight := Value;
    OwnerListview.Groups.Rebuild(False)
  end
end;

procedure TEasyHeader.SetAutoSizeHeightMargin(const Value: Integer);
begin
  if FAutoSizeHeightMargin <> Value then
  begin
    FAutoSizeHeightMargin := Value;
    if FAutoSizeHeightMargin < 0 then
      FAutoSizeHeightMargin := 0;
    if AutoSizeHeight then
      OwnerListview.Groups.Rebuild(False)
  end
end;

procedure TEasyHeader.SetDraggable(Value: Boolean);
begin
  DragManager.Enabled := Value
end;

procedure TEasyHeader.SetDropDownHoverColumn(const Value: TEasyColumn);
begin
  if FDropDownHoverColumn <> Value then
  begin
    if Assigned(FDropDownHoverColumn) then
    begin
      Exclude(FDropDownHoverColumn.DropDownButton.FState, cdbsHovering);
      Exclude(FDropDownHoverColumn.DropDownButton.FState, cdbsDown);
      Exclude(FDropDownHoverColumn.DropDownButton.FState, cdbsClickPending);
      FDropDownHoverColumn.Invalidate(True);
    end;
    FDropDownHoverColumn := Value;
    if Assigned(FDropDownHoverColumn) then
    begin
      Include(FDropDownHoverColumn.DropDownButton.FState, cdbsHovering);
      FDropDownHoverColumn.Invalidate(True);
    end;
  end
end;

procedure TEasyHeader.SetFixedSingleColumn(const Value: Boolean);
var
  i: Integer;
begin
//  Columns.Clear;
  if Value <> FFixedSingleColumn then
  begin
    if OwnerListview.HandleAllocated then
    begin
      for i := 1 to Columns.Count - 1 do
        Columns[i].Visible := not Value;
      if Value then
      begin
        if Columns.Count = 0 then
          Columns.Add.Width := OwnerListview.ClientWidth - 1
        else
          Columns[0].Width := OwnerListview.ClientWidth - 1;
      end;
      OwnerListview.Groups.Rebuild;
    end;
    FFixedSingleColumn := Value;
  end
end;

procedure TEasyHeader.SetHotTrackedColumn(const Value: TEasyColumn);
var
  Pt: TPoint;
begin
  if Value <> FHotTrackedColumn then
  begin
    Pt.x := 0;
    Pt.y := 0;
    if Assigned(HotTrackedColumn) then
      HotTrackedColumn.HotTracking[Pt] := False;
    if Assigned(Value) then
      Value.HotTracking[Pt] := True;
    FHotTrackedColumn := Value;
  end
end;

procedure TEasyHeader.SetShowInAllViews(const Value: Boolean);
begin
  if Value <> FShowInAllViews then
  begin
    FShowInAllViews := Value;
    OwnerListview.Groups.Rebuild
  end;
end;

procedure TEasyHeader.SizeFixedSingleColumn(NewWidth: Integer);
begin
  if Columns.Count > 0 then
  begin
    if Columns[0].Width <> NewWidth then
    begin
      Columns[0].Width := NewWidth;
      OwnerListview.Groups.Rebuild(True);
    end
  end
end;

procedure TEasyHeader.SpringColumns(NewWidth: Integer);
//
// Credit goes to VirtualTreeview by Mike Lischke
var
  I: Integer;
  SpringCount: Integer;
  Sign: Integer;
  ChangeBy: Single;
  Difference: Single;
  NewAccumulator: Single;
begin
  ChangeBy := RectWidth(DisplayRect) - FLastWidth;
  if (ChangeBy <> 0) then
  begin
    // Stay positive if downsizing the control.
    if ChangeBy < 0 then
      Sign := -1
    else
      Sign := 1;
    ChangeBy := Abs(ChangeBy);
    // Count how many columns have spring enabled.
    SpringCount := 0;
    for I := 0 to Columns.Count-1 do
      if Columns[i].AutoSpring and Columns[i].Visible then
        Inc(SpringCount);
    if SpringCount > 0 then
    begin
      // Calculate the size to add/sub to each columns.
      Difference := ChangeBy / SpringCount;
      // Adjust the column's size accumulators and resize if the result is >= 1.
      for I := 0 to Columns.Count - 1 do
        if Columns[i].AutoSpring and Columns[i].Visible then
        begin
          // Sum up rest changes from previous runs and the amount from this one and store it in the
          // column. If there is at least one pixel difference then do a resize and reset the accumulator.
          NewAccumulator := Columns[I].FSpringRest + Difference;
          // Set new width if at least one pixel size difference is reached.
          if NewAccumulator >= 1 then
            FColumns[I].SetWidth(FColumns[I].FWidth + (Trunc(NewAccumulator) * Sign));
          FColumns[I].FSpringRest := Frac(NewAccumulator);
          
          // Keep track of the size count.
          ChangeBy := ChangeBy - Difference;
          // Exit loop if resize count drops below freezing point.
          if ChangeBy < 0 then
            Break;
        end;
    end;
  end;
end;

procedure TEasyHeader.WMContextMenu(var Msg: TMessage);
var
  Column: TEasyColumn;
  ViewPt, Pt: TPoint;
  HitInfoColumn: TEasyHitInfoColumn;
  Menu: TPopupMenu;
begin
  Menu := OwnerListview.PopupMenuHeader;
  Pt := OwnerListview.ScreenToClient(Point( Msg.LParamLo, Msg.LParamHi));
  if OwnerListview.ScrollHeaderHorz then
    ViewPt := OwnerListview.Scrollbars.MapWindowToView(Pt, False);
  HitInfoColumn.Column := Columns.ColumnByPoint(ViewPt);
  Column := Columns.ColumnByPoint(ViewPt);
  if Assigned(HitInfoColumn.Column) then
    Column.HitTestAt(ViewPt, HitInfoColumn.HitInfo)
  else
    HitInfoColumn.HitInfo := [];
  // HitInfoColumn.Column will be nil if it hits the backgound of the header
  OwnerListview.DoColumnContextMenu(HitInfoColumn, Pt, Menu);
  if Assigned(Menu) then
  begin
    Menu.Popup(Msg.LParamLo, Msg.LParamHi);
    Msg.Result := 1
  end else
    inherited;
end;

procedure TEasyHeader.WMSize(var Msg: TWMSize);
begin
  {$IFDEF GXDEBUG}
  SendDebug('Header WMSize');
  {$ENDIF GXDEBUG}
  if LastWidth < 0 then
    LastWidth := RectWidth(DisplayRect);
  if FixedSingleColumn then
    SizeFixedSingleColumn(Msg.Width - 1)
  else
    SpringColumns(Msg.Width);
  LastWidth := RectWidth(DisplayRect)
end;

function SortByPosition(Item1, Item2: Pointer): Integer;
begin
  Result := TEasyColumn(Item1).Position - TEasyColumn(Item2).Position
end;

procedure TEasyHeader.Rebuild(Force: Boolean);
var
  i: Integer;
begin
  if Force or ((OwnerListview.UpdateCount = 0) and not(csLoading in OwnerListview.ComponentState) and (OwnerListview.HandleAllocated)) then
  begin
    Positions.Clear;
    Columns.VisibleList.Clear;
    Positions.Capacity := Positions.Count;
    for i := 0 to Columns.Count - 1 do
      Positions.Add(Columns[i]);
    Positions.Sort(SortByPosition);
    for i := 0 to Columns.Count - 1 do
      Positions[i].FPosition := i;

    for i := 0 to Columns.Count - 1 do
    begin
      if Columns[i].Visible then
      begin
        Columns.VisibleList.Add(Columns[i]);
        if FixedSingleColumn then
          Columns[i].FWidth := OwnerListview.ClientWidth; // do this direct so not to fire events
      end
    end;


    SetRect(FViewRect, 0, 0, 0, 0);
    for i := 0 to Positions.Count - 1 do
    begin
      if i > 0 then
      begin
        Positions[i].FDisplayRect := Positions[i-1].FDisplayRect;
        Positions[i].FDisplayRect.Left := Positions[i].FDisplayRect.Right;
        if Positions[i].Visible then
          Positions[i].FDisplayRect.Right := Positions[i].FDisplayRect.Left + Positions[i].Width
        else
          Positions[i].FDisplayRect.Right := Positions[i].FDisplayRect.Left;
      end else
      begin
        if Positions[i].Visible then
          Positions[i].FDisplayRect := Rect(0, 0, Positions[i].Width, Height)
        else
          Positions[i].FDisplayRect := Rect(0, 0, 0, Height)
      end;

      UnionRect(FViewRect, ViewRect, Positions[i].DisplayRect);
      if RectWidth(ViewRect) < OwnerListview.Width then
        FViewRect.Right := OwnerListview.ClientWidth;
    end;
  end
end;

procedure TEasyHeader.ReleaseMouse;
begin
  Exclude(FState, ehsMouseCaptured);
  Exclude(OwnerListview.FStates, ebcsHeaderCapture);
  if GetCapture = OwnerListview.Handle then
    ReleaseCapture
end;

procedure TEasyHeader.SetColor(Value: TColor);
begin
  if FColor <> Value then
  begin
    FColor := Value;
    Invalidate(False);
  end
end;

procedure TEasyHeader.SetFont(Value: TFont);
begin
  Font.Assign(Value)
end;

procedure TEasyHeader.SetHeight(Value: Integer);
begin
  if FHeight <> Value then
  begin
    if Value > -1 then
      FHeight := Value
    else
      FHeight := 0;
    OwnerListview.Groups.Rebuild
  end
end;

procedure TEasyHeader.SetImages(Value: TCustomImageList);
begin
  if Value <> FImages then
  begin
    FImages := Value;
    OwnerListview.SafeInvalidateRect(nil, False)
  end
end;

procedure TEasyHeader.SetVisible(Value: Boolean);
begin
  if Value <> FVisible then
  begin
    FVisible := Value;
    OwnerListview.Groups.Rebuild
  end;
end;

{ TEasyItemView }

procedure TEasyHeader.WMLButtonDblClk(var Msg: TWMLButtonDblClk);
var
  ViewPt: TPoint;
  Column: TEasyColumn;
  Button: TCommonMouseButton;
begin
  Button := KeyStatesToMouseButton(Msg.Keys);
  ViewPt := SmallPointToPoint(Msg.Pos);
  if OwnerListview.ScrollHeaderHorz then
    Inc(ViewPt.X, OwnerListview.Scrollbars.OffsetX);
  if InResizeZone(ViewPt, Column) and (ehsResizePending in State) then
  begin
    if Column.AutoSizeOnDblClk then
      Column.AutoSizeToFit
  end;
  Column := Columns.ColumnByPoint(ViewPt);
  OwnerListview.DoHeaderDblClick(Button, SmallPointToPoint(Msg.Pos), KeysToShiftState(Msg.Keys));
  if Assigned(Column) then
    OwnerListview.DoColumnDblClick(Button, KeysToShiftState(Msg.Keys), SmallPointToPoint(Msg.Pos), Column);
end;

procedure TEasyHeader.WMLButtonDown(var Msg: TWMLButtonDown);
var
  ViewPt: TPoint;
  TempColumn: TEasyColumn;
begin

  ViewPt := SmallPointToPoint(Msg.Pos);
  if OwnerListview.ScrollHeaderHorz then
    Inc(ViewPt.X, OwnerListview.Scrollbars.OffsetX);
  Include(FState, ehsLButtonDown);
  if ehsResizePending in State then
  begin
    Exclude(FState, ehsResizePending);
    Include(FState, ehsResizing);
  end else
  if InCheckZone(ViewPt, TempColumn) then
  begin
    Include(FState, ehsCheckboxClickPending);
    OwnerListview.HotTrack.PendingObjectCheck := nil;
    OwnerListview.CheckManager.PendingObject := TempColumn;
  end else
  if InDropDownButtonZone(ViewPt, TempColumn) then
  begin
    if TempColumn.DropDownButton.Enabled then
    begin
      DropDownHoverColumn := TempColumn;
      Include(DropDownHoverColumn.DropDownButton.FState, cdbsClickPending);
      Include(DropDownHoverColumn.DropDownButton.FState, cdbsDown);
      DropDownHoverColumn.Invalidate(True)
    end
  end else
  if InPressZone(ViewPt, FPressColumn) then
  begin
    // Clear the Hottrack item
    if OwnerListview.PaintInfoColumn.HotTrack then
      HandleHotTrack(Msg, True);
    CaptureMouse;
    if PressColumn.Clickable then
    begin
      Include(FState, ehsClickPending);
      Include(PressColumn.FState, esosClicking);
    end;
    if Draggable then
      Include(FState, ehsDragPending);
    PressColumn.Invalidate(True);
    DoMouseDown(Msg, cmbLeft, KeysToShiftState(Msg.Keys), Columns.ColumnByPoint(ViewPt));
  end
end;

procedure TEasyHeader.WMLButtonUp(var Msg: TWMLButtonUp);
const
  NEXT_SORT_DIRECTION: array[TEasySortDirection] of TEasySortDirection =
    (esdAscending, esdDescending, esdAscending);
var
  ViewPt, Pt: TPoint;
  PreviousFocusedColumn: TEasyColumn;
  DoDefault: Boolean;
  RectArray: TEasyRectArrayObject;
begin
  ViewPt := SmallPointToPoint(Msg.Pos);
  if OwnerListview.ScrollHeaderHorz then
    Inc(ViewPt.X, OwnerListview.Scrollbars.OffsetX);

  if ehsCheckboxClickPending in State then
  begin
    if InCheckZone(ViewPt, PreviousFocusedColumn) then
      PreviousFocusedColumn.Checked := not PreviousFocusedColumn.Checked;
    OwnerListview.CheckManager.PendingObject.CheckHovering := False;
    OwnerListview.CheckManager.PendingObject.CheckPending := False;
    OwnerListview.CheckManager.PendingObject := nil;
  end else
  if ehsResizing in State then
  begin
    OwnerListview.DoColumnSizeChanged(ResizeColumn);
  end else
  if Assigned(DropDownHoverColumn) and (cdbsClickPending in DropDownHoverColumn.DropDownButton.State) then
  begin
    // NEED SHORT CIRCUIT BOOLEAN EVALUATION for this to work
    if cdbsDown in DropDownHoverColumn.DropDownButton.State then
    begin
      DoDefault := True;
      OwnerListview.DoColumnDropDownButtonClick(DropDownHoverColumn, cmbLeft, KeysToShiftState(Msg.Keys), SmallPointToPoint(Msg.Pos), DoDefault);
      if DoDefault then
      begin
        if Assigned(DropDownHoverColumn.DropDownButton.Menu) then
        begin
          DropDownHoverColumn.View.ItemRectArray(DropDownHoverColumn, RectArray);
          Pt.x := RectArray.DropDownArrow.Left;
          Pt.y := RectArray.DropDownArrow.Bottom;
          Pt := OwnerListview.Scrollbars.MapViewToWindow(Pt, False);
          Pt := OwnerListview.ClientToScreen(Pt);
          DropDownHoverColumn.DropDownButton.Menu.Popup(Pt.x, Pt.y)
        end
      end
    end;
    Exclude(DropDownHoverColumn.DropDownButton.FState, cdbsClickPending);
    DropDownHoverColumn := nil;
  end else
  if Assigned(PressColumn) then
  begin
    if esosClicking in PressColumn.State then
    begin
      ClickColumn(PressColumn);
      OwnerListview.DoColumnClick(cmbLeft, PressColumn);
    end else
      PressColumn.Invalidate(True);
  end;
  if [ehsResizing, ehsDragging] * FState = [] then
    DoMouseUp(Msg, cmbLeft, KeysToShiftState(Msg.Keys), Columns.ColumnByPoint(ViewPt));

  Exclude(FState, ehsLButtonDown);
  Exclude(FState, ehsResizing);
  Exclude(FState, ehsClickPending);
  Exclude(FState, ehsDragging);
  Exclude(FState, ehsDragPending);
  Exclude(FState, ehsCheckboxClickPending);
end;

procedure TEasyHeader.WMMouseMove(var Msg: TWMMouseMove);

    procedure Press(Column: TEasyColumn; Pressed: Boolean);
    begin
      if Assigned(Column) then
      begin
        if Pressed then
          Include(Column.FState, esosClicking)
        else
          Exclude(Column.FState, esosClicking);
        Column.Invalidate(True);
      end;
    end;

var
  ViewPt: TPoint;
  Allow, ClearDropDownBtn: Boolean;
  TempColumn: TEasyColumn;
  ClientR: TRect;
  HotTrackCheckObj: TEasyCollectionItem;
  Effects: TCommonDropEffect;

begin
  ClearDropDownBtn := True;
  ClientR := ViewRect;
  ViewPt := SmallPointToPoint(Msg.Pos);
  if OwnerListview.ScrollHeaderHorz then
  begin
    ViewPt.X := ViewPt.X + OwnerListview.Scrollbars.OffsetX;
    OffsetRect(ClientR, OwnerListview.Scrollbars.OffsetX, 0);
  end;
  if not MouseCaptured and Assigned(Self.HotTrackedColumn) then
    CaptureMouse;

  HotTrackCheckObj := nil;

  if OwnerListview.PaintInfoColumn.HotTrack then
    HandleHotTrack(Msg, False);
                                  
  if ehsResizing in State then
  begin
    Allow := True;
    OwnerListview.DoColumnSizeChanging(ResizeColumn, ResizeColumn.Width, ViewPt.X - ResizeColumn.DisplayRect.Left, Allow);
    if Allow then
    begin
      ResizeColumn.Width := ViewPt.X - ResizeColumn.DisplayRect.Left;
      OwnerListview.Groups.Rebuild(True)
    end
  end else
  if ehsDragging in State then
  begin
    Effects := cdeMove;
    DragManager.Drag(OwnerListview.Canvas, ViewPt, KeyToKeyStates(Msg.Keys), Effects);
  end else
  if ehsResizePending in State then
  begin
    if not InResizeZone(ViewPt, FResizeColumn) then
    begin
      FResizeColumn := nil;
      Exclude(FState, ehsResizePending);
      ReleaseMouse;
      OwnerListview.Cursor := crDefault;
      if Assigned(HotTrackedColumn) then
        CaptureMouse
    end
  end else
  if (ehsDragPending in State) and DragManager.Enabled then
  begin
    if DragDetectPlus(OwnerListview.Handle, SmallPointToPoint(Msg.Pos)) then
    begin
      Exclude(FState, ehsDragPending);
      Exclude(FState, ehsClickPending);
      Include(FState, ehsDragging);
      DragManager.Column := PressColumn;
      Press(PressColumn, False);
      PressColumn := nil;
      DragManager.BeginDrag(ViewPt, KeyToKeyStates(Msg.Keys));
    end
  end else
  if ehsClickPending in State then
  begin
    //
  end else
  if Assigned(DropDownHoverColumn) and (cdbsClickPending in DropDownHoverColumn.DropDownButton.State) then
  begin
    // NEED SHORT CIRCUIT BOOLEAN EVALUATION for this to work
    if InDropDownButtonZone(ViewPt, TempColumn) and (TempColumn = DropDownHoverColumn) then
    begin
      Include(DropDownHoverColumn.DropDownButton.FState, cdbsDown);
      DropDownHoverColumn.Invalidate(True)
    end else
    begin
      Exclude(DropDownHoverColumn.DropDownButton.FState, cdbsDown);
      DropDownHoverColumn.Invalidate(True)
    end;
    ClearDropDownBtn := False;
  end else
  if InCheckZone(ViewPt, TempColumn) then
  begin
    HotTrackCheckObj := TempColumn;
  end else
  if InResizeZone(ViewPt, FResizeColumn) then
  begin
    Allow := True;
    OwnerListview.DoColumnSizeChanging(ResizeColumn, ResizeColumn.Width, ResizeColumn.Width, Allow);
    if Allow then
    begin
      // Some other thing may have the mouse captured and the Cursor won't take
      ReleaseCapture;
      Include(FState, ehsResizePending);
      OwnerListview.Cursor := crVHeaderSplit;
      CaptureMouse;
    end
  end else
  if InDropDownButtonZone(ViewPt, TempColumn) then
  begin
    DropDownHoverColumn := TempColumn;
    ClearDropDownBtn := False;
  end else
  begin
    OwnerListview.Cursor := crDefault;
    DoMouseMove(Msg, KeysToShiftState(Msg.Keys));  
  end;

  if (ehsCheckboxClickPending in State) then
  begin
    if Assigned(HotTrackCheckObj) then
    begin
      if OwnerListview.CheckManager.PendingObject <> HotTrackCheckObj then
      begin
        OwnerListview.CheckManager.PendingObject.CheckHovering := True;
        HotTrackCheckObj := nil
      end else
        OwnerListview.CheckManager.PendingObject.CheckHovering := False;
    end else
    begin
      OwnerListview.CheckManager.PendingObject.CheckHovering := True;
    end;
    if HotTrackCheckObj <> nil then
      HotTrackCheckObj := nil;
  end;

  if OwnerListview.CheckManager.PendingObject = HotTrackCheckObj then
    HotTrackCheckObj := nil;

  OwnerListview.HotTrack.PendingObjectCheck := HotTrackCheckObj;
  if ClearDropDownBtn then
    DropDownHoverColumn := nil;
end;

procedure TEasyHeader.WMRButtonDown(var Msg: TWMRButtonDown);
var
  ViewPt: TPoint;
begin
  ViewPt := SmallPointToPoint(Msg.Pos);
  if OwnerListview.ScrollHeaderHorz then
    Inc(ViewPt.X, OwnerListview.Scrollbars.OffsetX);

  Include(FState, ehsRButtonDown);
  DoMouseDown(Msg, cmbRight, KeysToShiftState(Msg.Keys), Columns.ColumnByPoint(ViewPt));
end;

procedure TEasyHeader.WMRButtonUp(var Msg: TWMRButtonUp);
var
  ViewPt: TPoint;
begin
  ViewPt := SmallPointToPoint(Msg.Pos);
  if OwnerListview.ScrollHeaderHorz then
    Inc(ViewPt.X, OwnerListview.Scrollbars.OffsetX);
  Exclude(FState, ehsRButtonDown);
  Exclude(FState, ehsResizing);
  Exclude(FState, ehsClickPending);
  Exclude(FState, ehsDragging);
  Exclude(FState, ehsDragPending);
  DoMouseUp(Msg, cmbRight, KeysToShiftState(Msg.Keys), Columns.ColumnByPoint(ViewPt));
end;

function TEasyViewItem.AllowDrag(Item: TEasyItem; ViewportPoint: TPoint): Boolean;
begin
  Result := True;
end;

function TEasyViewItem.EditAreaHitPt(Item: TEasyItem; ViewportPoint: TPoint): Boolean;
var
  RectArray: TEasyRectArrayObject;
begin
  Result := False;
  if Item.Enabled then
  begin
    ItemRectArray(Item, OwnerListview.Header.FirstColumn, Item.OwnerListview.ScratchCanvas, '', RectArray);
    Result := Windows.PtInRect(RectArray.TextRect, ViewportPoint);
  end
end;

function TEasyViewItem.ExpandIconR(Item: TEasyItem; RectArray: TEasyRectArrayObject; SelectType: TEasySelectHitType): TRect;
begin
  Result := RectArray.IconRect
end;

function TEasyViewItem.ExpandTextR(Item: TEasyItem; RectArray: TEasyRectArrayObject; SelectType: TEasySelectHitType): TRect;
begin
  if Item.Focused and (OwnerListview.Focused or Item.OwnerListview.Selection.PopupMode) then
    Result := RectArray.FullTextRect
  else
    Result := RectArray.TextRect;
end;

function TEasyViewItem.FullRowSelect: Boolean;
begin
  Result := False;
end;

function TEasyViewItem.GetImageList(Column: TEasyColumn; Item: TEasyItem; Image: TEasyImageKind): TCustomImageList;
begin
  if Image = eikNormal then
    Result := Item.ImageList[ValidateColumnIndex(Column), PaintImageSize]
  else
    Result := Item.StateImageList[ValidateColumnIndex(Column)];
end;

function TEasyViewItem.OverlappedFocus: Boolean;
//
// Returns true if the view will overlap another cell when the object has the focus
begin
  Result := False
end;

function TEasyViewItem.PaintImageSize: TEasyImageSize;
begin
  Result := eisSmall
end;

function TEasyViewItem.PaintStateImage: Boolean;
begin
  Result := False
end;

function TEasyViewItem.SelectionHit(Item: TEasyItem; SelectViewportRect: TRect;
  SelectType: TEasySelectHitType): Boolean;
var
  R: TRect;
  RectArray: TEasyRectArrayObject;
begin
  Result := False;
  if Item.Enabled then
  begin
    ItemRectArray(Item, nil, OwnerListview.ScratchCanvas, '', RectArray);
    Result := IntersectRect(R, SelectViewportRect, ExpandTextR(Item, RectArray, SelectType)) or
              IntersectRect(R, SelectViewportRect, ExpandIconR(Item, RectArray, SelectType))
  end
end;

function TEasyViewItem.SelectionHitPt(Item: TEasyItem; ViewportPoint: TPoint;
  SelectType: TEasySelectHitType): Boolean;
var
  RectArray: TEasyRectArrayObject;
begin
  Result := False;
  if Item.Enabled then
  begin
    ItemRectArray(Item, nil, OwnerListview.ScratchCanvas, '', RectArray);
    Result := Windows.PtInRect(ExpandTextR(Item, RectArray, SelectType), ViewportPoint) or
              Windows.PtInRect(ExpandIconR(Item, RectArray, SelectType), ViewportPoint)
  end
end;

function TEasyViewItem.ValidateColumnIndex(Column: TEasyColumn): Integer;
begin
  if Assigned(Column) then
    Result := Column.Index
  else
    Result := 0
end;

procedure TEasyViewItem.AfterSelRectCalc(Item: TEasyItem; Column: TEasyColumn;
  const Caption: WideString; var LocalSelRect: TRect);
begin

end;

procedure TEasyViewItem.CalculateTextRect(Item: TEasyItem; Column: TEasyColumn;
  var TextR: TRect; ACanvas: TControlCanvas);
// Fits the Text in the PaintInfo.Caption.Text field into the TextR based
// on the values in the PaintInfo record.  If Canvas is nil then a temporary
// canvas is created to fit the text based on the Font in the PaintInfo
var
  DrawTextFlags: TCommonDrawTextWFlags;
  LocalCanvas: TControlCanvas;
begin
  case PaintTextAlignment(Item, Column) of
    taLeftJustify:  Include(DrawTextFlags, dtLeft);
    taRightJustify: Include(DrawTextFlags, dtRight);
    taCenter: Include(DrawTextFlags, dtCenter);
  end;

  case PaintTextVAlignment(Item, Column) of
    cvaTop: Include(DrawTextFlags, dtTop);
    cvaBottom: Include(DrawTextFlags, dtBottom);
    cvaCenter: Include(DrawTextFlags, dtVCenter);
  end;

  if not Assigned(ACanvas) then
  begin
    LocalCanvas := TControlCanvas.Create;
    LocalCanvas.Control := OwnerListview
  end else
    LocalCanvas := ACanvas;

  try
    LoadTextFont(Item, 0, LocalCanvas, False);
    DrawTextFlags := DrawTextFlags + [dtCalcRectAdjR, dtCalcRect, dtCalcRectAlign];
    DrawTextWEx(LocalCanvas.Handle, Item.Captions[0], TextR, DrawTextFlags, 1);
  finally
    if not Assigned(ACanvas) then
      LocalCanvas.Free
  end;
end;

function TEasyViewItem.ItemRect(Item: TEasyItem; Column: TEasyColumn;
  RectType: TEasyCellRectType): TRect;
var
  RectArray: TEasyRectArrayObject;
begin
  Result := Rect(0, 0, 0, 0);
  // First look for cached rectangles during a drag
(*  CacheRectangle(Item, RectType, Result, cdrRetrieve); *)

  if IsRectEmpty(Result) then
  begin
    ItemRectArray(Item, Column, OwnerListview.ScratchCanvas, '', RectArray);
    case RectType of
      ertBounds: Result := RectArray.BoundsRect;
      ertIcon: Result := RectArray.IconRect;
      ertLabel: Result := RectArray.LabelRect;
      ertClickselectBounds: Result := RectArray.ClickselectBoundsRect;
      ertDragSelectBounds: Result := RectArray.DragSelectBoundsRect;
      ertText: Result := RectArray.TextRect;
      ertFullText: Result := RectArray.FullTextRect;
    end;
(*    CacheRectangle(Item, RectType, Result, cdrStore);  *)
  end;
end;

procedure TEasyViewItem.GetImageSize(Item: TEasyItem; Column: TEasyColumn; var ImageW, ImageH: Integer; Image: TEasyImageKind);
var
  Images: TCustomImageList;
  ColumnPos: Integer;
  IsCustom: Boolean;
begin
  ImageW := 0;
  ImageH := 0;
  ColumnPos := ValidateColumnIndex(Column);

  if Image = eikNormal then
  begin
    if Item.ImageIndexes[ColumnPos] > -1 then
    begin
      Item.ImageDrawIsCustom(Column, IsCustom);
      if IsCustom then
        Item.ImageDrawGetSize(Column, ImageW, ImageH)
      else begin
        Images := GetImageList(Column, Item, Image);
        if  Assigned(Images) then
        begin
          ImageW := Images.Width;
          ImageH := Images.Height
        end
      end
    end
  end else
  begin
    Images := GetImageList(Column, Item, Image);
    if Assigned(Images) then
    begin
      ImageW := Images.Width;
      ImageH := Images.Height
    end
  end
end;

function TEasyViewItem.GetStateImageList(Column: TEasyColumn; Item: TEasyItem): TCustomImageList;
begin
  Result := Item.StateImageList[Column.Index]
end;

procedure TEasyViewItem.ItemRectArray(Item: TEasyItem; Column: TEasyColumn; ACanvas: TCanvas; const Caption: WideString; var RectArray: TEasyRectArrayObject);
//
// Grabs all the rectangles for the items within a cell in one call
//
begin
  Item.Initialized := True;

  FillChar(RectArray, SizeOf(RectArray), #0);

  RectArray.BoundsRect := Item.DisplayRect;
  InflateRect(RectArray.BoundsRect, -Item.Border, -Item.Border);

  RectArray.IconRect := RectArray.BoundsRect;
  RectArray.LabelRect := RectArray.BoundsRect;
  RectArray.ClickselectBoundsRect := RectArray.BoundsRect;
  RectArray.DragSelectBoundsRect := RectArray.BoundsRect;
  RectArray.TextRect := RectArray.BoundsRect;
  RectArray.SelectionRect := RectArray.BoundsRect;
  RectArray.FocusChangeInvalidRect := RectArray.BoundsRect;
  RectArray.EditRect := RectArray.FullTextRect;
  SetRect(RectArray.CheckRect, 0, 0, 0, 0);
end;

procedure TEasyViewItem.LoadTextFont(Item: TEasyItem; Position: Integer; ACanvas: TCanvas; Hilightable: Boolean);
begin
  ACanvas.Font.Assign(OwnerListview.Font);
  ACanvas.Brush.Style := bsClear;
  if not OwnerListview.ShowInactive then
  begin
    if Hilightable then
    begin
      if OwnerListview.Focused or Item.OwnerListview.Selection.PopupMode or Item.Hilighted then
        ACanvas.Font.Color := OwnerListview.Selection.TextColor
      else
        ACanvas.Font.Color := OwnerListview.Selection.InactiveTextColor
    end;
    if OwnerListview.HotTrack.Enabled and not Item.Hilighted then
    begin
      if (OwnerListview.HotTrack.FPendingObject = Item) and not Item.Selected then
      begin
        ACanvas.Font.Color := OwnerListview.HotTrack.Color;
        if OwnerListview.HotTrack.Underline then
          ACanvas.Font.Style := ACanvas.Font.Style + [fsUnderline]
      end
    end
  end else
    ACanvas.Font.Color := clGrayText;

  if Item.Bold then
    ACanvas.Font.Style := ACanvas.Font.Style + [fsBold];
  OwnerListview.DoItemPaintText(Item, Position, ACanvas);
end;

procedure TEasyViewItem.Paint(Item: TEasyItem; Column: TEasyColumn; ACanvas: TCanvas; ViewportClipRect: TRect; ForceSelectionRectDraw: Boolean);
// The Canvas's DC offset has been shifted on entry so that the viewport coords
// of the item can be use direct within the method
var
  RectArray: TEasyRectArrayObject;
  Caption: WideString;
  Handled: Boolean;
begin
  if Item.Visible then
  begin
    CanvasStore.StoreCanvasState(ACanvas);
    try
      if Assigned(Column) then
        Caption := Item.Captions[Column.Index]
      else
        Caption := Item.Caption;

      ItemRectArray(Item, Column, ACanvas, Caption, RectArray);

      Handled := False;
      // First allow decendents a crack at the painting
      PaintBefore(Item, Column, Caption, ACanvas, RectArray, Handled);

      if not Handled then
      begin
        // Paint the Selection Rectangle
        // *************************
        if not(OwnerListview.EditManager.Editing and (OwnerListview.EditManager.EditItem = Item)) then
          PaintSelectionRect(Item, Column, Caption, RectArray, ACanvas, ViewportClipRect, ForceSelectionRectDraw);

        // Next Paint the Icon or Bitmap Image
        // *************************
        PaintImage(Item, Column, Caption, RectArray, eisSmall, ACanvas);

        // Now lets paint the Text
        // *************************
        // If focused then show as many lines as necessary
        // Decendents should override PaintText to change the number of lines
        // as necessary
        if not(OwnerListview.EditManager.Editing and (OwnerListview.EditManager.EditItem = Item)) or ((OwnerListview.View = elsReport) and (Column <> OwnerListview.EditManager.EditColumn)) then
        begin
          PaintText(Item, Column, Caption, RectArray, ACanvas, PaintTextLineCount(Item, Column));

          // Now lets paint Focus Rectangle
          // *************************
          if OwnerListview.Selection.UseFocusRect then
            PaintFocusRect(Item, Column, Caption, RectArray, ACanvas);
        end;

        // Now Paint the Checkbox if applicable
        PaintCheckBox(Item, Column, RectArray, ACanvas);
        // Now give decentant a chance to paint anything
        PaintAfter(Item, Column, Caption, ACanvas, RectArray);
      end
    finally
      CanvasStore.RestoreCanvasState(ACanvas)
    end
  end
end;

procedure TEasyViewItem.PaintAfter(Item: TEasyItem; Column: TEasyColumn; const Caption: WideString; ACanvas: TCanvas; RectArray: TEasyRectArrayObject);
begin
//
//  Called after all other drawing is done
//
end;

procedure TEasyViewItem.PaintAlphaBlendedRoundRect(ACanvas: TCanvas;
  AlphaColor: TColor; GradientBottom: TColor; GradientTop: TColor;
  var LocalSelWindowClippedRect: TRect; var Rgn: HRGN);
var
  Bits: TBitmap;
  GlassBits: TBitmap;
begin
  // If it is a round rectangle then we need to create a round region
  // to "clip" the square Alpha Blended memory bitmap to the round rectangle
  Bits := TBitmap.Create;
  try
    Bits.PixelFormat := pf32Bit;
    Bits.Width := RectWidth(LocalSelWindowClippedRect);
    Bits.Height := RectHeight(LocalSelWindowClippedRect);

            // Make a copy of the background image behind the square text rect
    BitBlt(Bits.Canvas.Handle, 0, 0, Bits.Width, Bits.Height, ACanvas.Handle,
      LocalSelWindowClippedRect.Left, LocalSelWindowClippedRect.Top, SRCCOPY);

    Bits.Canvas.Pen.Color := ACanvas.Pen.Color;
            // Draw the rectangle to it with a clear center
    Bits.Canvas.Brush.Style := bsClear;
    RoundRect(Bits.Canvas.Handle, 0, 0, Bits.Width, Bits.Height,
      OwnerListview.Selection.RoundRectRadius,
      OwnerListview.Selection.RoundRectRadius);

    if OwnerListview.Selection.Gradient then
    begin
      GlassBits := TBitmap.Create;
      GlassBits.Width := RectWidth(LocalSelWindowClippedRect);
      GlassBits.Height := RectHeight(LocalSelWindowClippedRect);
      GlassBits.PixelFormat := pf32Bit;

      FillGradient(0, 0, GlassBits.Width, GlassBits.Height,
        GradientTop, GradientBottom,
        0, GlassBits.Height, GlassBits.Canvas);
      BlendBits(GlassBits, Bits, 80, 20, OwnerListview.Selection.BlurAlphaBkGnd);
      GlassBits.Free;
    end
    else
              // AlphaBlend the memory bitmap
      AlphaBlend(0, Bits.Canvas.Handle, Rect(0, 0, Bits.Width, Bits.Height), Point(0, 0),
        cbmConstantAlphaAndColor, OwnerListview.Selection.BlendAlphaTextRect,
        ColorToRGB(AlphaColor));

    LocalSelWindowClippedRect := OwnerListview.Scrollbars.MapViewRectToWindowRect(LocalSelWindowClippedRect);
            // Create a round rect region in the Canvas DC to blast the alpha
            // blended memory bitmap to.  The Alpha blending also blended the
            // corners where we do not need it so we must only copy the actual
            // round rect area to the Canvas
    Rgn := CreateRoundRectRgn(
      LocalSelWindowClippedRect.Left,
      LocalSelWindowClippedRect.Top,
      LocalSelWindowClippedRect.Right + 1,
      LocalSelWindowClippedRect.Bottom + 1,
      OwnerListview.Selection.RoundRectRadius,
      OwnerListview.Selection.RoundRectRadius);
    LocalSelWindowClippedRect := OwnerListview.Scrollbars.MapWindowRectToViewRect(LocalSelWindowClippedRect);

            // Select the new User round rect region to the DC
    SelectClipRgn(ACanvas.Handle, Rgn);
    OwnerListview.ClipHeader(ACanvas, False);
    BitBlt(ACanvas.Handle,
      LocalSelWindowClippedRect.Left,
      LocalSelWindowClippedRect.Top,
      RectWidth(LocalSelWindowClippedRect),
      RectHeight(LocalSelWindowClippedRect),
      Bits.Canvas.Handle,
      0,
      0,
      SRCCOPY);
    OwnerListview.ClipHeader(ACanvas, True);
  finally
            // Remove the User clipping region from the DC
    if Rgn <> 0 then
      DeleteObject(Rgn);
    Bits.Free;
  end;
end;

procedure TEasyViewItem.PaintAlphaBlendedSelection(ACanvas: TCanvas;
  AlphaColor: TColor; HeaderClippedWindowRect: TRect; LocalSelRect: TRect);
begin
// AlphaBlend does not clip and does not use the DC Origins, must be absolute to physical screen pixels
  AlphaBlend(0, ACanvas.Handle, HeaderClippedWindowRect, Point(0, 0),
    cbmConstantAlphaAndColor, OwnerListview.Selection.BlendAlphaTextRect, ColorToRGB(AlphaColor));
  ACanvas.Brush.Style := bsClear;
  Rectangle(ACanvas.Handle, LocalSelRect.Left, LocalSelRect.Top, LocalSelRect.Right, LocalSelRect.Bottom);
end;

procedure TEasyViewItem.PaintBefore(Item: TEasyItem; Column: TEasyColumn; const Caption: WideString; ACanvas: TCanvas; RectArray: TEasyRectArrayObject; var Handled: Boolean);
//
//  Called before all other drawing is done
//
begin
  if Item.Border > 0 then
  begin
    ACanvas.Brush.Color := Item.BorderColor;
    ACanvas.FrameRect(RectArray.BoundsRect);
  end
end;

procedure TEasyViewItem.PaintCheckBox(Item: TEasyItem; Column: TEasyColumn;
  RectArray: TEasyRectArrayObject; ACanvas: TCanvas);
var
  PaintCheckBox: Boolean;
  AbsIndex: Integer;
begin
  PaintCheckBox := not ((Item.CheckType = ectNone) or (Item.CheckType = ettNoneWithSpace));

  if Assigned(Column) then
  begin
    AbsIndex := Column.Index;
    if AbsIndex > 0 then
      PaintCheckBox := False;
      // Future inhancement, checkboxes in columns
 //      PaintCheckBox := not ((Item.Details[ColumnIndex].CheckType = ectNone) or (Item.Details[ColumnIndex].CheckType = ettNoneWithSpace));
  end;

  if PaintCheckBox then
    PaintCheckboxCore(Item.CheckType,       // TEasyCheckType
                      OwnerListview,        // TCustomEasyListview
                      ACanvas,              // TCanvas
                      RectArray.CheckRect,  // TRect
                      Item.Enabled,         // IsEnabled
                      Item.Checked,         // IsChecked
                      OwnerListview.CheckManager.PendingObject = Item, // IsHot
                      Item.CheckFlat,       // IsFlat
                      Item.CheckHovering,   // IsHovering
                      Item.CheckPending,    // IsPending
                      Item,
                      Item.Checksize);

end;

procedure TEasyViewItem.PaintFocusRect(Item: TEasyItem; Column: TEasyColumn; const Caption: WideString; RectArray: TEasyRectArrayObject; ACanvas: TCanvas);
var
  AbsIndex: Integer;
  LocalFocusRect: TRect;
begin
  if not FullRowSelect then
  begin
    AbsIndex := ValidateColumnIndex(Column);
    // Only draw the focus rect if the window has focus
    if Item.Focused and OwnerListview.Focused and ((AbsIndex < 1)) then
    begin
      if OwnerListview.Selection.FullCellPaint then
      begin
        LocalFocusRect := Item.DisplayRect;
        UnionRect(LocalFocusRect, LocalFocusRect, RectArray.FullFocusSelRect);
        if (OwnerListview.View = elsReport) and (OwnerListview.Header.Columns.Count > 0) then
        begin
           LocalFocusRect.Left := OwnerListview.Header.Columns[0].DisplayRect.Left;
           LocalFocusRect.Right := LocalFocusRect.Left + OwnerListview.Header.Columns[0].Width;
        end
      end else
      begin
        LocalFocusRect := RectArray.FullFocusSelRect;
        if OwnerListview.Selection.FullItemPaint then
          UnionRect(LocalFocusRect, LocalFocusRect, RectArray.IconRect);
      end;
      ACanvas.Brush.Color := OwnerListview.Color;
      ACanvas.Font.Color := clBlack;
      DrawFocusRect(ACanvas.Handle, LocalFocusRect);
    end
  end
end;

procedure TEasyViewItem.PaintAlphaBlendedGradientFill(ACanvas: TCanvas;
  GradientBottom: TColor; GradientTop: TColor; LocalSelRect: TRect;
  LocalSelWindowClippedRect: TRect; LocalSelClippedRect: TRect);
var
  Bits: TBitmap;
  GlassBits: TBitmap;
begin
  Bits := TBitmap.Create;
  GlassBits := TBitmap.Create;
  try
    GlassBits.Width := RectWidth(LocalSelWindowClippedRect);
    GlassBits.Height := RectHeight(LocalSelWindowClippedRect);
    GlassBits.PixelFormat := pf32Bit;
    Bits.Width := GlassBits.Width;
    Bits.Height := GlassBits.Height;
    Bits.PixelFormat := pf32Bit;

    BitBlt(Bits.Canvas.Handle, 0, 0, Bits.Width, Bits.Height,
      ACanvas.Handle, LocalSelWindowClippedRect.Left, LocalSelWindowClippedRect.Top, SRCCOPY);

    FillGradient(0, 0, GlassBits.Width, GlassBits.Height, GradientTop, GradientBottom,
      0, GlassBits.Height, GlassBits.Canvas);
    BlendBits(GlassBits, Bits, 80, 20, OwnerListview.Selection.BlurAlphaBkGnd);
    BitBlt(ACanvas.Handle, LocalSelRect.Left, LocalSelWindowClippedRect.Top,
      Bits.Width, Bits.Height, Bits.Canvas.Handle, 0, 0, SRCCOPY);
  finally
    GlassBits.Free;
    Bits.Free;
  end;
end;

procedure TEasyViewItem.PaintImage(Item: TEasyItem; Column: TEasyColumn; const Caption: WideString; RectArray: TEasyRectArrayObject; ImageSize: TEasyImageSize; ACanvas: TCanvas);
var
  rgbBk, rgbFg: Longword;
  fStyle: Integer;
  OverlayIndex, ImageIndex, PositionIndex, AbsIndex, StateImageIndex: Integer;
  Images, StateImages: TCustomImageList;
  DoDefault, IsCustom: Boolean;
  Rgn: HRgn;
  TmpBits: TBitmap;
  R: TRect;
  W, H, Scale: Double;
begin
  TestAndClipImage(ACanvas, RectArray, Rgn);
  try
    if Assigned(Column) then
    begin
      AbsIndex := Column.Index;
      PositionIndex := Column.Position
    end else
    begin
      AbsIndex := 0;
      PositionIndex := 0;
    end;
    DoDefault := True;
    Images := GetImageList(Column, Item, eikNormal);
    if OwnerListview.IsThumbnailView then
    begin
      InflateRect(RectArray.IconRect, -Item.Border, -Item.Border);
      Item.ThumbnailDraw(ACanvas, RectArray.IconRect, AlphaBlender, DoDefault);
    end;

    // If not using the thumbnail then get the information for the ImageList Indexes
    if DoDefault then
    begin
      Item.ImageDrawIsCustom(Column, IsCustom);
      if IsCustom then
      begin
        Item.ImageDraw(Column, ACanvas, RectArray, AlphaBlender);
      end else
      if DoDefault then
      begin
        ImageIndex := Item.ImageIndexes[AbsIndex];
        OverlayIndex := Item.ImageOverlayIndexes[AbsIndex];

        if PaintStateImage then
        begin
          StateImageIndex := Item.StateImageIndexes[AbsIndex];

          StateImages := GetImageList(Column, Item, eikState);
          if Assigned(StateImages) and (StateImageIndex > -1) then
          begin
            // Set up a normal Imagelist icon
            fStyle := ILD_TRANSPARENT;
            rgbBk := CLR_NONE;
            rgbFg := CLR_NONE;

            RectArray.StateRect.Left := RectArray.StateRect.Left + (RectWidth(RectArray.StateRect) - Images.Width) div 2;
            RectArray.StateRect.Top := RectArray.StateRect.Top + (RectHeight(RectArray.StateRect) - Images.Height) div 2;
            
            ImageList_DrawEx(StateImages.Handle, StateImageIndex, ACanvas.Handle, RectArray.StateRect.Left,
              RectArray.StateRect.Top, 0, 0, rgbBk, rgbFg, fStyle);
          end
        end;

        if Assigned(Images) and (ImageIndex > -1) then
        begin
          // Set up a normal Imagelist icon
          fStyle := ILD_TRANSPARENT;
          rgbBk := CLR_NONE;
          rgbFg := CLR_NONE;

          // Set up to blend the Imagelist icon
          // The param is to allow Thumbnail view to use this paint method and not blend
          // the image
          if OwnerListview.Selection.BlendIcon and
            (((OwnerListview.Focused or Item.OwnerListview.Selection.PopupMode) and Item.Selected) or Item.Hilighted and
            (PositionIndex < 1)) then
          begin
            fStyle := fStyle or ILD_SELECTED;
            if OwnerListview.Selection.ForceDefaultBlend then
              rgbFg := CLR_DEFAULT
            else
              rgbFg := ColorToRGB(OwnerListview.Selection.Color)
          end;

          if not Item.Enabled or Item.Cut or Item.Ghosted or Item.OwnerListview.ShowInactive then
          begin
            fStyle := fStyle or ILD_SELECTED;
            rgbFg := ColorToRGB(OwnerListview.DisabledBlendColor)
          end;

          if OverlayIndex > -1 then
            fStyle := FStyle or IndexToOverLayMask(OverlayIndex);

          if (RectWidth(RectArray.IconRect) < Images.Width) or (RectHeight(RectArray.IconRect) < Images.Height) then
          begin
            if (Images.Width > 0) and (Images.Height > 0) then
            begin
              TmpBits := TBitmap.Create;
              try
                TmpBits.PixelFormat := pf32Bit;
                TmpBits.Width := Images.Width;
                TmpBits.Height := Images.Height;
                ImageList_DrawEx(Images.Handle, ImageIndex, TmpBits.Canvas.Handle, 0, 0, 0, 0, rgbBk, rgbFg, fStyle);


                W := RectWidth(RectArray.IconRect)/Images.Width;
                H := RectHeight(RectArray.IconRect)/Images.Height;

                if W > H then
                  Scale := H
                else
                  Scale := W;

                R := Rect(0, 0, Round(Images.Width*Scale), Round(Images.Height*Scale));

                StretchBlt(ACanvas.Handle,
                           RectArray.IconRect.Left + ((RectWidth(RectArray.IconRect) - RectWidth(R)) div 2),
                           RectArray.IconRect.Top + ((RectHeight(RectArray.IconRect) - RectHeight(R)) div 2),
                           RectWidth(R),
                           RectHeight(R),
                           TmpBits.Canvas.Handle,
                           0, 0,
                           TmpBits.Width,
                           TmpBits.Height,
                           SRCCOPY);
              finally
                TmpBits.Free
              end
            end
          end else
          begin
          RectArray.IconRect.Left := RectArray.IconRect.Left + (RectWidth(RectArray.IconRect) - Images.Width) div 2;
          RectArray.IconRect.Top := RectArray.IconRect.Top + (RectHeight(RectArray.IconRect) - Images.Height) div 2;
          
          ImageList_DrawEx(Images.Handle, ImageIndex, ACanvas.Handle, RectArray.IconRect.Left,
            RectArray.IconRect.Top, 0, 0, rgbBk, rgbFg, fStyle);
          end
        end
      end
    end
  finally
    TestAndUnClipImage(ACanvas, RectArray, Rgn);
  end
end;

procedure TEasyViewItem.PaintNonAlphaBlendedSelection(ACanvas: TCanvas;
  LocalSelRect: TRect);
begin
// Not AlphaBlended is much easier
  if OwnerListview.Selection.RoundRect then
    RoundRect(ACanvas.Handle,
      LocalSelRect.Left,
      LocalSelRect.Top,
      LocalSelRect.Right,
      LocalSelRect.Bottom,
      OwnerListview.Selection.RoundRectRadius,
      OwnerListview.Selection.RoundRectRadius)
  else
    Rectangle(ACanvas.Handle,
      LocalSelRect.Left,
      LocalSelRect.Top,
      LocalSelRect.Right,
      LocalSelRect.Bottom);
end;

procedure TEasyViewItem.PaintSelectionRect(Item: TEasyItem; Column: TEasyColumn; const Caption: WideString; RectArray: TEasyRectArrayObject; ACanvas: TCanvas; ViewportClipRect: TRect; ForceSelectionRectDraw: Boolean);
var
  Rgn: HRGN;
  // All these rectangles are necessary because the AlphaBlend routine does not respect DC origins and clipping regions.
  LocalSelRect,                    // The true Viewpoint Rectangle of the Selection Rectangle, including the Header area if applicable
  LocalSelWindowClippedRect,       // The true Viewpoint Rectangle of the Selection Rectangle, Clipped to the current Physical Window Coordinates
  LocalSelClippedRect,             // The true Viewpoint Rectangle of the Selection Rectangle, excluding the Header area if applicable
  HeaderClippedWindowRect: TRect;  // Rectangle containing the Selection Rectangle that has been translated to Physical Window Coordinates and the Header area clipped off it if necesasry.  This rectangle is also clipped to the ClientWindow rectangle
  AlphaColor,
  GradientTop,
  GradientBottom: TColor;
  DoDraw: Boolean;
begin
  DoDraw := Item.Selected or Item.Hilighted;
  if DoDraw and Assigned(Column) then
    DoDraw := (Column.Index < 1);

  if (DoDraw and not FullRowSelect) or ForceSelectionRectDraw then
  begin
    Rgn := 0;
    // If the Window control is not focused show the selection with innactive colors
    if OwnerListview.Focused or Item.Hilighted and not Item.OwnerListview.ShowInactive or Item.OwnerListview.Selection.PopupMode then
    begin
      ACanvas.Font.Color := OwnerListview.Selection.TextColor;
      if (Item <> OwnerListview.Selection.FocusedItem) or not OwnerListview.Selection.UseFocusRect then
        ACanvas.Pen.Color := OwnerListview.Selection.BorderColor
      else
        ACanvas.Pen.Color := clLtGray;


      ACanvas.Brush.Color := OwnerListview.Selection.Color;
      AlphaColor := OwnerListview.Selection.Color;
      GradientTop := OwnerListview.Selection.GradientColorTop;
      GradientBottom := OwnerListview.Selection.GradientColorBottom;
    end else
    begin
      ACanvas.Font.Color := OwnerListview.Selection.InactiveTextColor;
      ACanvas.Pen.Color := OwnerListview.Selection.InactiveBorderColor;
      ACanvas.Brush.Color := OwnerListview.Selection.InactiveColor;
      AlphaColor := OwnerListview.Selection.InactiveColor;
      GradientTop := OwnerListview.Color;
      GradientBottom := OwnerListview.Selection.InactiveColor
    end;

    if OwnerListview.Selection.GroupSelections and (OwnerListview.View = elsReport) and (Item.Selected or Item.Hilighted or Item.Selected) then
    begin
      SetRect(LocalSelRect, 0, 0, 0, 0);
      if Assigned(Item.SelectionGroup) then
      begin
        if Item.Hilighted then
        begin
          LocalSelRect := Item.DisplayRect;
          if not ((OwnerListview.Selection.FullItemPaint) or (OwnerListview.Selection.FullCellPaint) or (OwnerListview.Selection.FullItemPaint)) then
            LocalSelRect.Left := RectArray.TextRect.Left
        end else
        begin
        if Item.SelectionGroup.FirstItem = Item then
          begin
            LocalSelRect := Item.SelectionGroup.DisplayRect;
            if not(OwnerListview.Selection.FullItemPaint or OwnerListview.Selection.FullCellPaint) then
              LocalSelRect.Left := RectArray.SelectionRect.Left
          end
        end
      end else
      begin
        LocalSelRect := Item.DisplayRect;
        if not(OwnerListview.Selection.FullItemPaint or OwnerListview.Selection.FullCellPaint) then
          LocalSelRect.Left := RectArray.SelectionRect.Left
      end
    end else
    if FullRowSelect and (Item.Selected or Item.Hilighted or Item.Selected) then
    begin
      // If full row select then the First Column paints the entire row
      if OwnerListview.Selection.FullCellPaint then
        LocalSelRect := Item.DisplayRect
      else begin
        LocalSelRect := Item.DisplayRect;
        if not OwnerListview.Selection.FullItemPaint then
          LocalSelRect.Left := RectArray.SelectionRect.Left;
      end
    end else
    begin
      if Item.Focused and (OwnerListview.Focused or Item.OwnerListview.Selection.PopupMode) then
      begin
        if OwnerListview.Selection.FullCellPaint then
        begin
          LocalSelRect := Item.DisplayRect;
          UnionRect(LocalSelRect, LocalSelRect, RectArray.FullFocusSelRect);
           if (OwnerListview.View = elsReport) and (OwnerListview.Header.Columns.Count > 0) then
           begin
             LocalSelRect.Left := OwnerListview.Header.Columns[0].DisplayRect.Left;
             LocalSelRect.Right := LocalSelRect.Left + OwnerListview.Header.Columns[0].Width;
           end
        end else
        begin
          LocalSelRect := RectArray.FullFocusSelRect;
          // Cover the Icon if FullItemPaint
          if OwnerListview.Selection.FullItemPaint then
            UnionRect(LocalSelRect, LocalSelRect, RectArray.IconRect);
        end
      end else
      begin
        if OwnerListview.Selection.FullCellPaint then
        begin
          LocalSelRect := Item.DisplayRect;
          if (OwnerListview.View = elsReport) and (OwnerListview.Header.Positions.Count > 0) then
          begin
            LocalSelRect.Left := OwnerListview.Header.Columns[0].DisplayRect.Left;
            LocalSelRect.Right := LocalSelRect.Left + OwnerListview.Header.Columns[0].Width;
           end
        end else
        begin
          LocalSelRect := RectArray.SelectionRect;
          // Cover the Icon if FullItemPaint
          if OwnerListview.Selection.FullItemPaint then
            UnionRect(LocalSelRect, LocalSelRect, RectArray.IconRect);
        end
      end
    end;

    // LocalSelRect is now set up
    AfterSelRectCalc(Item, Column, Caption, LocalSelRect);

    // Clip out the header from the Selection Rectangle, don't need to account for Header
    // as the DC is offset to take it into account
    LocalSelClippedRect := LocalSelRect;
    HeaderClippedWindowRect := OwnerListview.Scrollbars.MapViewRectToWindowRect(LocalSelClippedRect);
    LocalSelWindowClippedRect := HeaderClippedWindowRect;
    if HeaderClippedWindowRect.Top < OwnerListview.Header.RuntimeHeight then
    begin
      HeaderClippedWindowRect.Top := OwnerListview.Header.RuntimeHeight;
      // Make it a 0 height rect if it is an improper rect after adjustment
      if HeaderClippedWindowRect.Top > HeaderClippedWindowRect.Bottom then
        HeaderClippedWindowRect.Top := HeaderClippedWindowRect.Bottom;
      LocalSelClippedRect := OwnerListview.Scrollbars.MapViewRectToWindowRect(HeaderClippedWindowRect);
    end;

    IntersectRect(LocalSelWindowClippedRect, LocalSelWindowClippedRect, OwnerListview.ClientRect);
    LocalSelWindowClippedRect := OwnerListview.Scrollbars.MapWindowRectToViewRect(LocalSelWindowClippedRect);
    IntersectRect(HeaderClippedWindowRect, HeaderClippedWindowRect, OwnerListview.ClientRect);

    // Stop right side from having rounded corners if extends past right edge of window,
    // but don't go past the actual width
    Inc(LocalSelWindowClippedRect.Right, OwnerListview.Selection.RoundRectRadius);
    if LocalSelWindowClippedRect.Right > LocalSelRect.Right then
      LocalSelWindowClippedRect.Right := LocalSelRect.Right;

    if not IsRectEmpty(LocalSelRect) then
    begin
      if OwnerListview.Selection.Gradient and not OwnerListview.Selection.AlphaBlend then
      begin
        if RectHeight(LocalSelWindowClippedRect) > 0 then
        begin
          FillGradient(LocalSelRect.Left, LocalSelWindowClippedRect.Top, LocalSelWindowClippedRect.Right,
            LocalSelWindowClippedRect.Bottom, GradientTop, GradientBottom,
            LocalSelWindowClippedRect.Top, LocalSelWindowClippedRect.Bottom, ACanvas);
          ACanvas.FrameRect(LocalSelRect);
        end
      end else
      if HasMMX and OwnerListview.Selection.AlphaBlend then
      begin
        if OwnerListview.Selection.RoundRect then
          PaintAlphaBlendedRoundRect(ACanvas, AlphaColor, GradientBottom, GradientTop, LocalSelWindowClippedRect, Rgn)
        else begin
         if OwnerListview.Selection.Gradient then
           PaintAlphaBlendedGradientFill(ACanvas, GradientBottom, GradientTop, LocalSelRect, LocalSelWindowClippedRect, LocalSelClippedRect)
         else
           PaintAlphaBlendedSelection(ACanvas, AlphaColor, HeaderClippedWindowRect, LocalSelRect);
        end
      end else
        PaintNonAlphaBlendedSelection(ACanvas, LocalSelRect);
    end
  end
end;

procedure TEasyViewItem.PaintText(Item: TEasyItem; Column: TEasyColumn; const Caption: WideString; RectArray: TEasyRectArrayObject; ACanvas: TCanvas; LinesToDraw: Integer);
var
  DrawTextFlags: TCommonDrawTextWFlags;
  AbsIndex: Integer;
  Hilightable: Boolean;
begin
  if not IsRectEmpty(RectArray.TextRect) then
  begin
    AbsIndex := ValidateColumnIndex(Column);

    Hilightable := (Item.Selected or Item.Hilighted) and ((AbsIndex = 0) or (FullRowSelect or OwnerListview.Selection.GroupSelections));
    LoadTextFont(Item, AbsIndex, ACanvas, Hilightable);

    DrawTextFlags := [dtEndEllipsis];

    if LinesToDraw = 1 then
      Include(DrawTextFlags, dtSingleLine);

    case PaintTextAlignment(Item, Column) of
      taLeftJustify: Include(DrawTextFlags, dtLeft);
      taRightJustify: Include(DrawTextFlags, dtRight);
      taCenter:  Include(DrawTextFlags, dtCenter);
    end;

    case PaintTextVAlignment(Item, Column) of
      cvaTop: Include(DrawTextFlags, dtTop);
      cvaCenter: Include(DrawTextFlags, dtVCenter);
      cvaBottom:  Include(DrawTextFlags, dtBottom);
    end;

    OwnerListview.ClipHeader(ACanvas, False);
    if Item.Focused and (OwnerListview.Focused or Item.OwnerListview.Selection.PopupMode) then
      DrawTextWEx(ACanvas.Handle, Caption, RectArray.TextRect, DrawTextFlags, LinesToDraw)
    else
      DrawTextWEx(ACanvas.Handle, Caption, RectArray.TextRect, DrawTextFlags, LinesToDraw)
  end
end;

function TEasyViewItem.PaintTextAlignment(Item: TEasyItem; Column: TEasyColumn): TAlignment;
begin
  Result := taCenter
end;

function TEasyViewItem.PaintTextLineCount(Item: TEasyItem; Column: TEasyColumn): Integer;
begin
  Result := 2
end;

function TEasyViewItem.PaintTextVAlignment(Item: TEasyItem; Column: TEasyColumn): TCommonVAlignment;
begin
  Result := cvaTop
end;

function TEasyViewItem.PtInRect(Item: TEasyItem; Column: TEasyColumn;
  Pt: TPoint): Integer;
// Compares the passed point with the rectangle of the item.
//
//  If Rect > Pt then Result = 1
//  If Rect < Pt then Result = -1
//  If Rect = Pt (i.e. the point is within the rect) then Result = 0
//
// This code works if the layout of the items is vertical with item numbering
// increasing from Left to Right in each Row
var
  CellRect: TRect;
begin
  CellRect := Item.DisplayRect;
  if Pt.y < CellRect.Top then
    Result := 1
  else
  if Pt.y > CellRect.Bottom then
    Result := -1
  else
  if Pt.x < CellRect.Left then
  begin
    Result := 1
  end
  else
  if Pt.x > CellRect.Right then
    Result := -1
  else
    Result := 0
end;

procedure TEasyViewItem.ReSizeRectArray(
  var RectArray: TEasyRectArrayObjectArray);
var
  OldLen, i: Integer;
begin
  if Length(RectArray) < OwnerListview.Header.Positions.Count then
  begin
    OldLen := Length(RectArray);
    SetLength(RectArray, OwnerListview.Header.Positions.Count);
    for i := OldLen to OwnerListview.Header.Positions.Count - 1 do
      FillChar(RectArray[i], SizeOf(RectArray[i]), #0);
  end else
  if Length(RectArray) > OwnerListview.Header.Positions.Count then
    SetLength(RectArray, OwnerListview.Header.Positions.Count);

  if Length(RectArray) = 0 then
  begin
    SetLength(RectArray, 1);
    FillChar(RectArray[0], SizeOf(RectArray[0]), #0);
  end
end;

procedure TEasyViewItem.TestAndClipImage(ACanvas: TCanvas; RectArray: TEasyRectArrayObject; var Rgn: HRgn);
var
  R, ImageRect: TRect;
begin
  Rgn := 0;
  UnionRect(ImageRect, RectArray.IconRect, RectArray.StateRect);
  if not ContainsRect(RectArray.BoundsRect, ImageRect) then
  begin
    // I was hoping that IntersectClipRect would set a clipping region when
    // I clipped the header in the main control. Then I would get it here and
    // resort it later.  Unfortunately it does not and GetClipRgn returns 0 so
    // I have to call ClipHeader explicitly.
    GetClipRgn(ACanvas.Handle, Rgn);
    IntersectRect(R, ImageRect, RectArray.BoundsRect);
    IntersectClipRect(ACanvas.Handle, R.Left, R.Top, R.Right, R.Bottom);
    OwnerListview.ClipHeader(ACanvas, False)
  end;
end;

procedure TEasyViewItem.TestAndUnClipImage(ACanvas: TCanvas; RectArray: TEasyRectArrayObject; Rgn: HRgn);
var
  ImageRect: TRect;
begin
  UnionRect(ImageRect, RectArray.IconRect, RectArray.StateRect);
  if not ContainsRect(RectArray.BoundsRect, ImageRect)  then
  begin
    if Rgn <> 0 then
    begin
      SelectClipRgn(ACanvas.Handle, Rgn);
      DeleteObject(Rgn)
    end
    else begin
      SelectClipRgn(ACanvas.Handle, 0);
      OwnerListview.ClipHeader(ACanvas, False)
    end
  end;
end;

function TEasyHeader.GetViewWidth: Integer;
begin
  if Positions.Count > 0 then
    Result := Positions[Positions.Count - 1].DisplayRect.Right
  else
    Result := 0
end;

{ TEasyDefaultCellSize }

constructor TEasyCellSize.Create(AnOwner: TCustomEasyListview);
begin
  inherited Create(AnOwner);
  FWidth := 75;
  FHeight := 75;
  FAutoSizeHeight := 75;
  FAutoSizeWidth := 75;
end;

function TEasyCellSize.GetHeight: Integer;
begin
  if AutoSize then
    Result := FAutoSizeHeight
  else begin
    Result := FHeight;
    if Result < 1 then
      Result := 1
  end
end;

function TEasyCellSize.GetWidth: Integer;
begin
  if AutoSize then
    Result := FAutoSizeWidth
  else begin
    Result := FWidth;
    if Result < 1 then
      Result := 1
  end
end;

procedure TEasyCellSize.Assign(Source: TPersistent);
begin
  if Source is TEasyCellSize then
  begin
    FHeight := TEasyCellSize(Source).Height;
    FWidth := TEasyCellSize(Source).Width;
 //   OwnerListview.Groups.Rebuild;
  end;
end;

procedure TEasyCellSize.SetAutoSize(const Value: Boolean);
begin
  if Value <> FAutoSize then
  begin
    FAutoSize := Value;
    if not FAutoSize then
    begin
      FAutoSizeWidth := FWidth;
      FAutoSizeHeight := FHeight
    end;
    OwnerListview.Groups.Rebuild;
  end
end;

procedure TEasyCellSize.SetSize(AWidth, AHeight: Integer);
begin
  if AWidth < 0 then AWidth := 0;
  if AHeight < 0 then AHeight := 0;

  if AutoSize then
  begin
    if (AWidth <> FWidth) or (AHeight <> FHeight) then
    begin
      FAutoSizeWidth := AWidth;
      FAutoSizeHeight := AHeight;
    end;
  end else
  begin
    if (AWidth <> FWidth) or (AHeight <> FHeight) then
    begin
      FWidth := AWidth;
      FHeight := AHeight;
  end;
  OwnerListview.Groups.Rebuild;
  end
end;

procedure TEasyCellSize.SetHeight(Value: Integer);
begin
  if AutoSize then
    SetSize(FAutoSizeWidth, Value)
  else
    SetSize(FWidth, Value);
end;

procedure TEasyCellSize.SetWidth(Value: Integer);
begin
  if AutoSize then
    SetSize(Value, FAutoSizeHeight)
  else
    SetSize(Value, FHeight);
end;

{ TEasyIconItemView}

function TEasyViewIconItem.ExpandIconR(Item: TEasyItem; RectArray: TEasyRectArrayObject; SelectType: TEasySelectHitType): TRect;
begin
  Result := RectArray.IconRect;
end;

function TEasyViewIconItem.OverlappedFocus: Boolean;
begin
  Result:= True
end;

function TEasyViewIconItem.PaintImageSize: TEasyImageSize;
begin
  Result := eisLarge
end;

function TEasyViewIconItem.PaintTextLineCount(Item: TEasyItem;
  Column: TEasyColumn): Integer;
begin
  if Item.Focused and OwnerListview.Focused then
    Result := -1
  else
    Result := 2
end;

procedure TEasyViewIconItem.AfterSelRectCalc(Item: TEasyItem;
  Column: TEasyColumn; const Caption: WideString; var LocalSelRect: TRect);
begin
  if OwnerListview.Selection.FullCellPaint then
    InflateRect(LocalSelRect, -2, -2)
end;

procedure TEasyViewIconItem.ItemRectArray(Item: TEasyItem; Column: TEasyColumn; ACanvas: TCanvas; const Caption: WideString; var RectArray: TEasyRectArrayObject);
var
  ImageW, ImageH, Left: Integer;
  DrawTextFlags: TCommonDrawTextWFlags;
  R: TRect;
  PositionIndex, AbsIndex: Integer;
  ACaption: WideString;
begin
  if Assigned(Item) then
  begin
    if not Item.Initialized then
      Item.Initialized := True;

    if Assigned(Column) then
    begin
      AbsIndex := Column.Index;
      PositionIndex := Column.Position
    end else
    begin
      AbsIndex := 0;
      PositionIndex := 0
    end;

    if PositionIndex > -1 then
    begin
      FillChar(RectArray, SizeOf(RectArray), #0);

      GetImageSize(Item, Column, ImageW, ImageH, eikNormal);

      // Calcuate the Bounds of the Cell that is allowed to be drawn in
      // **********
      RectArray.BoundsRect := Item.DisplayRect;
      Inc(RectArray.BoundsRect.Left);

      // Calculate where the Icon is positioned
      // **********
      // Center Icon horziontally and a few pixels from the top of the cell
      Left := RectArray.BoundsRect.Left + ((RectWidth(RectArray.BoundsRect) - ImageW) div 2);

      RectArray.IconRect := Rect(Left,
                              RectArray.BoundsRect.Top + 2,
                              Left + ImageW,
                              RectArray.BoundsRect.Top + 2 + ImageH);

      // Some margin between the Icon and the Text that belongs to the Icon Rect
      Inc(RectArray.IconRect.Bottom, 4);


      // Calculate area that the Checkbox may be drawn
      // **********
      if Item.CheckType <> ectNone then
      begin
        R := Checks.Bound[Item.Checksize];
        RectArray.CheckRect.Top := RectArray.IconRect.Bottom + 1;  // This looks better than centered
        RectArray.CheckRect.Left := RectArray.BoundsRect.Left + Item.CheckIndent;
        RectArray.CheckRect.Bottom := RectArray.CheckRect.Top + R.Bottom;
        RectArray.CheckRect.Right := RectArray.CheckRect.Left + R.Right;
      end else
      begin
        RectArray.CheckRect.Top := RectArray.IconRect.Bottom;
        RectArray.CheckRect.Left := RectArray.BoundsRect.Left;
        RectArray.CheckRect.Bottom := RectArray.BoundsRect.Bottom;
        RectArray.CheckRect.Right := RectArray.BoundsRect.Left;
      end;

      // Calculate area that the label may be drawn
      // **********

      // The Label Rect is the remaining area between the Icon Rect and the Bottom
      // of the Bounds Rect
      RectArray.LabelRect := Rect(RectArray.CheckRect.Right,
                               RectArray.IconRect.Bottom + 1,
                               RectArray.BoundsRect.Right,
                               RectArray.BoundsRect.Bottom);


      // Calculate the portion of the Label Rect that the current Text will use
      // **********
      RectArray.TextRect := RectArray.LabelRect;
      RectArray.FullTextRect := RectArray.LabelRect;
      // Leave room for a small border between edge of the selection rect and text
      InflateRect(RectArray.TextRect, -2, -2);
      InflateRect(RectArray.FullTextRect, -2, -2);

      DrawTextFlags := [dtCalcRect, dtCalcRectAlign];

      DrawTextFlags := DrawTextFlags + [dtCenter];

      case PaintTextAlignment(Item, Column) of
        taCenter: DrawTextFlags := DrawTextFlags + [dtCenter];
        taLeftJustify: DrawTextFlags := DrawTextFlags + [dtLeft];
        taRightJustify: DrawTextFlags := DrawTextFlags + [dtRight];
      end;

      case PaintTextVAlignment(Item, Column) of
        cvaCenter: DrawTextFlags := DrawTextFlags + [dtVCenter];
        cvaTop: DrawTextFlags := DrawTextFlags + [dtTop];
        cvaBottom: DrawTextFlags := DrawTextFlags + [dtBottom];
      end;

      if Assigned(OwnerListview.ScratchCanvas) then
      begin
        ACaption := Item.Captions[AbsIndex];
        if ACaption = '' then
          ACaption := ' ';
        LoadTextFont(Item, PositionIndex, OwnerListview.ScratchCanvas, Item.Selected);
        DrawTextWEx(OwnerListview.ScratchCanvas.Handle, ACaption, RectArray.FullTextRect, DrawTextFlags, -1);
        DrawTextWEx(OwnerListview.ScratchCanvas.Handle, ACaption, RectArray.TextRect, DrawTextFlags, 2);
      end;

      // Calculate Selection rectangle paint box
      // **********
      RectArray.SelectionRect := RectArray.TextRect;
      InflateRect(RectArray.SelectionRect, 2, 2);
      RectArray.FullFocusSelRect := RectArray.FullTextRect;
      InflateRect(RectArray.FullFocusSelRect, 2, 2);

      UnionRect(RectArray.ClickselectBoundsRect, RectArray.IconRect, RectArray.TextRect);
      RectArray.DragSelectBoundsRect := RectArray.ClickselectBoundsRect;
      UnionRect(RectArray.FocusChangeInvalidRect, RectArray.IconRect, RectArray.FullFocusSelRect);

      RectArray.EditRect := RectArray.FullTextRect;
    end
  end
end;

procedure TEasyViewIconItem.PaintBefore(Item: TEasyItem; Column: TEasyColumn; const Caption: WideString; ACanvas: TCanvas; RectArray: TEasyRectArrayObject; var Handled: Boolean);
begin
 // Skip inherited
end;

{ TEasySmallIconItemView }

function TEasyViewSmallIconItem.CalculateDisplayRect(Item: TEasyItem;
  Column: TEasyColumn): TRect;
begin
  Result := Item.DisplayRect
end;

function TEasyViewSmallIconItem.ExpandIconR(Item: TEasyItem; RectArray: TEasyRectArrayObject; SelectType: TEasySelectHitType): TRect;
begin
  Result := Rect(0, 0, 0, 0)
end;

function TEasyViewSmallIconItem.ExpandTextR(Item: TEasyItem; RectArray: TEasyRectArrayObject; SelectType: TEasySelectHitType): TRect;
begin
  UnionRect(Result, RectArray.IconRect, RectArray.TextRect);
  Result.Top := RectArray.BoundsRect.Top;
  Result.Bottom := RectArray.BoundsRect.Bottom;
end;

procedure TEasyViewSmallIconItem.ItemRectArray(Item: TEasyItem; Column: TEasyColumn; ACanvas: TCanvas; const Caption: WideString; var RectArray: TEasyRectArrayObject);
var
  TextSize: TSize;
  SelectW, BoundsH, CheckH: Integer;
  CheckType: TEasyCheckType;
  R: TRect;
  CheckIndent, CaptionIndent, Checksize, ImageIndex, ImageIndent, PositionIndex, AbsIndex, StateImageIndex: Integer;
  ImageW, ImageH: Integer;
  ACaption: WideString;
begin
  if Assigned(Item) then
  begin
    // This is faster
    if not Item.Initialized then
      Item.Initialized := True;
    if Assigned(Column) then
    begin
      AbsIndex := Column.Index;
      PositionIndex := Column.Position
    end else
    begin
      AbsIndex := 0;
      PositionIndex := 0
    end;

    if PositionIndex > -1 then
    begin
      FillChar(RectArray, SizeOf(RectArray), #0);

      // Calcuate the Bounds of the Cell that is allowed to be drawn in
      // **********
      // Report view is based on this class so take care if that possibility too.

      if not Assigned(Column) and (OwnerListview.Header.Positions.Count > 0) then
        RectArray.BoundsRect := CalculateDisplayRect(Item, OwnerListview.Header.Positions[0])
      else
        RectArray.BoundsRect := CalculateDisplayRect(Item, Column);
        
      CheckType := Item.CheckType;
      Checksize := Item.Checksize;
      ImageIndex := Item.ImageIndexes[AbsIndex];
      StateImageIndex := Item.StateImageIndexes[AbsIndex];
      CaptionIndent := Item.CaptionIndent;
      CheckIndent := Item.CheckIndent;
      ImageIndent := Item.ImageIndent;

      // Calculate the space necded for the CheckBox, if enabled
      if (CheckType <> ectNone) and (AbsIndex = 0) then
      begin
        R := Checks.Bound[Checksize];
        BoundsH := RectHeight(RectArray.BoundsRect);
        CheckH := RectHeight(R);
        RectArray.CheckRect := Rect(RectArray.BoundsRect.Left + CheckIndent,
                                    RectArray.BoundsRect.Top + (BoundsH - CheckH) div 2,
                                    RectArray.BoundsRect.Left + CheckIndent + R.Right,
                                    RectArray.BoundsRect.Top + CheckH + (BoundsH - CheckH) div 2)
      end else
        RectArray.CheckRect := Rect(RectArray.BoundsRect.Left,
                                    RectArray.BoundsRect.Top,
                                    RectArray.BoundsRect.Left,
                                    RectArray.BoundsRect.Bottom); // Check Rect has a width of 0
                                 
      // Set the rectangle of the Image if avaialable, note Bitmap is not supported
      if PaintStateImage and  ((StateImageIndex > -1) or (GetImageList(Column, Item, eikState) <> nil)) then
      begin
        GetImageSize(Item, Column, ImageW, ImageH, eikState);
        RectArray.StateRect.Top := RectArray.BoundsRect.Top + (RectHeight(RectArray.BoundsRect) - ImageH) div 2;
        RectArray.StateRect.Left := RectArray.CheckRect.Right + ImageIndent;
        // State Images must be the same size as the main image
        RectArray.StateRect.Bottom := RectArray.StateRect.Top + ImageH;
        RectArray.StateRect.Right := RectArray.StateRect.Left + ImageW;
      end else
        RectArray.StateRect := Rect(RectArray.CheckRect.Right,
                                RectArray.CheckRect.Top,
                                RectArray.CheckRect.Right,
                                RectArray.CheckRect.Bottom);
      
      // Set the rectangle of the Image if avaialable, note Bitmap is not supported
      if (ImageIndex > -1) then
      begin
        GetImageSize(Item, Column, ImageW, ImageH, eikNormal);
        RectArray.IconRect.Top := RectArray.BoundsRect.Top + (RectHeight(RectArray.BoundsRect) - ImageH) div 2;
        RectArray.IconRect.Left := RectArray.StateRect.Right + ImageIndent;
        RectArray.IconRect.Bottom := RectArray.IconRect.Top + ImageH;
        RectArray.IconRect.Right := RectArray.IconRect.Left + ImageW;
      end else
        RectArray.IconRect := Rect(RectArray.StateRect.Right,
                                RectArray.StateRect.Top,
                                RectArray.StateRect.Right,
                                RectArray.StateRect.Bottom);

      // Calculate where the Label is positioned
      // **********
      // The Cell may be narrow enough that only the image will fit. If that is
      // the case leave the LabelR a Zero sized rect
      if RectArray.BoundsRect.Right - 1 > RectArray.IconRect.Right + CaptionIndent {+ (2*LABEL_MARGIN) }then
        RectArray.LabelRect := Rect(RectArray.IconRect.Right + CaptionIndent {+ LABEL_MARGIN},
                                 RectArray.BoundsRect.Top,
                                 RectArray.BoundsRect.Right {- 2*LABEL_MARGIN},
                                 RectArray.BoundsRect.Bottom);

      // Calculate Text based rectangles
      // **********
      if Assigned(OwnerListview.ScratchCanvas) then
      begin
        LoadTextFont(Item, PositionIndex, OwnerListview.ScratchCanvas, Item.Selected);
        ACaption := Item.Captions[AbsIndex];
        if ACaption = '' then
          ACaption := ' ';
        TextSize := TextExtentW(ACaption, OwnerListview.ScratchCanvas);
      end else
      begin
       TextSize.cx := 0;
       TextSize.cy := 0
      end;

      // Calculate Text Rectangle
      // **********
      RectArray.TextRect := RectArray.LabelRect;
      InflateRect(RectArray.TextRect, -2, -2);

      // Center it horz and vert to start with
      // This will also clip the text rect to to size of the Label if necessary
      RectArray.TextRect := CenterRectInRect(RectArray.TextRect, Rect(0, 0, TextSize.cx, TextSize.cy));

      case PaintTextAlignment(Item, Column) of
        taLeftJustify:  OffsetRect(RectArray.TextRect, -(RectArray.TextRect.Left - RectArray.LabelRect.Left), 0);
        taRightJustify: OffsetRect(RectArray.TextRect, (RectArray.LabelRect.Right - RectArray.TextRect.Right - 8), 0);
      end;

      // Calculate Focus Text Rectangle
      // **********
      RectArray.FullTextRect := RectArray.TextRect;

      // Calculate Selection Rectangle
      // **********
      RectArray.SelectionRect := RectArray.TextRect;
      InflateRect(RectArray.SelectionRect, 2, 2);
      RectArray.FullFocusSelRect := RectArray.SelectionRect;

      // Calculate Rectangle used for Clickselecting
      // **********
      UnionRect(RectArray.ClickselectBoundsRect, RectArray.IconRect, RectArray.SelectionRect);

      // Calculate Rectangle used for DragSelecting
      // **********
      // During a drag selection the hit area is only a fraction of the Click bounds
      RectArray.DragSelectBoundsRect := RectArray.ClickselectBoundsRect;
      SelectW := Round(RectWidth(RectArray.DragSelectBoundsRect) * SELECTION_OFFSET);
      InflateRect(RectArray.DragSelectBoundsRect, -SelectW, 0);

      UnionRect(RectArray.FocusChangeInvalidRect, RectArray.IconRect, RectArray.FullTextRect);

      RectArray.EditRect := RectArray.FullTextRect;
    end
  end
end;

procedure TEasyViewSmallIconItem.PaintBefore(Item: TEasyItem; Column: TEasyColumn; const Caption: WideString; ACanvas: TCanvas; RectArray: TEasyRectArrayObject; var Handled: Boolean);
begin
  // Don't do default
end;

function TEasyViewSmallIconItem.PaintTextAlignment(Item: TEasyItem; Column: TEasyColumn): TAlignment;
begin
  if Assigned(Column) then
    Result := Column.Alignment
  else
    Result := taLeftJustify
end;

function TEasyViewSmallIconItem.PaintTextLineCount(Item: TEasyItem; Column: TEasyColumn): Integer;
begin
  Result := 1
end;

function TEasyViewSmallIconItem.PaintTextVAlignment(Item: TEasyItem; Column: TEasyColumn): TCommonVAlignment;
begin
  if Assigned(Column) then
    Result := Column.VAlignment
  else
    Result := cvaCenter
end;

{ TEasyDefaultCellSizes }

constructor TEasyCellSizes.Create(AnOwner: TCustomEasyListview);
begin
  inherited Create(AnOwner);
  FIcon := TEasyCellSizeIcon.Create(AnOwner);
  FSmallIcon := TEasyCellSizeSmallIcon.Create(AnOwner);
  FThumbnail := TEasyCellSizeThumbnail.Create(AnOwner);
  FTile := TEasyCellSizeTile.Create(AnOwner);
  FList := TEasyCellSizeList.Create(AnOwner);
  FReport := TEasyCellSizeReport.Create(AnOwner);
  FFilmStrip := TEasyCellSizeFilmStrip.Create(AnOwner);
  FGrid := TEasyCellGrid.Create(AnOwner);
end;

destructor TEasyCellSizes.Destroy;
begin
  FreeAndNil(FIcon);
  FreeAndNil(FSmallIcon);
  FreeAndNil(FThumbnail);
  FreeAndNil(FTile);
  FreeAndNil(FList);
  FreeAndNil(FReport);
  FreeAndNil(FFilmStrip);
  FreeAndNil(FGrid);
  inherited Destroy;
end;

{ TEasyDefaultSmallIconCellSize }

constructor TEasyCellSizeSmallIcon.Create(
  AnOwner: TCustomEasyListview);
begin
  inherited Create(AnOwner);
  FWidth := 200;
  FHeight := 17;
  FAutoSizeHeight := 17;
  FAutoSizeWidth := 200;
end;

{ TEasyDefaultThumbnailCellSize }

constructor TEasyCellSizeThumbnail.Create(
  AnOwner: TCustomEasyListview);
begin
  inherited Create(AnOwner);
  FWidth := 125;
  FHeight := 143;
  FAutoSizeHeight := 143;
  FAutoSizeWidth := 125;
end;

{ TEasyDefaultTileCellSize }

constructor TEasyCellSizeTile.Create(
  AnOwner: TCustomEasyListview);
begin
  inherited Create(AnOwner);
  FWidth := 218;
  FHeight := 58;
  FAutoSizeHeight := 58;
  FAutoSizeWidth := 218;
end;

{ TEasyDefaultListCellSize }

constructor TEasyCellSizeList.Create(
  AnOwner: TCustomEasyListview);
begin
  inherited Create(AnOwner);
  FWidth := 200;
  FHeight := 17;
  FAutoSizeHeight := 17;
  FAutoSizeWidth := 200;
end;

{ TEasyDefaultReportCellSize }

constructor TEasyCellSizeReport.Create(
  AnOwner: TCustomEasyListview);
begin
  inherited Create(AnOwner);
  FWidth := 75;
  FHeight := 17;
  FAutoSizeHeight := 17;
  FAutoSizeWidth := 75;
end;

function TEasyViewTileItem.ExpandIconR(Item: TEasyItem; RectArray: TEasyRectArrayObject; SelectType: TEasySelectHitType): TRect;
begin
  Result := Rect(0, 0, 0, 0)
end;

function TEasyViewTileItem.ExpandTextR(Item: TEasyItem; RectArray: TEasyRectArrayObject; SelectType: TEasySelectHitType): TRect;
begin
  UnionRect(Result, RectArray.IconRect, RectArray.TextRect);
  Result.Top := RectArray.BoundsRect.Top + 2;
  Result.Bottom := RectArray.BoundsRect.Bottom - 2;
end;

function TEasyViewTileItem.GetImageList(Column: TEasyColumn; Item: TEasyItem; Image: TEasyImageKind): TCustomImageList;
begin
  Result := inherited GetImageList(Column, Item, Image);
  if not Assigned(Result) then
    Result := Item.ImageList[ValidateColumnIndex(Column), eisLarge]
end;

function TEasyViewTileItem.PaintImageSize: TEasyImageSize;
begin
  Result := eisExtraLarge
end;

function TEasyViewTileItem.PaintTextAlignment(Item: TEasyItem;
  Column: TEasyColumn): TAlignment;
begin
  Result := taLeftJustify
end;

procedure TEasyViewTileItem.AfterSelRectCalc(Item: TEasyItem;
  Column: TEasyColumn; const Caption: WideString; var LocalSelRect: TRect);
begin
  InflateRect(LocalSelRect, -2, -2)
end;

procedure TEasyViewTileItem.ItemRectArray(Item: TEasyItem; Column: TEasyColumn; ACanvas: TCanvas; const Caption: WideString; var RectArray: TEasyRectArrayObject);
var
  ACaption: WideString;
  PositionIndex, i, YOffset, ImageW, ImageH, DetailCount: Integer;
  DrawTextFlags: TCommonDrawTextWFlags;
  R: TRect;
  Done: Boolean;
begin
  if Assigned(Item) then
  begin
    if not Item.Initialized then
      Item.Initialized := True;

    PositionIndex := ValidateColumnIndex(Column);

    if PositionIndex > -1 then
    begin
      FillChar(RectArray, SizeOf(RectArray), #0);

      GetImageSize(Item, Column, ImageW, ImageH, eikNormal);

      // Get the cell size for the main caption, aligned with the top
      case PaintTextAlignment(Item, Column) of
        taLeftJustify: Include(DrawTextFlags, dtLeft);
        taRightJustify: Include(DrawTextFlags, dtRight);
        taCenter:  Include(DrawTextFlags, dtCenter);
      end;
      DrawTextFlags := [dtCalcRect, dtCalcRectAlign, dtEndEllipsis, dtTop];

      // Calcuate the Bounds of the Cell that is allowed to be drawn in
      // **********
      RectArray.BoundsRect := Item.DisplayRect;
      InflateRect(RectArray.BoundsRect, -Item.Border, -Item.Border);

      // Calculate where the Checkbox is positioned
      // **********
      if Item.CheckType <> ectNone then
      begin
        R := Checks.Bound[Item.Checksize];
        RectArray.CheckRect.Left := RectArray.BoundsRect.Left + Item.CheckIndent;
        RectArray.CheckRect.Top := RectArray.BoundsRect.Top + (RectHeight(RectArray.BoundsRect) - R.Bottom) div 2;
        RectArray.CheckRect.Right := RectArray.CheckRect.Left + R.Right;
        RectArray.CheckRect.Bottom := RectArray.CheckRect.Top + R.Bottom;
      end else
        SetRect(RectArray.CheckRect, RectArray.BoundsRect.Left,
                                     RectArray.BoundsRect.Top,
                                     RectArray.BoundsRect.Left,
                                     RectArray.BoundsRect.Bottom);

      // Calculate where the Icon is positioned
      // **********
      RectArray.IconRect := Rect(RectArray.CheckRect.Right + Item.ImageIndent,
                              RectArray.BoundsRect.Top,
                              RectArray.CheckRect.Right + Item.ImageIndent + ImageW + 4,
                              RectArray.BoundsRect.Bottom);

      // Calculate where the Label Rect is positioned
      // **********
      RectArray.LabelRect := RectArray.BoundsRect;
      RectArray.LabelRect.Left := RectArray.IconRect.Right + Item.CaptionIndent;
      InflateRect(RectArray.LabelRect, -2, 0);

      // Calculate the Text Rectangles
      // **********

      RectArray.TextRect := RectArray.LabelRect;
      // Leave border for the Selection Painted rectangle around text
      InflateRect(RectArray.TextRect, -2, -2);

      DetailCount := Item.DetailCount;

      // Assume that there is enough room for all the Captions to be show that the user desired
      SetLength(RectArray.TextRects, DetailCount);

      for i := 0 to Length(RectArray.TextRects) - 1 do
        RectArray.TextRects[i] := Rect(0, 0, 0, 0);

      if Length(RectArray.TextRects) > 0 then
      begin
        // Lets work on the Main Caption, it gets the full LabelR to use
        RectArray.TextRects[0] := RectArray.LabelRect;
        // TextRects[0] is the main caption that can fill more then one line if necessary
        // if it does then details are omitted as necded
        if Item.Details[0] > -1 then
          ACaption := Item.Captions[Item.Details[0]]
        else
          ACaption := '';
        // Make the first line contain something
        if ACaption = '' then
          ACaption := ' ';
        if Assigned(OwnerListview.ScratchCanvas) then
        begin
          LoadTextFont(Item, PositionIndex, OwnerListview.ScratchCanvas, Item.Selected);
          DrawTextWEx(OwnerListview.ScratchCanvas.Handle, ACaption, RectArray.TextRects[0], DrawTextFlags, PaintTextLineCount(Item, Column));
        end;
        if RectArray.TextRects[0].Bottom > RectArray.LabelRect.Bottom then
          RectArray.TextRects[0] := Rect(0, 0, 0, 0)
        else begin
          // Only fill in as many detail lines that fit based on if the caption
          // necded two lines or not
          Done := False;
          i := 1;
          while not Done and (i < Length(RectArray.TextRects)) do
          begin
            if RectArray.TextRects[i - 1].Bottom < RectArray.LabelRect.Bottom then
            begin
              if Item.Details[i] > -1 then
                ACaption := Item.Captions[Item.Details[i]]
              else
                ACaption := '';
              RectArray.TextRects[i] := RectArray.LabelRect;
              RectArray.TextRects[i].Top := RectArray.TextRects[i - 1].Bottom;
              if Assigned(OwnerListview.ScratchCanvas) then
              begin
                LoadTextFont(Item, i, OwnerListview.ScratchCanvas, Item.Selected);
                // Details only get one line
                DrawTextWEx(OwnerListview.ScratchCanvas.Handle, ACaption, RectArray.TextRects[i], DrawTextFlags, 1);
              end;
              if RectArray.TextRects[i].Bottom > RectArray.LabelRect.Bottom then
              begin
                RectArray.TextRects[i] := Rect(0, 0, 0, 0);
                Done := True
              end else
                Inc(i)
            end else
            begin
              RectArray.TextRects[i] := Rect(0, 0, 0, 0);
              Done := True;
            end
          end;
        end;

        RectArray.TextRect := Rect(0, 0, 0, 0);

        Done := False;
        i := 0;
        while not Done and (i < Length(RectArray.TextRects)) do
        begin
          if not IsRectEmpty(RectArray.TextRects[i]) then
          begin
            UnionRect(RectArray.TextRect, RectArray.TextRect, RectArray.TextRects[i]);
            Inc(i)
          end else
            Done := True
        end;
        if Done then
          SetLength(RectArray.TextRects, i);

        YOffset := 0;
        if Item.VAlignment = cvaCenter then
          YOffset := (RectHeight(RectArray.LabelRect)-RectHeight(RectArray.TextRect)) div 2
        else
        if Item.VAlignment = cvaBottom then
          YOffset := RectArray.LabelRect.Bottom - RectHeight(RectArray.TextRect);

        for i := 0 to Length(RectArray.TextRects) - 1 do
        begin
          if Item.VAlignment = cvaCenter then
            OffsetRect(RectArray.TextRects[i], 0, YOffset)
          else
          if Item.VAlignment = cvaBottom then
            OffsetRect(RectArray.TextRects[i], 0, YOffset);
        end;

        if Item.VAlignment = cvaCenter then
          OffsetRect(RectArray.TextRect, 0, YOffset)
        else
        if Item.VAlignment = cvaBottom then
          OffsetRect(RectArray.TextRect, 0, YOffset);
      end;


      RectArray.FullTextRect := RectArray.TextRect;
      RectArray.SelectionRect := RectArray.TextRect;
      InflateRect(RectArray.SelectionRect, 2, 2);
      RectArray.FullFocusSelRect := RectArray.SelectionRect;

      RectArray.ClickselectBoundsRect := RectArray.IconRect;
      RectArray.ClickselectBoundsRect.Right := RectArray.TextRect.Right;

      RectArray.DragSelectBoundsRect := RectArray.TextRect;
      RectArray.DragSelectBoundsRect.Right := RectArray.DragSelectBoundsRect.Right - Round(RectWidth(RectArray.DragSelectBoundsRect)*0.20);

      UnionRect(RectArray.FocusChangeInvalidRect, RectArray.IconRect, RectArray.FullTextRect);

      RectArray.EditRect := RectArray.FullTextRect;
    end
  end
end;


procedure TEasyViewTileItem.PaintBefore(Item: TEasyItem; Column: TEasyColumn; const Caption: WideString; ACanvas: TCanvas; RectArray: TEasyRectArrayObject; var Handled: Boolean);
begin

end;

procedure TEasyViewTileItem.PaintText(Item: TEasyItem; Column: TEasyColumn; const Caption: WideString; RectArray: TEasyRectArrayObject; ACanvas: TCanvas; LinesToDraw: Integer);
//
//  Need to handle this paint directly
//
var
  DrawTextFlags: TCommonDrawTextWFlags;
  i: Integer;
begin
  DrawTextFlags := [dtEndEllipsis, dtTop];

  case PaintTextAlignment(Item, Column) of
    taLeftJustify: Include(DrawTextFlags, dtLeft);
    taRightJustify: Include(DrawTextFlags, dtRight);
    taCenter:  Include(DrawTextFlags, dtCenter);
  end;

  if Length(RectArray.TextRects) > 0 then
  begin
    if Item.Details[0] > -1 then
    begin
      LoadTextFont(Item, 0, ACanvas, Item.Selected);
      DrawTextWEx(ACanvas.Handle, Item.Captions[Item.Details[0]], RectArray.TextRects[0], DrawTextFlags, PaintTextLineCount(Item, Column));
    end;
    for i := 1 to Length(RectArray.TextRects) - 1 do
    begin
      if Item.Details[i] > -1 then
      begin
        LoadTextFont(Item, i, ACanvas, Item.Selected);
        DrawTextWEx(ACanvas.Handle, Item.Captions[Item.Details[i]], RectArray.TextRects[i], DrawTextFlags, 1);
      end
    end
  end
end;

function TEasyViewThumbnailItem.ExpandTextR(Item: TEasyItem; RectArray: TEasyRectArrayObject; SelectType: TEasySelectHitType): TRect;
begin
  Result := inherited ExpandTextR(Item, RectArray, SelectType);
  Result.Top := RectArray.IconRect.Bottom
end;

function TEasyViewThumbnailItem.GetImageList(Column: TEasyColumn; Item: TEasyItem; Image: TEasyImageKind): TCustomImageList;
begin
  Result := inherited GetImageList(Column, Item, Image);
  if not Assigned(Result) then
    Result := Item.ImageList[ValidateColumnIndex(Column), eisLarge]
end;

{ TEasyThumbnailItemView}

function TEasyViewThumbnailItem.OverlappedFocus: Boolean;
begin
  Result := True
end;

function TEasyViewThumbnailItem.PaintImageSize: TEasyImageSize;
begin
  Result := eisExtraLarge
end;

function TEasyViewThumbnailItem.SelectionHit(Item: TEasyItem; SelectViewportRect: TRect; SelectType: TEasySelectHitType): Boolean;
var
  R: TRect;
  RectArray: TEasyRectArrayObject;
  SelectMargin: Integer;
begin
  Result := False;
  if Item.Enabled then
  begin
    ItemRectArray(Item, nil, OwnerListview.ScratchCanvas,'', RectArray);
    if SelectType = eshtClickselect then
      Result := IntersectRect(R, SelectViewportRect, ExpandTextR(Item, RectArray, SelectType)) or
                IntersectRect(R, SelectViewportRect, ExpandIconR(Item, RectArray, SelectType))
    else begin
      R := ExpandIconR(Item, RectArray, SelectType);
      SelectMargin := Round( RectWidth(R) * 0.10);
      InflateRect(R, -SelectMargin, -SelectMargin);
      Result := IntersectRect(R, SelectViewportRect, R)
    end
  end
end;

function TEasyViewThumbnailItem.SelectionHitPt(Item: TEasyItem; ViewportPoint: TPoint; SelectType: TEasySelectHitType): Boolean;
var
  RectArray: TEasyRectArrayObject;
  R: TRect;
  SelectMargin: Integer;
begin
  Result := False;
  if Item.Enabled then
  begin
    ItemRectArray(Item, nil, OwnerListview.ScratchCanvas, '', RectArray);
    if SelectType = eshtClickselect then
      Result := Windows.PtInRect(ExpandTextR(Item, RectArray, SelectType), ViewportPoint) or
                Windows.PtInRect(ExpandIconR(Item, RectArray, SelectType), ViewportPoint)
    else
    begin
      R := ExpandIconR(Item, RectArray, SelectType);
      SelectMargin := Round( RectWidth(R) * 0.10);
      InflateRect(R, -SelectMargin, -SelectMargin);
       Result := Windows.PtInRect(R, ViewportPoint)
    end
  end
end;

procedure TEasyViewThumbnailItem.AfterSelRectCalc(Item: TEasyItem;
  Column: TEasyColumn; const Caption: WideString; var LocalSelRect: TRect);
begin
  InflateRect(LocalSelRect, -2, -2)
end;

procedure TEasyViewThumbnailItem.ItemRectArray(Item: TEasyItem; Column: TEasyColumn; ACanvas: TCanvas; const Caption: WideString; var RectArray: TEasyRectArrayObject);
var
  DrawTextFlags: TCommonDrawTextWFlags;
  Canvas: TControlCanvas;
  R: TRect;
  PositionIndex, AbsIndex: Integer;
  Metrics: TTextMetric;
  ACaption: WideString;
begin
  if Assigned(Item) then
  begin
    if not Item.Initialized then
      Item.Initialized := True;

    if Assigned(Column) then
    begin
      AbsIndex := Column.Index;
      PositionIndex := Column.Position
    end else
    begin
      AbsIndex := 0;
      PositionIndex := 0
    end;

    if PositionIndex > -1 then
    begin
      FillChar(RectArray, SizeOf(RectArray), #0);

      Canvas := TControlCanvas.Create;
      try
        Canvas.Control := OwnerListview;
        Canvas.Lock;

        // Calcuate the Bounds of the Cell that is allowed to be drawn in
        // **********
        RectArray.BoundsRect := Item.DisplayRect;
        InflateRect(RectArray.BoundsRect, -Item.Border, -Item.Border);

        // Calcuate the Bounds of the Cell that is allowed to be drawn in
        // **********
        RectArray.IconRect := RectArray.BoundsRect;
        if not OwnerListview.PaintInfoItem.HideCaption then
        begin
          GetTextMetrics(Canvas.Handle, Metrics);
          RectArray.IconRect.Bottom := RectArray.IconRect.Bottom - Metrics.tmHeight * 2
        end else
        if Item.CheckType <> ectNone then
          RectArray.IconRect.Bottom := RectArray.IconRect.Bottom - (RectHeight(Checks.Bound[Item.Checksize]) + 2);

        // Calculate area that the Checkbox may be drawn
        // **********
        if Item.CheckType <> ectNone then
        begin
          R := Checks.Bound[Item.Checksize];
          RectArray.CheckRect.Top := RectArray.IconRect.Bottom + 1; // Looks best here not centered
          RectArray.CheckRect.Left := RectArray.BoundsRect.Left + Item.CheckIndent;
          RectArray.CheckRect.Bottom := RectArray.CheckRect.Top + R.Bottom;
          RectArray.CheckRect.Right := RectArray.CheckRect.Left + R.Right;
        end else
        begin
          RectArray.CheckRect.Top := RectArray.IconRect.Bottom;
          RectArray.CheckRect.Left := RectArray.BoundsRect.Left;
          RectArray.CheckRect.Bottom := RectArray.BoundsRect.Bottom;
          RectArray.CheckRect.Right := RectArray.CheckRect.Left;
        end;

        // Calcuate the Bounds of the Cell that is allowed to be drawn in
        // **********
        if not OwnerListview.PaintInfoItem.HideCaption then
        begin
          RectArray.LabelRect.Left := RectArray.CheckRect.Right + Item.CaptionIndent;
          RectArray.LabelRect.Top := RectArray.IconRect.Bottom + 1;
          RectArray.LabelRect.Right := RectArray.BoundsRect.Right;
          RectArray.LabelRect.Bottom := RectArray.BoundsRect.Bottom;

          // Calcuate the Text rectangle based on the current text
          // **********
          RectArray.TextRect := RectArray.LabelRect;
          RectArray.FullTextRect := RectArray.LabelRect;
          // Leave room for a small border between edge of the selection rect and text
          InflateRect(RectArray.TextRect, -2, -2);
          InflateRect(RectArray.FullTextRect, -2, -2);

          DrawTextFlags := [dtCalcRect, dtCalcRectAlign];

          case PaintTextAlignment(Item, Column) of
            taLeftJustify: Include(DrawTextFlags, dtLeft);
            taRightJustify: Include(DrawTextFlags, dtRight);
            taCenter: Include(DrawTextFlags, dtCenter);
          end;

          case PaintTextVAlignment(Item, Column) of
            cvaTop: Include(DrawTextFlags, dtTop);
            cvaBottom: Include(DrawTextFlags, dtBottom);
            cvaCenter: Include(DrawTextFlags, dtCenter);
          end;

          LoadTextFont(Item, PositionIndex, Canvas, Item.Selected);
          ACaption := Item.Captions[AbsIndex];
          DrawTextWEx(Canvas.Handle, ACaption, RectArray.FullTextRect, DrawTextFlags, -1);
          DrawTextWEx(Canvas.Handle, ACaption, RectArray.TextRect, DrawTextFlags, 2);

          // Calculate Selection rectangle paint box
          // **********
          RectArray.SelectionRect := RectArray.TextRect;
          InflateRect(RectArray.SelectionRect, 2, 2);
          RectArray.FullFocusSelRect := RectArray.FullTextRect;
          InflateRect(RectArray.FullFocusSelRect, 2, 2);
          UnionRect(RectArray.FocusChangeInvalidRect, RectArray.IconRect, RectArray.FullFocusSelRect);
          if Item.Focused then
            UnionRect(RectArray.ClickselectBoundsRect, RectArray.IconRect, RectArray.FullFocusSelRect)
          else
            UnionRect(RectArray.ClickselectBoundsRect, RectArray.IconRect, RectArray.SelectionRect);
          RectArray.ClickselectBoundsRect := RectArray.ClickselectBoundsRect;
          RectArray.DragSelectBoundsRect := RectArray.TextRect;
          RectArray.EditRect := RectArray.FullTextRect;
        end else
        begin
          RectArray.SelectionRect := RectArray.IconRect;
          RectArray.FullFocusSelRect := RectArray.IconRect;
          RectArray.FocusChangeInvalidRect := RectArray.IconRect;
          RectArray.ClickselectBoundsRect := RectArray.IconRect;
          RectArray.DragSelectBoundsRect := RectArray.IconRect;
          RectArray.EditRect := Rect(0, 0, 0, 0);
          RectArray.LabelRect := Rect(0, 0, 0, 0);
          RectArray.TextRect := Rect(0, 0, 0, 0);
          RectArray.FullTextRect := Rect(0, 0, 0, 0);
        end;
      finally
        Canvas.UnLock;
        Canvas.Free;
      end;
    end
  end
end;

function TEasyViewThumbnailItem.PaintTextLineCount(Item: TEasyItem; Column: TEasyColumn): Integer;
begin
  if Item.Focused and OwnerListview.Focused then
    Result := -1
  else
    Result := 2
end;

function TEasyViewThumbnailItem.PaintTextVAlignment(Item: TEasyItem; Column: TEasyColumn): TCommonVAlignment;
begin
  Result := cvaCenter
end;

procedure TEasyViewThumbnailItem.PaintAfter(Item: TEasyItem; Column: TEasyColumn; const Caption: WideString; ACanvas: TCanvas; RectArray: TEasyRectArrayObject);
begin
  if OwnerListview.PaintInfoItem.ShowBorder then
  begin
    // Paint the Frame around the Thumbnail
    if (Item.Selected or Item.Hilighted) and not OwnerListview.Selection.Gradient  then
    begin
      if OwnerListview.Focused or Item.OwnerListview.Selection.PopupMode or Item.Hilighted then
        ACanvas.Brush.Color := OwnerListview.Selection.Color
      else
        ACanvas.Brush.Color := OwnerListview.Selection.InactiveColor;
      ACanvas.FrameRect(RectArray.IconRect);
      InflateRect(RectArray.IconRect, -1, -1);
      ACanvas.FrameRect(RectArray.IconRect);
      InflateRect(RectArray.IconRect, -1, -1);
      ACanvas.FrameRect(RectArray.IconRect);
    end else
    begin
      ACanvas.Brush.Color := Item.BorderColor;
      ACanvas.FrameRect(RectArray.IconRect);
    end
  end
end;

procedure TEasyViewThumbnailItem.PaintBefore(Item: TEasyItem; Column: TEasyColumn; const Caption: WideString; ACanvas: TCanvas; RectArray: TEasyRectArrayObject; var Handled: Boolean);
begin
  // Skip inherited
end;

{ TColumnView }

function TEasyViewColumn.EditAreaHitPt(Column: TEasyColumn;
  ViewportPoint: TPoint): Boolean;
begin
  Result := False
end;

function TEasyViewColumn.GetImageList(Column: TEasyColumn): TCustomImageList;
begin
  Result := Column.OwnerHeader.Images
end;

function TEasyViewColumn.ItemRect(Column: TEasyColumn; RectType: TEasyCellRectType): TRect;
var
  RectArray: TEasyRectArrayObject;
begin
  Result := Rect(0, 0, 0, 0);
  if IsRectEmpty(Result) then
  begin
    ItemRectArray(Column, RectArray);
    case RectType of
      ertBounds: Result := RectArray.BoundsRect;
      ertIcon: Result := RectArray.IconRect;
      ertLabel: Result := RectArray.LabelRect;
      ertClickselectBounds: Result := RectArray.ClickselectBoundsRect;
      ertDragSelectBounds: Result := RectArray.DragSelectBoundsRect;
      ertText: Result := RectArray.TextRect;
      ertFullText: Result := RectArray.FullTextRect;
    end;
  end;
end;

function TEasyViewColumn.PaintImageSize(Column: TEasyColumn;
  HeaderType: TEasyHeaderType): TEasyImageSize;
begin
  Result := eisSmall
end;

function TEasyViewColumn.SelectionHit(Column: TEasyColumn;
  SelectViewportRect: TRect; SelectType: TEasySelectHitType): Boolean;
begin
  Result := False
end;

function TEasyViewColumn.SelectionHitPt(Column: TEasyColumn;
  ViewportPoint: TPoint; SelectType: TEasySelectHitType): Boolean;
begin
  Result := False
end;

procedure TEasyViewColumn.CalculateTextRect(Column: TEasyColumn; Canvas: TControlCanvas;
  var TextR: TRect);
// Fits the Text in the PaintInfo.Caption.Text field into the TextR based
// on the values in the PaintInfo record.  If Canvas is nil then a temporary
// canvas is created to fit the text based on the Font in the PaintInfo
var
  DrawTextFlags: TCommonDrawTextWFlags;
  LocalCanvas: TControlCanvas;
begin
  case Column.Alignment of
    taLeftJustify:  Include(DrawTextFlags, dtLeft);
    taRightJustify: Include(DrawTextFlags, dtRight);
    taCenter: Include(DrawTextFlags, dtCenter);
  end;

  case Column.VAlignment of
    cvaTop: Include(DrawTextFlags, dtTop);
    cvaBottom: Include(DrawTextFlags, dtBottom);
    cvaCenter: Include(DrawTextFlags, dtVCenter);
  end;

  if not Assigned(Canvas) then
  begin
    LocalCanvas := TControlCanvas.Create;
    LocalCanvas.Control := OwnerListview
  end else
    LocalCanvas := Canvas;

  try
    LoadTextFont(Column, LocalCanvas);
    DrawTextFlags := DrawTextFlags + [dtCalcRectAdjR, dtCalcRect, dtCalcRectAlign];
    DrawTextWEx(LocalCanvas.Handle, Column.Caption, TextR, DrawTextFlags, 1);
  finally
    if not Assigned(Canvas) then
      LocalCanvas.Free
  end;
end;

procedure TEasyViewColumn.GetImageSize(Column: TEasyColumn; var ImageW, ImageH: Integer);
var
  Images: TCustomImageList;
  IsCustom: Boolean;
begin
  ImageW := 0;
  ImageH := 0;
  Column.ImageDrawIsCustom(Column, IsCustom);
  if IsCustom then
    Column.ImageDrawGetSize(Column, ImageW, ImageH)
  else begin
    Images := GetImageList(Column);
    if (Column.ImageIndexes[0] > -1) and Assigned(Images) then
    begin
      ImageW := Images.Width;
      ImageH := Images.Height
    end
  end
end;

procedure TEasyViewColumn.ItemRectArray(Column: TEasyColumn; var RectArray: TEasyRectArrayObject);
var
   DrawTextFlags: TCommonDrawTextWFlags;
   i, CaptionLines: integer;
   R: TRect;
   ImageW, ImageH: Integer;
   Pt: TPoint;
begin
  Pt.x := 0;
  Pt.y := 0;
  if Assigned(Column) then
  begin
    if not Column.Initialized then
      Column.Initialized := True;


    FillChar(RectArray, SizeOf(RectArray), #0);

    GetImageSize(Column, ImageW, ImageH);

    RectArray.BoundsRect := Column.DisplayRect;
    InflateRect(RectArray.BoundsRect, -2, -2);

    // Make the CheckRect 0 width to initialize it
    RectArray.CheckRect := RectArray.BoundsRect;
    RectArray.CheckRect.Right := RectArray.CheckRect.Left;

    // Make the DropDownArrow 0 width to initialize it to the right side
    RectArray.DropDownArrow := RectArray.BoundsRect;
    RectArray.DropDownArrow.Left := RectArray.DropDownArrow.Right;

    // First calculate where the CheckRect goes
    if Column.CheckType <> ectNone then
    begin
      R := Checks.Bound[Column.Checksize];
      RectArray.CheckRect.Left := RectArray.CheckRect.Left + Column.CheckIndent;
      RectArray.CheckRect.Top := RectArray.CheckRect.Top + (RectHeight(RectArray.BoundsRect) - RectHeight(R)) div 2;
      RectArray.CheckRect.Right := RectArray.CheckRect.Left + RectWidth(R);
      RectArray.CheckRect.Bottom := RectArray.CheckRect.Top + RectHeight(R);
    end;

    // Initialize IconRect to 0 width
    RectArray.IconRect := RectArray.BoundsRect;
    RectArray.IconRect.Left := RectArray.CheckRect.Right;
    RectArray.IconRect.Right := RectArray.CheckRect.Right;

    // Next comes the State Image if enabled
    if Column.ImageIndex > -1 then
    begin
      case Column.ImagePosition of
        ehpLeft:
          begin
            RectArray.IconRect.Left := RectArray.CheckRect.Right + Column.ImageIndent;
            RectArray.IconRect.Right := RectArray.IconRect.Left + ImageW;
            RectArray.IconRect.Top := RectArray.BoundsRect.Top + (RectHeight(RectArray.BoundsRect) - ImageH) div 2;
            RectArray.IconRect.Bottom := RectArray.IconRect.Top + ImageH;
            RectArray.LabelRect := RectArray.BoundsRect;
            RectArray.LabelRect.Left := RectArray.IconRect.Right + Column.CaptionIndent;
          end;
        ehpTop:
          begin
            RectArray.IconRect.Top := RectArray.BoundsRect.Top + Column.ImageIndent;
            RectArray.IconRect.Bottom := RectArray.IconRect.Top + ImageH;
            RectArray.IconRect.Left := RectArray.BoundsRect.Left;
            RectArray.IconRect.Right := RectArray.BoundsRect.Right;
            RectArray.LabelRect := RectArray.BoundsRect;
            RectArray.LabelRect.Left := RectArray.CheckRect.Right;
            RectArray.LabelRect.Top := RectArray.IconRect.Bottom + Column.CaptionIndent;
          end;
        ehpRight:
          begin
            RectArray.IconRect.Right := RectArray.BoundsRect.Right - Column.ImageIndent;
            RectArray.IconRect.Left := RectArray.IconRect.Right - ImageW;
            RectArray.IconRect.Top := RectArray.BoundsRect.Top + (RectHeight(RectArray.BoundsRect) - ImageH) div 2;
            RectArray.IconRect.Bottom := RectArray.IconRect.Top + ImageH;
            RectArray.LabelRect := RectArray.BoundsRect;
            RectArray.LabelRect.Left := RectArray.CheckRect.Right;
            RectArray.LabelRect.Right := RectArray.IconRect.Left - Column.CaptionIndent;
          end;
        ehpBottom:
          begin
            RectArray.IconRect.Bottom := RectArray.BoundsRect.Bottom - Column.ImageIndent;
            RectArray.IconRect.Top := RectArray.IconRect.Bottom - ImageH;
            RectArray.IconRect.Left := RectArray.BoundsRect.Left;
            RectArray.IconRect.Right := RectArray.BoundsRect.Right;
            RectArray.LabelRect := RectArray.BoundsRect;
            RectArray.LabelRect.Left := RectArray.CheckRect.Right;
            RectArray.LabelRect.Bottom := RectArray.IconRect.Top - Column.CaptionIndent;
          end;
      end
    end else
    begin
      RectArray.LabelRect := RectArray.BoundsRect;
      RectArray.LabelRect.Left := RectArray.IconRect.Right;
    end;

    if Column.SortDirection <> esdNone then
    begin
      case Column.SortGlyphAlign of
        esgaLeft:
          begin
            RectArray.SortRect := RectArray.BoundsRect;
            RectArray.SortRect.Left := RectArray.LabelRect.Left;
            RectArray.LabelRect.Left := RectArray.LabelRect.Left + Column.SortGlyphIndent + OwnerListview.GlobalImages.ColumnSortUp.Width;
            RectArray.SortRect.Right := RectArray.SortRect.Left + OwnerListview.GlobalImages.ColumnSortUp.Width;
            RectArray.SortRect.Top := RectArray.SortRect.Top + (RectHeight(RectArray.SortRect) - OwnerListview.GlobalImages.ColumnSortUp.Height) div 2;
            RectArray.SortRect.Bottom := RectArray.SortRect.Top + OwnerListview.GlobalImages.ColumnSortUp.Height
          end;
        esgaRight:
          begin
            RectArray.SortRect := RectArray.BoundsRect;
            RectArray.SortRect.Right := RectArray.LabelRect.Right;
            RectArray.LabelRect.Right := RectArray.LabelRect.Right - Column.SortGlyphIndent - OwnerListview.GlobalImages.ColumnSortUp.Width;
            RectArray.SortRect.Left := RectArray.SortRect.Right - OwnerListview.GlobalImages.ColumnSortUp.Width;
            RectArray.SortRect.Top := RectArray.SortRect.Top + (RectHeight(RectArray.SortRect) - OwnerListview.GlobalImages.ColumnSortUp.Height) div 2;
            RectArray.SortRect.Bottom := RectArray.SortRect.Top + OwnerListview.GlobalImages.ColumnSortUp.Height
          end
      else
        // no Sort Glyph
        RectArray.SortRect := RectArray.LabelRect;
        RectArray.SortRect.Right := RectArray.SortRect.Left;
      end
    end;

    if Column.DropDownButton.Visible then
    begin
      if RectWidth(RectArray.LabelRect) > RectHeight(RectArray.BoundsRect) + 10 then
      begin
        if Column.DropDownButton.AlwaysShow or Column.HotTracking[Pt] then
        begin
          RectArray.LabelRect.Right := RectArray.LabelRect.Right - RectHeight(RectArray.BoundsRect);
          RectArray.DropDownArrow.Left := RectArray.DropDownArrow.Right - RectHeight(RectArray.BoundsRect)
        end
      end
    end;

    // See if there is enough room for the label
    if IsRectProper(RectArray.LabelRect) then
    begin

      RectArray.TextRect := RectArray.LabelRect;

      case Column.Alignment of
        taLeftJustify:
          begin
            RectArray.TextRect.Left := RectArray.TextRect.Left + Column.CaptionIndent;
            RectArray.TextRect.Right := RectArray.TextRect.Right - 4;
          end;
        taRightJustify:
          begin
            RectArray.TextRect.Right := RectArray.TextRect.Right - Column.CaptionIndent;
            RectArray.TextRect.Left := RectArray.TextRect.Left + 4;
          end;
      end;   

      // Leave room for a small border between edge of the selection rect and text
      InflateRect(RectArray.TextRect, -2, -2);

      DrawTextFlags := [dtCalcRect, dtCalcRectAlign];

      case Column.Alignment of
        taCenter: DrawTextFlags := DrawTextFlags + [dtCenter];
        taLeftJustify: DrawTextFlags := DrawTextFlags + [dtLeft];
        taRightJustify: DrawTextFlags := DrawTextFlags + [dtRight];
      end;

      // Vertical Alignment has no meaning in mulitiple line output need to calculate
      // the entire text block then vert align it
      DrawTextFlags := DrawTextFlags + [dtTop];

      CaptionLines := OwnerListview.PaintInfoColumn.CaptionLines;

      // Make enough room for the Details and the Caption Rect
      SetLength(RectArray.TextRects, CaptionLines + 1);

      // Get the Caption Rect
      RectArray.TextRects[0] := RectArray.TextRect;
      LoadTextFont(Column, OwnerListview.ScratchCanvas);
      DrawTextWEx(OwnerListview.ScratchCanvas.Handle, Column.Caption, RectArray.TextRects[0], DrawTextFlags, CaptionLines);

      RectArray.TextRect := Rect(0, 0, 0, 0);
      for i := 0 to Length(RectArray.TextRects) - 1 do
        UnionRect(RectArray.TextRect, RectArray.TextRect, RectArray.TextRects[i]);

      case Column.VAlignment of
        cvaCenter: OffsetRect(RectArray.TextRect, 0, (RectHeight(RectArray.LabelRect) - RectHeight(RectArray.TextRect)) div 2);
        cvaBottom: OffsetRect(RectArray.TextRect, 0, (RectHeight(RectArray.LabelRect) - RectHeight(RectArray.TextRect)));
      end;

      for i := 0 to Length(RectArray.TextRects) - 1 do
      begin
        case Column.VAlignment of
          cvaCenter: OffsetRect(RectArray.TextRects[i], 0, ((RectHeight(RectArray.LabelRect) - 4) - RectHeight(RectArray.TextRect)) div 2);
          cvaBottom: OffsetRect(RectArray.TextRects[i], 0, ((RectHeight(RectArray.LabelRect) - 4) - RectHeight(RectArray.TextRect)));
        end;
      end;
    end else
      RectArray.TextRect := Rect(0, 0, 0, 0);

    // Put the Sort Arrow right next to the Text
    OffsetRect(RectArray.SortRect, RectArray.TextRect.Right - RectArray.SortRect.Left, 0);

    RectArray.ClickselectBoundsRect := RectArray.BoundsRect;
    RectArray.DragSelectBoundsRect := RectArray.BoundsRect;
    RectArray.SelectionRect := RectArray.BoundsRect;
    RectArray.FullTextRect := RectArray.BoundsRect;
    RectArray.FullTextRect := RectArray.BoundsRect;
    RectArray.FocusChangeInvalidRect := RectArray.BoundsRect;
    RectArray.EditRect := RectArray.BoundsRect;

    InflateRect(RectArray.BoundsRect, 2, 2);
  end
end;

procedure TEasyViewColumn.LoadTextFont(Column: TEasyColumn; ACanvas: TCanvas);
begin
  ACanvas.Font.Assign(OwnerListview.Header.Font);
  if Column.Bold then
    ACanvas.Font.Style := ACanvas.Font.Style +[fsBold];
end;

procedure TEasyViewColumn.Paint(Column: TEasyColumn; ACanvas: TCanvas;
  HeaderType: TEasyHeaderType);

  procedure AlphaBlendColumn(AColor: TColor; R: TRect);
  var
    Bitmap: TBitmap;
  begin
    Bitmap := TBitmap.Create;
    try
      Bitmap.PixelFormat := pf32Bit;
      Bitmap.Width := RectWidth(R);
      Bitmap.Height := RectHeight(R);
      if (Bitmap.Width > 0) and (Bitmap.Height > 0) then
      begin
        BitBlt(Bitmap.Canvas.Handle, 0, 0, Bitmap.Width, Bitmap.Height, ACanvas.Handle, R.Left, R.Top, srcCopy);
        MPCommonUtilities.AlphaBlend(0, Bitmap.Canvas.Handle, Bitmap.Canvas.ClipRect, Point(0, 0),
          cbmConstantAlphaAndColor, 128, ColorToRGB(AColor));
        BitBlt(ACanvas.Handle, R.Left, R.Top, Bitmap.Width, Bitmap.Height, Bitmap.Canvas.Handle, 0, 0, srcCopy);
      end
    finally
      Bitmap.Free
    end
  end;

var
  RectArray: TEasyRectArrayObject;
begin
  ItemRectArray(Column, RectArray);

  with RectArray.BoundsRect do
    IntersectClipRect(ACanvas.Handle, Left, Top, Right, Bottom);

  // First allow decendents a crack at the painting
  PaintBefore(Column, ACanvas, HeaderType, RectArray);

  // Paint the Background button
  PaintBkGnd(Column, ACanvas, HeaderType, RectArray);

  // Paint the Selection Rectangle
  // *************************
//    PaintSelectionRect(Column, RectArray, ACanvas);

  // Next Paint the Icon or Bitmap Image
  // *************************
  PaintImage(Column, ACanvas, HeaderType, RectArray, PaintImageSize(Column, HeaderType));

  // Now lets paint the Text
  // *************************
  // If focused then show as many lines as necessary
  // Decendents should override PaintText to change the number of lines
  // as necessary
  PaintText(Column, ACanvas, HeaderType, RectArray, OwnerListview.PaintInfoColumn.CaptionLines);

  PaintSortGlyph(Column, ACanvas, HeaderType, RectArray);

  // Now lets paint Focus Rectangle
  // *************************
  PaintFocusRect(Column, ACanvas, HeaderType, RectArray);

  // Now Paint the Checkbox if applicable
  PaintCheckBox(Column, ACanvas, HeaderType, RectArray);

  PaintDropGlyph(Column, ACanvas, HeaderType, RectArray);

  PaintDropDownArrow(Column, ACanvas, HeaderType, RectArray);

  // Now give decentant a chance to paint anything
  PaintAfter(Column, ACanvas, HeaderType, RectArray);
end;

procedure TEasyViewColumn.PaintAfter(Column: TEasyColumn; ACanvas: TCanvas;
  HeaderType: TEasyHeaderType; RectArray: TEasyRectArrayObject);
begin

end;

procedure TEasyViewColumn.PaintBefore(Column: TEasyColumn; ACanvas: TCanvas;
  HeaderType: TEasyHeaderType; RectArray: TEasyRectArrayObject);
begin

end;

procedure TEasyViewColumn.PaintBkGnd(Column: TEasyColumn; ACanvas: TCanvas;
  HeaderType: TEasyHeaderType; RectArray: TEasyRectArrayObject);

      procedure SpiegelnHorizontal(Bitmap:TBitmap);
      type
        TRGBArray = array[0..0] OF TRGBQuad;
        pRGBArray = ^TRGBArray;
      var
        i, j, w :  Integer;
        RowIn :  pRGBArray;
        RowOut:  pRGBArray;
      begin
        w := Bitmap.Width*SizeOf(TRGBQuad);
        GetMem(RowIn, w);
        for j := 0 to Bitmap.Height-1 do
        begin
          Move(Bitmap.Scanline[j]^, RowIn^,w);
          RowOut := Bitmap.Scanline[j];
          for i := 0 to Bitmap.Width-1 do
            RowOut[i] := RowIn[Bitmap.Width-1-i];
        end;
        Bitmap.Assign(Bitmap);
        Freemem(RowIn);
      end;

var
  NormalButtonFlags, NormalButtonStyle, PressedButtonStyle, PressedButtonFlags,
  RaisedButtonStyle, RaisedButtonFlags: LongWord;
  R: TRect;
  Pt: TPoint;
  {$IFDEF USETHEMES}
  PartID,
  StateID: LongWord;
  Bits: TBitmap;
  {$ENDIF}
begin
  Pt.x := 0;
  Pt.y := 0;
  {$IFDEF USETHEMES}
  if OwnerListview.DrawWithThemes then
  begin
    PartID := HP_HEADERITEM;
    if Column.Clicking then
      StateID := HIS_PRESSED
    else
    if Column.HotTracking[Pt] then
      StateID := HIS_HOT
    else
      StateID := HIS_NORMAL;

    if HeaderType = ehtFooter then
    begin
      Bits := TBitmap.Create;
      try
        Bits.Width := RectWidth(Column.DisplayRect);
        Bits.Height := RectHeight(Column.DisplayRect);
        Bits.PixelFormat := pf32Bit;
        DrawThemeBackground(OwnerListview.Themes.HeaderTheme, Bits.Canvas.Handle, PartID, StateID, Rect(0, 0, Bits.Width, Bits.Height), nil);
        SpiegelnHorizontal(Bits);
        BitBlt(ACanvas.Handle, Column.DisplayRect.Left, Column.DisplayRect.Top, Bits.Width, Bits.Height, Bits.Canvas.Handle, 0, 0, SRCCOPY);
      finally
        Bits.Free
      end;
    end else
    begin
      R := Column.DisplayRect;
      // The divider is drawn by this as well and if shorted the divider is before the button
  //    if Column.HotTracking[Pt] and Column.DropDownButton.Visible and (RectWidth(RectArray.DropDownArrow) > 0) then
  //      R.Right := RectArray.DropDownArrow.Left;
      DrawThemeBackground(OwnerListview.Themes.HeaderTheme, ACanvas.Handle, PartID, StateID, R, nil);
    end;
    Exit;
  end;
  {$ENDIF USETHEMES}

{  if Column.Selected then
  begin
    if OwnerListview.Focused then
      ACanvas.Brush.Color := Column.Selection.Color
    else
      ACanvas.Brush.Color := Column.Selection.InactiveColor
  end else
  if Column.HotTrack and Column.Hilighted then
    ACanvas.Brush.Color := PaintInfo.HotTrack.Color
  else     }
    ACanvas.Brush.Color := Column.Color;

  ACanvas.FillRect(Column.DisplayRect);

  RaisedButtonStyle := 0;
  RaisedButtonFlags := 0;

  case Column.Style of
    ehbsThick:
      begin
        NormalButtonStyle := BDR_RAISEDINNER or BDR_RAISEDOUTER;
        NormalButtonFlags := BF_LEFT or BF_TOP or BF_BOTTOM or BF_RIGHT or BF_SOFT or BF_ADJUST;
        PressedButtonStyle := BDR_RAISEDINNER or BDR_RAISEDOUTER;
        PressedButtonFlags := NormalButtonFlags or BF_RIGHT or BF_FLAT or BF_ADJUST;
      end;
    ehbsFlat:
      begin
        NormalButtonStyle := BDR_RAISEDINNER;
        NormalButtonFlags := BF_LEFT or BF_TOP or BF_BOTTOM or BF_RIGHT or BF_ADJUST;
        PressedButtonStyle := BDR_SUNKENOUTER;
        PressedButtonFlags := BF_RECT or BF_ADJUST;
      end;
    else
      begin
        NormalButtonStyle := BDR_RAISEDINNER;
        NormalButtonFlags := BF_RECT or BF_SOFT or BF_ADJUST;
        PressedButtonStyle := BDR_SUNKENOUTER;
        PressedButtonFlags := BF_RECT or BF_ADJUST;
        RaisedButtonStyle := BDR_RAISEDINNER;
        RaisedButtonFlags := BF_LEFT or BF_TOP or BF_BOTTOM or BF_RIGHT or BF_ADJUST;
      end;
  end;

  R := Column.DisplayRect;
  if Column.Clicking then
    DrawEdge(ACanvas.Handle, R, PressedButtonStyle, PressedButtonFlags)
  else begin
    if (Column.Hilighted) and (Column.Style = ehbsPlate) then
      DrawEdge(ACanvas.Handle, R, RaisedButtonStyle, RaisedButtonFlags)
    else
      DrawEdge(ACanvas.Handle, R, NormalButtonStyle, NormalButtonFlags)
  end
end;

procedure TEasyViewColumn.PaintCheckBox(Column: TEasyColumn; ACanvas: TCanvas;
  HeaderType: TEasyHeaderType; RectArray: TEasyRectArrayObject);
begin
  if  not ((Column.CheckType = ectNone) or (Column.CheckType = ettNoneWithSpace)) then
    PaintCheckboxCore(Column.CheckType,       // TEasyCheckType
                      OwnerListview,        // TCustomEasyListview
                      ACanvas,              // TCanvas
                      RectArray.CheckRect,  // TRect
                      Column.Enabled,         // IsEnabled
                      Column.Checked,         // IsChecked
                      False,                  // IsHot
                      Column.CheckFlat,       // IsFlat
                      Column.CheckHovering,   // IsHovering
                      Column.CheckPending,    // IsPending
                      Column,
                      Column.Checksize);
end;

procedure TEasyViewColumn.PaintDropDownArrow(Column: TEasyColumn; ACanvas: TCanvas; HeaderType: TEasyHeaderType; RectArray: TEasyRectArrayObject);
var
  CtlState, CtlType: Longword;
  R: TRect;
begin
  if Column.DropDownButton.Visible and (RectWidth(RectArray.DropDownArrow) > 0) then
  begin
    R := RectArray.DropDownArrow;
    {$IFDEF USETHEMES}
    if OwnerListview.DrawWithThemes then
    begin
      { Draw the DropDown Button }
      CtlType := CP_DROPDOWNBUTTON;
      if Column.DropDownButton.Enabled then
      begin
        CtlState := CBXS_NORMAL;
          if cdbsDown in Column.DropDownButton.State then
          CtlState := CBXS_PRESSED
        else
        if cdbsHovering in Column.DropDownButton.State then
          CtlState := CBXS_HOT;
      end  else
        CtlState := CBXS_DISABLED;
      DrawThemeBackground(OwnerListview.Themes.ComboBoxTheme, ACanvas.Handle, CtlType, CtlState, R, nil)
    end else
    {$ENDIF USETHEMES}
    begin
      InflateRect(R, -1, -1);  // Looks better
      { Draw the DropDown Button }
      CtlType := DFC_SCROLL;
      CtlState := DFCS_SCROLLCOMBOBOX or DFCS_FLAT;

      if Column.DropDownButton.Enabled then
      begin
        if cdbsHovering in Column.DropDownButton.State then
          CtlState := CtlState or DFCS_HOT;
        if cdbsDown in Column.DropDownButton.State then
          CtlState := CtlState or DFCS_PUSHED;
      end else
        CtlState := CtlState or DFCS_INACTIVE;

      DrawFrameControl(ACanvas.Handle, R, CtlType, CtlState);
    end
  end
end;

procedure TEasyViewColumn.PaintDropGlyph(Column: TEasyColumn; ACanvas: TCanvas;
  HeaderType: TEasyHeaderType; RectArray: TEasyRectArrayObject);
var
  R: TRect;
begin
  if esosDropTarget in Column.State then
  begin
    ACanvas.Brush.Color := clblue;
    R := Column.DisplayRect;
    R.Left := R.Right - 2;
    ACanvas.FillRect(R); 
  end
end;

procedure TEasyViewColumn.PaintFocusRect(Column: TEasyColumn; ACanvas: TCanvas;
  HeaderType: TEasyHeaderType; RectArray: TEasyRectArrayObject);
begin

end;

procedure TEasyViewColumn.PaintImage(Column: TEasyColumn; ACanvas: TCanvas;
  HeaderType: TEasyHeaderType; RectArray: TEasyRectArrayObject;
  ImageSize: TEasyImageSize);
//
// Paints the Icon/Bitmap to the Column
//
var
  fStyle: Integer;
  Images: TCustomImageList;
  IsCustom: Boolean;
begin
  Column.ImageDrawIsCustom(Column, IsCustom);
  if IsCustom then
    Column.ImageDraw(Column, ACanvas, RectArray, AlphaBlender)
  else begin
    Images := GetImageList(Column);
    // Draw the image in the ImageList if available
    if Assigned(Column.OwnerHeader.Images) and (Column.ImageIndex > -1) then
    begin
      fStyle := ILD_TRANSPARENT;
      if Column.ImageOverlayIndex > -1 then
      begin
        ImageList_SetOverlayImage(Images.Handle, Column.ImageOverlayIndex, 1);
        fStyle := fStyle or INDEXTOOVERLAYMASK(1);
      end;

      // Get the "normalized" rectangle for the image
      RectArray.IconRect.Left := RectArray.IconRect.Left + (RectWidth(RectArray.IconRect) - Images.Width) div 2;
      RectArray.IconRect.Top := RectArray.IconRect.Top + (RectHeight(RectArray.IconRect) - Images.Height) div 2;
      ImageList_DrawEx(Images.Handle,
        Column.ImageIndex,
        ACanvas.Handle,
        RectArray.IconRect.Left,
        RectArray.IconRect.Top,
        0,
        0,
        CLR_NONE,
        CLR_NONE,
        fStyle);
    end
  end
end;

procedure TEasyViewColumn.PaintSortGlyph(Column: TEasyColumn; ACanvas: TCanvas;
  HeaderType: TEasyHeaderType; RectArray: TEasyRectArrayObject);
var
  Image: TBitmap;
begin
  if Column.SortDirection <> esdNone then
  begin
    if Column.SortDirection = esdAscending then
      Image := OwnerListview.GlobalImages.ColumnSortUp
    else
      Image := OwnerListview.GlobalImages.ColumnSortDown;

    ACanvas.Draw(RectArray.SortRect.Left, RectArray.SortRect.Top, Image);
  end
end;

procedure TEasyViewColumn.PaintText(Column: TEasyColumn; ACanvas: TCanvas;
  HeaderType: TEasyHeaderType; RectArray: TEasyRectArrayObject;
  LinesToDraw: Integer);
var
   DrawTextFlags: TCommonDrawTextWFlags;
begin
   if not IsRectEmpty(RectArray.TextRect) then
   begin
     ACanvas.Brush.Style := bsClear;

     DrawTextFlags := [dtEndEllipsis];

     if LinesToDraw = 1 then
       Include(DrawTextFlags, dtSingleLine);

     case Column.Alignment of
       taLeftJustify: Include(DrawTextFlags, dtLeft);
       taRightJustify: Include(DrawTextFlags, dtRight);
       taCenter:  Include(DrawTextFlags, dtCenter);
     end;

     // Vertical Alignment is accounted for in the Text Rects

     LoadTextFont(Column, ACanvas);
     if Column.Bold then
       ACanvas.Font.Style := ACanvas.Font.Style + [fsBold];

     OwnerListview.DoColumnPaintText(Column, ACanvas);
     DrawTextWEx(ACanvas.Handle, Column.Caption, RectArray.TextRects[0], DrawTextFlags, OwnerListview.PaintInfoColumn.CaptionLines);
end;

end;

procedure TEasyViewColumn.ReSizeRectArray(
  var RectArray: TEasyRectArrayObjectArray);
var
  OldLen, i: Integer;
begin
  if Length(RectArray) < OwnerListview.Header.Positions.Count then
  begin
    OldLen := Length(RectArray);
    SetLength(RectArray, OwnerListview.Header.Positions.Count);
    for i := OldLen to OwnerListview.Header.Positions.Count - 1 do
      FillChar(RectArray[i], SizeOf(RectArray[i]), #0);
  end else
  if Length(RectArray) > OwnerListview.Header.Positions.Count then
    SetLength(RectArray, OwnerListview.Header.Positions.Count);

  if Length(RectArray) = 0 then
  begin
    SetLength(RectArray, 1);
    FillChar(RectArray[0], SizeOf(RectArray[0]), #0);
  end
end;

{ TEasyGroupGrid }

constructor TEasyGridGroup.Create(AnOwner: TCustomEasyListview; AnOwnerGroup: TEasyGroup);
begin
  inherited Create(AnOwner);
  FOwnerGroup := AnOwnerGroup;
  FLayout := eglVert  // Default orientation
end;

destructor TEasyGridGroup.Destroy;
begin
  inherited;
end;

function TEasyGridGroup.AdjacentItem(Item: TEasyItem; Direction: TEasyAdjacentCellDir): TEasyItem;
var
  ColumnPos: Integer;
  TestPt: TPoint;
  LastItem: TEasyItem;
  RectArray: TEasyRectArrayObject;
  AdjacentGroup: TEasyGroup;
begin
  Result := nil;
  if Assigned(Item) then
  begin
    Assert(Item.Visible, 'Can not find TEasyGroups.AdjacentItem of an Invisible Item');
    case Direction of
      acdLeft:
        begin
          Result := Item;
          if Item.VisibleIndexInGroup > 0 then
            Result := OwnerGroup.VisibleItem[Item.VisibleIndexInGroup - 1]
          else begin
            AdjacentGroup := PrevVisibleGroupWithNItems(OwnerGroup, 0);
            if Assigned(AdjacentGroup) then
              Result := AdjacentGroup.VisibleItem[AdjacentGroup.VisibleCount - 1]
          end
        end;
      acdRight:
        begin
          Result := Item;
          if Item.VisibleIndexInGroup < OwnerGroup.VisibleCount - 1 then
            Result := OwnerGroup.VisibleItem[Item.VisibleIndexInGroup + 1]
          else begin
            AdjacentGroup := NextVisibleGroupWithNItems(OwnerGroup, 0);
            if Assigned(AdjacentGroup) then
              Result := AdjacentGroup.VisibleItem[0]
          end
        end;
      acdUp:
        begin
          // First see if we can stay in the same group
          if Item.VisibleIndexInGroup - ColumnCount >= 0 then
            Result := OwnerGroup.VisibleItems[Item.VisibleIndexInGroup - ColumnCount]
          else begin
            ColumnPos := Item.ColumnPos;
            while not Assigned(Result) and (ColumnPos > -1) do
            begin
              AdjacentGroup := PrevVisibleGroupWithNItems(OwnerGroup, ColumnPos);
              if Assigned(AdjacentGroup) then
                Result := LastItemInNColumn(AdjacentGroup, ColumnPos);
              Dec(ColumnPos)
            end
          end
        end;
      acdDown:
        begin
          // First see if we can stay in the same group
          if Item.VisibleIndexInGroup + ColumnCount < OwnerGroup.VisibleCount then
            Result := OwnerGroup.VisibleItems[Item.VisibleIndexInGroup + ColumnCount]
          else begin
            ColumnPos := Item.ColumnPos;
            while not Assigned(Result) and (ColumnPos > -1) do
            begin
              AdjacentGroup := NextVisibleGroupWithNItems(OwnerGroup, ColumnPos);
              if Assigned(AdjacentGroup) then
                Result := AdjacentGroup.VisibleItem[ColumnPos];
              Dec(ColumnPos)
            end
          end
        end;
      acdPageUp:
        begin
          TestPt := Item.DisplayRect.BottomRight;
          // The Right is actually the Left of the next object
          Dec(TestPt.X);
          Dec(TestPt.Y, OwnerListview.ClientHeight);

          // Make sure we don't run past the last row of items.
          AdjacentGroup := OwnerGroups.FirstVisibleGroup;
          if Assigned(AdjacentGroup) then
            if TestPt.Y < AdjacentGroup.DisplayRect.Top + OwnerGroup.Grid.StaticTopItemMargin then
              TestPt.Y := AdjacentGroup.BoundsRectBkGnd.Top + OwnerGroup.Grid.StaticTopItemMargin;

          Result := OwnerGroups.ItembyPoint(TestPt);
          if not Assigned(Result) then
          begin
            Result := SearchForHitRight(Item.ColumnPos, TestPt);
            while not Assigned(Result) and (TestPt.Y > OwnerGroups.ViewRect.Top) do
            begin
              Inc(TestPt.Y, CellSize.Height);
              Result := SearchForHitRight(Item.ColumnPos, TestPt);
            end
          end
        end;
      acdPageDown:
        begin
           TestPt := Item.DisplayRect.TopLeft;
           Inc(TestPt.Y, OwnerListview.ClientHeight);

           // Make sure we don't run past the last row of items.
           LastItem := OwnerGroups.LastItem;
           if Assigned(LastItem) then
           begin
             LastItem.View.ItemRectArray(LastItem, nil, nil, Item.Caption, RectArray);
             if TestPt.Y > RectArray.LabelRect.Bottom - 1 then
               TestPt.Y := RectArray.LabelRect.Bottom - 1;
           end;

           // Look for an item directly below the currently selected item
           Result := OwnerGroups.ItembyPoint(TestPt);

           // If not found then look to the right until we find an item
           if not Assigned(Result) then
           begin
             Result := SearchForHitRight(Item.ColumnPos, TestPt);
             while not Assigned(Result) and (TestPt.Y < OwnerGroups.ViewRect.Bottom) do
             begin
               Inc(TestPt.Y, CellSize.Height);
               Result := SearchForHitRight(Item.ColumnPos, TestPt);
             end
           end
        end
    end
  end
end;

procedure TEasyGridGroup.AutoSizeCells;
var
  Item: TEasyItem;
  RectArray: TEasyRectArrayObject;
  ACanvas: TCanvas;
  TextSize: TSize;
begin
  if CellSize.AutoSize then
  begin
    FindLongestCaption(nil, Item);
    if Assigned(Item) then
    begin
      ACanvas := TCanvas.Create;
      try
        ACanvas.Handle := GetDC(0);
        Item.View.ItemRectArray(Item, nil, ACanvas, '', RectArray);
        Item.View.LoadTextFont(Item, 0, ACanvas, True);
        TextSize := TextExtentW(Item.Caption, ACanvas);
        CellSize.AutoSizeWidth := RectWidth(RectArray.CheckRect) +
                           RectWidth(RectArray.IconRect) +
                           RectWidth(RectArray.StateRect) +
                           TextSize.cx + 18;

      finally
        ReleaseDC(0, ACanvas.Handle);
        ACanvas.Handle := 0;
        ACanvas.Free;
      end
    end
  end
end;

procedure TEasyGridGroup.FindInsertPosition(ViewportPoint: TPoint; var Group: TEasyGroup; var Index: Integer);
//
// The default implementation assumes a "grid" type arrangement of the grid
//
var
  Item: TEasyItem;
begin
  Index := -1;
  Group := OwnerGroups.GroupByPoint(ViewportPoint);
  if Assigned(Group) then
  begin
    Item := Group.ItemByPoint(ViewportPoint);
    if Assigned(Item) then
    begin
      if ViewportPoint.X < RectWidth( Item.DisplayRect) div 2 then
        Index := Item.Index
      else
        Index := Item.Index + 1
    end
  end
end;

function TEasyGridGroup.GetMaxColumns(Group: TEasyGroup; WindowWidth: Integer): Integer;
begin
  Result := (WindowWidth -
            (Group.MarginLeft.RuntimeSize + Group.MarginRight.RuntimeSize)) div CellSize.Width;
  if FColumnCount = 0 then
    Inc(FColumnCount);
end;

function TEasyGridGroup.GetOwnerGroups: TEasyGroups;
begin
  Result := OwnerGroup.OwnerGroups
end;

function TEasyGridGroup.LastItemInNColumn(Group: TEasyGroup; N: Integer): TEasyItem;
//
// Finds the last item in the group in the specified column.  It assumes
// that the item does exist (N >= Group.ItemCount)
//
var
  RowPos: Integer;
begin
  RowPos := Group.VisibleCount div Group.Grid.ColumnCount;
  if RowPos * ColumnCount + N >= Group.VisibleCount then
    Dec(RowPos);
  Result := Group.Items[RowPos * ColumnCount + N]
end;

function TEasyGridGroup.NextVisibleGroupWithNItems(StartGroup: TEasyGroup; N: Integer): TEasyGroup;
  //
  //  Returns the first next group encountered when at least N items in it
  //
var
  i: Integer;
begin
  Result := nil;
  i := StartGroup.VisibleIndex + 1;
  while not Assigned(Result) and (i < OwnerGroups.VisibleCount) do
  begin
    if OwnerGroups.VisibleGroup[i].VisibleCount > N then
      Result := OwnerGroups.VisibleGroup[i];
    Inc(i)
  end
end;

function TEasyGridGroup.PrevVisibleGroupWithNItems(StartGroup: TEasyGroup; N: Integer): TEasyGroup;
//
//  Returns the first prev group encountered when at least N items in it
//
var
  i: Integer;
begin
  Result := nil;
  i := StartGroup.VisibleIndex - 1;
  while not Assigned(Result) and (i >= 0) do
  begin
    if OwnerGroups.VisibleGroup[i].VisibleCount > N then
      Result := OwnerGroups.VisibleGroup[i];
    Dec(i)
  end

end;

function TEasyGridGroup.SearchForHitRight(ColumnPos: Integer; Pt: TPoint): TEasyItem;
begin
  Result := nil;
  while not Assigned(Result) and (ColumnPos >= 0) do
  begin
    Result := OwnerGroups.ItembyPoint(Pt);
    Dec(Pt.X, CellSize.Width);
    Dec(ColumnPos)
  end;
end;

function TEasyGridGroup.StaticTopItemMargin: Integer;
// Distance added between the bottom of the top margin and the first
// item, and the distance between the bottom of the last item and the
// top of the bottom margin
begin
   Result := 0
end;

function TEasyGridGroup.StaticTopMargin: Integer;
// Distance added from top of control to the top of the first group
begin
  Result := 0;
end;

procedure TEasyGridGroup.FindLongestCaption(Column: TEasyColumn; var Item: TEasyItem);
var
  TempItem: TEasyItem;
  ColumnIndex, i: Integer;
begin
  Item := nil;

  if Assigned(Column) then
    ColumnIndex := Column.Index
  else
    ColumnIndex := 0;


  // can't use enumerators here as the list is not reindexed yet
  for i := 0 to OwnerGroup.Items.Count - 1 do
  begin
    TempItem := TEasyItem( OwnerGroup.Items[i]);
    if TempItem.Visible then
    begin
      if not Assigned(Item) then
        Item := TempItem
      else begin
        if Length(TempItem.Captions[ColumnIndex]) > Length(Item.Captions[ColumnIndex]) then
          Item := TempItem;
      end
    end
  end
end;

procedure TEasyGridGroup.Rebuild(PrevGroup: TEasyGroup; var NextVisibleItemIndex: Integer);
var
  TopLeft: TPoint;
  i, LeftEdge, BottomEdge, ItemGroupVisibleIndex: Integer;
  RectArray: TEasyRectArrayObject;
  WndWidth, VisibleCount, ItemMargin, GroupMargin, MarginTop, MarginLeft, MarginRight: Integer;
  FocusedItem, Item: TEasyItem;
  LocalCellSize: TEasyCellSize;
begin
  AutoSizeCells;
  BottomEdge := 0;
  GroupMargin := StaticTopMargin;
  ItemMargin := StaticTopItemMargin;
  LocalCellSize := CellSize;
  MarginLeft := OwnerGroup.MarginLeft.RuntimeSize;
  MarginTop := OwnerGroup.MarginTop.RuntimeSize;
  MarginRight := OwnerGroup.MarginRight.RuntimeSize;

  // First calculate the Width of the group box.  The height will be dynamically
  // calculated during the enumeration of the items
  WndWidth := OwnerListview.ClientWidth - 1;
  if not OwnerListview.Scrollbars.VertBarVisible then
    WndWidth := WndWidth - GetSystemMetrics(SM_CYVSCROLL);
  if WndWidth < MarginRight + MarginLeft + LocalCellSize.Width then
    WndWidth := MarginRight + MarginLeft + LocalCellSize.Width;
  OwnerGroup.FDisplayRect := Rect(0, StaticTopMargin, WndWidth,  GroupMargin);

  VisibleCount := 0;
  FocusedItem := nil;

  // Prepare the VisibleList for the worse case, all are visible
  OwnerGroup.VisibleItems.Clear;
  OwnerGroup.VisibleItems.Capacity := OwnerGroup.Items.Count;
  ItemGroupVisibleIndex := 0;

  if OwnerGroup.Visible then
  begin
    if OwnerGroup.Expanded and (OwnerGroup.Items.Count > 0)  then
    begin
      // First calculate the number of columns we can accommodate
      FColumnCount := GetMaxColumns(OwnerGroup, WndWidth);

      TopLeft := Point(0, 0);
      LeftEdge := ColumnCount * LocalCellSize.Width;

      for i := 0 to OwnerGroup.Items.Count - 1 do
      begin
        if TopLeft.X + LocalCellSize.Width > LeftEdge then
        begin
          TopLeft.X := 0;
          Inc(TopLeft.Y, LocalCellSize.Height);
        end;

        Item := OwnerGroup.Items[i];

        if Item.Visible then
        begin
          Item.FVisibleIndexInGroup := ItemGroupVisibleIndex;
          Item.FVisibleIndex := NextVisibleItemIndex;
          Inc(NextVisibleItemIndex);
          Inc(ItemGroupVisibleIndex);
          OwnerGroup.VisibleItems.Add(Item);
          BottomEdge := TopLeft.Y + LocalCellSize.Height;
          Item.FDisplayRect := Rect(TopLeft.X, TopLeft.Y, TopLeft.X + LocalCellSize.Width, BottomEdge);
          Inc(TopLeft.X, LocalCellSize.Width);
          Inc(VisibleCount);
        end else
        begin
          Item.FDisplayRect := Rect(TopLeft.X, TopLeft.Y, TopLeft.X, TopLeft.Y + LocalCellSize.Height);
          Item.FVisibleIndexInGroup := -1;
        end;

        OffsetRect(Item.FDisplayRect, MarginLeft, MarginTop + GroupMargin + ItemMargin);

        if Item.Focused then
          FocusedItem := Item;

        if Assigned(PrevGroup) then
          OffsetRect(Item.FDisplayRect, 0, PrevGroup.DisplayRect.Bottom);
      end
    end else
    begin
      // Collapsed group can't have focused item, move it to the next Visible item
      FocusedItem := OwnerListview.Selection.FocusedItem;
      if Assigned(FocusedItem) then
        if FocusedItem.OwnerGroup = OwnerGroup then
          OwnerListview.Selection.FocusedItem := Self.OwnerGroups.NextVisibleItem(FocusedItem)
    end;

    if ColumnCount > 0 then
      FRowCount := OwnerGroup.Items.Count div ColumnCount
    else
      FRowCount := 0;
    if ColumnCount > 0 then
      if OwnerGroup.Items.Count mod ColumnCount > 0 then
        Inc(FRowCount);

    if OwnerGroup.Expanded then
      Inc(OwnerGroup.FDisplayRect.Bottom, OwnerGroup.MarginBottom.RuntimeSize + OwnerGroup.MarginTop.RuntimeSize + BottomEdge + ItemMargin * 2)
    else
      Inc(OwnerGroup.FDisplayRect.Bottom, OwnerGroup.MarginBottom.RuntimeSize + OwnerGroup.MarginTop.RuntimeSize + BottomEdge)
  end;

  // Take care of the items in groups that are not visible or expanded
  // If the Group's is not visible then its items can not be selected.
  // Current it is allowed to have selected items in collapsed groups
  if not OwnerGroup.Visible then
  begin
    i := 0;
    while (i < OwnerGroup.Items.Count) and (OwnerListview.Selection.Count > 0) do
    begin
      OwnerGroup.Items[i].Selected := False;
      Inc(i)
    end;
    if Assigned(OwnerListview.Selection.FocusedItem) then
      if OwnerListview.Selection.FocusedItem.OwnerGroup = OwnerGroup then
        OwnerListview.Selection.FocusedItem := nil;
  end;

  // Special case if all the items are not visible
  if VisibleCount = 0 then
  begin
    FColumnCount := 0;
    FRowCount := 0;
  end;

  if Assigned(PrevGroup) then
    OffsetRect(OwnerGroup.FDisplayRect, 0, PrevGroup.DisplayRect.Bottom);


  // Always resize the group if on the bottom the the control
  if Assigned(FocusedItem) and (OwnerListview.Selection.ResizeGroupOnFocus or (OwnerGroup.Index + 1  = OwnerGroups.Count))  then
  begin
    FocusedItem.ItemRectArray(nil, OwnerListview.ScratchCanvas, RectArray);
    if RectArray.FullTextRect.Bottom > OwnerGroup.FDisplayRect.Bottom - OwnerGroup.MarginBottom.RuntimeSize then
      Inc(OwnerGroup.FDisplayRect.Bottom, RectArray.FullTextRect.Bottom - FocusedItem.DisplayRect.Bottom);
  end
end;

constructor TEasyGridReportGroup.Create(AnOwner: TCustomEasyListview;
  AnOwnerGroup: TEasyGroup);
begin
  inherited Create(AnOwner, AnOwnerGroup);
  FLayout := eglGrid
end;

function TEasyGridReportGroup.AdjacentItem(Item: TEasyItem;
  Direction: TEasyAdjacentCellDir): TEasyItem;
var
  TestPt: TPoint;
begin
  Result := nil;
  if Assigned(Item) then
  begin
    case Direction of
      acdUp: Result := OwnerGroups.PrevVisibleItem(Item);
      acdDown: Result := OwnerGroups.NextVisibleItem(Item);
      acdPageUp: // WL, 05/01/05
        begin
          // Look for first visible item at top.
          // The Makevisible offsets a bit to deal with the inplace editor
          TestPt := Point(0, OwnerListview.ClientInViewportCoords.Top + V_STRINGEDITORMARGIN div 2);
          repeat
            Result := OwnerGroups.ItembyPoint(TestPt);
            Inc(TestPt.Y, CellSize.Height);
          until (Result <> nil) or (Result = Item) or (TestPt.Y >= OwnerListview.ClientInViewportCoords.Bottom);

          // If first visible item is the parameter Item already we must look one
          // page further up. Find the furthest item which still allows Result
          // and item to be on one page.
          if (Result <> nil) and (Result = Item) then
          begin
            TestPt := Point(0, Item.DisplayRect.Bottom - RectHeight(OwnerListview.ClientInViewportCoords) - 1);
            repeat
              Result := OwnerGroups.ItembyPoint(TestPt);
              Inc(TestPt.Y, CellSize.Height);
            until Result <> nil; // loop terminates at Item at the latest (which must be below)
            if Result = Item then
              Result := nil; // no adjacent page-down item found, Item was already the first one
          end;
        end;
      acdPageDown: // WL, 05/01/05
        begin
          // Look for last visible item at bottom.
          // The Makevisible offsets a bit to deal with the inplace editor
          TestPt := Point(0, OwnerListview.ClientInViewportCoords.Bottom - 1 - V_STRINGEDITORMARGIN div 2);
          repeat
            Result := OwnerGroups.ItembyPoint(TestPt);
            Dec(TestPt.Y, CellSize.Height);
          until (Result <> nil) or (Result = Item) or (TestPt.Y < OwnerListview.ClientInViewportCoords.Top);

          // If last visible item is the parameter Item already we must look one
          // page further down. Find the furthest item which still allows Result
          // and item to be on one page.
          if (Result <> nil) and (Result = Item) then
          begin
            TestPt := Point(0, Item.DisplayRect.Top + RectHeight(OwnerListview.ClientInViewportCoords) - 1);
            repeat
              Result := OwnerGroups.ItembyPoint(TestPt);
              Dec(TestPt.Y, CellSize.Height);
            until Result <> nil; // loop terminates at Item at the latest (which must be above)
            if Result = Item then
              Result := nil; // no adjacent page-down item found, Item was already the last one
          end;
        end
    else
      Result := inherited AdjacentItem(Item, Direction);
    end
  end
end;

procedure TEasyGridReportGroup.FindInsertPosition(ViewportPoint: TPoint; var Group: TEasyGroup; var Index: Integer);
begin

end;

function TEasyGridReportGroup.GetCellSize: TEasyCellSize;
begin
  Result := OwnerListview.CellSizes.Report
end;

{ TEasyReportGroupGrid }


procedure TEasyGridReportGroup.Rebuild(PrevGroup: TEasyGroup; var NextVisibleItemIndex: Integer);

var
  i, Top, Bottom, Width, Left, Offset, TopMargin,
  BottomMargin, VisibleCount: Integer;
  Item: TEasyItem;
begin
  AutoSizeCells;

  if CellSize.AutoSize and (OwnerListview.Header.Columns.Count > 0)  then
    OwnerListview.Header.Columns[0].Width := CellSize.Width;

  if Assigned(PrevGroup) then
    Offset := PrevGroup.DisplayRect.Bottom
  else
    Offset := V_STRINGEDITORMARGIN;

  OwnerGroup.FDisplayRect := Rect(0, Offset, OwnerListview.Header.ViewWidth, Offset);
  // Prepare the VisibleList for the worse case, all are visible
  OwnerGroup.VisibleItems.Clear;
  OwnerGroup.VisibleItems.Capacity := OwnerGroup.Items.Count;

  Left := OwnerGroup.MarginLeft.RuntimeSize;
  if OwnerListview.Header.LastColumnByPosition <> nil then
  begin
    Width := OwnerListview.Header.LastColumnByPosition.DisplayRect.Right;
    Width := Width - OwnerGroup.MarginRight.RuntimeSize
  end else
    Width := 0;

  TopMargin := OwnerGroup.MarginTop.RuntimeSize;
  BottomMargin := OwnerGroup.MarginBottom.RuntimeSize;
  if OwnerGroup.Visible then
  begin
    if OwnerGroup.Expanded and (OwnerGroup.Items.Count > 0) then
    begin
      VisibleCount := 0;
      Top := Offset + TopMargin;
      Bottom := Offset + CellSize.Height + TopMargin;
      for i := 0 to OwnerGroup.Items.Count - 1 do
      begin
        Item := OwnerGroup.Items.List.List[i]; // Direct Access for Speed
        if Item.Visible then
        begin
          Item.FVisibleIndex := NextVisibleItemIndex;
          Item.FVisibleIndexInGroup := VisibleCount;
          Item.FDisplayRect := Rect(Left, Top, Width, Bottom);
          OwnerGroup.VisibleItems.Add(Item);
          Inc(Top, CellSize.Height);
          Inc(Bottom, CellSize.Height);
          Inc(VisibleCount);
          Inc(NextVisibleItemIndex);
        end else
          Item.FDisplayRect := Rect(Left, Top, Width, Top);
        OwnerGroup.FDisplayRect.Bottom := Item.FDisplayRect.Bottom + BottomMargin;
        if OwnerGroups.LastGroup = OwnerGroup then
          OwnerGroup.FDisplayRect.Bottom := Item.FDisplayRect.Bottom + V_STRINGEDITORMARGIN * 2;
      end
    end else
      OwnerGroup.FDisplayRect := Rect(0, Offset, OwnerListview.Header.ViewWidth, Offset + TopMargin + BottomMargin);
  end;
  // Column Count does not relate to Report view columns.  It is a more primitive
  // and the Report columns are within the Grid Column
  FColumnCount := 1;
end;

procedure TEasyGridReportGroup.SetCellSize(Value: TEasyCellSize);
begin
  OwnerListview.CellSizes.Report.Assign(Value)
end;

constructor TEasyGridListGroup.Create(AnOwner: TCustomEasyListview; AnOwnerGroup: TEasyGroup);
begin
  inherited Create(AnOwner, AnOwnerGroup);
  FLayout := eglHorz
end;

function TEasyGridListGroup.AdjacentItem(Item: TEasyItem;
  Direction: TEasyAdjacentCellDir): TEasyItem;
  function NextVisibleGroupWithNItems(StartGroup: TEasyGroup; N: Integer): TEasyGroup;
  //
  //  Returns the first next group encountered when at least N items in it
  //
  var
    i: Integer;
  begin
    Result := nil;
    i := StartGroup.VisibleIndex + 1;
    while not Assigned(Result) and (i < OwnerGroups.VisibleCount) do
    begin
      if OwnerGroups.VisibleGroup[i].VisibleCount > N then
        Result := OwnerGroups.VisibleGroup[i];
      Inc(i)
    end
  end;

  function PrevVisibleGroupWithNItems(StartGroup: TEasyGroup; N: Integer): TEasyGroup;
  //
  //  Returns the first prev group encountered when at least N items in it
  //
  var
    i: Integer;
  begin
    Result := nil;
    i := StartGroup.VisibleIndex - 1;
    while not Assigned(Result) and (i >= 0) do
    begin
      if OwnerGroups.VisibleGroup[i].VisibleCount > N then
        Result := OwnerGroups.VisibleGroup[i];
      Dec(i)
    end
  end;

  function LastItemInNRow(Group: TEasyGroup; N: Integer): TEasyItem;
  //
  // Finds the last item in the group in the specified column.  It assumes
  // that the item does exist (N >= Group.ItemCount)
  //
  var
    ColumnPos: Integer;
  begin
    ColumnPos := Group.VisibleCount div Group.Grid.RowCount;
    if ColumnPos * RowCount + N >= Group.VisibleCount then
      Dec(ColumnPos);
    Result := Group.Items[ColumnPos * RowCount + N]
  end;

  function SearchForHitRight(ColumnPos: Integer; Pt: TPoint): TEasyItem;
  begin
    Result := nil;
    while not Assigned(Result) and (ColumnPos >= 0) do
    begin
      Result := OwnerGroups.ItembyPoint(Pt);
      Dec(Pt.X, CellSize.Width);
      Dec(ColumnPos)
    end;
  end;

var
  RowPos, ItemIndex: Integer;
  AdjacentGroup: TEasyGroup;
begin
  Result := nil;
  if Assigned(Item) then
  begin
    Assert(Item.Visible, 'Can not find TEasyGroups.AdjacentItem of an Invisible Item');
    case Direction of
      acdUp:
        begin
          Result := Item;
          if Item.VisibleIndexInGroup > 0 then
            Result := OwnerGroup.VisibleItem[Item.VisibleIndexInGroup - 1]
          else begin
            AdjacentGroup := PrevVisibleGroupWithNItems(OwnerGroup, 0);
            if Assigned(AdjacentGroup) then
              Result := AdjacentGroup.VisibleItem[AdjacentGroup.VisibleCount - 1]
          end
        end;
      acdDown:
        begin
          Result := Item;
          if Item.VisibleIndexInGroup < OwnerGroup.VisibleCount - 1 then
            Result := OwnerGroup.VisibleItem[Item.VisibleIndexInGroup + 1]
          else begin
            AdjacentGroup := NextVisibleGroupWithNItems(OwnerGroup, 0);
            if Assigned(AdjacentGroup) then
              Result := AdjacentGroup.VisibleItem[0]
          end
        end;
      acdLeft:
        begin
          // First see if we can stay in the same group
          if Item.VisibleIndexInGroup - RowCount >= 0 then
            Result := OwnerGroup.VisibleItems[Item.VisibleIndexInGroup - RowCount]
          else begin
            RowPos := Item.RowPos;
            while not Assigned(Result) and (RowPos > -1) do
            begin
              AdjacentGroup := PrevVisibleGroupWithNItems(OwnerGroup, RowPos);
              if Assigned(AdjacentGroup) then
                Result := LastItemInNRow(AdjacentGroup, RowPos);
              Dec(RowPos)
            end
          end
        end;
      acdRight:
        begin
          // First see if we can stay in the same group
          if Item.VisibleIndexInGroup + RowCount < OwnerGroup.VisibleCount then
            Result := OwnerGroup.VisibleItems[Item.VisibleIndexInGroup + RowCount]
          else begin
            RowPos := Item.RowPos;
            while not Assigned(Result) and (RowPos > -1) do
            begin
              AdjacentGroup := NextVisibleGroupWithNItems(OwnerGroup, RowPos);
              if Assigned(AdjacentGroup) then
                Result := AdjacentGroup.VisibleItem[RowPos];
              Dec(RowPos)
            end
          end
        end;
      acdPageUp:
        begin
          ItemIndex := Item.VisibleIndexInGroup;
          while (ItemIndex > 0) and (ItemIndex mod RowCount <> 0) do
            Dec(ItemIndex);
          Result := OwnerGroup.Items[ItemIndex]
        end;
      acdPageDown:
        begin
          ItemIndex := Item.VisibleIndexInGroup;
          while (ItemIndex < Item.OwnerGroup.Items.Count - 1) and (ItemIndex mod RowCount <> RowCount - 1) do
            Inc(ItemIndex);
          Result := OwnerGroup.Items[ItemIndex]
        end
    end
  end
end;

procedure TEasyGridListGroup.FindInsertPosition(ViewportPoint: TPoint; var Group: TEasyGroup; var Index: Integer);
begin

end;

function TEasyGridListGroup.GetCellSize: TEasyCellSize;
begin
  Result := OwnerListview.CellSizes.List
end;

{ TEasyListGroupGrid }

procedure TEasyGridListGroup.Rebuild(PrevGroup: TEasyGroup;
  var NextVisibleItemIndex: Integer);
var
  ScrollSize, ClientHeight: Integer;
  TopLeft: TPoint;
  i, Offset, VisibleCount, Height: Integer;
  RectArray: TEasyRectArrayObject;
  TextSize: TSize;
  R: TRect;
  Item: TEasyItem;
begin
  AutoSizeCells;

  VisibleCount := 0;
  // Always assume a scroll bar
  ScrollSize := GetSystemMetrics(SM_CXHSCROLL);
  ClientHeight := OwnerListview.ClientHeight;

  if Assigned(PrevGroup) then
    Offset := PrevGroup.DisplayRect.Right
  else
    Offset := 0;

  Height := ClientHeight - ScrollSize - OwnerListview.Header.RuntimeHeight;

  // May have to show a vertical scrollbar if the entire thing won't fit in the window
  if Height < OwnerGroup.MarginBottom.RuntimeSize + OwnerGroup.MarginTop.RuntimeSize + CellSize.Height then
    Height := OwnerGroup.MarginBottom.RuntimeSize + OwnerGroup.MarginTop.RuntimeSize + CellSize.Height;

  OwnerGroup.FDisplayRect := Rect(Offset, 0, Offset, Height);
  // Prepare the VisibleList for the worse case, all are visible
  OwnerGroup.VisibleItems.Clear;
  OwnerGroup.VisibleItems.Capacity := OwnerGroup.Items.Count;

  if OwnerGroup.Visible then
  begin
    if OwnerGroup.Expanded and (OwnerGroup.Items.Count > 0) then
    begin
      // First calculate the number of rows we can accommodate
      FRowCount := (Height - 1 - (OwnerGroup.MarginBottom.RuntimeSize + OwnerGroup.MarginTop.RuntimeSize)) div CellSize.Height;
      if FRowCount = 0 then
        Inc(FRowCount);

      FColumnCount := 1;

      TopLeft := Point(Offset + OwnerGroup.MarginLeft.RuntimeSize, OwnerGroup.MarginTop.RuntimeSize);

      for i := 0 to OwnerGroup.Items.Count - 1 do
      begin
        Item := OwnerGroup.Items[i];
        if Item.Visible then
        begin
          Item.FVisibleIndex := NextVisibleItemIndex;
          Item.FVisibleIndexInGroup := VisibleCount;
          OwnerGroup.VisibleItems.Add(Item);
          R := Rect(TopLeft.X, TopLeft.Y, TopLeft.X + CellSize.Width, TopLeft.Y + CellSize.Height);
          Inc(TopLeft.Y, CellSize.Height);
          Inc(VisibleCount)
        end else
          R := Rect(TopLeft.X, TopLeft.Y, TopLeft.X, TopLeft.Y + CellSize.Height);

        if R.Bottom > Height - OwnerGroup.MarginBottom.RuntimeSize then
        begin
          OffsetRect(R, CellSize.Width, -(TopLeft.Y - CellSize.Height - OwnerGroup.MarginTop.RuntimeSize));
          TopLeft.Y := OwnerGroup.MarginTop.RuntimeSize + CellSize.Height;
          Inc(TopLeft.X, CellSize.Width);
          Inc(FColumnCount);
        end;

        OwnerGroup.Items[i].FDisplayRect := R
      end;
      OwnerGroup.FDisplayRect.Right := TopLeft.X + CellSize.Width + OwnerGroup.MarginRight.RuntimeSize;

    end;
    // Special case if all the items are not visible
    if VisibleCount = 0 then
    begin
      TextSize := TextExtentW(OwnerGroup.Caption, OwnerListview.GroupFont);
      OwnerGroup.View.GroupRectArray(OwnerGroup, egmeTop, OwnerGroup.BoundsRectTopMargin, RectArray);
      FColumnCount := 0;
      FRowCount := 0;
      OwnerGroup.FDisplayRect.Right := OwnerGroup.MarginLeft.RuntimeSize +
        OwnerGroup.MarginRight.RuntimeSize + RectArray.IconRect.Right +
        OwnerGroup.CaptionIndent + TextSize.cx;
    end;
  end;
end;

procedure TEasyGridListGroup.SetCellSize(Value: TEasyCellSize);
begin
  OwnerListview.CellSizes.List.Assign(Value)
end;

{ TEasyFooterMargin }

constructor TCustomEasyFooterMargin.Create(AnOwner: TCustomEasyListview);
begin
  inherited;
  FImageIndex := -1;
  FImageOverlayIndex := -1;
  FSize := 30;
end;

destructor TCustomEasyFooterMargin.Destroy;
begin
  FreeAndNil(FPaintInfo);
  inherited;
end;

function TCustomEasyFooterMargin.GetAlignment: TAlignment;
begin
  Result := PaintInfo.Alignment
end;

function TCustomEasyFooterMargin.GetCaptionIndent: Integer;
begin
  Result := PaintInfo.CaptionIndent
end;

function TCustomEasyFooterMargin.GetCaptionLines: Integer;
begin
  Result := PaintInfo.CaptionLines
end;

function TCustomEasyFooterMargin.GetImageIndent: Integer;
begin
  Result := PaintInfo.ImageIndent
end;

function TCustomEasyFooterMargin.GetPaintInfo: TEasyPaintInfoBaseGroup;
begin
  if not Assigned(FPaintInfo) then
    Result := OwnerListview.PaintInfoGroup.MarginBottom.FPaintInfo
  else
    Result := FPaintInfo
end;

function TCustomEasyFooterMargin.GetVAlignment: TCommonVAlignment;
begin
  Result := PaintInfo.VAlignment
end;

procedure TCustomEasyFooterMargin.Assign(Source: TPersistent);
var
  Temp: TCustomEasyFooterMargin;
begin
  inherited Assign(Source);
  if Source is TCustomEasyFooterMargin then
  begin
    Temp := TCustomEasyFooterMargin(Source);
    FCaption := Temp.Caption;
  end
end;

procedure TCustomEasyFooterMargin.SetAlignment(Value: TAlignment);
begin
  if Alignment <> Value then
  begin
    PaintInfo.Alignment := Value;
    OwnerListview.Groups.Rebuild
  end;
end;

procedure TCustomEasyFooterMargin.SetCaption(Value: WideString);
begin
  if FCaption <> Value then
  begin
    FCaption := Value;
    OwnerListview.Groups.Rebuild
  end;
end;

procedure TCustomEasyFooterMargin.SetCaptionIndent(Value: Integer);
begin
  if CaptionIndent <> Value then
  begin
    PaintInfo.CaptionIndent := Value;
    OwnerListview.SafeInvalidateRect(nil, False)
  end
end;

procedure TCustomEasyFooterMargin.SetCaptionLines(Value: Integer);
begin
  if CaptionLines <> Value then
  begin
    PaintInfo.CaptionLines := Value;
    OwnerListview.SafeInvalidateRect(nil, False)
  end
end;

procedure TCustomEasyFooterMargin.SetImageIndent(Value: Integer);
begin
  if ImageIndent <> Value then
  begin
    PaintInfo.ImageIndent := Value;
    OwnerListview.SafeInvalidateRect(nil, False)
  end
end;

procedure TCustomEasyFooterMargin.SetImageIndex(const Value: TCommonImageIndexInteger);
begin
  if FImageIndex <> Value then
  begin
    FImageIndex := Value;
    OwnerListview.SafeInvalidateRect(nil, False)
  end
end;

procedure TCustomEasyFooterMargin.SetImageOveralyIndex(const Value: TCommonImageIndexInteger);
begin
  if FImageOverlayIndex <> Value then
  begin
    FImageOverlayIndex := Value;
    OwnerListview.SafeInvalidateRect(nil, False)
  end
end;

procedure TCustomEasyFooterMargin.SetPaintInfo(const Value: TEasyPaintInfoBaseGroup);
begin
  if not Assigned(FPaintInfo) then
    OwnerListview.PaintInfoGroup.Assign(Value)
  else
    FPaintInfo.Assign(Value)
end;

procedure TCustomEasyFooterMargin.SetVAlignment(
  Value: TCommonVAlignment);
begin
  if VAlignment <> Value then
  begin
    PaintInfo.VAlignment := Value;
    OwnerListview.SafeInvalidateRect(nil, False)
  end;
end;

{ TEasyBasicItemPaintInfo }

constructor TEasyPaintInfoBasic.Create(AnOwner: TCustomEasyListview);
begin
  inherited Create(AnOwner);
  FImageIndent := 2;
  FCaptionIndent := 4;
  FCaptionLines := 1;
  FBorder := 4;
  FBorderColor := clHighlight;
  FCheckIndent := 2;
  FChecksize := 12;
  FVAlignment := cvaCenter;
  FShowBorder := True;
end;

procedure TEasyPaintInfoBasic.Assign(Source: TPersistent);
var
  Temp: TEasyPaintInfoBasic;
begin
  if Source is TEasyPaintInfoBasic then
  begin
    Temp := TEasyPaintInfoBasic(Source);
    FAlignment := Temp.Alignment;
    FBorder := Temp.Border;
    FBorderColor := Temp.BorderColor;
    FCaptionIndent := Temp.CaptionIndent;
    FCaptionLines := Temp.CaptionLines;
    FCheckFlat := Temp.CheckFlat;
    FCheckIndent := Temp.CheckIndent;
    FChecksize := Temp.Checksize;
    FCheckType := Temp.CheckType;
    FImageIndent := Temp.ImageIndent;
    FVAlignment := Temp.VAlignment;
  end
end;

procedure TEasyPaintInfoBasic.Invalidate(ImmediateUpdate: Boolean);
begin
  OwnerListview.SafeInvalidateRect(nil, ImmediateUpdate)
end;

procedure TEasyPaintInfoBasic.SetAlignment(Value: TAlignment);
begin
  if Value <> FAlignment then
  begin
    FAlignment := Value;
    Invalidate(False)
  end
end;

procedure TEasyPaintInfoBasic.SetBorder(Value: Integer);
begin
  if Value <> FBorder then
  begin
    FBorder := Value;
    Invalidate(False)
  end
end;

procedure TEasyPaintInfoBasic.SetBorderColor(Value: TColor);
begin
  if Value <> FBorderColor then
  begin
    FBorderColor := Value;
    Invalidate(False)
  end
end;

procedure TEasyPaintInfoBasic.SetCaptionIndent(Value: Integer);
begin
  if Value <> FCaptionIndent then
  begin
    FCaptionIndent := Value;
    Invalidate(False)
  end
end;

procedure TEasyPaintInfoBasic.SetCaptionLines(Value: Integer);
begin
  if Value <> FCaptionLines then
  begin
    FCaptionLines := Value;
    Invalidate(False)
  end
end;

procedure TEasyPaintInfoBasic.SetCheckFlat(Value: Boolean);
begin
  if Value <> FCheckFlat then
  begin
    FCheckFlat := Value;
    Invalidate(False)
  end
end;

procedure TEasyPaintInfoBasic.SetCheckIndent(Value: Integer);
begin
  if Value <> FCheckIndent then
  begin
    FCheckIndent := Value;
    Invalidate(False)
  end
end;

procedure TEasyPaintInfoBasic.SetChecksize(Value: Integer);
begin
  if Value <> FChecksize then
  begin
    FChecksize := Value;
    Invalidate(False)
  end
end;

procedure TEasyPaintInfoBasic.SetCheckType(Value: TEasyCheckType);
begin
   if Value <> FCheckType then
  begin
    FCheckType := Value;
    Invalidate(False)
  end
end;

procedure TEasyPaintInfoBasic.SetImageIndent(Value: Integer);
begin
  if Value <> FImageIndent then
  begin
    FImageIndent := Value;
    Invalidate(False)
  end
end;

procedure TEasyPaintInfoBasic.SetShowBorder(const Value: Boolean);
begin
  FShowBorder := Value;
end;

procedure TEasyPaintInfoBasic.SetVAlignment(Value: TCommonVAlignment);
begin
  if Value <> FVAlignment then
  begin
    FVAlignment := Value;
    Invalidate(False)
  end
end;

{ TEasyBasicGroupPaintInfo }
constructor TEasyPaintInfoBaseGroup.Create(AnOwner: TCustomEasyListview);
begin
  inherited Create(AnOwner);
  FBandBlended := True;
  FBandColor := clBlue;
  FBandColorFade := clWindow;
  FBandEnabled := True;
  FBandLength := 300;
  FBandMargin := 2;
  FBandRadius := 4;
  FBandThickness := 3;
  FExpandable := True;
  FExpanded := True;
  FExpandImageIndent := 4;
  FMarginBottom := TEasyFooterMargin.Create(AnOwner);
  FMarginLeft := TEasyMargin.Create(AnOwner);
  FMarginRight := TEasyMargin.Create(AnOwner);
  FMarginTop := TEasyHeaderMargin.Create(AnOwner);
end;

destructor TEasyPaintInfoBaseGroup.Destroy;
begin
  inherited;
  FreeAndNil(FMarginBottom);
  FreeAndNil(FMarginLeft);
  FreeAndNil(FMarginRight);
  FreeAndNil(FMarginTop);
end;

function TEasyPaintInfoBaseGroup.GetMarginBottom: TCustomEasyFooterMargin;
begin
  Result := FMarginBottom
end;

function TEasyPaintInfoBaseGroup.GetMarginLeft: TEasyMargin;
begin
  Result := FMarginLeft
end;

function TEasyPaintInfoBaseGroup.GetMarginRight: TEasyMargin;
begin
  Result := FMarginRight
end;

function TEasyPaintInfoBaseGroup.GetMarginTop: TEasyHeaderMargin;
begin
  Result := FMarginTop
end;

procedure TEasyPaintInfoBaseGroup.Assign(Source: TPersistent);
var
  Temp: TEasyPaintInfoBaseGroup;
begin
  inherited Assign(Source);
  if Source is TEasyPaintInfoBaseGroup then
  begin
    Temp := TEasyPaintInfoBaseGroup(Source);
    FBandBlended := Temp.BandBlended;
    FBandColor := Temp.BandColor;
    FBandColorFade := Temp.BandColorFade;
    FBandEnabled := Temp.BandEnabled;
    FBandFullWidth := Temp.BandFullWidth;
    FBandIndent := Temp.BandIndent;
    FBandLength := Temp.BandLength;
    FBandMargin := Temp.BandMargin;
    FBandRadius := Temp.BandRadius;
    FBandThickness := Temp.BandThickness;
    FExpandable := Temp.Expandable;
    FExpandImageIndent := Temp.ExpandImageIndent;
    MarginBottom.Assign(Temp.MarginBottom);
    MarginLeft.Assign(Temp.MarginLeft);
    MarginRight.Assign(Temp.MarginRight);
    MarginTop.Assign(Temp.MarginTop);
  end
end;

procedure TEasyPaintInfoBaseGroup.SetBandBlended(Value: Boolean);
begin
  if Value <> FBandBlended then
  begin
    FBandBlended := Value;
    Invalidate(False)
  end
end;

procedure TEasyPaintInfoBaseGroup.SetBandColor(Value: TColor);
begin
  if Value <> FBandColor then
  begin
    FBandColor := Value;
    Invalidate(False)
  end
end;

procedure TEasyPaintInfoBaseGroup.SetBandColorFade(Value: TColor);
begin
  if Value <> FBandColorFade then
  begin
    FBandColorFade := Value;
    Invalidate(False)
  end
end;

procedure TEasyPaintInfoBaseGroup.SetBandEnabled(Value: Boolean);
begin
  if Value <> FBandEnabled then
  begin
    FBandEnabled := Value;
    Invalidate(False)
  end
end;

procedure TEasyPaintInfoBaseGroup.SetBandFullWidth(Value: Boolean);
begin
  if Value <> FBandFullWidth then
  begin
    FBandFullWidth := Value;
    Invalidate(False)
  end
end;

procedure TEasyPaintInfoBaseGroup.SetBandIndent(Value: Integer);
begin
  if Value <> FBandIndent then
  begin
    FBandIndent := Value;
    Invalidate(False)
  end
end;

procedure TEasyPaintInfoBaseGroup.SetBandLength(Value: Integer);
begin
  if Value <> FBandLength then
  begin
    FBandLength := Value;
    Invalidate(False)
  end
end;

procedure TEasyPaintInfoBaseGroup.SetBandMargin(Value: Integer);
begin
  if Value <> FBandMargin then
  begin
    FBandMargin := Value;
    Invalidate(False)
  end
end;

procedure TEasyPaintInfoBaseGroup.SetBandRadius(Value: Byte);
begin
  if Value <> FBandRadius then
  begin
    FBandRadius := Value;
    Invalidate(False)
  end
end;

procedure TEasyPaintInfoBaseGroup.SetBandThickness(Value: Integer);
begin
  if Value <> FBandThickness then
  begin
    FBandThickness := Value;
    Invalidate(False)
  end
end;

procedure TEasyPaintInfoBaseGroup.SetExpandable(Value: Boolean);
begin
  if Value <> FExpandable then
  begin
    FExpandable := Value;
    Invalidate(False)
  end
end;

procedure TEasyPaintInfoBaseGroup.SetExpandImageIndent(Value: Integer);
begin
  if Value <> FExpandImageIndent then
  begin
    FExpandImageIndent := Value;
    Invalidate(False)
  end
end;

procedure TEasyPaintInfoBaseGroup.SetMarginBottom(
  Value: TCustomEasyFooterMargin);
begin
  if Value <> FMarginBottom then
  begin
    FreeAndNil(FMarginBottom);
    FMarginBottom := Value
  end
end;

procedure TEasyPaintInfoBaseGroup.SetMarginLeft(Value: TEasyMargin);
begin
  if Value <> FMarginLeft then
  begin
    FreeAndNil(FMarginLeft);
    FMarginLeft := Value
  end
end;

procedure TEasyPaintInfoBaseGroup.SetMarginRight(Value: TEasyMargin);
begin
  if Value <> FMarginRight then
  begin
    FreeAndNil(FMarginRight);
    FMarginRight := Value
  end
end;

procedure TEasyPaintInfoBaseGroup.SetMarginTop(Value: TEasyHeaderMargin);
begin
  if Value <> FMarginTop then
  begin
    FreeAndNil(FMarginTop);
    FMarginTop := Value
  end
end;

{ TEasyHotTrackManager}
constructor TEasyHotTrackManager.Create(AnOwner: TCustomEasyListview);
begin
  inherited Create(AnOwner);
  FColor := clHighlight;
  FUnderLine := True;
  FCursor := crHandPoint;
  FGroupTrack := [htgIcon, htgText];
  FItemTrack := [htiIcon, htiText];
  FColumnTrack := [htcIcon, htcText]
end;

function TEasyHotTrackManager.GetPendingObject(MousePos: TPoint): TEasyCollectionItem;
begin
  Result := FPendingObject
end;

procedure TEasyHotTrackManager.SetPendingObject(MousePos: TPoint; Value: TEasyCollectionItem);
var
  TempOldItem: TEasyCollectionItem;
  OldCapture: HWnd;
begin
  if FPendingObject <> Value then
  begin
    TempOldItem := FPendingObject;
    FPendingObject := nil;
    if Enabled or (Value = nil) then
    begin
      if Assigned(OwnerListview) and OwnerListview.HandleAllocated then
      begin
        // Make sure the hot track will end
        if Assigned(Value) then
          Mouse.Capture := OwnerListview.Handle
        else
          if Mouse.Capture = OwnerListview.Handle then
            Mouse.Capture := 0;

        // Cursor only works if no Window is captured
        OldCapture := Mouse.Capture;
        Mouse.Capture := 0;
        if Assigned(Value) then
          OwnerListview.Cursor := Cursor
        else
          OwnerListview.Cursor := crDefault;
        Mouse.Capture := OldCapture;

      end
    end;
    // PendingObject must be nil when this is executed
    // Always fire the event for custom hottracking
    if Assigned(TempOldItem) then
      TempOldItem.HotTracking[MousePos] := False;
    FPendingObject := Value;
    if Assigned(FPendingObject) then
      FPendingObject.HotTracking[MousePos] := True;
  end
end;

procedure TEasyHotTrackManager.SetPendingObjectCheck(const Value: TEasyCollectionItem);
begin
  if Value <> FPendingObjectCheck then
  begin
    if Assigned(FPendingObjectCheck) then
      FPendingObjectCheck.CheckHovering := False;
    FPendingObjectCheck := Value;
    if Assigned(FPendingObjectCheck) then
      FPendingObjectCheck.CheckHovering := True;
  end
end;

{ TEasyPaintInfoBaseColumn }

constructor TEasyPaintInfoBaseColumn.Create(AnOwner: TCustomEasyListview);
begin
  inherited;
  FColor := clBtnFace;
  FSortGlyphAlign := esgaRight;
  FSortGlyphIndent := 2;
  FHotTrack := True;
  FStyle := ehbsThick;
  FImagePosition := ehpLeft;
  FHilightFocusedColor := $00F7F7F7;
end;

procedure TEasyPaintInfoBaseColumn.SetColor(Value: TColor);
begin
  if Value <> FColor then
  begin
    FColor := Value;
    OwnerListview.Header.Invalidate(False);
  end
end;

procedure TEasyPaintInfoBaseColumn.SetHilightFocused(const Value: Boolean);
begin
  if FHilightFocused <> Value then
  begin
    FHilightFocused := Value;
    if Assigned(OwnerListview) then
      OwnerListview.SafeInvalidateRect(nil, False)
  end
end;

procedure TEasyPaintInfoBaseColumn.SetHilightFocusedColor(const Value: TColor);
begin
  if FHilightFocusedColor <> Value then
  begin
    FHilightFocusedColor := Value;
    if Assigned(OwnerListview) then
      OwnerListview.SafeInvalidateRect(nil, False)
  end
end;

procedure TEasyPaintInfoBaseColumn.SetImagePosition(Value: TEasyHeaderImagePosition);
begin
  if Value <> FImagePosition then
  begin
    FImagePosition := Value;
    OwnerListview.Header.Invalidate(False);
  end
end;

procedure TEasyPaintInfoBaseColumn.SetSortGlpyhAlign(Value: TEasySortGlyphAlign);
begin
  if Value <> FSortGlyphAlign then
  begin
    FSortGlyphAlign := Value;
    OwnerListview.Header.Invalidate(False);
  end
end;

procedure TEasyPaintInfoBaseColumn.SetSortGlyphIndent(Value: Integer);
begin
  if Value <> FSortGlyphIndent then
  begin
    FSortGlyphIndent := Value;
    OwnerListview.Header.Invalidate(False)
  end
end;

procedure TEasyPaintInfoBaseColumn.SetStyle(Value: TEasyHeaderButtonStyle);
begin
  if Value <> FStyle then
  begin
    FStyle := Value;
    OwnerListview.Header.Invalidate(False)
  end
end;

{ TEasyViewReportItem}
function TEasyViewReportItem.AllowDrag(Item: TEasyItem; ViewportPoint: TPoint): Boolean;
var
  RectArray: TEasyRectArrayObject;
  R: TRect;
begin
  if FullRowSelect then
  begin
    ItemRectArray(Item, nil, OwnerListview.ScratchCanvas, Item.Caption, RectArray);
    UnionRect(R, RectArray.TextRect, RectArray.IconRect);
    if Item.Selected and Windows.PtInRect(R, ViewportPoint) then
      Result := True
    else
      Result := False
  end else
    Result := inherited AllowDrag(Item, ViewportPoint);
end;


function TEasyViewReportItem.CalculateDisplayRect(Item: TEasyItem;
  Column: TEasyColumn): TRect;
begin
  Result := Item.DisplayRect;
  if Assigned(Column) then
  begin
    Result.Left := Column.DisplayRect.Left;
    Result.Right := Column.DisplayRect.Right;

    if Column.Position = 0 then
      Result.Left := Item.OwnerGroup.MarginLeft.RuntimeSize;
    if Column.Position = Column.OwnerColumns.Count - 1 then
      Result.Right := Result.Right - Item.OwnerGroup.MarginRight.RuntimeSize
  end
end;

function TEasyViewReportItem.ExpandTextR(Item: TEasyItem; RectArray: TEasyRectArrayObject; SelectType: TEasySelectHitType): TRect;
begin
  Result := inherited ExpandTextR(Item, RectArray, SelectType);
  // If dragging and dropping only concider the basic ReportView item as the drop target.
  if FullRowSelect and not Assigned(OwnerListview.DragManager.DragItem) {and (SelectType = eshtClickselect)} then
  begin
    Result.Left := Item.OwnerGroup.MarginLeft.RunTimeSize;
    Result.Right := Item.DisplayRect.Right;
  end
end;

function TEasyViewReportItem.FullRowSelect: Boolean;
begin
  Result := False;
  if Assigned(OwnerListview) then
    Result := OwnerListview.Selection.FullRowSelect
end;

function TEasyViewReportItem.PaintStateImage: Boolean;
begin
  Result := True
end;

function TEasyViewReportItem.SelectionHit(Item: TEasyItem; SelectViewportRect: TRect; SelectType: TEasySelectHitType): Boolean;
var
  R: TRect;
  RectArray: TEasyRectArrayObject;
begin
  Result := False;
  if Item.Enabled and not IsRectEmpty(SelectViewportRect) then
  begin
    // Selection is always based on the first column
    ItemRectArray(Item, OwnerListview.Header.FirstColumn, OwnerListview.ScratchCanvas, '', RectArray);
    Result := IntersectRect(R, SelectViewportRect, ExpandTextR(Item, RectArray, SelectType)) or
              IntersectRect(R, SelectViewportRect, ExpandIconR(Item, RectArray, SelectType))
  end
end;

function TEasyViewReportItem.SelectionHitPt(Item: TEasyItem; ViewportPoint: TPoint; SelectType: TEasySelectHitType): Boolean;
var
  RectArray: TEasyRectArrayObject;
begin
  Result := False;
  if Item.Enabled then
  begin
    // Selection is always based on the first column
    ItemRectArray(Item, OwnerListview.Header.FirstColumn, OwnerListview.ScratchCanvas, '', RectArray);
    Result := Windows.PtInRect(ExpandTextR(Item, RectArray, SelectType), ViewportPoint) or
              Windows.PtInRect(ExpandIconR(Item, RectArray, SelectType), ViewportPoint)
  end
end;

{ TEasyHintWindow }
constructor TEasyHintWindow.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  {$IFDEF GXDEBUG_HINT}
  SendDebug('TEasyHintWindow.Create');
  {$ENDIF GXDEBUG_HINT}
end;


destructor TEasyHintWindow.Destroy;
begin
  {$IFDEF GXDEBUG_HINT}
  SendDebug('TEasyHintWindow.Destroy');
  {$ENDIF GXDEBUG_HINT}
  inherited Destroy;
end;

procedure TEasyHintWindow.ActivateHint(ARect: TRect; const AHint: string);
begin
  {$IFDEF GXDEBUG_HINT}
  SendDebug('TEasyHintWindow.ActivateHint');
  {$ENDIF GXDEBUG_HINT}
  inherited;
end;

procedure TEasyHintWindow.ActivateHintData(ARect: TRect;
  const AHint: string; AData: Pointer);
begin
  {$IFDEF GXDEBUG_HINT}
  SendDebug('TEasyHintWindow.ActivateHintData');
  {$ENDIF GXDEBUG_HINT}
  inherited;
end;

function TEasyHintWindow.CalcHintRect(MaxWidth: Integer;
  const AHint: string; AData: Pointer): TRect;
var
  TextFlags: TCommonDrawTextWFlags;
begin
  {$IFDEF GXDEBUG_HINT}
  SendDebug('TEasyHintWindow.CalcHintRect');
  {$ENDIF GXDEBUG_HINT}
  // We passed in our HintInfo through the AData parameter
  HintInfo := PEasyHintInfoRec(AData);

  EasyHintInfo := HintInfo.Listview.HintInfo;

  EasyHintInfo.FCanvas := Canvas;
  EasyHintInfo.FHintType := HintInfo.HintType;
  EasyHintInfo.FText := HintInfo.HintStr;
  EasyHintInfo.FColor := HintInfo.HintColor;
  EasyHintInfo.FCursorPos := HintInfo.CursorPos;
  EasyHintInfo.FHideTimeout := HintInfo.HideTimeout;
  EasyHintInfo.FMaxWidth := HintInfo.HintMaxWidth;
  EasyHintInfo.FReshowTimeout := HintInfo.ReshowTimeout;
  EasyHintInfo.FWindowPos := HintInfo.HintPos;

  EasyHintInfo.FBounds := Rect(0, 0, MaxWidth, 0);

  HintInfo.Listview.DoHintCustomInfo(HintInfo.TargetObj, EasyHintInfo);

  HintInfo.HintStr := FEasyHintInfo.Text;
  HintInfo.HintType := EasyHintInfo.FHintType;
  HintInfo.HintColor := EasyHintInfo.FColor;
  HintInfo.CursorPos := EasyHintInfo.FCursorPos;
  HintInfo.HideTimeout := EasyHintInfo.FHideTimeout;
  HintInfo.HintMaxWidth := EasyHintInfo.FMaxWidth;
  HintInfo.ReshowTimeout := EasyHintInfo.FReshowTimeout;
  HintInfo.HintPos := EasyHintInfo.FWindowPos;


  if (HintInfo.HintType = ehtText) or (HintInfo.HintType = ehtToolTip) then
  begin
    case PEasyHintInfoRec( HintInfo)^.Listview.HintAlignment of
      taLeftJustify:   TextFlags := [dtLeft, dtCalcRect, dtCalcRectAdjR, dtWordBreak];
      taRightJustify:  TextFlags := [dtRight, dtCalcRect, dtCalcRectAdjR, dtWordBreak];
      taCenter:        TextFlags := [dtCenter, dtCalcRect, dtCalcRectAdjR, dtWordBreak];
    end;   
    DrawTextWEx(Canvas.Handle, HintInfo.HintStr, EasyHintInfo.FBounds, TextFlags, -1);
    Inc(EasyHintInfo.FBounds.Right, 6);
    Inc(EasyHintInfo.FBounds.Bottom, 2)
  end;

  Result := EasyHintInfo.FBounds;
end;

function TEasyHintWindow.IsHintMsg(var Msg: TMsg): Boolean;
begin
  {$IFDEF GXDEBUG_HINT}
//  SendDebug('TEasyHintWindow.IsHintMsg');
  {$ENDIF GXDEBUG_HINT}
  Result := inherited IsHintMsg(Msg)
end;

procedure TEasyHintWindow.Paint;
var
  TextFlags: TCommonDrawTextWFlags;
  R: TRect;
begin
  {$IFDEF GXDEBUG_HINT}
  SendDebug('TEasyHintWindow.IsHintMsg');
  {$ENDIF GXDEBUG_HINT}
  if HintInfo.HintType <> ehtCustomDraw then
  begin
    case PEasyHintInfoRec( HintInfo)^.Listview.HintAlignment of
      taLeftJustify:   TextFlags := [dtLeft, dtVCenter];
      taRightJustify:  TextFlags := [dtRight, dtVCenter];
      taCenter:        TextFlags := [dtCenter, dtVCenter];
    end;
    R := FEasyHintInfo.Bounds;
    InflateRect(R, -2, -2);
    DrawTextWEx(Canvas.Handle, HintInfo.HintStr, R, TextFlags, -1);
  end else
  begin
    R := ClientRect;
    InflateRect(R, -2, -2);
    HintInfo.Listview.DoHintCustomDraw(HintInfo.TargetObj, FEasyHintInfo)
  end
end;

{ TEasySortManager }

constructor TEasySortManager.Create(AnOwner: TCustomEasyListview);
begin
  inherited Create(AnOwner);
  FAlgorithm := esaMergeSort;
  FSorter := TEasyMergeSort.Create(Self);
end;

destructor TEasySortManager.Destroy;
begin
  FreeAndNil(FSorter);
  inherited Destroy;
end;

function TEasySortManager.CollectionSupportsInterfaceSorting(Collection: TEasyCollection): Boolean;
var
  i: Integer;
begin
  Result := True;
  i := 0;
  while (i < Collection.Count) and Result do
  begin
     Result := CommonSupports(Collection[i].DataInf, IEasyCompare);
     Inc(i)
  end
end;

procedure TEasySortManager.BeginUpdate;
begin
  InterlockedIncrement(FUpdateCount);
end;

procedure TEasySortManager.EndUpdate;
begin
  InterlockedDecrement(FUpdateCount);
  if (UpdateCount <= 0) and AutoSort then
  begin
    UpdateCount := 0;
    SortAll
  end
end;

procedure TEasySortManager.GroupItem(Item: TEasyItem; ColumnIndex: Integer;
  Key: LongWord);
//
// WARNING:  Do not access the items OwnerListview property as it is an orphaned
// item when it is passed to this method (the collection property is nil)
//
var
  i: Integer;
  Done, DefaultAction: Boolean;
  Groups: TEasyGroups;
  Group: TEasyGroup;
begin
  i := 0;
  Done := False;
  Groups := OwnerListview.Groups;
  while not Done and (i < Groups.Count) do
  begin
    if Key = Groups[i].Key then
    begin
      Groups[i].Items.List.Add(Item);
      Item.FCollection := Groups[i].Items;
      Done := True;
    end;
    Inc(i)
  end;
  if not Done then
  begin
    Group := nil;
    DefaultAction := True;
    OwnerListview.DoAutoSortGroupCreate(Item, ColumnIndex, Groups, Group, DefaultAction);

    if DefaultAction then
    begin
      if Key > 0 then
        Group.Caption := UpperCase( WideChar(Key))
    end;

    Group.Key := Key;
    Group.Items.List.Add(Item);
    Item.FCollection := Groups[i].Items;
  end
end;

procedure TEasySortManager.ReGroup(Column: TEasyColumn);
var
  Groups: TEasyGroups;
  Item: TEasyItem;
  i, j, ColumnIndex, Index: Integer;
  Key: Integer;
  Caption: WideString;
begin
  OwnerListview.BeginUpdate;
  try
    if Assigned(Column) then
      ColumnIndex := Column.Index
    else
      ColumnIndex := 0;
    // Move the items into a temporary storage structure
    Groups := OwnerListview.Groups;
    SetLength(FSortList, Groups.ItemCount);
    Index := 0;
    for i := 0 to Groups.Count - 1 do
      for j := 0 to Groups[i].ItemCount - 1 do
      begin
        Item := Groups[i][j];
        if OwnerListview.Sort.AutoRegroup then
        begin
          SortList[Index].Key := $FFFF;
          OwnerListview.DoAutoGroupGetKey(Item, ColumnIndex, Groups, SortList[Index].Key);
          if SortList[Index].Key = $FFFF then
          begin
            Caption := Item.Caption;
            if Length(Caption) = 0 then
              SortList[Index].Key := 0
            else
              SortList[Index].Key := Ord(WideLowerCase(Caption)[1])
          end else
            Item.GroupKey[ColumnIndex] := SortList[Index].Key;
        end;
        SortList[Index].Item := Item;
        Item.FCollection := nil; // Orphan the item from the collection
        Groups[i].Item[j] := nil;
        Inc(Index)
      end;
    for i := 0 to Groups.Count - 1 do
      Groups[i].FItems.FList.Pack;
    // Clear the control of items
    Groups.Clear;

    for i := 0 to Length(SortList) - 1 do
    begin
      Item := TEasyItem( SortList[i].Item);
      Key := SortList[i].Key;
      GroupItem(Item, ColumnIndex, Key);
    end;
    OwnerListview.Sort.SortAll;
  finally
    // done with SortList
    SetLength(FSortList, 0);
    OwnerListview.EndUpdate(True)
  end
end;

procedure TEasySortManager.SetAlgorithm(Value: TEasySortAlgorithm);
begin
  if FAlgorithm <> Value then
  begin
    FreeAndNil(FSorter);
    case Value of
      esaQuicksort: FSorter := TEasyQuicksort.Create(Self);
      esaBubbleSort: FSorter := TEasyBubbleSort.Create(Self);
      esaMergeSort: FSorter := TEasyMergeSort.Create(Self);
    end;
    FAlgorithm := Value
  end
end;

procedure TEasySortManager.SetAutoRegroup(Value: Boolean);
begin
  if Value <> FAutoRegroup then
  begin
    OwnerListview.BeginUpdate;
    try
      OwnerListview.ShowGroupMargins := Value;
      FAutoRegroup := Value;
      ReGroup(OwnerListview.Selection.FocusedColumn);
    finally
      OwnerListview.EndUpdate(True);
    end
  end
end;

procedure TEasySortManager.SetAutoSort(Value: Boolean);
begin
  if Value <> FAutoSort then
  begin
    FAutoSort := Value;
    if not Assigned(OwnerListview.Selection.FocusedColumn) then
      OwnerListview.Selection.FocusedColumn := OwnerListview.Header.FirstColumn;
    if Value then
      SortAll
  end
end;

procedure TEasySortManager.SortAll(Force: Boolean = False);
var
  i: Integer;
  SupportsInterfaces: Boolean;
begin
  if Assigned(Sorter) and Assigned(OwnerListview) and (({(OwnerListview.UpdateCount = 0) and} (UpdateCount = 0) and not LockoutSort) or Force) then
  begin
    SupportsInterfaces := CollectionSupportsInterfaceSorting(OwnerListview.Groups);
    Sorter.Sort(nil, OwnerListview.Groups, 0, OwnerListview.Groups.Count - 1, OwnerListview.DoGroupCompare, nil, SupportsInterfaces);
    for i := 0 to OwnerListview.Groups.Count - 1 do
    begin
      SupportsInterfaces := CollectionSupportsInterfaceSorting(OwnerListview.Groups[i].Items);
      Sorter.Sort(OwnerListview.GetSortColumn, OwnerListview.Groups[i].Items, 0, OwnerListview.Groups[i].ItemCount - 1, nil, OwnerListview.DoItemCompare, SupportsInterfaces);
    end;
    if not (egsRebuilding in OwnerListview.Groups.GroupsState) then
      OwnerListview.Groups.Rebuild(True)
  end
end;

{ TEasyQuicksort}
procedure TEasyQuicksort.Sort(Column: TEasyColumn; Collection: TEasyCollection; Min, Max: Integer; GroupCompare: TEasyDoGroupCompare; ItemCompare: TEasyDoItemCompare; UseInterfaces: Boolean);
var
 I, J: Integer;
 P, Temp: TEasyCollectionItem;
begin
  // Quicksort is not Stable, i.e. duplicate items may not be in the same
  // order with each pass of the sort.
  if Max > Collection.Count - 1 then
    Max := Collection.Count - 1;

  if Max > Min then
  begin
    repeat
      I := Min;
      J := Max;
      P := Collection.Items[(Min + Max) shr 1];
      repeat
        if UseInterfaces then
        begin
          while (P.DataInf as IEasyCompare).Compare(Collection.Items[I].DataInf, Column) < 0 do
              Inc(I);
          while (P.DataInf as IEasyCompare).Compare(Collection.Items[J].DataInf, Column) > 0 do
              Dec(J);
        end else
        if Assigned(GroupCompare) then
        begin
          while GroupCompare(Column, TEasyGroup(Collection.Items[I]), TEasyGroup(P)) < 0 do
            Inc(I);
          while GroupCompare(Column, TEasyGroup(Collection.Items[J]), TEasyGroup(P)) > 0 do
            Dec(J);
        end else
        if Assigned(ItemCompare) then
        begin
          while ItemCompare(Column, TEasyItems(Collection).OwnerGroup, TEasyItem(Collection.Items[I]), TEasyItem(P)) < 0 do
            Inc(I);
          while ItemCompare(Column, TEasyItems(Collection).OwnerGroup, TEasyItem(Collection.Items[J]), TEasyItem(P)) > 0 do
            Dec(J);
        end else
        begin
          while DefaultSort(Column, Collection.Items[I], P) < 0 do
            Inc(I);
          while DefaultSort(Column, Collection.Items[J], P) > 0 do
            Dec(J);
        end;
        if I <= J then
        begin
          Temp := Collection.Items[I];
          Collection.Items[I] := Collection.Items[J];
          Collection.Items[J] := Temp;
          Inc(I);
          Dec(J);
        end;
      until I > J;
      if Min < J then Sort(Column, Collection, Min, J, GroupCompare, ItemCompare, UseInterfaces);
        Min := I;
    until I >= Max;
  end
end;

{ TEasyBubbleSort}
procedure TEasyBubbleSort.Sort(Column: TEasyColumn; Collection: TEasyCollection; Min, Max: Integer; GroupCompare: TEasyDoGroupCompare; ItemCompare: TEasyDoItemCompare; UseInterfaces: Boolean);
var
  LastSwap, i, j, SortResult : Integer;
  Tmp: TEasyCollectionItem;
begin
  // During this loop, min and max are the smallest and largest
  // indexes of items that might still be out of order.

  // Repeat until we are done.
  while (Min < Max) do
  begin
    // Bubble up.
    LastSwap := Min - 1;
    // for i := min + 1 to max
    i := min + 1;
    while (i <= Max) do
    begin
      // Find a bubble.
      if UseInterfaces then
        SortResult := (Collection.Items[i].DataInf as IEasyCompare).Compare(Collection.Items[i - 1].DataInf, Column)
      else
      if Assigned(GroupCompare) then
        SortResult := GroupCompare(Column, TEasyGroup(Collection.Items[i - 1]), TEasyGroup(Collection.Items[i]))
      else
      if Assigned(ItemCompare) then
        SortResult := ItemCompare(Column, TEasyItems(Collection).OwnerGroup, TEasyItem(Collection.Items[i - 1]), TEasyItem(Collection.Items[i]))
      else
        SortResult := DefaultSort(Column, Collection.Items[i - 1], Collection.Items[i]);

      if SortResult > 0 then
      begin
        // See where to drop the bubble.
        Tmp := Collection.Items[i - 1];
        j := i;
        repeat
          Collection.Items[j - 1] := Collection.Items[j];
          j := j + 1;
          if (j > max) then
            Break;

          if UseInterfaces then
            SortResult := (Tmp.DataInf as IEasyCompare).Compare(Collection.Items[j].DataInf, Column)
          else
          if Assigned(GroupCompare) then
            SortResult := GroupCompare(Column, TEasyGroup(Collection.Items[j]), TEasyGroup(Tmp))
          else
          if Assigned(ItemCompare) then
            SortResult := ItemCompare(Column, TEasyItems(Collection).OwnerGroup, TEasyItem(Collection.Items[j]), TEasyItem(Tmp))
          else
            SortResult := DefaultSort(Column, Collection.Items[j], Tmp);

        until SortResult >= 0;
        Collection.Items[j - 1] := Tmp;
        LastSwap := j - 1;
        i := j + 1;
      end else
        i := i + 1;
    end;
    // End bubbling up.

    // Update max.
    Max := LastSwap - 1;

    // Bubble down.
    LastSwap := Max + 1;
    // for i := max - 1 downto min
    i := Max - 1;
    while (i >= Min) do
    begin
      // Find a bubble.
      if UseInterfaces then
        SortResult := (Collection.Items[i].DataInf as IEasyCompare).Compare(Collection.Items[i + 1].DataInf, Column)
      else
      if Assigned(GroupCompare) then
        SortResult := GroupCompare(Column, TEasyGroup(Collection.Items[i + 1]), TEasyGroup(Collection.Items[i]))
      else
      if Assigned(ItemCompare) then
        SortResult := ItemCompare(Column, TEasyItems(Collection).OwnerGroup, TEasyItem(Collection.Items[i + 1]), TEasyItem(Collection.Items[i]))
      else
        SortResult := DefaultSort(Column, Collection.Items[i + 1], Collection.Items[i]);

      if SortResult < 0 then
      begin
        // See where to drop the bubble.
        Tmp := Collection.Items[i + 1];
        j := i;
        repeat
          Collection.Items[j + 1] := Collection.Items[j];
          j := j - 1;
          if j < Min then
            Break;

          if UseInterfaces then
            SortResult := (Tmp.DataInf as IEasyCompare).Compare(Collection.Items[j].DataInf, Column)
          else
          if Assigned(GroupCompare) then
            SortResult := GroupCompare(Column, TEasyGroup(Collection.Items[j]), TEasyGroup(Tmp))
          else
          if Assigned(ItemCompare) then
            SortResult := ItemCompare(Column, TEasyItems(Collection).OwnerGroup, TEasyItem(Collection.Items[j]), TEasyItem(Tmp))
          else
            SortResult := DefaultSort(Column, Collection.Items[j], Tmp);

        until SortResult <= 0;
        Collection.Items[j + 1] := Tmp;
        LastSwap := j + 1;
        i := j - 1;
      end else
          i := i - 1;
    end;
    // End bubbling down.

    // Update min.
    Min := LastSwap + 1;
  end;
end;

{ TEasyMergeSort }
function TEasyMergeSort.CompareDefault(i1, i2: TEasyCollectionItem): Boolean;
begin
  Result := DefaultSort(Column, i1, i2) <= 0;
end;

function TEasyMergeSort.CompareGroup(i1, i2: TEasyCollectionItem): Boolean;
begin
  Result := GroupCompareFunc(Column, TEasyGroup(i1), TEasyGroup(i2)) <= 0;
end;

function TEasyMergeSort.CompareInterfaces(i1, i2: TEasyCollectionItem): Boolean;
begin
  Result:=(i2.DataInf as IEasyCompare).Compare(i1.DataInf, Column) <= 0;
end;

function TEasyMergeSort.CompareItem(i1, i2: TEasyCollectionItem): Boolean;
begin
  Result := ItemCompareFunc(Column, OwnerGroup, TEasyItem(i1), TEasyItem(i2)) <= 0;
end;

procedure TEasyMergeSort.Sort(Column: TEasyColumn; Collection: TEasyCollection; Min, Max: Integer; GroupCompare: TEasyDoGroupCompare; ItemCompare: TEasyDoItemCompare; UseInterfaces: Boolean);
type
  TEasyMergeSortCompare=function (i1, i2: TEasyCollectionItem): Boolean of object;
var
  CompareFunc: TEasyMergeSortCompare;

  procedure subMerge(dst: TEasyCollection; ld, md, hd: Integer; src: TEasyCollection; ls, ms: Integer);
  { dst    src
  hd
    aa
  md      ms
    xx      bb     bb+aa->xxaa
  ld      ls }
  var
    i, j, d: Integer;
  begin
    i := ls;
    j := md;
    d := ld;
    while i< ms do
      begin
        if (j = hd) or (compareFunc(src[i], dst[j])) then
          begin
            dst[d] := src[i];
            Inc(i);
          end
        else
          begin
            dst[d] := dst[j];
            Inc(j);
          end;
        Inc(d);
      end;
  end;

  procedure subSortM(dst: TEasyCollection; ld, hd: Integer; src: TEasyCollection; ls, hs: Integer); forward;

  procedure subSortI(dst: TEasyCollection; ld, hd: Integer; tmp: TEasyCollection; lt, ht: Integer);
  var
    m2: Integer; //I for Inplace
    x3: Pointer;
  { -  hd
    |     A               inplace AA->AA
    m2    A    ht         move    bbb->ccc
          b       c       merge   ccc+AA->bbbAA
          b       c
       ld b    lt c       ht-lt>=hd-m2-ld !    }
  begin
    if (hd >= ld + 3) then
      begin
        m2 := (hd - ld) div 2;
        subSortI(dst, hd-m2, hd, tmp, lt, lt + m2);             //sort random AA->sorted AA, using cc as scratchpad
        subSortM(tmp, lt, lt+(hd-ld-m2), dst, ld, hd  -m2);     //sort random bbb->sorted ccc
        subMerge(dst, ld, hd - m2, hd, tmp, lt, lt + (hd - ld - m2)); //merge sorted ccc+sorted AA->bbbAA
      end
    else if (hd = ld + 2) then
      begin
        if not compareFunc(dst[ld], dst[ld + 1]) then
          begin
            x3 := dst[ld];
            dst[ld] := dst[ld + 1];
            dst[ld + 1] := x3;
          end;
      end;
  end;

  procedure subSortM(dst: TEasyCollection; ld, hd: Integer; src: TEasyCollection; ls, hs: Integer);
  var m2: Integer; //M for Move
  {  hd      hs                    hd      hs
        c       A                     c       b
        c       A                     c       b
  m2    c       a                  m2 c       A
        d       b                     d       a
     ld d    ls b                  ld d    ls a      }
  begin
    if (hs >= ls + 3) then
      begin
        m2 := (hs - ls) div 2;
        subSortM(dst, ld  +m2, hd, src, ls + m2, hs);     //sort random aAA->sorted ccc
        subSortI(src, ls, ls + m2, dst, ld, ld + m2);     //sort random bb->sorted bb, using dd as scratchpad
        subMerge(dst, ld, ld + m2, hd, src, ls, ls + m2); //merge bb+ccc->ddccc
      end
    else if (hs = ls + 2) then
      begin
        if not compareFunc(src[ls], src[ls+1]) then
          begin
            dst[ld] := src[ls + 1];
            dst[ld+1] := src[ls];
          end
        else
          begin
            dst[ld] := src[ls];
            dst[ld + 1] := src[ls + 1];
          end
      end
    else if (hs = ls + 1) then
      dst[ld] := src[ls];
  end;

var m: Integer;
    TempList: TEasyCollection;
begin
  if Max > Collection.Count - 1 then
    Max := Collection.Count - 1;
  if (Max <= Min) then
    Exit;

  Self.Column := Column;
  OwnerGroup := TEasyItems(Collection).OwnerGroup;
  GroupCompareFunc := GroupCompare;
  ItemCompareFunc := ItemCompare;

  TempList := TEasyCollection.Create(nil);
  m:=(Max - Min + 2) div 2;
  TempList.List.Capacity := m;
  TempList.List.Count := m;

  if (UseInterfaces) then
    CompareFunc := CompareInterfaces
  else if Assigned(GroupCompareFunc) then
    CompareFunc := CompareGroup
  else if Assigned(ItemCompareFunc) then
    CompareFunc := CompareItem
  else
    CompareFunc := CompareDefault;

  subSortI(Collection, Min, Max + 1, TempList, Min, (Min + Max + 2) div 2);

  TempList.List.Count := 0;
  TempList.List.Capacity := 0;
  FreeAndNil(TempList);
end;

{ TEasyStringEditor }

function TEasyBaseEditor.AcceptEdit: Boolean;
var
  WS: Variant;
begin
  Result := True;
  WS := GetText;
  Listview.DoItemEdited(Item, WS, Result);
  if Result then
  begin
    if Assigned(EditColumn) then
      Item.Captions[EditColumn.Index] := WS
    else
      Item.Caption := WS;
    Listview.EditManager.EndEdit
  end
end;

function TEasyBaseEditor.GetEditor: TWinControl;
begin
  Result := FEditor;
end;

function TEasyBaseEditor.GetEditorColor: TColor;
begin
  Result := Listview.EditManager.Color
end;

function TEasyBaseEditor.GetHandle: HWnd;
begin
  if Assigned(FEditor) and (FEditor.HandleAllocated) then
    Result := FEditor.Handle
  else
    Result := 0;
end;

function TEasyBaseEditor.GetListview: TCustomEasyListview;
begin
  Result := nil;
  if Assigned(Item) then
    Result := Item.OwnerListview
end;

function TEasyBaseEditor.GetModified: Boolean;
begin
  Result := Modified
end;

function TEasyBaseEditor.PtInEditControl(WindowPt: TPoint): Boolean;
begin
  Result := PtInRect(Editor.BoundsRect, WindowPt)
end;

procedure TEasyBaseEditor.ControlWndHookProc(var Message: TMessage);
//
// Window procedure hook for the Edit box, allows autosizing of edit during user
// input
//
var
  Menu: TPopupMenu;
begin
  case Message.Msg of
  WM_EDITORRESIZE:
    begin
      ResizeEditor;
    end;
    WM_CHAR:
      begin
        if Message.WParam = VK_TAB then
          Message.WParam := Ord(' ');
      end;
    WM_CONTEXTMENU:
      begin
        Menu := nil;
        Listview.DoItemGetEditMenu(Self, Menu);
        if Assigned(Menu) then
          Menu.Popup(LOWORD(Message.LParam), HIWORD(Message.LParam))
        else
          // Don't let the VCL hook the parent window background menu to the editor
          CallWindowProc(TWinControlHack(Editor).DefWndProc, Editor.Handle, Message.Msg, Message.wParam, Message.lParam);
        Message.Result := 1;
      end;
  end;
  FOldWndProc(Message)
end;

function TEasyBaseEditor.EditText(Item: TEasyItem; Column: TEasyColumn): WideString;
begin
  Result := '';
  Item.OwnerListview.DoItemGetEditCaption(Item, Column, Result);
  if Result = '' then
  begin
    if Assigned(Column) then
      Result := Item.Captions[Column.Index]
    else
      Result := Item.Caption
  end
end;

procedure TEasyBaseEditor.Finalize;     
begin
  EditColumn := nil;
  // Only unhook if it is our hook
  if EqualWndMethod(Editor.WindowProc, ControlWndHookProc) then
    Editor.WindowProc := OldWndProc;
  FreeAndNil(FEditor)
end;

procedure TEasyBaseEditor.Hide;
begin
  Editor.Visible := False
end;

procedure TEasyBaseEditor.Initialize(AnItem: TEasyItem; Column: TEasyColumn);
begin
  FItem := AnItem;
  FEditColumn := Column;
  CreateEditor(FEditor, Column, Listview);
  Editor.Visible := False;
  OldWndProc := Editor.WindowProc;
  Editor.WindowProc := ControlWndHookProc;
  Editor.DoubleBuffered := True;
  TWinControlHack( Editor).Color := GetEditorColor;
  GetEditorFont.Assign(Listview.EditManager.Font);
  Item.ItemRectArray(Column, AnItem.OwnerListview.ScratchCanvas, FRectArray);
  ResizeEditor;
  SetWindowLong(Editor.Handle, GWL_EXSTYLE, GetWindowLong(Editor.Handle, GWL_EXSTYLE) or WS_EX_TOPMOST);
end;

procedure TEasyBaseEditor.ResizeEditor;
var
  R: TRect;
begin
  CalculateEditorRect(GetText, R);
  Editor.SetBounds(R.Left,
    R.Top,
    RectWidth(R),
    RectHeight(R));
end;

procedure TEasyBaseEditor.SetEditor(const Value: TWinControl);
begin
  FEditor := Value;
end;

procedure TEasyBaseEditor.SetEditorFocus;
begin
  Editor.SetFocus;
end;

procedure TEasyBaseEditor.Show;
begin
  Editor.Visible := True;   
end;

{ TEasySelectionGroupList }

constructor TEasySelectionGroupList.Create;
begin
  FList := TList.Create
end;

destructor TEasySelectionGroupList.Destroy;
begin
  FreeAndNil(FList);
  inherited
end;

function TEasySelectionGroupList.Count: Integer;
begin
  Result := List.Count
end;

function TEasySelectionGroupList.GetItems(Index: Integer): TEasyItem;
begin
  Result := TEasyItem( List.Items[Index])
end;

procedure TEasySelectionGroupList.Add(Item: TEasyItem);
begin
  List.Add(Item)
end;

procedure TEasySelectionGroupList.Clear;
begin
  List.Clear
end;

procedure TEasySelectionGroupList.DecRef;
begin
  Dec(FRefCount);
  if FRefCount = 0 then
    Destroy
end;

procedure TEasySelectionGroupList.IncRef;
begin
  Inc(FRefCount)
end;

procedure TEasySelectionGroupList.SetItems(Index: Integer; Value: TEasyItem);
begin
  List.Items[Index] := Value
end;

function TEasyGridIconGroup.GetCellSize: TEasyCellSize;
begin
  Result := OwnerListview.CellSizes.Icon
end;

procedure TEasyGridIconGroup.AutoSizeCells;
begin
  // Do nothing
end;

procedure TEasyGridIconGroup.SetCellSize(Value: TEasyCellSize);
begin
  OwnerListview.CellSizes.Icon.Assign(Value)
end;

function TEasyGridSmallIconGroup.GetCellSize: TEasyCellSize;
begin
  Result := OwnerListview.CellSizes.SmallIcon
end;

procedure TEasyGridSmallIconGroup.SetCellSize(Value: TEasyCellSize);
begin
  OwnerListview.CellSizes.SmallIcon.Assign(Value)
end;

function TEasyGridThumbnailGroup.GetCellSize: TEasyCellSize;
begin
  Result := OwnerListview.CellSizes.Thumbnail
end;

procedure TEasyGridThumbnailGroup.AutoSizeCells;
begin
  // Do nothing
end;

procedure TEasyGridThumbnailGroup.SetCellSize(Value: TEasyCellSize);
begin
  OwnerListview.CellSizes.Thumbnail.Assign(Value)
end;

{ TEasyGridTileGroup}
procedure TEasyGridTileGroup.FindInsertPosition(ViewportPoint: TPoint; var Group: TEasyGroup; var Index: Integer);
begin

end;


function TEasyGridTileGroup.GetCellSize: TEasyCellSize;
begin
  Result := OwnerListview.CellSizes.Tile
end;

procedure TEasyGridTileGroup.SetCellSize(Value: TEasyCellSize);
begin
  OwnerListview.CellSizes.Tile.Assign(Value)
end;

{ TEasyItemInterfaced}
function TEasyItemInterfaced.ExtractObject: TObject;
begin
  if Supports(DataInf, ICommonExtractObj) then
    Result := (DataInf as ICommonExtractObj).Obj
  else
    Result := nil
end;

function TEasyItemInterfaced.GetCaptions(Column: Integer): Widestring;
var
  CaptionInf: IEasyCaptions;
begin
  CaptionInf := nil;
  if Supports(DataInf, IEasyCaptions, CaptionInf) then
    Result := CaptionInf.Captions[Column]
end;

function TEasyItemInterfaced.GetChecked: Boolean;
var
  Checks: IEasyChecks;
begin
  Result := False;
  Checks := nil;
  if Supports(DataInf, IEasyChecks, Checks) then
    Result := Checks.GetChecked(0)
end;

function TEasyItemInterfaced.GetCommonImageIndex(Column: Integer; Kind: TEasyImageKind): TCommonImageIndexInteger;
var
  ImageInf: IEasyImages;
begin
  Result := -1;
  ImageInf := nil;
  if Supports(DataInf, IEasyImages, ImageInf) then
    Result := ImageInf.ImageIndexes[Column, Kind]
end;

function TEasyItemInterfaced.GetDetailCount: Integer;
var
  TileInf: IEasyDetails;
begin
  Result := 1;
  TileInf := nil;
  if Supports(DataInf, IEasyDetails, TileInf) then
    Result := TileInf.GetDetailCount
end;

function TEasyItemInterfaced.GetDetails(Line: Integer): Integer;
var
  TileInf: IEasyDetails;
begin
  Result := 0;
  TileInf := nil;
  if Supports(DataInf, IEasyDetails, TileInf) then
    Result := TileInf.Detail[Line]
end;

function TEasyItemInterfaced.GetGroupKey(FocusedColumn: Integer): LongWord;
var
  KeyInf: IEasyGroupKey;
begin
  Result := 0;
  KeyInf := nil;
  if Supports(DataInf, IEasyGroupKey, KeyInf) then
    Result := KeyInf.Key[FocusedColumn]
end;

function TEasyItemInterfaced.GetImageIndexes(Column: Integer): TCommonImageIndexInteger;
begin
  Result := GetCommonImageIndex(Column, eikNormal);
end;

function TEasyItemInterfaced.GetImageList(Column: Integer; IconSize: TEasyImageSize): TCustomImageList;
var
  ImageList: IEasyImageList;
begin
  Result := nil;
  if Supports(DataInf, IEasyImageList, ImageList) then
    Result := ImageList.ImageList[Column, IconSize];
  if not Assigned(Result) then
    Result := DefaultImageList(IconSize)
end;

function TEasyItemInterfaced.GetImageOverlayIndexes(Column: Integer): TCommonImageIndexInteger;
begin
  Result := GetCommonImageIndex(Column, eikOverlay);
end;

function TEasyItemInterfaced.GetStateImageIndexes(Column: Integer): TCommonImageIndexInteger;
begin
  Result := GetCommonImageIndex(Column, eikState);
end;

function TEasyItemInterfaced.GetStateImageList(Column: Integer): TCustomImageList;
var
  ImageList: IEasyStateImageList;
begin
  Result := nil;
  if Supports(DataInf, IEasyStateImageList, ImageList) then
    Result := ImageList.ImageList[Column];
  if not Assigned(Result) then
    Result := DefaultStateImageList
end;

procedure TEasyItemInterfaced.ImageDraw(Column: TEasyColumn; ACanvas: TCanvas; const RectArray: TEasyRectArrayObject; AlphaBlender: TEasyAlphaBlender);
var
  ImageInf: IEasyCustomImage;
begin
  if Supports(DataInf, IEasyCustomImage, ImageInf) then
    ImageInf.DrawImage(Column, ACanvas, RectArray, AlphaBlender)
end;

procedure TEasyItemInterfaced.ImageDrawGetSize(Column: TEasyColumn; var ImageW, ImageH: Integer);
var
  ImageInf: IEasyCustomImage;
begin
  if Supports(DataInf, IEasyCustomImage, ImageInf) then
    ImageInf.GetSize(Column, ImageW, ImageH)
end;

procedure TEasyItemInterfaced.ImageDrawIsCustom(Column: TEasyColumn;
  var IsCustom: Boolean);
var
  ImageInf: IEasyCustomImage;
begin
  IsCustom := False;
  if Supports(DataInf, IEasyCustomImage, ImageInf) then
    ImageInf.CustomDrawn(Column, IsCustom)
end;

procedure TEasyItemInterfaced.SetCaptions(Column: Integer; Value: Widestring);
var
  CaptionInf: IEasyCaptionsEditable;
begin
  CaptionInf := nil;
  if Supports(DataInf, IEasyCaptionsEditable, CaptionInf) then
  begin
    CaptionInf.SetCaption(Column, Value);
    Invalidate(False)
  end
end;

procedure TEasyItemInterfaced.SetChecked(Value: Boolean);
var
  Checks: IEasyChecks;
begin
  Checks := nil;
  if Supports(DataInf, IEasyChecks, Checks) then
  begin
    Checks.SetChecked(0, Value);
    Invalidate(False)
  end
end;

procedure TEasyItemInterfaced.SetCommonImageIndex(Column: Integer; Kind: TEasyImageKind; Value: TCommonImageIndexInteger);
var
  ImageInf: IEasyImagesEditable;
begin
  ImageInf := nil;
  if Supports(DataInf, IEasyImagesEditable, ImageInf) then
    ImageInf.SetImageIndex(Column, Kind, Value)
end;

procedure TEasyItemInterfaced.SetDetailCount(Value: Integer);
var
  DetailsInf: IEasyDetailsEditable;
begin
  DetailsInf := nil;
  if Supports(DataInf, IEasyDetailsEditable, DetailsInf) then
  begin
    DetailsInf.DetailCount := Value;
    Invalidate(False)
  end
end;

procedure TEasyItemInterfaced.SetDetails(Line: Integer; Value: Integer);
var
  DetailsInf: IEasyDetailsEditable;
begin
  DetailsInf := nil;
  if Supports(DataInf, IEasyDetailsEditable, DetailsInf) then
  begin
    DetailsInf.Detail[Line] := Value;
    Invalidate(False)
  end
end;

procedure TEasyItemInterfaced.SetGroupKey(FocusedColumn: Integer;
  Value: LongWord);
var
  KeyInf: IEasyGroupKeyEditable;
begin
  KeyInf := nil;
  if Supports(DataInf, IEasyGroupKeyEditable, KeyInf) then
    KeyInf.Key[FocusedColumn] := Value;
end;

procedure TEasyItemInterfaced.SetImageIndexes(Column: Integer; Value: TCommonImageIndexInteger);
begin
  SetCommonImageIndex(Column, eikNormal, Value)
end;

procedure TEasyItemInterfaced.SetImageOverlayIndexes(Column: Integer; Value: TCommonImageIndexInteger);
begin
  SetCommonImageIndex(Column, eikOverlay, Value)
end;

procedure TEasyItemInterfaced.SetStateImageIndexes(Column: Integer; Value: TCommonImageIndexInteger);
begin
  SetCommonImageIndex(Column, eikState, Value)
end;

procedure TEasyItemInterfaced.ThumbnailDraw(ACanvas: TCanvas; ARect: TRect;
  AlphaBlender: TEasyAlphaBlender; var DoDefault: Boolean);
var
  ThumbInf: IEasyThumbnail;
begin
  if Supports(DataInf, IEasyThumbnail, ThumbInf) then
    ThumbInf.ThumbnailDraw(ACanvas, ARect, AlphaBlender, DoDefault)
end;

{ TEasyItem }

constructor TEasyItem.Create(ACollection: TEasyCollection);
begin
  inherited Create(ACollection);
  FVisibleIndexInGroup := -1;
end;

destructor TEasyItem.Destroy;
begin
  SetDestroyFlags;
  if Assigned(OwnerListview) then
  begin
    if OwnerListview.DragManager.DropTarget = Self then
      OwnerListview.DragManager.FDropTarget := nil;
    if OwnerListview.EditManager.TabMoveFocusItem = Self then
      OwnerListview.EditManager.TabMoveFocusItem := nil;
    if OwnerListview.Selection.AnchorItem = Self then
      OwnerListview.Selection.AnchorItem := nil;
    if OwnerListview.Selection.FocusedItem = Self then
      OwnerListview.Selection.FocusedItem := nil;
    if OwnerListview.IncrementalSearch.SearchItem = Self then
      OwnerListview.IncrementalSearch.SearchItem := nil;
    if OwnerListview.IncrementalSearch.NextSearchItem = Self then
      OwnerListview.IncrementalSearch.NextSearchItem := nil;
  end;
  Visible := False;  // will UnSelect and UnFocus if necessary
  inherited;
  ReleaseSelectionGroup;
  FreeAndNil(FView);
end;

function TEasyItem.AllowDrag(ViewportPt: TPoint): Boolean;
begin
  Result := View.AllowDrag(Self, ViewportPt)
end;

function TEasyItem.CanChangeBold(NewValue: Boolean): Boolean;
begin
  Result := True
end;

function TEasyItem.CanChangeCheck(NewValue: Boolean): Boolean;
begin
  if Enabled then
  begin
    Result := True;
    OwnerListview.DoItemCheckChanging(Self, Result);
  end
  else
   Result := False;
end;

function TEasyItem.CanChangeEnable(NewValue: Boolean): Boolean;
begin
  Result := True;
  OwnerListview.DoItemEnableChanging(Self, Result)
end;

function TEasyItem.CanChangeFocus(NewValue: Boolean): Boolean;
begin
  Result := True;
  OwnerListview.DoItemFocusChanging(Self, Result)
end;

function TEasyItem.CanChangeHotTracking(NewValue: Boolean): Boolean;
begin
  Result := True
end;

function TEasyItem.CanChangeSelection(NewValue: Boolean): Boolean;
begin
  Result := True;
  OwnerListview.DoItemSelectionChanging(Self, Result)
end;

function TEasyItem.CanChangeVisibility(NewValue: Boolean): Boolean;
begin
  if OwnerGroup.Visible or not NewValue then
  begin
    Result := True;
    OwnerListview.DoItemVisibilityChanging(Self, Result);
  end else
    Result := False
end;

function TEasyItem.DefaultStateImageList: TCustomImageList;
begin
  Result := OwnerListview.ImagesState
end;

function TEasyItem.EditAreaHitPt(ViewportPoint: TPoint): Boolean;
// Returns true if the passed point is in an area where the Item conciders it a
// place where inplace editing can be activated.
//
begin
  Result := View.EditAreaHitPt(Self, ViewportPoint)
end;

function TEasyItem.GetColumnPos: Integer;
//
// Returns the current column that the item is in within the grid or -1 if it
// is not visible
//
begin
  Result := -1;
  if Visible then
    Result := VisibleIndexInGroup mod OwnerGroup.Grid.ColumnCount
end;

function TEasyItem.GetDefaultViewClass: TEasyViewItemClass;
begin
  Result := TEasyViewItem;
  if Assigned(OwnerListview) then
  begin
    case OwnerListview.View of
      elsIcon: Result := TEasyViewIconItem;
      elsSmallIcon: Result := TEasyViewSmallIconItem;
      elsList: Result := TEasyViewListItem;
      elsReport: Result := TEasyViewReportItem;
      elsThumbnail: Result := TEasyViewThumbnailItem;
      elsTile: Result := TEasyViewTileItem;
      elsFilmStrip: Result := TEasyViewFilmStripItem;
      elsGrid: Result := TEasyViewGridItem;
    end
  end
end;

function TEasyItem.GetGroupKey(FocusedColumn: Integer): LongWord;
begin
  Result := 0
end;

function TEasyItem.GetIndex: Integer;
begin
  Result := FIndex
end;

function TEasyItem.GetOwnerGroup: TEasyGroup;
begin
  Result := OwnerItems.OwnerGroup
end;

function TEasyItem.GetOwnerItems: TEasyItems;
begin
  Result := TEasyItems(Collection)
end;

function TEasyItem.GetPaintInfo: TEasyPaintInfoItem;
begin
  Result := TEasyPaintInfoItem( inherited PaintInfo)
end;

function TEasyItem.GetRowPos: Integer;
//
// Returns the current column that the item is in within the grid or -1 if it
// is not visible
//
begin
  Result := -1;
  if Visible then
    Result := VisibleIndexInGroup mod OwnerGroup.Grid.RowCount
end;

function TEasyItem.GetStateImageIndex: TCommonImageIndexInteger;
begin
  Result := GetStateImageIndexes(0)
end;

function TEasyItem.GetView: TEasyViewItem;
begin
  if Assigned(FView) then
  begin
    if FView.ClassType <> ViewClass then
      FreeAndNil(FView);
  end;
  if not Assigned(FView) then
    FView := ViewClass.Create(OwnerGroup);  
  Result := FView
end;

function TEasyItem.GetViewClass: TEasyViewItemClass;
begin
  Result := nil;
  if Assigned(OwnerListview) then
    OwnerListview.DoItemCustomView(Self, OwnerListview.View, Result);
  if not Assigned(Result) then
    Result := GetDefaultViewClass;
end;

function TEasyItem.HitTestAt(ViewportPoint: TPoint; var HitInfo: TEasyItemHitTestInfoSet): Boolean;
var
  RectArray: TEasyRectArrayObject;
  R: TRect;
begin
  HitInfo := [];
  ItemRectArray(OwnerListview.Header.FirstColumn, OwnerListview.ScratchCanvas, RectArray);
  R := RectArray.IconRect;
  // Make the blank area between the image and text part of the image
  if OwnerListview.IsVertView then
     R.Bottom := R.Bottom + OwnerListview.PaintInfoItem.CaptionIndent
  else
    R.Right := R.Right + OwnerListview.PaintInfoItem.CaptionIndent;

  if PtInRect(R, ViewportPoint) then
    Include(HitInfo, ehtOnIcon);
  if PtInRect(RectArray.CheckRect, ViewportPoint) then
    Include(HitInfo, ehtOnCheck);
  if PtInRect(RectArray.FullFocusSelRect, ViewportPoint) then
    Include(HitInfo, ehtOnText);
  if PtInRect(RectArray.LabelRect, ViewportPoint) then
    Include(HitInfo, ehtOnLabel);
  if PtInRect(RectArray.ClickselectBoundsRect, ViewportPoint) then
    Include(HitInfo, ehtOnClickselectBounds);
  if PtInRect(RectArray.DragSelectBoundsRect, ViewportPoint) then
    Include(HitInfo, ehtOnDragSelectBounds);
  if PtInRect(RectArray.DragSelectBoundsRect, ViewportPoint) then
    Include(HitInfo, ehtOnDragSelectBounds);
  if PtInRect(RectArray.StateRect, ViewportPoint) then
    Include(HitInfo, ehtStateIcon);
  Result := HitInfo <> [];
end;

function TEasyItem.LocalPaintInfo: TEasyPaintInfoBasic;
begin
  Result := OwnerListview.PaintInfoItem
end;

function TEasyItem.SelectionHit(SelectViewportRect: TRect; SelectType: TEasySelectHitType): Boolean;
begin
  Result := View.SelectionHit(Self, SelectViewportRect, SelectType)
end;

function TEasyItem.SelectionHitPt(ViewportPoint: TPoint; SelectType: TEasySelectHitType): Boolean;
begin
  Result := View.SelectionHitPt(Self, ViewportPoint, SelectType)
end;

procedure TEasyItem.Edit(Column: TEasyColumn = nil);
begin
  OwnerListview.EditManager.BeginEdit(Self, Column)
end;

procedure TEasyItem.Freeing;
begin
  OwnerListview.DoItemFreeing(Self)
end;

procedure TEasyItem.GainingBold;
begin
  Invalidate(False)
end;

procedure TEasyItem.GainingCheck;
begin
  Inc(OwnerListview.CheckManager.FCount);
  OwnerListview.DoItemCheckChanged(Self);
  Invalidate(False);
end;

procedure TEasyItem.GainingEnable;
begin
  OwnerListview.DoItemEnableChanged(Self);
  Invalidate(False);
end;

procedure TEasyItem.GainingFocus;
begin
  OwnerListview.DoItemFocusChanged(Self);
  OwnerListview.Selection.FocusedItem := Self;
  Invalidate(False);
  if OwnerListview.Selection.GroupSelections then
  begin
    OwnerListview.Selection.BuildSelectionGroupings(False);
    OwnerListview.SafeInvalidateRect(nil, False);
  end
end;

procedure TEasyItem.GainingGhosted;
begin
//  OwnerListview.DoItemCheckChanged(Self);
  if State * [esosDestroying] = [] then
    Invalidate(False);
end;

procedure TEasyItem.GainingHilight;
begin
  Invalidate(True);
end;

procedure TEasyItem.GainingHotTracking(MousePos: TPoint);
begin
  OwnerListview.DoItemHotTrack(Self, ehsEnable, MousePos);
  Invalidate(True)
end;

procedure TEasyItem.GainingSelection;
begin
  OwnerListview.Selection.GainingSelection(Self);
  OwnerListview.DoItemSelectionChanged(Self);
  if OwnerListview.Selection.GroupSelections then
  begin
    OwnerListview.Selection.BuildSelectionGroupings(False);
    OwnerListview.SafeInvalidateRect(nil, False);
  end else
    Invalidate(False);
end;

procedure TEasyItem.GainingVisibility;
begin
//  Inc(OwnerGroup.FVisibleCount);
  OwnerListview.Groups.Rebuild;
  OwnerListview.DoItemVisibilityChanged(Self);
end;

procedure TEasyItem.Initialize;
begin
  OwnerListview.DoItemInitialize(Self);
  Include(FState, esosInitialized);
end;

procedure TEasyItem.Invalidate(ImmediateUpdate: Boolean);
var
  RectArray: TEasyRectArrayObject;
  R: TRect;
  Listview: TCustomEasyListview;
begin
  Listview := TEasyCollection( Collection).FOwnerListview;
  if Listview.HandleAllocated then
  begin
    R := Listview.Scrollbars.MapWindowRectToViewRect(Listview.ClientRect, True);
    // This is a bit odd as a long text caption is scrolled off the top of the window
    if IntersectRect(R, R, DisplayRect) then
    begin
      View.ItemRectArray(Self, nil, OwnerListview.ScratchCanvas, '', RectArray);
      UnionRect(R, RectArray.FocusChangeInvalidRect, DisplayRect);
      R := Listview.Scrollbars.MapViewRectToWindowRect(R, True);
      Listview.SafeInvalidateRect(@R, ImmediateUpdate);
    end
  end
end;

procedure TEasyItem.ItemRectArray(Column: TEasyColumn; ACanvas: TCanvas; var RectArray: TEasyRectArrayObject);
begin
  if Assigned(View) then
    View.ItemRectArray(Self, Column, ACanvas, '', RectArray)
  else
    FillChar(RectArray, SizeOf(RectArray), #0)
end;

procedure TEasyItem.LoadFromStream(S: TStream; var Version: Integer);
begin
  inherited LoadFromStream(S, Version);
  OwnerListview.DoItemLoadFromStream(Self, S, Version);

  // For new objects test the stream version first
  // if Version > X then
  // begin
  //   ReadStream....
  // end
end;

procedure TEasyItem.LosingBold;
begin
  Invalidate(False)
end;

procedure TEasyItem.LosingCheck;
begin
  Dec(OwnerListview.CheckManager.FCount);
  OwnerListview.DoItemCheckChanged(Self);
  if State * [esosDestroying] = [] then
    Invalidate(False);
end;

procedure TEasyItem.LosingEnable;
begin
  ReleaseSelectionGroup;
  OwnerListview.DoItemEnableChanged(Self);
  if State * [esosDestroying] = [] then
    Invalidate(False);
end;

procedure TEasyItem.LosingFocus;
begin
  if OwnerListview.Selection.FocusedItem = Self then
    OwnerListview.Selection.FocusedItem := nil;
  OwnerListview.DoItemFocusChanged(Self);
  // Need to repaint before Losing the focus
  if State * [esosDestroying] = [] then
  begin
    Include(FState, esosFocused);
    Invalidate(False);
    Exclude(FState, esosFocused);
    if OwnerListview.Selection.GroupSelections then
      OwnerListview.SafeInvalidateRect(nil, False);
  end;
end;

procedure TEasyItem.LosingGhosted;
begin
//  OwnerListview.DoItemCheckChanged(Self);
  if State * [esosDestroying] = [] then
    Invalidate(False);
end;

procedure TEasyItem.LosingHilight;
begin
  Invalidate(True);
end;

procedure TEasyItem.LosingHotTracking;
begin
  OwnerListview.DoItemHotTrack(Self, ehsDisable, Point(0, 0));
  Invalidate(True)
end;

procedure TEasyItem.LosingSelection;
begin
  OwnerListview.Selection.LosingSelection(Self);
  OwnerListview.DoItemSelectionChanged(Self);
  // Need to repaint before Losing the focus
  if State * [esosDestroying] = [] then
  begin
    if OwnerListview.Selection.GroupSelections then
    begin
      OwnerListview.Selection.BuildSelectionGroupings(False);
      OwnerListview.SafeInvalidateRect(nil, False);
    end else
      Invalidate(False);
  end
end;

procedure TEasyItem.LosingVisibility;
begin
//  Dec(OwnerGroup.FVisibleCount);
  ReleaseSelectionGroup;
  OwnerListview.Groups.Rebuild;
  OwnerListview.DoItemVisibilityChanged(Self);
end;

procedure TEasyItem.MakeVisible(Position: TEasyMakeVisiblePos);
var
  RectArray: TEasyRectArrayObject;
  R: TRect;
begin
  if Visible then
  begin
    View.ItemRectArray(Self, OwnerListview.Header.FirstVisibleColumn, OwnerListview.ScratchCanvas, '', RectArray);
    R := RectArray.BoundsRect;
  //  UnionRect(R, RectArray.FullFocusSelRect, RectArray.IconRect); // WL, 01/10/05: bottom line of focus rect was missing if item was made visible at the bottom of the window
 //   InflateRect(R, H_STRINGEDITORMARGIN div 2, V_STRINGEDITORMARGIN div 2);
    if not ContainsRect(OwnerListview.ClientInViewportCoords, R) then
    begin
      case Position of
        emvTop:
          begin
            OwnerListview.Scrollbars.OffsetY := R.Top;
            OwnerListview.Scrollbars.OffsetX := R.Left;
          end;
        emvMiddle:
          begin
            OwnerListview.Scrollbars.OffsetY := R.Top + OwnerListview.ClientHeight div 2;
            OwnerListview.Scrollbars.OffsetX := R.Left + OwnerListview.ClientWidth div 2;
          end;
        emvBottom:
          begin
            OwnerListview.Scrollbars.OffsetY := R.Bottom - OwnerListview.ClientHeight + OwnerListview.Header.RuntimeHeight;
            OwnerListview.Scrollbars.OffsetX := R.Right - OwnerListview.ClientWidth;
          end;
        emvAuto:
          begin
            if R.Bottom > OwnerListview.ClientInViewportCoords.Bottom then
              OwnerListview.Scrollbars.OffsetY := R.Bottom - OwnerListview.ClientHeight + OwnerListview.Header.RuntimeHeight
            else
            if R.Top < OwnerListview.ClientInViewportCoords.Top then
              OwnerListview.Scrollbars.OffsetY := R.Top;
            if R.Right > OwnerListview.ClientInViewportCoords.Right then
              OwnerListview.Scrollbars.OffsetX := R.Left
            else
            if R.Left < OwnerListview.ClientInViewportCoords.Left then
              OwnerListview.Scrollbars.OffsetX := R.Left;
          end
      end
    end
  end
end;

procedure TEasyItem.Paint(ACanvas: TCanvas; ViewportClipRect: TRect; Column: TEasyColumn; ForceSelectionRectDraw: Boolean);
begin
  View.Paint(Self, Column, ACanvas, ViewportClipRect, ForceSelectionRectDraw)
end;

procedure TEasyItem.ReleaseSelectionGroup;
var
  Temp: TEasySelectionGroupList;
begin
  if Assigned(FSelectionGroup) then
  begin
    Temp := FSelectionGroup;
    FSelectionGroup := nil;
    Temp.DecRef
  end
end;

procedure TEasyItem.SaveToStream(S: TStream; Version: Integer = EASYLISTVIEW_STREAM_VERSION);
begin
  inherited SaveToStream(S);
  OwnerListview.DoItemSaveToStream(Self, S, Version);

  // For new objects test the stream version first
  // if Version > X then
  // begin
  //   WriteStream....
  // end
end;

procedure TEasyItem.SetGroupKey(FocusedColumn: Integer; Value: LongWord);
begin

end;

procedure TEasyItem.SetPaintInfo(const Value: TEasyPaintInfoItem);
begin
  inherited PaintInfo := Value
end;

procedure TEasyItem.SetSelectionGroup(Value: TEasySelectionGroupList);
begin
  if Value <> FSelectionGroup then
  begin
    if Assigned(FSelectionGroup) then
      FSelectionGroup.DecRef;
    FSelectionGroup := Value
  end
end;

procedure TEasyItem.SetStateImageIndex(const Value: TCommonImageIndexInteger);
begin
  SetStateImageIndexes(0, Value)
end;

{ TEasyItemVirtual}
function TEasyItemVirtual.GetCaptions(Column: Integer): Widestring;
begin
  Result := '';
  OwnerListview.DoItemGetCaption(Self, Column, Result)
end;

function TEasyItemVirtual.GetDetailCount: Integer;
begin
  Result := 0;
  OwnerListview.DoItemGetTileDetailCount(Self, Result)
end;

function TEasyItemVirtual.GetDetails(Line: Integer): Integer;
begin
  Result := 0;
  OwnerListview.DoItemGetTileDetail(Self, Line, Result)
end;

function TEasyItemVirtual.GetGroupKey(FocusedColumn: Integer): LongWord;
begin
  Result := 0;
  OwnerListview.DoItemGetGroupKey(Self, FocusedColumn, Result)
end;

function TEasyItemVirtual.GetImageIndexes(Column: Integer): TCommonImageIndexInteger;
begin
  Result := -1;
  OwnerListview.DoItemGetImageIndex(Self, Column, eikNormal, Result)
end;

function TEasyItemVirtual.GetImageList(Column: Integer; IconSize: TEasyImageSize): TCustomImageList;
begin
  Result := nil;
  OwnerListview.DoItemGetImageList(Self, Column, Result);
  if not Assigned(Result) then
    Result := DefaultImageList(IconSize)
end;

function TEasyItemVirtual.GetImageOverlayIndexes(Column: Integer): TCommonImageIndexInteger;
begin
  Result := -1;
  OwnerListview.DoItemGetImageIndex(Self, Column, eikOverlay, Result)
end;

function TEasyItemVirtual.GetStateImageIndexes(Column: Integer): TCommonImageIndexInteger;
begin
  Result := -1;
  OwnerListview.DoItemGetImageIndex(Self, Column, eikState, Result)
end;

function TEasyItemVirtual.GetStateImageList(Column: Integer): TCustomImageList;
begin
  Result := nil;
  OwnerListview.DoItemGetStateImageList(Self, Column, Result);
  if not Assigned(Result) then
    Result := DefaultStateImageList
end;

procedure TEasyItemVirtual.ImageDrawIsCustom(Column: TEasyColumn; var IsCustom: Boolean);
begin
  IsCustom := False;
  OwnerListview.DoItemImageDrawIsCustom(Column, Self, IsCustom)
end;

procedure TEasyItemVirtual.ImageDraw(Column: TEasyColumn; ACanvas: TCanvas; const RectArray: TEasyRectArrayObject; AlphaBlender: TEasyAlphaBlender);
begin
  OwnerListview.DoItemImageDraw(Self, Column, ACanvas, RectArray, AlphaBlender)
end;

procedure TEasyItemVirtual.ImageDrawGetSize(Column: TEasyColumn; var ImageW: Integer; var ImageH: Integer);
begin
  OwnerListview.DoItemImageGetSize(Self, Column, ImageW, ImageH)
end;

procedure TEasyItemVirtual.SetCaptions(Column: Integer; Value: Widestring);
begin
  OwnerListview.DoItemSetCaption(Self, Column, Value);
  Invalidate(False)
end;

procedure TEasyItemVirtual.SetDetailCount(Value: Integer);
begin
  OwnerListview.DoItemSetTileDetailCount(Self, Value);
  Invalidate(False)
end;

procedure TEasyItemVirtual.SetDetails(Line: Integer; Value: Integer);
begin
  OwnerListview.DoItemSetTileDetail(Self, Line, Value);
  Invalidate(False)
end;

procedure TEasyItemVirtual.SetGroupKey(FocusedColumn: Integer; Value: LongWord);
begin
  OwnerListview.DoItemSetGroupKey(Self, FocusedColumn, Value);
end;

procedure TEasyItemVirtual.SetImageIndexes(Column: Integer; Value: TCommonImageIndexInteger);
begin
  OwnerListview.DoItemSetImageIndex(Self, Column, eikNormal, Value);
  Invalidate(False)
end;

procedure TEasyItemVirtual.SetImageOverlayIndexes(Column: Integer; Value: TCommonImageIndexInteger);
begin
  OwnerListview.DoItemSetImageIndex(Self, Column, eikOverlay, Value);
  Invalidate(False)
end;

procedure TEasyItemVirtual.SetStateImageIndexes(Column: Integer; Value: TCommonImageIndexInteger);
begin
  OwnerListview.DoItemSetImageIndex(Self, Column, eikState, Value);
  Invalidate(False)
end;

procedure TEasyItemVirtual.ThumbnailDraw(ACanvas: TCanvas; ARect: TRect;
  AlphaBlender: TEasyAlphaBlender; var DoDefault: Boolean);
begin
  OwnerListview.DoItemThumbnailDraw(Self, ACanvas, ARect, AlphaBlender, DoDefault)
end;

{ TEasyItemStored }

constructor TEasyItemStored.Create(ACollection: TEasyCollection);
begin
  inherited Create(ACollection);
  FDataHelper := TEasyItemDynamicDataHelper.Create;
end;

destructor TEasyItemStored.Destroy;
begin
  SetDestroyFlags;
  inherited Destroy;
  FreeAndNil(FDataHelper);
end;

function TEasyItemStored.GetCaptions(Column: Integer): Widestring;
begin
  Result := '';
  if Assigned(DataHelper) then
    Result := DataHelper.Captions[Column]
end;

function TEasyItemStored.GetDetailCount: Integer;
begin
  Result := OwnerListview.PaintInfoItem.TileDetailCount
end;

function TEasyItemStored.GetDetails(Line: Integer): Integer;
begin
  Result := 0;
  if Assigned(DataHelper) then
    Result := DataHelper.Details[Line]
end;

function TEasyItemStored.GetGroupKey(FocusedColumn: Integer): LongWord;
begin
   Result := 0;
  if Assigned(DataHelper) then
    Result := DataHelper.GroupKey[FocusedColumn]
end;

function TEasyItemStored.GetImageIndexes(Column: Integer): TCommonImageIndexInteger;
begin
  Result := -1;
  if Assigned(DataHelper) then
    Result := DataHelper.ImageIndexes[Column]
end;

function TEasyItemStored.GetImageList(Column: Integer; IconSize: TEasyImageSize): TCustomImageList;
begin
  Result := DefaultImageList(IconSize)
end;

function TEasyItemStored.GetImageOverlayIndexes(Column: Integer): TCommonImageIndexInteger;
begin
  Result := -1;
  if Assigned(DataHelper) then
    Result := DataHelper.ImageOverlayIndexes[Column]
end;

function TEasyItemStored.GetStateImageIndexes(Column: Integer): TCommonImageIndexInteger;
begin
  Result := -1;
  if Assigned(DataHelper) then
    Result := DataHelper.StateImageIndexes[Column]
end;

function TEasyItemStored.GetStateImageList(Column: Integer): TCustomImageList;
begin
  Result := DefaultStateImageList
end;

procedure TEasyItemStored.ImageDraw(Column: TEasyColumn; ACanvas: TCanvas; const RectArray: TEasyRectArrayObject; AlphaBlender: TEasyAlphaBlender);
begin
  OwnerListview.DoItemImageDraw(Self, Column, ACanvas, RectArray, AlphaBlender)
end;

procedure TEasyItemStored.ImageDrawGetSize(Column: TEasyColumn; var ImageW: Integer; var ImageH: Integer);
begin
  OwnerListview.DoItemImageGetSize(Self, Column, ImageW, ImageH)
end;

procedure TEasyItemStored.ImageDrawIsCustom(Column: TEasyColumn;
  var IsCustom: Boolean);
begin
  IsCustom := False;
  OwnerListview.DoItemImageDrawIsCustom(Column, Self, IsCustom)
end;

procedure TEasyItemStored.LoadFromStream(S: TStream; var Version: Integer);
begin
  inherited LoadFromStream(S, Version);
  if Assigned(DataHelper) then
    DataHelper.LoadFromStream(S, Version);

  // For new objects test the stream version first
  // if Version > X then
  // begin
  //   ReadStream....
  // end
end;

procedure TEasyItemStored.SaveToStream(S: TStream; Version: Integer = EASYLISTVIEW_STREAM_VERSION);
begin
  inherited SaveToStream(S);
  if Assigned(DataHelper) then
    DataHelper.SaveToStream(S, Version);

  // For new objects test the stream version first
  // if Version > X then
  // begin
  //   WriteStream....
  // end
end;

procedure TEasyItemStored.SetCaptions(Column: Integer; Value: Widestring);
begin
  if Assigned(DataHelper) then
  begin
    DataHelper.Captions[Column] := Value;
    Invalidate(False);
  end;
end;

procedure TEasyItemStored.SetDetailCount(Value: Integer);
begin
  OwnerListview.PaintInfoItem.TileDetailCount := Value
end;

procedure TEasyItemStored.SetDetails(Column: Integer; Value: Integer);
begin
  if Assigned(DataHelper) then
  begin
    DataHelper.Details[Column] := Value;
    Invalidate(False);
  end;
end;

procedure TEasyItemStored.SetGroupKey(FocusedColumn: Integer; Value: LongWord);
begin
  if Assigned(DataHelper) then
    DataHelper.GroupKey[FocusedColumn] := Value;
end;

procedure TEasyItemStored.SetImageIndexes(Column: Integer; Value: TCommonImageIndexInteger);
begin
  if Assigned(DataHelper) then
  begin
    DataHelper.ImageIndexes[Column] := Value;
    Invalidate(False);
  end;
end;

procedure TEasyItemStored.SetImageOverlayIndexes(Column: Integer; Value: TCommonImageIndexInteger);
begin
  if Assigned(DataHelper) then
  begin
    DataHelper.ImageOverlayIndexes[Column] := Value;
    Invalidate(False);
  end;
end;

procedure TEasyItemStored.SetStateImageIndexes(Column: Integer; Value: TCommonImageIndexInteger);
 begin
  if Assigned(DataHelper) then
  begin
    DataHelper.StateImageIndexes[Column] := Value;
    Invalidate(False);
  end;
end;

procedure TEasyItemStored.ThumbnailDraw(ACanvas: TCanvas; ARect: TRect;
  AlphaBlender: TEasyAlphaBlender; var DoDefault: Boolean);
begin
  OwnerListview.DoItemThumbnailDraw(Self, ACanvas, ARect, AlphaBlender, DoDefault)
end;

{ TEasyDynamicDataHelper}

function TEasyDynamicDataHelper.GetCaptions(Index: Integer): Widestring;
begin
  if Index < Length(CaptionArray) then
    Result := CaptionArray[Index]
  else
    Result := ''
end;

function TEasyDynamicDataHelper.GetDetails(Index: Integer): Integer;
begin
  if Index < Length(DetailArray) then
    Result := DetailArray[Index]
  else
    Result := 0
end;

function TEasyDynamicDataHelper.GetImageIndexes(Index: Integer): Integer;
begin
  if Index < Length(ImageIndexArray) then
    Result := ImageIndexArray[Index]
  else
    Result := -1
end;

function TEasyDynamicDataHelper.GetImageOverlayIndexes(Index: Integer): Integer;
begin
  if Index < Length(OverlayIndexArray) then
    Result := OverlayIndexArray[Index]
  else
    Result := -1
end;

procedure TEasyDynamicDataHelper.Clear;
begin
  SetLength(FCaptionArray, 0);
  SetLength(FDetailArray, 0);
  SetLength(FImageIndexArray, 0);
  SetLength(FOverlayIndexArray, 0);
end;

procedure TEasyDynamicDataHelper.LoadFromStream(S: TStream; Version: Integer);
begin
  LoadWideStrArrayFromStream(S, FCaptionArray);
  LoadIntArrayFromStream(S, FDetailArray);
  LoadIntArrayFromStream(S, FImageIndexArray);
  LoadIntArrayFromStream(S, FOverlayIndexArray);
end;

procedure TEasyDynamicDataHelper.LoadIntArrayFromStream(S: TStream; var AnArray: TCommonIntegerDynArray);
var
  Len, i: Integer;
begin
  Setlength(AnArray, 0);
  S.ReadBuffer(Len, SizeOf(Len));
  Setlength(AnArray, Len);
  for i := 0 to Len - 1 do
    S.ReadBuffer(AnArray[i], SizeOf(Integer))
end;

procedure TEasyDynamicDataHelper.LoadWideStrArrayFromStream(S: TStream; var AnArray: TCommonWideStringDynArray);
var
  Len, Count, i: Integer;
begin
  Setlength(AnArray, 0);
  S.ReadBuffer(Len, SizeOf(Len));
  Setlength(AnArray, Len);
  for i := 0 to Len - 1 do
  begin
    S.ReadBuffer(Count, SizeOf(Count));
    SetLength(AnArray[i], Count);
    S.ReadBuffer(PWideChar(AnArray[i])^, Count * 2);
  end
end;

procedure TEasyDynamicDataHelper.SaveIntArrayToStream(S: TStream; var AnArray: TCommonIntegerDynArray);
var
  Len, i: Integer;
begin
  Len := Length(AnArray);
  S.WriteBuffer(Len, SizeOf(Len));
  for i := 0 to Len - 1 do
    S.WriteBuffer(AnArray[i], SizeOf(Integer))
end;

procedure TEasyDynamicDataHelper.SaveWideStrArrayToStream(S: TStream; var AnArray: TCommonWideStringDynArray);
var
  Len, i, Count: Integer;
begin
  Len := Length(AnArray);
  S.WriteBuffer(Len, SizeOf(Len));
  for i := 0 to Len - 1 do
  begin
    Count := Length(AnArray[i]);
    S.WriteBuffer(Count, SizeOf(Count));
    S.WriteBuffer(PWideChar(AnArray[i])^, Count * 2);
  end
end;

procedure TEasyDynamicDataHelper.SaveToStream(S: TStream; Version: Integer);
begin
  SaveWideStrArrayToStream(S, FCaptionArray);
  SaveIntArrayToStream(S, FDetailArray);
  SaveIntArrayToStream(S, FImageIndexArray);
  SaveIntArrayToStream(S, FOverlayIndexArray);
end;

procedure TEasyDynamicDataHelper.SetCaptions(Index: Integer; Value: Widestring);
var
  OldLen, i: Integer;
begin
  if Index >= Length(CaptionArray) then
  begin
    OldLen := Length(CaptionArray);
    SetLength(FCaptionArray, Index + 1);
    for i := OldLen to Length(CaptionArray) - 1 do
      CaptionArray[i] := ''
  end;
  CaptionArray[Index] := Value
end;

procedure TEasyDynamicDataHelper.SetDetails(Index: Integer; Value: Integer);
var
  OldLen, i: Integer;
begin
  if Index >= Length(DetailArray) then
  begin
    OldLen := Length(DetailArray);
    SetLength(FDetailArray, Index + 1);
    for i := OldLen to Length(DetailArray) - 1 do
      DetailArray[i] := 0
  end;
  DetailArray[Index] := Value
end;

procedure TEasyDynamicDataHelper.SetImageIndexes(Index: Integer; Value: Integer);
var
  OldLen, i: Integer;
begin
  if Index >= Length(ImageIndexArray) then
  begin
    OldLen := Length(ImageIndexArray);
    SetLength(FImageIndexArray, Index + 1);
    for i := OldLen to Length(ImageIndexArray) - 1 do
      ImageIndexArray[i] := -1
  end;
  ImageIndexArray[Index] := Value
end;

procedure TEasyDynamicDataHelper.SetImageOverlayIndexes(Index: Integer; Value: Integer);
var
  OldLen, i: Integer;
begin
  if Index >= Length(OverlayIndexArray) then
  begin
    OldLen := Length(OverlayIndexArray);
    SetLength(FOverlayIndexArray, Index + 1);
    for i := OldLen to Length(OverlayIndexArray) - 1 do
      OverlayIndexArray[i] := -1
  end;
  OverlayIndexArray[Index] := Value
end;

{ TEasyGroupStored }
constructor TEasyBaseGroupStored.Create(ACollection: TEasyCollection);
begin
  inherited Create(ACollection);
  FDataHelper := TEasyDynamicDataHelper.Create;
end;

destructor TEasyBaseGroupStored.Destroy;
begin
  SetDestroyFlags;
  inherited Destroy;
  FreeAndNil(FDataHelper);
end;

function TEasyBaseGroupStored.GetCaptions(Line: Integer): Widestring;
begin
  Result := DataHelper.Captions[Line]
end;

function TEasyBaseGroupStored.GetDetailCount: Integer;
begin
  Result := OwnerListview.PaintInfoGroup.CaptionLines
end;

function TEasyBaseGroupStored.GetDetails(Line: Integer): Integer;
begin
  Result := DataHelper.Details[Line]
end;

function TEasyBaseGroupStored.GetImageIndexes(Column: Integer): TCommonImageIndexInteger;
begin
  Result := DataHelper.ImageIndexes[Column]
end;

function TEasyBaseGroupStored.GetImageList(Column: Integer; IconSize: TEasyImageSize): TCustomImageList;
begin
  Result := DefaultImageList(IconSize)
end;

function TEasyBaseGroupStored.GetImageOverlayIndexes(Column: Integer): TCommonImageIndexInteger;
begin
  Result := DataHelper.ImageOverlayIndexes[Column]
end;

procedure TEasyBaseGroupStored.ImageDraw(Column: TEasyColumn; ACanvas: TCanvas; const RectArray: TEasyRectArrayObject; AlphaBlender: TEasyAlphaBlender);
begin
  OwnerListview.DoGroupImageDraw(Self, ACanvas, RectArray, AlphaBlender)
end;

procedure TEasyBaseGroupStored.ImageDrawGetSize(Column: TEasyColumn; var ImageW: Integer; var ImageH: Integer);
begin
  OwnerListview.DoGroupImageGetSize(Self, ImageW, ImageH)
end;

procedure TEasyBaseGroupStored.ImageDrawIsCustom(Column: TEasyColumn;
  var IsCustom: Boolean);
begin
  IsCustom := False;
  OwnerListview.DoGroupImageDrawIsCustom(Self, IsCustom)
end;

procedure TEasyBaseGroupStored.LoadFromStream(S: TStream; var Version: Integer);
begin
  inherited LoadFromStream(S, Version);
  DataHelper.LoadFromStream(S, Version);

  // For new objects test the stream version first
  // if Version > X then
  // begin
  //   ReadStream....
  // end
end;

procedure TEasyBaseGroupStored.SaveToStream(S: TStream; Version: Integer = EASYLISTVIEW_STREAM_VERSION);
begin
  inherited SaveToStream(S);
  DataHelper.SaveToStream(S, Version);

  // For new objects test the stream version first
  // if Version > X then
  // begin
  //   WriteStream....
  // end
end;

procedure TEasyBaseGroupStored.SetCaptions(Column: Integer; Value: Widestring);
begin
  DataHelper.Captions[Column] := Value;
  Invalidate(False)
end;

procedure TEasyBaseGroupStored.SetDetailCount(Value: Integer);
begin
  OwnerListview.PaintInfoGroup.CaptionLines := Value
end;

procedure TEasyBaseGroupStored.SetDetails(Line: Integer; Value: Integer);
begin
  DataHelper.Details[Line] := Value;
  Invalidate(False)
end;

procedure TEasyBaseGroupStored.SetImageIndexes(Column: Integer; Value: TCommonImageIndexInteger);
begin
  DataHelper.ImageIndexes[Column] := Value;
  Invalidate(False)
end;

procedure TEasyBaseGroupStored.SetImageOverlayIndexes(Column: Integer; Value: TCommonImageIndexInteger);
begin
  DataHelper.ImageOverlayIndexes[Column] := Value;
  Invalidate(False)
end;

procedure TEasyBaseGroupStored.ThumbnailDraw(ACanvas: TCanvas; ARect: TRect;
  AlphaBlender: TEasyAlphaBlender; var DoDefault: Boolean);
begin
  // not implemented
end;

{ TEasyGroupVirtual}
function TEasyGroupVirtual.GetCaptions(Line: Integer): Widestring;
begin
  Result := '';
  OwnerListview.DoGroupGetCaption(Self, Result)
end;

function TEasyGroupVirtual.GetDetailCount: Integer;
begin
  Result := 0;
  OwnerListview.DoGroupGetDetailCount(Self, Result)
end;

function TEasyGroupVirtual.GetDetails(Line: Integer): Integer;
begin
  Result := 0;
  OwnerListview.DoGroupGetDetail(Self, Line, Result)
end;

function TEasyGroupVirtual.GetImageIndexes(Column: Integer): TCommonImageIndexInteger;
begin
  Result := -1;
  OwnerListview.DoGroupGetImageIndex(Self, eikNormal, Result)
end;

function TEasyGroupVirtual.GetImageList(Column: Integer; IconSize: TEasyImageSize): TCustomImageList;
begin
  Result := nil;
  OwnerListview.DoGroupGetImageList(Self, Result);
  if not Assigned(Result) then
    Result := DefaultImageList(IconSize)
end;

function TEasyGroupVirtual.GetImageOverlayIndexes(Column: Integer): TCommonImageIndexInteger;
begin
  OwnerListview.DoGroupGetImageIndex(Self, eikOverlay, Result)
end;

procedure TEasyGroupVirtual.ImageDraw(Column: TEasyColumn; ACanvas: TCanvas; const RectArray: TEasyRectArrayObject; AlphaBlender: TEasyAlphaBlender);
begin
  OwnerListview.DoGroupImageDraw(Self, ACanvas, RectArray, AlphaBlender)
end;

procedure TEasyGroupVirtual.ImageDrawGetSize(Column: TEasyColumn; var ImageW: Integer; var ImageH: Integer);
begin
  OwnerListview.DoGroupImageGetSize(Self, ImageW, ImageH)
end;

procedure TEasyGroupVirtual.ImageDrawIsCustom(Column: TEasyColumn;
  var IsCustom: Boolean);
begin
  IsCustom := False;
  OwnerListview.DoGroupImageDrawIsCustom(Self, IsCustom)
end;

procedure TEasyGroupVirtual.SetCaptions(Column: Integer; Value: Widestring);
begin
  OwnerListview.DoGroupSetCaption(Self, Value);
  Invalidate(False)
end;

procedure TEasyGroupVirtual.SetDetailCount(Value: Integer);
begin
  OwnerListview.DoGroupSetDetailCount(Self, Value);
  Invalidate(False)
end;

procedure TEasyGroupVirtual.SetDetails(Line: Integer; Value: Integer);
begin
  OwnerListview.DoGroupSetDetail(Self, Line, Value);
  Invalidate(False)
end;

procedure TEasyGroupVirtual.SetImageIndexes(Column: Integer; Value: TCommonImageIndexInteger);
begin
  OwnerListview.DoGroupSetImageIndex(Self, eikNormal, Value);
  Invalidate(False)
end;

procedure TEasyGroupVirtual.SetImageOverlayIndexes(Column: Integer; Value: TCommonImageIndexInteger);
begin
  OwnerListview.DoGroupSetImageIndex(Self, eikOverlay, Value);
  Invalidate(False)
end;

procedure TEasyGroupVirtual.ThumbnailDraw(ACanvas: TCanvas; ARect: TRect;
  AlphaBlender: TEasyAlphaBlender; var DoDefault: Boolean);
begin
  // not Implemented
end;

{ TEasyGroupInterfaced}
function TEasyGroupInterfaced.GetCaptions(Line: Integer): Widestring;
var
  CaptionInf: IEasyCaptions;
begin
  CaptionInf := nil;
  if Supports(DataInf, IEasyCaptions, CaptionInf) then
    Result := CaptionInf.Captions[Line]
end;

function TEasyGroupInterfaced.GetDetailCount: Integer;
var
  DetailInf: IEasyDetails;
begin
  Result := 0;
  DetailInf := nil;
  if Supports(DataInf, IEasyDetails, DetailInf) then
    Result := DetailInf.GetDetailCount
end;

function TEasyGroupInterfaced.GetDetails(Line: Integer): Integer;
var
  DetailInf: IEasyDetails;
begin
  Result := 0;
  DetailInf := nil;
  if Supports(DataInf, IEasyDetails, DetailInf) then
    Result := DetailInf.Detail[Line]
end;

function TEasyGroupInterfaced.GetImageIndexes(Column: Integer): TCommonImageIndexInteger;
var
  ImageInf: IEasyImages;
begin
  Result := -1;
  ImageInf := nil;
  if Supports(DataInf, IEasyImages, ImageInf) then
    Result := ImageInf.ImageIndexes[Column, eikNormal]
end;

function TEasyGroupInterfaced.GetImageList(Column: Integer; IconSize: TEasyImageSize): TCustomImageList;
var
  ImageList: IEasyImageList;
begin
  Result := nil;
  if Supports(DataInf, IEasyImageList, ImageList) then
    Result := ImageList.ImageList[Column, IconSize];
  if not Assigned(Result) then
    Result := DefaultImageList(IconSize)
end;

function TEasyGroupInterfaced.GetImageOverlayIndexes(Column: Integer): TCommonImageIndexInteger;
var
  ImageInf: IEasyImages;
begin
  Result := -1;
  ImageInf := nil;
  if Supports(DataInf, IEasyImages, ImageInf) then
    Result := ImageInf.ImageIndexes[Column, eikOverlay]
end;

procedure TEasyGroupInterfaced.ImageDraw(Column: TEasyColumn; ACanvas: TCanvas; const RectArray: TEasyRectArrayObject; AlphaBlender: TEasyAlphaBlender);
var
  ImageInf: IEasyCustomImage;
begin
  if Supports(DataInf, IEasyCustomImage, ImageInf) then
    ImageInf.DrawImage(Column, ACanvas, RectArray, AlphaBlender)
end;

procedure TEasyGroupInterfaced.ImageDrawGetSize(Column: TEasyColumn; var ImageW: Integer; var ImageH: Integer);
var
  ImageInf: IEasyCustomImage;
begin
  if Supports(DataInf, IEasyCustomImage, ImageInf) then
    ImageInf.GetSize(Column, ImageW, ImageH)
end;

procedure TEasyGroupInterfaced.ImageDrawIsCustom(Column: TEasyColumn;
  var IsCustom: Boolean);
var
  ImageInf: IEasyCustomImage;
begin
  IsCustom := False;
  if Supports(DataInf, IEasyCustomImage, ImageInf) then
    ImageInf.CustomDrawn(Column, IsCustom)
end;

procedure TEasyGroupInterfaced.SetCaptions(Column: Integer; Value: Widestring);
var
  CaptionInf: IEasyCaptionsEditable;
begin
  CaptionInf := nil;
  if Supports(DataInf, IEasyCaptionsEditable, CaptionInf) then
  begin
    CaptionInf.SetCaption(Column, Value);
    Invalidate(False)
  end
end;

procedure TEasyGroupInterfaced.SetDetailCount(Value: Integer);
var
  DetailsInf: IEasyDetailsEditable;
begin
  DetailsInf := nil;
  if Supports(DataInf, IEasyDetailsEditable, DetailsInf) then
  begin
    DetailsInf.DetailCount := Value;
    Invalidate(False)
  end
end;

procedure TEasyGroupInterfaced.SetDetails(Line: Integer; Value: Integer);
var
  DetailsInf: IEasyDetailsEditable;
begin
  DetailsInf := nil;
  if Supports(DataInf, IEasyDetailsEditable, DetailsInf) then
  begin
    DetailsInf.Detail[Line] := Value;
    Invalidate(False)
  end
end;

procedure TEasyGroupInterfaced.SetImageIndexes(Column: Integer; Value: TCommonImageIndexInteger);
var
  ImageInf: IEasyImagesEditable;
begin
  ImageInf := nil;
  if Supports(DataInf, IEasyImagesEditable, ImageInf) then
  begin
    ImageInf.ImageIndexes[Column, eikNormal] := Value;
    Invalidate(False)
  end
end;

procedure TEasyGroupInterfaced.SetImageOverlayIndexes(Column: Integer; Value: TCommonImageIndexInteger);
var
  ImageInf: IEasyImagesEditable;
begin
  ImageInf := nil;
  if Supports(DataInf, IEasyImagesEditable, ImageInf) then
  begin
    ImageInf.ImageIndexes[Column, eikOverlay] := Value;
    Invalidate(False)
  end
end;

procedure TEasyGroupInterfaced.ThumbnailDraw(ACanvas: TCanvas; ARect: TRect;
  AlphaBlender: TEasyAlphaBlender; var DoDefault: Boolean);
begin
  // not supported
end;

{ TEasyColumnStored }

constructor TEasyColumnStored.Create(ACollection: TEasyCollection);
begin
  inherited Create(ACollection);
  FDataHelper := TEasyDynamicDataHelper.Create;
end;

destructor TEasyColumnStored.Destroy;
begin
  SetDestroyFlags;
  inherited Destroy;
  FreeAndNil(FDataHelper);
end;

function TEasyColumnStored.GetCaptions(Line: Integer): Widestring;
begin
  Result := DataHelper.Captions[Line]
end;

function TEasyColumnStored.GetDetailCount: Integer;
begin
  Result := OwnerListview.PaintInfoColumn.CaptionLines
end;

function TEasyColumnStored.GetDetails(Line: Integer): Integer;
begin
  Result := DataHelper.Details[Index]
end;

function TEasyColumnStored.GetImageIndexes(Column: Integer): TCommonImageIndexInteger;
begin
  Result := DataHelper.ImageIndexes[Column]
end;

function TEasyColumnStored.GetImageList(Column: Integer; IconSize: TEasyImageSize): TCustomImageList;
begin
  Result := DefaultImageList(IconSize)
end;

function TEasyColumnStored.GetImageOverlayIndexes(Column: Integer): TCommonImageIndexInteger;
begin
  Result := DataHelper.ImageOverlayIndexes[Column]
end;

procedure TEasyColumnStored.ImageDraw(Column: TEasyColumn; ACanvas: TCanvas; const RectArray: TEasyRectArrayObject; AlphaBlender: TEasyAlphaBlender);
begin
  OwnerListview.DoColumnImageDraw(Self, ACanvas, RectArray, AlphaBlender)
end;

procedure TEasyColumnStored.ImageDrawGetSize(Column: TEasyColumn; var ImageW: Integer; var ImageH: Integer);
begin
  OwnerListview.DoColumnImageGetSize(Self, ImageW, ImageH)
end;

procedure TEasyColumnStored.ImageDrawIsCustom(Column: TEasyColumn;
  var IsCustom: Boolean);
begin
  IsCustom := False;
  OwnerListview.DoColumnImageDrawIsCustom(Self, IsCustom)
end;

procedure TEasyColumnStored.LoadFromStream(S: TStream; var Version: Integer);
begin
  inherited LoadFromStream(S, Version);
  DataHelper.LoadFromStream(S, Version);

  // For new objects test the stream version first
  // if Version > X then
  // begin
  //   ReadStream....
  // end
end;

procedure TEasyColumnStored.SaveToStream(S: TStream; Version: Integer = EASYLISTVIEW_STREAM_VERSION);
begin
  inherited SaveToStream(S);
  DataHelper.SaveToStream(S, Version);

  // For new objects test the stream version first
  // if Version > X then
  // begin
  //   WriteStream....
  // end
end;

procedure TEasyColumnStored.SetCaptions(Column: Integer; Value: Widestring);
begin
  DataHelper.Captions[Column] := Value;
  Invalidate(False)
end;

procedure TEasyColumnStored.SetDetailCount(Value: Integer);
begin
  OwnerListview.PaintInfoColumn.CaptionLines := Value;
end;

procedure TEasyColumnStored.SetDetails(Line: Integer; Value: Integer);
begin
  DataHelper.Details[Line] := Value;
  Invalidate(False)
end;

procedure TEasyColumnStored.SetImageIndexes(Column: Integer; Value: TCommonImageIndexInteger);
begin
  DataHelper.ImageIndexes[Column] := Value;
  Invalidate(False)
end;

procedure TEasyColumnStored.SetImageOverlayIndexes(Column: Integer; Value: TCommonImageIndexInteger);
begin
  DataHelper.ImageOverlayIndexes[Column] := Value;
  Invalidate(False)
end;

procedure TEasyColumnStored.ThumbnailDraw(ACanvas: TCanvas; ARect: TRect;
  AlphaBlender: TEasyAlphaBlender; var DoDefault: Boolean);
begin
  // not implemented
end;

function TEasyColumnInterfaced.GetCaptions(Line: Integer): Widestring;
var
  CaptionInf: IEasyCaptions;
begin
  CaptionInf := nil;
  if Supports(DataInf, IEasyCaptions, CaptionInf) then
    Result := CaptionInf.Captions[Line]
end;

function TEasyColumnInterfaced.GetDetailCount: Integer;
var
  DetailInf: IEasyDetails;
begin
  Result := 0;
  DetailInf := nil;
  if Supports(DataInf, IEasyDetails, DetailInf) then
    Result := DetailInf.GetDetailCount
end;

function TEasyColumnInterfaced.GetDetails(Line: Integer): Integer;
var
  DetailInf: IEasyDetails;
begin
  Result := 0;
  DetailInf := nil;
  if Supports(DataInf, IEasyDetails, DetailInf) then
    Result := DetailInf.Detail[Line]
end;

function TEasyColumnInterfaced.GetImageIndexes(Column: Integer): TCommonImageIndexInteger;
var
  ImageInf: IEasyImages;
begin
  Result := -1;
  ImageInf := nil;
  if Supports(DataInf, IEasyImages, ImageInf) then
    Result := ImageInf.ImageIndexes[Column, eikNormal]
end;

function TEasyColumnInterfaced.GetImageList(Column: Integer; IconSize: TEasyImageSize): TCustomImageList;
var
  ImageList: IEasyImageList;
begin
  Result := nil;
  if Supports(DataInf, IEasyImageList, ImageList) then
    Result := ImageList.ImageList[Column, IconSize];
  if not Assigned(Result) then
    Result := DefaultImageList(IconSize)
end;

function TEasyColumnInterfaced.GetImageOverlayIndexes(Column: Integer): TCommonImageIndexInteger;
var
  ImageInf: IEasyImages;
begin
  Result := -1;
  ImageInf := nil;
  if Supports(DataInf, IEasyImages, ImageInf) then
    Result := ImageInf.ImageIndexes[Column, eikOverlay]
end;

procedure TEasyColumnInterfaced.ImageDraw(Column: TEasyColumn; ACanvas: TCanvas; const RectArray: TEasyRectArrayObject; AlphaBlender: TEasyAlphaBlender);
var
  ImageInf: IEasyCustomImage;
begin
  if Supports(DataInf, IEasyCustomImage, ImageInf) then
    ImageInf.DrawImage(Column, ACanvas, RectArray, AlphaBlender)
end;

procedure TEasyColumnInterfaced.ImageDrawGetSize(Column: TEasyColumn; var ImageW: Integer; var ImageH: Integer);
var
  ImageInf: IEasyCustomImage;
begin
  if Supports(DataInf, IEasyCustomImage, ImageInf) then
    ImageInf.GetSize(Column, ImageW, ImageH)
end;

procedure TEasyColumnInterfaced.ImageDrawIsCustom(Column: TEasyColumn;
  var IsCustom: Boolean);
var
  ImageInf: IEasyCustomImage;
begin
  IsCustom := False;
  if Supports(DataInf, IEasyCustomImage, ImageInf) then
    ImageInf.CustomDrawn(Column, IsCustom)
end;

procedure TEasyColumnInterfaced.SetCaptions(Column: Integer; Value: Widestring);
var
  CaptionInf: IEasyCaptionsEditable;
begin
  CaptionInf := nil;
  if Supports(DataInf, IEasyCaptionsEditable, CaptionInf) then
    CaptionInf.SetCaption(Column, Value);
  Invalidate(False);
end;

procedure TEasyColumnInterfaced.SetDetailCount(Value: Integer);
var
  DetailsInf: IEasyDetailsEditable;
begin
  DetailsInf := nil;
  if Supports(DataInf, IEasyDetailsEditable, DetailsInf) then
  begin
    DetailsInf.DetailCount := Value;
    Invalidate(False)
  end
end;

procedure TEasyColumnInterfaced.SetDetails(Line: Integer; Value: Integer);
var
  DetailsInf: IEasyDetailsEditable;
begin
  DetailsInf := nil;
  if Supports(DataInf, IEasyDetailsEditable, DetailsInf) then
  begin
    DetailsInf.Detail[Line] := Value;
    Invalidate(False)
  end
end;

procedure TEasyColumnInterfaced.SetImageIndexes(Column: Integer; Value: TCommonImageIndexInteger);
var
  ImageInf: IEasyImagesEditable;
begin
  ImageInf := nil;
  if Supports(DataInf, IEasyImagesEditable, ImageInf) then
  begin
    ImageInf.ImageIndexes[Column, eikNormal] := Value;
    Invalidate(False)
  end
end;

procedure TEasyColumnInterfaced.SetImageOverlayIndexes(Column: Integer; Value: TCommonImageIndexInteger);
var
  ImageInf: IEasyImagesEditable;
begin
  ImageInf := nil;
  if Supports(DataInf, IEasyImagesEditable, ImageInf) then
  begin
    ImageInf.ImageIndexes[Column, eikOverlay] := Value;
    Invalidate(False)
  end
end;

procedure TEasyColumnInterfaced.ThumbnailDraw(ACanvas: TCanvas; ARect: TRect;
  AlphaBlender: TEasyAlphaBlender; var DoDefault: Boolean);
begin
  // not implemented
end;

{ TEasyColumnVirtual}
function TEasyColumnVirtual.GetCaptions(Line: Integer): Widestring;
begin
  Result := '';
  OwnerListview.DoColumnGetCaption(Self, Line, Result)
end;

function TEasyColumnVirtual.GetDetailCount: Integer;
begin
  Result := 0;
  OwnerListview.DoColumnGetDetailCount(Self, Result)
end;

function TEasyColumnVirtual.GetDetails(Line: Integer): Integer;
begin
  Result := 0;
  OwnerListview.DoColumnGetDetail(Self, Line, Result)
end;

function TEasyColumnVirtual.GetImageIndexes(Column: Integer): TCommonImageIndexInteger;
begin
  Result := -1;
  OwnerListview.DoColumnGetImageIndex(Self, eikNormal, Result)
end;

function TEasyColumnVirtual.GetImageList(Column: Integer; IconSize: TEasyImageSize): TCustomImageList;
begin
  Result := nil;
  OwnerListview.DoColumnGetImageList(Self, Result);
  if not Assigned(Result) then
    Result := DefaultImageList(IconSize)
end;

function TEasyColumnVirtual.GetImageOverlayIndexes(Column: Integer): TCommonImageIndexInteger;
begin
  Result := -1;
  OwnerListview.DoColumnGetImageIndex(Self, eikOverlay, Result)
end;

procedure TEasyColumnVirtual.ImageDraw(Column: TEasyColumn; ACanvas: TCanvas; const RectArray: TEasyRectArrayObject; AlphaBlender: TEasyAlphaBlender);
begin
  OwnerListview.DoColumnImageDraw(Self, ACanvas, RectArray, AlphaBlender)
end;

procedure TEasyColumnVirtual.ImageDrawGetSize(Column: TEasyColumn; var ImageW: Integer; var ImageH: Integer);
begin
  OwnerListview.DoColumnImageGetSize(Self, ImageW, ImageH)
end;

procedure TEasyColumnVirtual.ImageDrawIsCustom(Column: TEasyColumn;
  var IsCustom: Boolean);
begin
  IsCustom := False;
  OwnerListview.DoColumnImageDrawIsCustom(Self, IsCustom)
end;

procedure TEasyColumnVirtual.SetCaptions(Column: Integer; Value: Widestring);
begin
  OwnerListview.DoColumnSetCaption(Self, Value);
  Invalidate(False)
end;

procedure TEasyColumnVirtual.SetDetailCount(Value: Integer);
begin
  OwnerListview.DoColumnSetDetailCount(Self, Value);
  Invalidate(False)
end;

procedure TEasyColumnVirtual.SetDetails(Line: Integer; Value: Integer);
begin
  OwnerListview.DoColumnSetDetail(Self, Line, Value);
  Invalidate(False)
end;

procedure TEasyColumnVirtual.SetImageIndexes(Column: Integer; Value: TCommonImageIndexInteger);
begin
  OwnerListview.DoColumnSetImageIndex(Self, eikNormal, Value);
  Invalidate(False)
end;

procedure TEasyColumnVirtual.SetImageOverlayIndexes(Column: Integer; Value: TCommonImageIndexInteger);
begin
  OwnerListview.DoColumnSetImageIndex(Self, eikOverlay, Value);
  Invalidate(False)
end;

procedure TEasyColumnVirtual.ThumbnailDraw(ACanvas: TCanvas; ARect: TRect;
  AlphaBlender: TEasyAlphaBlender; var DoDefault: Boolean);
begin
  // not Implemented
end;

{ TEasyPaintInfoBaseItem }

constructor TEasyPaintInfoBaseItem.Create(AnOwner: TCustomEasyListview);
begin
  inherited Create(AnOwner);
  FTileDetailCount := 1;
  FGridLineColor := clBtnFace;
end;

procedure TEasyPaintInfoBaseItem.SetGridLineColor(const Value: TColor);
begin
  if FGridLineColor <> Value then
  begin
    FGridLineColor := Value;
    OwnerListview.SafeInvalidateRect(nil, False)
  end
end;

procedure TEasyPaintInfoBaseItem.SetGridLines(const Value: Boolean);
begin
  if FGridLines <> Value then
  begin
    FGridLines := Value;
    OwnerListview.SafeInvalidateRect(nil, False)
  end
end;

procedure TEasyPaintInfoBaseItem.SetHideCaption(const Value: Boolean);
begin
  if FHideCaption <> Value then
  begin
    FHideCaption := Value;
    OwnerListview.SafeInvalidateRect(nil, False)
  end
end;

procedure TEasyPaintInfoBaseItem.SetTileDetailCount(Value: Integer);
begin
  if Value <> FTileDetailCount then
  begin
    FTileDetailCount := Value;
    OwnerListview.SafeInvalidateRect(nil, False)
  end
end;

{ TColumnPos }

function TColumnPos.Get(Index: Integer): TEasyColumn;
begin
  Result := inherited Get(Index)
end;

procedure TColumnPos.Put(Index: Integer; Item: TEasyColumn);
begin
  inherited Put(Index, Item)
end;

{ TEasyHeaderDragManager }

constructor TEasyHeaderDragManager.Create(AnOwner: TCustomEasyListview);
begin
  inherited Create(AnOwner);
  FEnableDragImage := True;
  FDragImageWidth := 200;
  FDragImageHeight := 300;
  FEnabled := False;
  DefaultImageEvent := DefaultImage;
  DragMode := dmManual;
  DragCursor := crDrag
end;

function TEasyHeaderDragManager.DoPtInAutoScrollDownRegion(WindowPoint: TPoint): Boolean;
begin
  Result := False
end;

function TEasyHeaderDragManager.DoPtInAutoScrollLeftRegion(WindowPoint: TPoint): Boolean;
begin
  Result := (WindowPoint.X < AutoScrollMargin) and (OwnerListview.View = elsReport)
end;

function TEasyHeaderDragManager.DoPtInAutoScrollRightRegion(WindowPoint: TPoint): Boolean;
begin
  Result := (WindowPoint.X > (OwnerListview.ClientWidth - AutoScrollMargin)) and (OwnerListview.View = elsReport)
end;

function TEasyHeaderDragManager.DoPtInAutoScrollUpRegion(WindowPoint: TPoint): Boolean;
begin
  Result := False
end;

function TEasyHeaderDragManager.GetDragCursor: TCursor;
begin
  Result := OwnerListview.DragCursor
end;


function TEasyHeaderDragManager.GetDragMode: TDragMode;
begin
  Result := FDragMode;
end;

procedure TEasyHeaderDragManager.DefaultImage(Sender: TCustomEasyListview; Image: TBitmap; DragStartPt: TPoint; var HotSpot: TPoint; var TransparentColor: TColor; var Handled: Boolean);
// Generates the drag image for the dragging items from the Easy Control
var
  R, ViewR: TRect;
  Pt: TPoint;
begin
  if Assigned(Image) then
  begin
    TransparentColor := OwnerListview.Color;
    Image.Canvas.Brush.Color := Header.Color;

    Image.Canvas.FillRect(Rect(0, 0, Image.Width, Image.Height));
    R := Column.DisplayRect;

    HotSpot.X := DragStartPt.X - Column.DisplayRect.Left;
    HotSpot.Y := DragStartPt.Y - Column.DisplayRect.Top;

    if R.Left < 0 then
      HotSpot.X := HotSpot.X + R.Left;
    if R.Top < 0 then
      HotSpot.Y := HotSpot.Y + R.Top;

    ViewR := OwnerListview.ClientRect;
    OffsetRect(ViewR, OwnerListview.Scrollbars.OffsetX, 0);
    IntersectRect(R, R, ViewR);
    SetViewPortOrgEx(Image.Canvas.Handle, -R.Left, 0, @Pt);
    Header.PaintTo(Image.Canvas, R);
    SetViewPortOrgEx(Image.Canvas.Handle, Pt.X, Pt.Y, nil);
    Handled := True;
  end
end;

procedure TEasyHeaderDragManager.DoDrag(Canvas: TCanvas; WindowPoint: TPoint; KeyState: TCommonKeyStates; var Effects: TCommonDropEffect);
var
  Temp: TEasyColumn;
begin
  Effects := cdeNone;
  if AllowDrop then
  begin
    if WindowPoint.Y < Header.Height then
    begin
      Effects := cdeMove;
      if OwnerListview.ScrollHeaderHorz then
        Inc(WindowPoint.X, OwnerListview.Scrollbars.OffsetX);
      Temp := Header.Columns.ColumnByPoint(WindowPoint);
      if Temp <> TargetColumn then
      begin
        if Assigned(TargetColumn) then
        begin
          Exclude(TargetColumn.FState, esosDropTarget);
          TargetColumn.Invalidate(True);
        end;
        TargetColumn := Temp;
        if Assigned(TargetColumn) then
        begin
          Include(TargetColumn.FState, esosDropTarget);
          TargetColumn.Invalidate(True);
        end
      end
    end else
    begin
      if Assigned(TargetColumn) then
      begin
        Exclude(TargetColumn.FState, esosDropTarget);
        TargetColumn.Invalidate(True);
        TargetColumn := nil;
      end;
    end
  end
end;

procedure TEasyHeaderDragManager.DoDragBegin(WindowPoint: TPoint; KeyStates: TCommonKeyStates);
var
  DropSource: IDropSource;
  DataObject: TEasyDataObjectManager;
  DataObjInf: ICommonExtractObj;
  Image: TBitmap;
  TransparentColor: TColor;
  dwEffect, ImageWidth, ImageHeight: Integer;
  AvailableEffects: TCommonDropEffects;
  DragResultEffect: TCommonOLEDragResult;
  AllowDrag, Handled: Boolean;
  DragResult: HRESULT;
  HotPtOffset: TPoint;
  DataObjectInf: IDataObject;
begin
  if Enabled then
  begin
    inherited;
    if DragType = edtOLE then
    begin
      DataObjectInf := nil;
      DataObject := nil;
      AvailableEffects := [cdeNone];
      dwEffect := 0;
      AllowDrag := False;
      if Self is TEasyOLEDragManager then
        OwnerListview.DoOLEGetDataObject(DataObjectInf);  
      if not Assigned(DataObjectInf) then
      begin
        DataObject := TEasyDataObjectManager.Create;
        // Get a reference right away so it won't be freed from under us
        DataObjectInf := DataObject as IDataObject;
        DataObject.Listview := OwnerListview;
      end;
      DoOLEDragStart(DataObjectInf, AvailableEffects, AllowDrag);
      if AllowDrag then
      begin
        if (DataObjectInf.QueryInterface(ICommonExtractObj, DataObjInf) = S_OK) then
          DataObject := DataObjInf.GetObj as TEasyDataObjectManager;
        if EnableDragImage and Assigned(DataObject) and OwnerListview.UseInternalDragImage then
        begin
          Image := TBitmap.Create;
          try
            ImageWidth := 0;
            ImageHeight := 0;
            ImageSize(ImageWidth, ImageHeight);
            Image.Width := ImageWidth;
            Image.Height := ImageHeight;
            Image.PixelFormat := pf32Bit;
            TransparentColor := clWindow;
            Handled := False;
            DoGetDragImage(Image, WindowPoint, HotPtOffset, TransparentColor, Handled);
            if not Handled and Assigned(DefaultImageEvent) then
              DefaultImageEvent(OwnerListview, Image, WindowPoint, HotPtOffset, TransparentColor, Handled);
            if Handled then
              DataObject.AssignDragImage(Image, HotPtOffset, TransparentColor);
          finally
            Image.Free
          end
        end;
        DropSource := TEasyDropSourceManager.Create(OwnerListview);
        DragResult := OwnerListview.ExecuteDragDrop(AvailableEffects, DataObjectInf, DropSource, dwEffect);
        if DragResult = DRAGDROP_S_DROP then
          DragResultEffect := cdrDrop
        else
        if DragResult = DRAGDROP_S_CANCEL then
          DragResultEffect := cdrCancel
        else
          DragResultEffect := cdrError;
        // Set it back to the default drag manager to be ready for a drop
        DoOLEDragEnd(DataObjectInf, DragResultEffect, DropEffectToDropEffectStates(dwEffect), KeyStates);
        DataObject.Listview := nil;
      end
    end else
      VCLDragStart
  end;
  DataObjectInf := nil
end;

procedure TEasyHeaderDragManager.DoDragDrop(WindowPoint: TPoint; KeyState: TCommonKeyStates; var Effects: TCommonDropEffect);
begin
  if Assigned(TargetColumn) then
    Column.Position :=  TargetColumn.Position;
  DoDragEnd(nil, WindowPoint, KeyState);
end;

procedure TEasyHeaderDragManager.DoDragEnd(Canvas: TCanvas; WindowPoint: TPoint; KeyState: TCommonKeyStates);
begin
  if Assigned(TargetColumn) then
  begin
    Exclude(TargetColumn.FState, esosDropTarget);
    TargetColumn.Invalidate(True);
  end;
  TargetColumn := nil; 
end;

procedure TEasyHeaderDragManager.DoDragEnter(const DataObject: IDataObject; Canvas: TCanvas; WindowPoint: TPoint; KeyState: TCommonKeyStates; var Effects: TCommonDropEffect);
var
  Medium: TStgMedium;
  DataPtr: PHeaderClipData;
begin        
  AllowDrop := False;
  Effects := cdeNone;
  if Succeeded(DataObject.GetData(HeaderClipFormat, Medium)) then
  begin
    DataPtr := GlobalLock(Medium.hGlobal);
    try
      AllowDrop := (DataPtr^.Thread = GetCurrentThread) and (DataPtr^.Listview = OwnerListview)
    finally
      GlobalUnlock(Medium.hGlobal);
      ReleaseStgMedium(Medium)
    end
  end;
end;

procedure TEasyHeaderDragManager.DoOLEDragEnd(const ADataObject: IDataObject; DragResult: TCommonOLEDragResult; ResultEffect: TCommonDropEffects; KeyStates: TCommonKeyStates);
begin
  Header.ClearStates
end;

procedure TEasyHeaderDragManager.DoOLEDragStart(const ADataObject: IDataObject; var AvailableEffects: TCommonDropEffects; var AllowDrag: Boolean);
var
  Medium: TStgMedium;
  DataPtr: PHeaderClipData;
begin
  AllowDrag := Enabled;
  AvailableEffects := [cdeMove];
  FillChar(Medium, SizeOf(Medium), #0);
  Medium.tymed := TYMED_HGLOBAL;
  Medium.hGlobal := GlobalAlloc(GHND, SizeOf(THeaderClipData));
  try
    DataPtr := GlobalLock(Medium.hGlobal);
    DataPtr^.Column := Column;
    DataPtr^.Listview := Header.OwnerListview;
    DataPtr^.Thread := GetCurrentThread;
  finally
    GlobalUnLock(Medium.hGlobal)
  end;
  // Give the block to the IDataObject to dispose of
  ADataObject.SetData(HeaderClipFormat, Medium, True);
end;

procedure TEasyHeaderDragManager.ImageSize(var Width, Height: Integer);
begin
  Width := Column.Width;
  Height := OwnerListview.Header.Height
end;

procedure TEasyHeaderDragManager.SetDragCursor(Value: TCursor);
begin
  OwnerListview.DragCursor := Value
end;

procedure TEasyHeaderDragManager.SetDragMode(Value: TDragMode);
begin
  FDragMode := Value
end;

procedure TEasyHeaderDragManager.SetDragType(Value: TEasyDragType);
begin
  if FDragType <> Value then
  begin
    if Value = edtVCL then
      Registered := False;
    FDragType := Value;
  end
end;

{ TEasyItemDynamicDataHelper }
function TEasyItemDynamicDataHelper.GetGroupKey(Index: Integer): LongWord;
begin
  if Index < Length(GroupKeyArray) then
    Result := GroupKeyArray[Index]
  else
    Result := EGT_FIRSTLETTER
end;

function TEasyItemDynamicDataHelper.GetStateImageIndexes(Index: Integer): TCommonImageIndexInteger;
begin
   if Index < Length(StateImageArray) then
    Result := StateImageArray[Index]
  else
    Result := -1
end;

procedure TEasyItemDynamicDataHelper.LoadFromStream(S: TStream; Version: Integer);
begin
  inherited LoadFromStream(S, Version);
  LoadIntArrayFromStream(S, FGroupKeyArray);
  if Version > 2 then
  begin
    LoadIntArrayFromStream(S, FStateImageArray);
  end
end;

procedure TEasyItemDynamicDataHelper.SaveToStream(S: TStream; Version: Integer);
begin
  inherited SaveToStream(S, Version);
  SaveIntArrayToStream(S, FGroupKeyArray);
  SaveIntArrayToStream(S, FStateImageArray);
end;

procedure TEasyItemDynamicDataHelper.SetGroupKey(Index: Integer;
  Value: LongWord);
var
  OldLen, i: Integer;
begin
  if Index >= Length(GroupKeyArray) then
  begin
    OldLen := Length(GroupKeyArray);
    SetLength(FGroupKeyArray, Index + 1);
    for i := OldLen to Length(GroupKeyArray) - 1 do
      GroupKeyArray[i] := EGT_FIRSTLETTER
  end;
  GroupKeyArray[Index] := Value
end;

procedure TEasyItemDynamicDataHelper.SetStateImageIndexes(Index: Integer; Value: TCommonImageIndexInteger);
var
  OldLen, i: Integer;
begin
  if Index >= Length(StateImageArray) then
  begin
    OldLen := Length(StateImageArray);
    SetLength(FStateImageArray, Index + 1);
    for i := OldLen to Length(StateImageArray) - 1 do
      StateImageArray[i] := -1
  end;
  StateImageArray[Index] := Value
end;

{ TEasySorter}

constructor TEasySorter.Create(AnOwner: TEasySortManager);
begin
  inherited Create;
  FOwner := AnOwner;
end;

destructor TEasyOwnedPersistentView.Destroy;
begin
  FreeAndNil(FCanvasStore);
  inherited Destroy;
end;

function TEasyOwnedPersistentView.GetCanvasStore: TEasyCanvasStore;
begin
  if not Assigned(FCanvasStore) then
    FCanvasStore := TEasyCanvasStore.Create;
  Result := FCanvasStore
end;

procedure TEasyOwnedPersistentView.PaintCheckboxCore(CheckType: TEasyCheckType;
  OwnerListView: TCustomEasyListView; ACanvas: TCanvas; ARect: TRect; IsEnabled,
  IsChecked, IsHot, IsFlat, IsHovering, IsPending: Boolean; Obj: TEasyCollectionItem; Size: Integer);
var
  {$IFDEF SpTBX}
  CheckState: TCheckBoxState;
  {$ENDIF}
  {$IFDEF USETHEMES}
  uState: Longword;
  Part: BUTTONPARTS;
  {$ENDIF}
  Pt: TPoint;
begin
  {$IFDEF SpTBX}
  if not ((CheckType = ectNone) or (CheckType = ettNoneWithSpace)) then
  begin
    if CurrentTheme.Name <> 'Default' then
    begin
      if IsChecked then
        CheckState := cbChecked
      else
        CheckState := cbUnChecked;
      InflateRect(ARect, -1, -1);
      SpDrawXPCheckBoxGlyph(ACanvas, ARect, IsEnabled, CheckState, IsHovering, IsPending, True, thtTBX);
      Exit;
    end
  end;
  {$ENDIF}

  {$IFDEF USETHEMES}
  if OwnerListview.DrawWithThemes then
    begin
      uState := 0;
      Part := 0;
      case CheckType of
      ectBox:
        begin
          Part := BP_CHECKBOX;
          if IsEnabled then
          begin
            if IsHovering then
            begin
              if IsChecked then
                uState := CBS_CHECKEDHOT
              else
                uState := CBS_UNCHECKEDHOT
            end else
            if IsPending then
            begin
              if IsChecked then
                uState := CBS_CHECKEDPRESSED
              else
                uState := CBS_UNCHECKEDPRESSED
            end else
            begin
              if IsChecked then
                uState := CBS_CHECKEDNORMAL
              else
                uState := CBS_UNCHECKEDNORMAL
            end
          end else
          begin
            if IsChecked then
              uState := CBS_CHECKEDDISABLED
            else
              uState := CBS_UNCHECKEDDISABLED
          end
        end;
      ectRadio:
        begin
          Part := BP_RADIOBUTTON;
          if IsEnabled then
          begin
            if IsHovering then
            begin
              if IsChecked then
                uState := RBS_CHECKEDHOT
              else
                uState := RBS_UNCHECKEDHOT
            end else
            if IsPending then
            begin
              if IsChecked then
                uState := RBS_CHECKEDPRESSED
              else
                uState := RBS_UNCHECKEDPRESSED
            end else
            begin
              if IsChecked then
                uState := RBS_CHECKEDNORMAL
              else
                uState := RBS_UNCHECKEDNORMAL
            end
          end else
          begin
            if IsChecked then
              uState := RBS_CHECKEDDISABLED
            else
              uState := RBS_UNCHECKEDDISABLED
          end
        end
    end;

    DrawThemeBackground(OwnerListview.Themes.ButtonTheme, ACanvas.Handle, Part, uState, ARect, nil);
    Exit;
  end;
  {$ENDIF}

  case CheckType of
    ectBox:
      begin
        Pt := ARect.TopLeft;
        if IsEnabled then
        begin
          if IsFlat then
            DrawCheckBox(ACanvas, Pt, Size, clWhite, clBtnFace, clBlack, clBlack, clBlack, clBlack, IsChecked, IsEnabled, IsHot)
          else
            DrawCheckBox(ACanvas, Pt, Size, clWhite, clBtnFace, clBtnShadow, clBtnHighlight, cl3DDkShadow, cl3DLight, IsChecked, IsEnabled, IsHot)
        end else
        begin
          if IsFlat then
            DrawCheckBox(ACanvas, Pt, Size, clBtnFace, clBtnFace, clBtnShadow, clBtnShadow, clBtnShadow, clBtnShadow, IsChecked, IsEnabled, IsHot)
          else
            DrawCheckBox(ACanvas, Pt, Size, clBtnFace, clBtnFace, clBtnShadow, clBtnHighlight, cl3DDkShadow, cl3DLight, IsChecked, IsEnabled, IsHot)
        end;
      end;
    ectRadio:
      begin
        Pt :=ARect.TopLeft;
        if IsEnabled then
        begin
          if IsFlat then
            DrawRadioButton(ACanvas, Pt, Size, clWhite, clBtnFace, clBlack, clBlack, clBlack, clBlack, IsChecked, IsEnabled, IsHot)
          else
            DrawRadioButton(ACanvas, Pt, Size, clWhite, clBtnFace, clBtnShadow, clBtnHighlight, cl3DDkShadow, cl3DLight, IsChecked, IsEnabled, IsHot)
        end else
        begin
          if IsFlat then
            DrawRadioButton(ACanvas, Pt, Size, clBtnFace, clBtnFace, clBtnShadow, clBtnShadow, clBtnShadow, clBtnShadow, IsChecked, IsEnabled, IsHot)
          else
            DrawRadioButton(ACanvas, Pt, Size, clBtnFace, clBtnFace, clBtnShadow, clBtnHighlight, cl3DDkShadow, cl3DLight, IsChecked, IsEnabled, IsHot)
        end;
     end
  end
end;

constructor TEasyIncrementalSearchManager.Create(AnOwner: TCustomEasyListview);
begin
  inherited Create(AnOwner);
  FItemType := eisiVisible;
  FResetTime := 2000;
end;

destructor TEasyIncrementalSearchManager.Destroy;
begin
  EndTimer;
  inherited
end;

function TEasyIncrementalSearchManager.IsSearching: Boolean;
//
// Returns true if the manager is in within a search sequence
//
begin
  Result := eissSearching in State
end;

procedure TEasyIncrementalSearchManager.ClearSearch;
//
// Clears the search item and does any necessary clean up
begin
  FSearchItem := nil;
  FNextSearchItem := nil;
end;

procedure TEasyIncrementalSearchManager.EndTimer;
//
// Kills and destroys the Timer
//
begin
  if eissTimerRunning in State then
  begin
    if KillTimer(0, hTimer) then
    begin
      hTimer := 0;
      Exclude(FState, eissTimerRunning);
      DisposeStub(FTimerStub);
    end else
      Exception.Create('Can not Destroy Incremental Search Timer');
  end
end;

procedure TEasyIncrementalSearchManager.HandleWMChar(var Msg: TWMChar);
//
// The WM_CHAR message is passed to the manager to handle the incremental
// search
//

  function CodePageFromLocale(Language: LCID): Integer;
  var
    Buf: array[0..6] of Char;
  begin
    // Determines the code page for a given locale.
    // Unfortunately there is no easier way than this, currently.
    GetLocaleInfo(Language, LOCALE_IDEFAULTANSICODEPAGE, Buf, 6);
    Result := StrToIntDef(Buf, GetACP);
  end;

  function KeyUnicode(C: Char): WideChar;
  // Converts the given character into its corresponding Unicode character
  // depending on the active keyboard layout.
  begin
    MultiByteToWideChar(CodePageFromLocale(GetKeyboardLayout(0) and $FFFF),
      MB_USEGLYPHCHARS, @C, 1, @Result, 1);
  end;


  procedure WrapToListStart(First, Last: TItemEnumFirstLast; var SearchItem: TEasyItem);
  begin
    case Direction of
      eisdForward: SearchItem := First;
      eisdBackward: SearchItem := Last;
    end;
  end;

  procedure SearchToListEnd(Next, Prev :TItemNextEnum; var SearchItem: TEasyItem);
  var
    CompareResult: Integer;
  begin
    CompareResult := -1;
    while (CompareResult <> 0) and Assigned(SearchItem) do
    begin
      if Assigned(SearchItem) and (SearchBuffer <> #13) then
        OwnerListview.DoIncrementalSearch(SearchItem, SearchBuffer, CompareResult);
      if CompareResult <> 0 then
      begin
        case Direction of
          eisdForward: SearchItem := Next(SearchItem);
          eisdBackward: SearchItem := Prev(SearchItem);
        end;
      end
    end;
  end;

  procedure SearchToItem(Next, Prev :TItemNextEnum; TargetItem: TEasyItem; var SearchItem: TEasyItem);
  var
    CompareResult: Integer;
  begin
    CompareResult := -1;
    while (CompareResult <> 0) and (SearchItem <> TargetItem) do
    begin
      if Assigned(SearchItem) and (SearchBuffer <> #13) then
        OwnerListview.DoIncrementalSearch(SearchItem, SearchBuffer, CompareResult);
      if CompareResult <> 0 then
      begin
        case Direction of
          eisdForward: SearchItem := OwnerListview.Groups.NextItem(SearchItem);
          eisdBackward: SearchItem := OwnerListview.Groups.PrevItem(SearchItem);
        end
      end
    end;
    
    if CompareResult <> 0 then
      SearchItem := nil
  end;

  procedure SearchList(Next, Prev :TItemNextEnum; First, Last: TItemEnumFirstLast; var SearchItem: TEasyItem);
  var
    StartItem: TEasyItem;
  begin
    StartItem := SearchItem;
    SearchToListEnd(Next, Prev, SearchItem);
    // Keep wrapping if not found
    if not Assigned(SearchItem) then
    begin
      WrapToListStart(First, Last, SearchItem);
      SearchToItem(Next, Prev, StartItem, SearchItem);
    end
  end;

var
  CompareResult: Integer;
  Shift: TShiftState;
begin
  if Enabled and IsSearching then
  begin
    ResetTimer;
    Shift := KeyDataToShiftState(Msg.KeyData);
    if ((Shift * [ssAlt, ssCtrl]) = []) and (Msg.CharCode > 31) then
    begin
      CompareResult := 0;
      SearchBuffer := SearchBuffer + KeyUnicode(Char( Msg.CharCode));

      if not Assigned(NextSearchItem) then
        LoadFutureItem;

      // Mimic how Explorer works
      if Length(SearchBuffer) >= 2 then
      begin
        if SearchBuffer[1] = SearchBuffer[2] then
        begin
          SearchBuffer := SearchBuffer[1];
          if (StartType <> eissStartOver) and Assigned(NextSearchItem) then
            SearchItem := NextSearchItem
        end
      end else
      begin
        if (Length(SearchBuffer) = 1) and (StartType <> eissStartOver) and Assigned(NextSearchItem) then
          SearchItem := NextSearchItem
      end;

      if Assigned(SearchItem) then
      begin
        if SearchBuffer <> #13 then
          OwnerListview.DoIncrementalSearch(SearchItem, SearchBuffer, CompareResult);
        ResetTimer;
        if CompareResult <> 0 then
        begin
          case ItemType of
          eisiAll:               // Search All items in list
            begin
              SearchList(OwnerListview.Groups.NextItem,
                         OwnerListview.Groups.PrevItem,
                         OwnerListview.Groups.FirstItem,
                         OwnerListview.Groups.LastItem,
                         FSearchItem);

            end;
          eisiInitializedOnly:    // Search only initialized items
            begin
              SearchList(OwnerListview.Groups.NextInitializedItem,
                         OwnerListview.Groups.PrevInitializedItem,
                         OwnerListview.Groups.FirstInitializedItem,
                         OwnerListview.Groups.LastInitializedItem,
                         FSearchItem);
            end;
          eisiVisible:            // Search only visible items
            begin
              SearchList(OwnerListview.Groups.NextVisibleItem,
                         OwnerListview.Groups.PrevVisibleItem,
                         OwnerListview.Groups.FirstVisibleItem,
                         OwnerListview.Groups.LastVisibleItem,
                         FSearchItem);
            end
          end
        end;
        if Assigned(SearchItem) then
        begin
          // Found the item
          OwnerListview.Selection.ClearAll;
          OwnerListview.Selection.FocusedItem := SearchItem;
          if Assigned(SearchItem) then
          begin
            SearchItem.Selected := True;
            SearchItem.MakeVisible(emvAuto)
          end;
          if StartType <> eissStartOver then
            LoadFutureItem;
        end
      end
    end else
      ClearSearch
  end
end;

procedure TEasyIncrementalSearchManager.HandleWMKeyDown(var Msg:
TWMKeyDown);
//
// The WM_KEYDOWN message is passed to the manager to handle the incremental
// search
//
var
  Shift: TShiftState;
begin
  if Enabled then
  begin
    Shift := KeyDataToShiftState(Msg.KeyData);
 //   if (Shift * [ssCtrl, ssAlt] = []) and ((Msg.CharCode > VK_HELP) and (Msg.CharCode < VK_LWIN)) or ((Msg.CharCode > VK_APPS) and (Msg.CharCode < VK_F1) )  then
      StartSearch
  end
end;

procedure TEasyIncrementalSearchManager.LoadFutureItem;
begin
  NextSearchItem := SearchItem;
  case ItemType of
    eisiAll:
      begin
        SearchPreloadItem(OwnerListview.Groups.NextItem,
          OwnerListview.Groups.PrevItem,
          OwnerListview.Groups.FirstItem,
          OwnerListview.Groups.LastItem,
          FNextSearchItem);
      end;
    eisiInitializedOnly:    // Search only initialized items
      begin
        SearchPreloadItem(OwnerListview.Groups.NextInitializedItem,
          OwnerListview.Groups.PrevInitializedItem,
          OwnerListview.Groups.FirstInitializedItem,
          OwnerListview.Groups.LastInitializedItem,
          FNextSearchItem);
      end;
    eisiVisible:            // Search only visible items
      begin
        SearchPreloadItem(OwnerListview.Groups.NextVisibleItem,
          OwnerListview.Groups.PrevVisibleItem,
          OwnerListview.Groups.FirstVisibleItem,
          OwnerListview.Groups.LastVisibleItem,
          FNextSearchItem);
      end;
  end;
end;

procedure TEasyIncrementalSearchManager.ResetSearch;
//
// Ends and resets all search criteria
//
begin
  SearchBuffer := '';
  if StartType <> eissLastHit then
    ClearSearch;
  Exclude(FState, eissSearching);
  EndTimer;
end;

procedure TEasyIncrementalSearchManager.ResetTimer;
//
// Resets the timer to another FResetTime interval without stopping the current
// search process
//
begin
  EndTimer;
  StartTimer;
end;

procedure TEasyIncrementalSearchManager.SearchPreloadItem(Next,
  Prev: TItemNextEnum; First, Last: TItemEnumFirstLast;
  var SearchItem: TEasyItem);
begin
  case Direction of
    eisdForward: SearchItem := Next(SearchItem);
    eisdBackward: SearchItem := Prev(SearchItem);
  end;
  if not Assigned(SearchItem) then
  begin
    case Direction of
      eisdForward: SearchItem := First;
      eisdBackward: SearchItem := Last;
    end;
  end
end;

procedure TEasyIncrementalSearchManager.SetSearchItem(Value: TEasyItem);
begin
  if Value <> FSearchItem then
  begin
    ClearSearch;
    FSearchItem := Value
  end
end;

procedure TEasyIncrementalSearchManager.StartSearch;

  procedure SetupStartOver;
  begin
    case ItemType of
      eisiAll:                // Search All items in list
        begin
          if Direction = eisdForward then
            SearchItem := OwnerListview.Groups.FirstItem
          else
            SearchItem := OwnerListview.Groups.LastItem
        end;
      eisiInitializedOnly:   // Search only initialized items
        begin
          if Direction = eisdForward then
            SearchItem := OwnerListview.Groups.FirstInitializedItem
          else
            SearchItem := OwnerListview.Groups.LastInitializedItem
        end;
      eisiVisible:           // Search only visible items
        begin
          if Direction = eisdForward then
            SearchItem := OwnerListview.Groups.FirstVisibleItem
          else
            SearchItem := OwnerListview.Groups.LastVisibleItem
        end;
      end
  end;

//
// Initializes the search called on WMKEYDOWN
//
begin
  // Only call once during a Search Sequence
  if not (eissSearching in State) then
  begin
    case StartType of
      eissStartOver:
        begin
          SetupStartOver;
        end;
      eissLastHit:
        begin
          // The Search Item is already set up, or not
          if not Assigned(SearchItem) then
            SetupStartOver;
          LoadFutureItem;
        end;
      eissFocusedNode:
        begin
          // By definition the Focused Item is Visible and Initalized so
          // no other checks are necessary
          if Assigned(OwnerListview.Selection.FocusedItem) then
          begin
            SearchItem := OwnerListview.Selection.FocusedItem;
            LoadFutureItem;
          end else
            SetupStartOver;
        end;
    end;
    StartTimer; // JDK 5.3.05
    Include(FState, eissSearching);
  end;
end;

procedure TEasyIncrementalSearchManager.StartTimer;
//
// Starts the Timer for the search
//
begin
  if Enabled and not(eissTimerRunning in State) then
  begin
    TimerStub := CreateStub(Self, @TEasyIncrementalSearchManager.TimerProc);
    hTimer := SetTimer(0, 0, ResetTime, TimerStub);
    Include(FState, eissTimerRunning);
  end
end;

procedure TEasyIncrementalSearchManager.TimerProc(Window: HWnd; uMsg: UINT;
  idEvent: UINT; dwTime: DWORD);
//
// The callback for the Timer.  A callback is not effected by any blocked message
// handler taking their sweet time.
//
begin
  ResetSearch;
end;

{ TEasyGridFilmStripGroup }
function TEasyGridFilmStripGroup.GetCellSize: TEasyCellSize;
begin
  Result := OwnerListview.CellSizes.FilmStrip
end;

procedure TEasyGridFilmStripGroup.AutoSizeCells;
begin
  // Do nothing
end;

procedure TEasyGridFilmStripGroup.SetCellSize(Value: TEasyCellSize);
begin
  OwnerListview.CellSizes.FilmStrip.Assign(Value)
end;

{ TEasyPersistent }
constructor TEasyPersistent.Create;
begin
  inherited Create;
end;

destructor TEasyPersistent.Destroy;
begin
  inherited Destroy;
end;

procedure TEasyPersistent.Assign(Source: TPersistent);
begin
  inherited Assign(Source);
end;

{ TEasyGridGridGroup }
constructor TEasyGridGridGroup.Create(AnOwner: TCustomEasyListview; AnOwnerGroup: TEasyGroup);
begin
  inherited Create(AnOwner, AnOwnerGroup);
  FLayout := eglGrid
end;

function TEasyGridGridGroup.GetCellSize: TEasyCellSize;
begin
  Result := OwnerListview.CellSizes.Grid
end;

procedure TEasyGridGridGroup.Rebuild(PrevGroup: TEasyGroup; var NextVisibleItemIndex: Integer);
var
  i, Top, Bottom, Left, Offset, TopMargin,
  BottomMargin, VisibleCount, ColumnIndex, ColumnLeft: Integer;
  Item: TEasyItem;
  Columns: TEasyColumns;
begin
  AutoSizeCells;

  if Assigned(PrevGroup) then
    Offset := PrevGroup.DisplayRect.Bottom
  else
    Offset := 0;

  ColumnIndex := 0;
  Columns := OwnerListview.Header.Columns;
  OwnerGroup.FDisplayRect := Rect(0, Offset, OwnerListview.Header.ViewWidth, Offset);
  // Prepare the VisibleList for the worse case, all are visible
  OwnerGroup.VisibleItems.Clear;
  OwnerGroup.VisibleItems.Capacity := OwnerGroup.Items.Count;

  Left := OwnerGroup.MarginLeft.RuntimeSize;

  TopMargin := OwnerGroup.MarginTop.RuntimeSize;
  BottomMargin := OwnerGroup.MarginBottom.RuntimeSize;
  if OwnerGroup.Visible then
  begin
    if OwnerGroup.Expanded and (OwnerGroup.Items.Count > 0) then
    begin
      VisibleCount := 0;
      Top := Offset + TopMargin;
      Bottom := Offset + CellSize.Height + TopMargin;
      for i := 0 to OwnerGroup.Items.Count - 1 do
      begin
        Item := OwnerGroup.Items.List.List[i]; // Direct Access for Speed
        if Item.Visible then
        begin
          Item.FVisibleIndex := NextVisibleItemIndex;
          Item.FVisibleIndexInGroup := VisibleCount;
          ColumnLeft := Columns[ColumnIndex].DisplayRect.Left;
          Item.FDisplayRect := Rect(Left + ColumnLeft, Top, Left + ColumnLeft + Columns[ColumnIndex].Width, Bottom);
          OwnerGroup.VisibleItems.Add(Item);
          Inc(ColumnIndex);
          if ColumnIndex >= Columns.Count then
          begin
            ColumnIndex := 0;
              Inc(Top, CellSize.Height);
              Inc(Bottom, CellSize.Height);
          end;
          Inc(VisibleCount);
          Inc(NextVisibleItemIndex);
        end else
          Item.FDisplayRect := Rect(Left, Top, Left, Top);
        OwnerGroup.FDisplayRect.Bottom := Item.FDisplayRect.Bottom + BottomMargin;
      end
    end else
      OwnerGroup.FDisplayRect := Rect(0, Offset, OwnerListview.Header.ViewWidth, Offset + TopMargin + BottomMargin);
  end;
  // Column Count does not relate to Report view columns.  It is a more primitive
  // and the Report columns are within the Grid Column
  FColumnCount := Columns.Count;
  if FColumnCount > 0 then
  begin
    FRowCount := OwnerGroup.VisibleItems.Count div FColumnCount;
    if OwnerGroup.VisibleItems.Count mod FColumnCount > 0 then
      Inc(FRowCount)
  end else
    FRowCount := 0
end;

procedure TEasyGridGridGroup.SetCellSize(Value: TEasyCellSize);
begin
  OwnerListview.CellSizes.Grid := Value
end;

{ TEasyAlphaBlender}
destructor TEasyAlphaBlender.Destroy;
begin
  inherited Destroy;
end;

procedure TEasyAlphaBlender.BasicBlend(Listview: TCustomEasyListview; ACanvas: TCanvas; ViewportRect: TRect; Color: TColor; Alpha: Byte = 128; UseScrollPostion: Boolean = True);
var
  R: TRect;
  Obj: THandle;
  Bits: Windows.BITMAP;
begin
  if Assigned(ACanvas) and Assigned(Listview) and not IsRectEmpty(ViewportRect) then
  begin
    Obj := GetCurrentObject(ACanvas.Handle, OBJ_BITMAP);
    if Obj <> 0 then
      if GetObject(Obj, SizeOf(Windows.BITMAP), @Bits) > 0 then
      begin
        if UseScrollPostion then
        begin
          R := Listview.Scrollbars.MapWindowRectToViewRect(Rect(0, 0, Bits.bmWidth, Bits.bmHeight));
          IntersectRect(R, ViewportRect, R);
          R := Listview.Scrollbars.MapViewRectToWindowRect(R);
          AlphaBlend(0, ACanvas.Handle, R, Point(0, 0), cbmConstantAlphaAndColor, Alpha, ColorToRGB(Color))
        end else
        begin
          IntersectRect(R, ViewportRect, Listview.ClientRect);
          AlphaBlend(0, ACanvas.Handle, R, Point(0, 0), cbmConstantAlphaAndColor, Alpha, ColorToRGB(Color))
        end
      end
  end

end;

procedure TEasyAlphaBlender.Blend(Listview: TCustomEasyListview; Obj: TEasyCollectionItem; ACanvas: TCanvas; ViewportRect: TRect; Image: TBitmap);
var
  BlendColor: TColor;
  BlendAlpha: Byte;
  Scratch: TBitmap;
  DoBlend: Boolean;
begin
  if not IsRectEmpty(ViewportRect) then
  begin
    GetBlendParams(Listview, Obj, BlendAlpha, BlendColor, DoBlend);
    if DoBlend and HasMMX then
    begin
      Scratch := TBitmap.Create;
      try
        Scratch.Width := Image.Width;
        Scratch.Height := Image.Height;
        Scratch.Assign(Image);
        Scratch.PixelFormat := pf32Bit;
        Scratch.TransparentMode := Image.TransparentMode;
        Scratch.TransparentColor := Image.TransparentColor;
        Scratch.Transparent := True;

        // Now force the bitmap to create a mask based on the transparent color
        Scratch.MaskHandle;
        // The AlphaBlend function is low level so TBitmap does not know anything happened to it
        AlphaBlend(0, Scratch.Canvas.Handle, Rect(0, 0, Scratch.Width, Scratch.Height), Point(0, 0), cbmConstantAlphaAndColor, BlendAlpha, ColorToRGB(BlendColor));
        // Since we got the mask before the AlphaBlend occured the original mask is
        // used and all is correct.
        ACanvas.Draw(ViewportRect.Left + (RectWidth(ViewportRect) - Scratch.Width) div 2,
                     ViewportRect.Top + (RectHeight(ViewportRect) - Scratch.Height) div 2,
                     Scratch);

      finally
        Scratch.Free
      end
    end else
      ACanvas.Draw(ViewportRect.Left + (RectWidth(ViewportRect) - Image.Width) div 2,
                     ViewportRect.Top + (RectHeight(ViewportRect) - Image.Height) div 2,
                     Image);
  end
end;

procedure TEasyAlphaBlender.GetBlendParams(Listview: TCustomEasyListview; Obj: TEasyCollectionItem; var BlendAlpha: Byte; var BlendColor: TColor; var DoBlend: Boolean);
begin
  DoBlend := False;
  if Assigned(Listview) then
  begin
    if Listview.Focused then
      begin
        if Obj.Enabled then
        begin
          BlendAlpha := Listview.Selection.BlendAlphaImage;
          BlendColor := Listview.Selection.Color
        end else
        begin
          BlendAlpha := Listview.DisabledBlendAlpha;
          BlendColor := Listview.DisabledBlendColor;
        end
      end else
      begin
        BlendAlpha := Listview.DisabledBlendAlpha;
        BlendColor := Listview.Selection.InactiveColor;
      end;

      DoBlend := Obj.Selected or Obj.Hilighted or (not Obj.Enabled and Listview.Focused)
  end
end;

{ TEasyBaseTaskBand }
constructor TEasyBaseTaskBand.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  BackGround := TEasyTaskBandBackgroundManager.Create(Self);
  HotTrack.Enabled := True;
  PaintInfoGroup.CaptionIndent := 9;
  PaintInfoGroup.MarginBottom.Size := 3;
  PaintInfoGroup.MarginBottom.Visible := True;
  PaintInfoGroup.MarginLeft.Size := 11;
  PaintInfoGroup.MarginLeft.Visible := True;
  PaintInfoGroup.MarginRight.Size := 11;
  PaintInfoGroup.MarginRight.Visible := True;
  PaintInfoGroup.MarginTop.Size := 26;
  ShowGroupMargins := True;
  Selection.Enabled := False;
  HotTrack.GroupTrack := HotTrack.GroupTrack + [htgTopMargin]
end;

function TEasyBaseTaskBand.CreateColumnPaintInfo: TEasyPaintInfoBaseColumn;
begin
  Result:= TEasyPaintInfoTaskBandColumn.Create(Self)
end;

function TEasyBaseTaskBand.CreateGroupPaintInfo: TEasyPaintInfoBaseGroup;
begin
  Result := TEasyPaintInfoTaskBandGroup.Create(Self)
end;

function TEasyBaseTaskBand.CreateItemPaintInfo: TEasyPaintInfoBaseItem;
begin
  Result:= TEasyPaintInfoTaskBandItem.Create(Self)
end;

function TEasyBaseTaskBand.GetPaintInfoColumn: TEasyPaintInfoTaskBandColumn;
begin
  Result := inherited PaintInfoColumn as TEasyPaintInfoTaskbandColumn
end;

function TEasyBaseTaskBand.GetPaintInfoGroup: TEasyPaintInfoTaskbandGroup;
begin
  Result := inherited PaintInfoGroup as TEasyPaintInfoTaskbandGroup
end;

function TEasyBaseTaskBand.GetPaintInfoItem: TEasyPaintInfoTaskBandItem;
begin
  Result := inherited PaintInfoItem as TEasyPaintInfoTaskbandItem
end;

function TEasyBaseTaskBand.GroupTestExpand(HitInfo: TEasyGroupHitTestInfoSet): Boolean;
begin
  Result:= egtOnHeader in HitInfo
end;

procedure TEasyBaseTaskBand.DoCustomGrid(Group: TEasyGroup; ViewStyle: TEasyListStyle; var Grid: TEasyGridGroupClass);
begin
  if Assigned(OnCustomGrid) then
    OnCustomGrid(Self, Group, ViewStyle, Grid);
  if not Assigned(Grid) then
   Grid := TEasyGridTaskBandGroup
end;

procedure TEasyBaseTaskBand.DoGroupCustomView(Group: TEasyGroup; ViewStyle: TEasyListStyle; var View: TEasyViewGroupClass);
begin
  if Assigned(OnGroupCustomView) then
    OnGroupCustomView(Self, Group, ViewStyle, View);
  if not Assigned(View) then
    View := TEasyViewTaskBandGroup
end;

procedure TEasyBaseTaskBand.DoItemCustomView(Item: TEasyItem; ViewStyle: TEasyListStyle; var View: TEasyViewItemClass);
begin
  if Assigned(OnItemCustomView) then
    OnItemCustomView(Self, Item, ViewStyle, View);
  if not Assigned(View) then
  View := TEasyViewTaskBandItem
end;

procedure TEasyBaseTaskBand.SetPaintInfoColumn(const Value: TEasyPaintInfoTaskBandColumn);
begin
  inherited PaintInfoColumn := Value
end;

procedure TEasyBaseTaskBand.SetPaintInfoGroup(const Value: TEasyPaintInfoTaskbandGroup);
begin
  inherited PaintInfoGroup := Value
end;

procedure TEasyBaseTaskBand.SetPaintInfoItem(const Value: TEasyPaintInfoTaskBandItem);
begin
  inherited PaintInfoItem := Value
end;

{ TGridTaskBandGroup }
constructor TEasyGridTaskBandGroup.Create(AnOwner: TCustomEasyListview; AnOwnerGroup: TEasyGroup);
begin
  inherited Create(AnOwner, AnOwnerGroup);
  CellSize := TEasyCellSize.Create(AnOwner)
end;

destructor TEasyGridTaskBandGroup.Destroy;
begin
  inherited Destroy;
  FreeAndNil(FCellSize);
end;

function TEasyGridTaskBandGroup.GetCellSize: TEasyCellSize;
begin
  FCellSize.FWidth := OwnerListview.ClientWidth - 1 - OwnerListview.PaintInfoGroup.MarginLeft.Size - OwnerListview.PaintInfoGroup.MarginRight.Size;
//  if OwnerListview.Scrollbars.VertBarVisible then
 //   Dec(FCellSize.FWidth, GetSystemMetrics(SM_CXVSCROLL));
  FCellSize.FHeight := 24;
  Result := FCellSize
end;

function TEasyGridTaskBandGroup.StaticTopItemMargin: Integer;
begin
  Result := 10;
end;

function TEasyGridTaskBandGroup.StaticTopMargin: Integer;
begin
  Result := 11;
end;

procedure TEasyGridTaskBandGroup.SetCellSize(Value: TEasyCellSize);
begin

end;

procedure TEasyGridSingleColumn.AutoSizeCells;
begin
  // Do nothing
end;

procedure TEasyGridSingleColumn.FindInsertPosition(ViewportPoint: TPoint; var Group: TEasyGroup; var Index: Integer);
begin
  Group := nil;
  Index := -1
end;

function TEasyGridSingleColumn.GetCellSize: TEasyCellSize;
begin
  Result := nil
end;

{ TGridSingleColumn }
function TEasyGridSingleColumn.GetMaxColumns(Group: TEasyGroup; WindowWidth: Integer): Integer;
begin
  Result := 1;
end;

procedure TEasyGridSingleColumn.SetCellSize(Value: TEasyCellSize);
begin

end;

{ TEasyViewTaskBandGroup}
function TEasyViewTaskBandGroup.DrawThemed: Boolean;
begin
  Result := True
end;

procedure TEasyViewTaskBandGroup.GetExpandImageSize(Group: TEasyGroup; var ImageW: Integer; var ImageH: Integer);
{$IFDEF USETHEMES}
var
  PartID, StateID: LongWord;
  R: TRect;
{$ENDIF}
begin
  ImageW := 0;
  ImageH := 0;
  if Group.Expandable then
  begin
    {$IFDEF USETHEMES}
    if Group.OwnerListview.DrawWithThemes then
    begin
      StateID := EBNGC_NORMAL;
      if Group.Expanded then
        PartID := EBP_NORMALGROUPCOLLAPSE
      else
        PartID := EBP_NORMALGROUPEXPAND;
      if Group.Hilighted then
        StateID := StateID or EBNGC_HOT;
      R := Rect(0, 0, 0, 0);
      // If too small it returns the negative rectangle of the ideal size
      if Succeeded(GetThemeBackgroundExtent(OwnerListview.Themes.ExplorerBarTheme, 0, PartID, StateID, R, R)) then
      begin
        ImageW := Abs(RectWidth(R));
        ImageH := Abs(RectHeight(R));
      end
    end else
    begin
      ImageW := 16;
      ImageH := 16;
    end;
    {$ELSE}
      ImageW := 16;
      ImageH := 16;
    {$ENDIF}
  end;
end;

procedure TEasyViewTaskBandGroup.GroupRectArray(Group: TEasyGroup; MarginEdge: TEasyGroupMarginEdge; ObjRect: TRect; var RectArray: TEasyRectArrayObject);
//
// Grabs all the rectangles for the items within a cell in one call
// If PaintInfo is nil then the information is fetched automaticlly
//
var
  TextSize: TSize;
  HeaderBand, FooterBand: TRect;
  TempRectArray: TEasyRectArrayObject;
  ImageW, ImageH, ExpandImageW, ExpandImageH: Integer;
begin
  Group.Initialized := True;
  
  FillChar(RectArray, SizeOf(RectArray), #0);

  RectArray.GroupRect := ObjRect;
  RectArray.BackGndRect :=  Group.BoundsRectBkGnd;

  GetExpandImageSize(Group, ExpandImageW, ExpandImageH);
  GetImageSize(Group, ImageW, ImageH);

  if MarginEdge in [egmeTop, egmeBottom] then
  begin
    // Calculate the Expansion button first for the Header only
    if Group.Expandable and (MarginEdge = egmeTop) then
      RectArray.ExpandButtonRect := Rect(RectArray.BackGndRect.Right - Group.ExpandImageIndent - ExpandImageW,
                      RectArray.GroupRect.Top,
                      RectArray.BackGndRect.Right,
                      RectArray.GroupRect.Bottom)
    else   // Make the ExpandButton R a width of 0
      RectArray.ExpandButtonRect := Rect(RectArray.GroupRect.Left,
                      RectArray.GroupRect.Top,
                      RectArray.GroupRect.Left,
                      RectArray.GroupRect.Bottom);

    if (Group.CheckType <> ectNone) and (MarginEdge in [egmeTop]) then
    begin
      RectArray.CheckRect := Checks.Bound[Group.Checksize];
      OffsetRect(RectArray.CheckRect, RectArray.BackGndRect.Left + Group.CheckIndent, ObjRect.Top + (RectHeight(ObjRect) - RectHeight(RectArray.CheckRect)) div 2);
    end else
    begin
      // CheckRect is a 0 width
      RectArray.CheckRect := ObjRect;
      RectArray.CheckRect.Left := RectArray.BackGndRect.Left;
      RectArray.CheckRect.Right := RectArray.BackGndRect.Left;
    end;


    // Now Calculate the image for the header or the footer
    if Group.ImageIndex > -1 then
      RectArray.IconRect := Rect(RectArray.CheckRect.Right + Group.ImageIndent,
                    RectArray.GroupRect.Top,
                    RectArray.CheckRect.Right + ImageW + Group.ImageIndent,
                    RectArray.GroupRect.Bottom)
    else   // Make the IconR a width of 0
      RectArray.IconRect := Rect(RectArray.CheckRect.Right,
                    RectArray.CheckRect.Top,
                    RectArray.CheckRect.Right,
                    RectArray.CheckRect.Bottom);

    // Now the Label rect may be calculated for the header or footer
    RectArray.LabelRect := Rect(RectArray.IconRect.Right + Group.CaptionIndent,
                   RectArray.ExpandButtonRect.Top,
                   RectArray.ExpandButtonRect.Left,
                   RectArray.ExpandButtonRect.Bottom);


    // Calculate the text size for the text based on the above font
    if Assigned(OwnerListview.ScratchCanvas) then
    begin
      LoadTextFont(Group, OwnerListview.ScratchCanvas);
      TextSize := TextExtentW(Group.Caption, OwnerListview.ScratchCanvas.Font);
    end else
    begin
      TextSize.cx := 0;
      TextSize.cy := 0
    end;
    RectArray.TextRect := Rect(RectArray.LabelRect.Left,
                               RectArray.LabelRect.Top,
                               RectArray.LabelRect.Left + TextSize.cx,
                               RectArray.LabelRect.Top + TextSize.cy);

    if RectArray.TextRect.Right > RectArray.LabelRect.Right then
      RectArray.TextRect.Right := RectArray.LabelRect.Right;
    if RectArray.TextRect.Bottom > RectArray.LabelRect.Bottom then
      RectArray.TextRect.Bottom := RectArray.LabelRect.Bottom;

    case Group.Alignment of
      taLeftJustify:  OffsetRect(RectArray.TextRect, 0, 0);
      taRightJustify: OffsetRect(RectArray.TextRect, RectWidth(RectArray.LabelRect) - (RectWidth(RectArray.TextRect)), 0);
      taCenter: OffsetRect(RectArray.TextRect, (RectWidth(RectArray.LabelRect) - RectWidth(RectArray.TextRect)) div 2, 0);
    end;

    case Group.VAlignment of
      cvaBottom: OffsetRect(RectArray.TextRect, 0, RectHeight(RectArray.GroupRect) - (RectHeight(RectArray.TextRect) + Group.BandThickness + Group.BandMargin));
      cvaCenter: OffsetRect(RectArray.TextRect, 0, (RectHeight(RectArray.GroupRect) - RectHeight(RectArray.TextRect)) div 2);
    end;
    // Use the calculated label rectangle to position where the text goes


    if Group.BandEnabled then
    begin
      if Group.BandFullWidth then
        RectArray.BandRect := Rect(RectArray.GroupRect.Left,
                           RectArray.GroupRect.Bottom - Group.BandMargin - Group.BandThickness,
                           RectArray.GroupRect.Right,
                           RectArray.GroupRect.Bottom - Group.BandMargin)
      else
        RectArray.BandRect := Rect(RectArray.GroupRect.Left,
                           RectArray.GroupRect.Bottom - Group.BandMargin - Group.BandThickness,
                           RectArray.GroupRect.Left + Group.BandLength,
                           RectArray.GroupRect.Bottom - Group.BandMargin);

      OffsetRect(RectArray.BandRect, Group.BandIndent, Group.BandMargin);
    end;

  end else
  begin  // Calculate the margin rectangles

    // Need to send nil so the user attributes are fetched for the header
    GroupRectArray(Group, egmeTop, Group.BoundsRectTopMargin, TempRectArray);
    HeaderBand := TempRectArray.BandRect;

    // Need to send nil so the user attributes are fetched for the footer
    GroupRectArray(Group, egmeBottom, Group.BoundsRectBottomMargin, TempRectArray);
    FooterBand := TempRectArray.BandRect;

    if MarginEdge  = egmeLeft then
      RectArray.BandRect := Rect(RectArray.GroupRect.Left + (RectWidth(RectArray.GroupRect) - Group.BandThickness) div 2,
                            HeaderBand.Top,
                            RectArray.GroupRect.Right,
                            FooterBand.Bottom - 1);
    if MarginEdge  = egmeRight then
      RectArray.BandRect := Rect(RectArray.GroupRect.Left,
                            HeaderBand.Top,
                            RectArray.GroupRect.Right - (RectWidth(RectArray.GroupRect) - Group.BandThickness) div 2,
                            FooterBand.Bottom - 1)
  end;
end;

procedure TEasyViewTaskBandGroup.LoadTextFont(Group: TEasyGroup; ACanvas: TCanvas);
begin
  {$IFDEF USETHEMES}
  inherited LoadTextFont(Group, ACanvas);
  if not(Group.OwnerListview.DrawWithThemes) then
  begin
    if Group.Bold then
      ACanvas.Font.Color := clHighlightText
  end;
  {$ELSE}
  inherited LoadTextFont(Group, ACanvas);
  if Group.Bold then
    ACanvas.Font.Color := clHighlightText
  {$ENDIF}
end;

procedure TEasyViewTaskBandGroup.PaintBackground(Group: TEasyGroup; ACanvas: TCanvas; MarginEdge: TEasyGroupMarginEdge; ObjRect: TRect; RectArray: TEasyRectArrayObject);

  procedure DrawNonThemed;
  begin
    if Group.Bold then
      ACanvas.Brush.Color := clHighlight
    else
      ACanvas.Brush.Color := clBtnFace;
    case MarginEdge of
      egmeBackground: ACanvas.FrameRect(RectArray.BackGndRect);
      egmeTop: ACanvas.FillRect(RectArray.GroupRect);
    end
  end;

{$IFDEF USETHEMES}
var
  PartID, StateID: LongWord;
  R, HeaderR: TRect;
{$ENDIF}
begin
  {$IFDEF USETHEMES}
  if Group.OwnerListview.DrawWithThemes then
  begin
    case MarginEdge of
    egmeBackground:
      begin
        // Draw the group background
        R := RectArray.BackGndRect;
        if Group.Bold then
          PartID := EBP_SPECIALGROUPBACKGROUND
        else
          PartID := EBP_NORMALGROUPBACKGROUND;
        StateID := 0;
        DrawThemeBackground(OwnerListview.Themes.ExplorerBarTheme, ACanvas.Handle, PartID, StateID, R, nil);
     end;
    egmeTop:
      begin
        // Draw the group header
        HeaderR := RectArray.BackGndRect;
        HeaderR.Bottom := HeaderR.Top - 1;
        HeaderR.Top := HeaderR.Top - OwnerListview.PaintInfoGroup.MarginTop.RuntimeSize;
        if Group.Bold then
          PartID := EBP_SPECIALGROUPHEAD
        else
          PartID := EBP_NORMALGROUPHEAD;
        StateID := 0;
        DrawThemeBackground(OwnerListview.Themes.ExplorerBarTheme, ACanvas.Handle, PartID, StateID, HeaderR, nil);
      end
    end
  end else
    DrawNonThemed;
  Exit;
  {$ELSE}
  DrawNonThemed;
  {$ENDIF}
end;

procedure TEasyViewTaskBandGroup.PaintBand(Group: TEasyGroup; ACanvas: TCanvas; MarginEdge: TEasyGroupMarginEdge; ObjRect: TRect; RectArray: TEasyRectArrayObject);
begin
  // Don't do inherited
end;

procedure TEasyViewTaskBandGroup.PaintExpandButton(Group: TEasyGroup; ACanvas: TCanvas; MarginEdge: TEasyGroupMarginEdge; ObjRect: TRect; RectArray: TEasyRectArrayObject);

  procedure DrawNonThemed;
  var
    RCenterX, RCenterY, RCenterY1, RCenterY2: Integer;
    ExpandW, ExpandH: Integer;
    R: TRect;
  begin
    GetExpandImageSize(Group, ExpandW, ExpandH);
    if Group.Bold then
      ACanvas.Pen.Color := clHighlightText
    else
      ACanvas.Pen.Color := clBlack;
    ACanvas.Pen.Width := 1;
    RCenterX := RectArray.ExpandButtonRect.Left + ExpandW div 2;
    RCenterY := RectArray.ExpandButtonRect.Top + ((RectHeight(RectArray.ExpandButtonRect)-ExpandH) div 2) + (ExpandH div 2);
    if Group.Expanded then
    begin
      RCenterY2 := RCenterY;
      RCenterY1 := RCenterY2 - 4;
      ACanvas.MoveTo(RCenterX, RCenterY1);
      ACanvas.LineTo(RCenterX - 4, RCenterY1 + 4);
      ACanvas.MoveTo(RCenterX, RCenterY1);
      ACanvas.LineTo(RCenterX + 4, RCenterY1 + 4);
      ACanvas.MoveTo(RCenterX, RCenterY2);
      ACanvas.LineTo(RCenterX - 4, RCenterY2 + 4);
      ACanvas.MoveTo(RCenterX, RCenterY2);
      ACanvas.LineTo(RCenterX + 4, RCenterY2 + 4);
      Inc(RCenterY1);
      Inc(RCenterY2);
      ACanvas.MoveTo(RCenterX, RCenterY1);
      ACanvas.LineTo(RCenterX - 3, RCenterY1 + 3);
      ACanvas.MoveTo(RCenterX, RCenterY1);
      ACanvas.LineTo(RCenterX + 3, RCenterY1 + 3);
      ACanvas.MoveTo(RCenterX, RCenterY2);
      ACanvas.LineTo(RCenterX - 3, RCenterY2 + 3);
      ACanvas.MoveTo(RCenterX, RCenterY2);
      ACanvas.LineTo(RCenterX + 3, RCenterY2 + 3);
    end else
    begin
      RCenterY2 := RCenterY;
      RCenterY1 := RCenterY2 + 4;
      ACanvas.MoveTo(RCenterX, RCenterY1);
      ACanvas.LineTo(RCenterX - 4, RCenterY1 - 4);
      ACanvas.MoveTo(RCenterX, RCenterY1);
      ACanvas.LineTo(RCenterX + 4, RCenterY1 - 4);
      ACanvas.MoveTo(RCenterX, RCenterY2);
      ACanvas.LineTo(RCenterX - 4, RCenterY2 - 4);
      ACanvas.MoveTo(RCenterX, RCenterY2);
      ACanvas.LineTo(RCenterX + 4, RCenterY2 - 4);
      Dec(RCenterY1);
      Dec(RCenterY2);
      ACanvas.MoveTo(RCenterX, RCenterY1);
      ACanvas.LineTo(RCenterX - 3, RCenterY1 - 3);
      ACanvas.MoveTo(RCenterX, RCenterY1);
      ACanvas.LineTo(RCenterX + 3, RCenterY1 - 3);
      ACanvas.MoveTo(RCenterX, RCenterY2);
      ACanvas.LineTo(RCenterX - 3, RCenterY2 - 3);
      ACanvas.MoveTo(RCenterX, RCenterY2);
      ACanvas.LineTo(RCenterX + 3, RCenterY2 - 3);
    end;

    if Group.Hilighted then
    begin
      R := Rect(RCenterX - ExpandW div 2,
                RCenterY - ExpandH div 2,
                RCenterX + ExpandW div 2,
                RCenterY + ExpandH div 2);
      DrawEdge(ACanvas.Handle, R, BDR_RAISEDOUTER, BF_RECT);
    end
  end;

{$IFDEF USETHEMES}
var
  PartID, StateID: LongWord;
{$ENDIF}
begin
  if (MarginEdge = egmeTop) and Group.Expandable then
  begin
    {$IFDEF USETHEMES}
    if Group.OwnerListview.DrawWithThemes then
    begin
      StateID := EBNGC_NORMAL;
      if Group.Bold then
      begin
        if Group.Expanded then
          PartID := EBP_SPECIALGROUPCOLLAPSE
        else
          PartID := EBP_SPECIALGROUPEXPAND;
      end else
      begin
        if Group.Expanded then
          PartID := EBP_NORMALGROUPCOLLAPSE
        else
          PartID := EBP_NORMALGROUPEXPAND;
      end;
      if Group.Hilighted then
        StateID := StateID or EBNGC_HOT;
      DrawThemeBackground(OwnerListview.Themes.ExplorerBarTheme, ACanvas.Handle, PartID, StateID, RectArray.ExpandButtonRect, nil);
    end else
      DrawNonThemed;
    {$ELSE}
    DrawNonThemed;
    {$ENDIF}
  end
end;

procedure TEasyViewTaskBandGroup.PaintText(Group: TEasyGroup; MarginEdge: TEasyGroupMarginEdge; ACanvas: TCanvas; ObjRect: TRect; RectArray: TEasyRectArrayObject);
begin
  {$IFDEF USETHEMES}
  if DrawThemed and (MarginEdge = egmeTop) and Group.OwnerListview.DrawWithThemes then
  begin
    PaintTextTopThemed(ACanvas, Group, ObjRect, RectArray);
  end else
    inherited;
  {$ELSE}
    inherited;
  {$ENDIF}
end;

procedure TEasyViewTaskBandGroup.PaintTextTopThemed(ACanvas: TCanvas; Group: TEasyGroup; ObjRect: TRect; RectArray: TEasyRectArrayObject);
{$IFDEF USETHEMES}
var
  PartID, StateID: LongWord;
  Flags, Flags2: DWORD;
{$ENDIF}
begin
  {$IFDEF USETHEMES}
  if Group.Bold then
    PartID := EBP_SPECIALGROUPHEAD
  else
    PartID := EBP_NORMALGROUPHEAD;
  StateID := 0;

  Flags := 0;
  case Group.Alignment of
    taLeftJustify: Flags := Flags or DT_LEFT;
    taRightJustify: Flags := Flags or DT_RIGHT;
    taCenter: Flags := Flags or DT_CENTER;
  end;

  case Group.VAlignment of
    cvaTop: Flags := Flags or DT_TOP;
    cvaCenter: Flags := Flags or DT_VCENTER;
    cvaBottom: Flags := Flags or DT_BOTTOM;
  end;

  Flags := Flags or DT_SINGLELINE or DT_END_ELLIPSIS;
  if Group.Enabled then
    Flags2 := 0
  else
    Flags2 := 1;
  DrawThemeText(OwnerListview.Themes.ExplorerBarTheme, ACanvas.Handle, PartID, StateID, PWideChar(Group.Caption), -1, Flags, Flags2, RectArray.LabelRect);
  {$ENDIF USETHEMES}
end;

{ TEasyTaskBandBackgroundManager }
procedure TEasyTaskBandBackgroundManager.PaintTo(ACanvas: TCanvas; ARect: TRect; PaintDefault: Boolean);
{$IFDEF USETHEMES}
var
  PartID, StateID: LongWord;
  R: TRect;
  Theme: HTheme;
{$ENDIF}
begin
  {$IFDEF USETHEMES}
  Theme := OwnerListview.Themes.ExplorerBarTheme;
  if OwnerListview.DrawWithThemes then
  begin
    // Draw the blue background
    R := OwnerListview.ClientRect;
    PartID := 0;
    StateID := 0;
    DrawThemeBackground(Theme, ACanvas.Handle, PartID, StateID, R, nil);
  end
  {$ENDIF}
end;

function TEasyListview.GetPaintInfoColumn: TEasyPaintInfoColumn;
begin
  Result := inherited PaintInfoColumn as TEasyPaintInfoColumn
end;

function TEasyListview.GetPaintInfoGroup: TEasyPaintInfoGroup;
begin
  Result :=  inherited PaintInfoGroup as TEasyPaintInfoGroup
end;

function TEasyListview.GetPaintInfoItem: TEasyPaintInfoItem;
begin
   Result := inherited PaintInfoItem as TEasyPaintInfoItem
end;

procedure TEasyListview.SetPaintInfoColumn(const Value: TEasyPaintInfoColumn);
begin
  inherited PaintInfoColumn := Value
end;

procedure TEasyListview.SetPaintInfoGroup(const Value: TEasyPaintInfoGroup);
begin
  inherited PaintInfoGroup := Value
end;

procedure TEasyListview.SetPaintInfoItem(const Value: TEasyPaintInfoItem);
begin
  inherited PaintInfoItem := Value
end;

{ TCanvasStore }
destructor TEasyCanvasStore.Destroy;
begin
  FreeAndNil(FFont);
  FreeAndNil(FBrush);
  FreeAndNil(FPen);
  inherited Destroy;
end;

procedure TEasyCanvasStore.RestoreCanvasState(Canvas: TCanvas);
begin
  Canvas.Pen.Assign(Pen);
  Canvas.Brush.Assign(Brush);
  Canvas.Font.Assign(Font);
end;

procedure TEasyCanvasStore.StoreCanvasState(Canvas: TCanvas);
begin
  if not Assigned(Pen) then
    Pen := TPen.Create;
  if not Assigned(Brush) then
    Brush := TBrush.Create;
  if not Assigned(Font) then
    Font := TFont.Create;
  Pen.Assign(Canvas.Pen);
  Brush.Assign(Canvas.Brush);
  Font.Assign(Canvas.Font)
end;

procedure TEasyMemoEditor.CalculateEditorRect(NewText: WideString; var NewRect: TRect);
var
  DrawFlags: TCommonDrawTextWFlags;
  DC: HDC;
  Font, OldFont: HFont;
  TextM: TTextMetric;
begin
  OldFont := 0;

  if NewText = '' then
    NewText := ' ';

  Font := GetEditorFont.Handle;
  DrawFlags := [dtCalcRectAdjR, dtCalcRect, dtCalcRectAlign];

  // Center horizontally for multi-line edits
  DrawFlags := DrawFlags + [dtCenter];

  DC := GetDC(Editor.Handle);
  try
    OldFont := SelectObject(DC, Font);

    NewRect := RectArray.LabelRect;
    InflateRect(NewRect, -4, -2);

    NewRect := Listview.Scrollbars.MapViewRectToWindowRect(NewRect);

    DrawTextWEx(DC, NewText, NewRect, DrawFlags, -1);

    InflateRect(NewRect, H_STRINGEDITORMARGIN, V_STRINGEDITORMARGIN + 1);

    GetTextMetrics(DC, TextM);

    if NewRect.Right > Listview.ClientWidth - TextM.tmAveCharWidth + H_STRINGEDITORMARGIN then
      NewRect.Right := Listview.ClientWidth - TextM.tmAveCharWidth + H_STRINGEDITORMARGIN;
    if NewRect.Bottom > Listview.ClientHeight - V_STRINGEDITORMARGIN then
      NewRect.Bottom := Listview.ClientHeight - V_STRINGEDITORMARGIN;

    // Center horizontally for multi-line edits
    NewRect.Right := NewRect.Left + RectWidth(NewRect)
  finally
    if OldFont <> 0 then
      SelectObject(DC, OldFont);
    ReleaseDC(Editor.Handle, DC);
  end
end;

procedure TEasyMemoEditor.CreateEditor(var AnEditor: TWinControl; Column: TEasyColumn; Parent: TWinControl);
var
  Memo: TEasyMemo;
begin
  AnEditor := TEasyMemo.Create(nil);
  Memo := (AnEditor as TEasyMemo);
  Memo.Visible := False;
  Memo.Parent := Listview; // Do this first or it resets at least BorderStyle to True
  Memo.Alignment := taCenter;
  Memo.Text := EditText(Item, Column);
  Memo.OnKeyDown := OnEditKeyDown;
  {$IFDEF USETHEMES}
  if not Listview.Themed then
  {$ENDIF USETHEMES}
  begin
    Memo.Ctl3D := False;
    Memo.BorderStyle := bsSingle;
    {$IFDEF COMPILER_6_UP}
    Memo.BevelInner := bvNone;
    Memo.BevelOuter := bvNone;
    Memo.BevelKind := bkNone;
    {$ENDIF}
  end
end;

function TEasyMemoEditor.GetEditorFont: TFont;
begin
  Result := (Editor as TEasyMemo).Font
end;

function TEasyMemoEditor.GetText: VAriant;
begin
  Result := (Editor as TEasyMemo).Text
end;

procedure TEasyMemoEditor.DoEditKeyDown(Sender: TObject; var Key: Word;
  Shift: TShiftState; var DoDefault: Boolean);
begin

end;

procedure TEasyMemoEditor.OnEditKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
//
// Check to see if the user is finished, if not test to see if the edit needs
// to be resized to reflect the new text
//
var
  DoDefault: Boolean;
begin
  DoDefault := True;
  DoEditKeyDown(Sender, Key, Shift, DoDefault);
  if DoDefault then
  begin
    if Key = VK_RETURN then
    begin
      Key := 0;
      AcceptEdit;
    end else
    if Key = VK_F2 then
    begin
      SelectAll
    end else
      PostMessage(Editor.Handle, WM_EDITORRESIZE, 0, 0);
  end;
  FModified := True;
end;

procedure TEasyMemoEditor.SelectAll;
begin
 (Editor as TEasyMemo).SelectAll;
end;

procedure TEasyMemoEditor.SetEditorFocus;
begin
  inherited SetEditorFocus;
  SelectAll;
end;

{ TEasyStringEditor}
procedure TEasyStringEditor.CalculateEditorRect(NewText: WideString; var NewRect: TRect);
var
  DrawFlags: TCommonDrawTextWFlags;
  DC: HDC;
  Font, OldFont: HFont;
  TextM: TTextMetric;
begin
  OldFont := 0;

  if NewText = '' then
    NewText := ' ';

  Font := GetEditorFont.Handle;
  DrawFlags := [dtCalcRectAdjR, dtCalcRect, dtCalcRectAlign];

  DrawFlags := DrawFlags + [dtLeft, dtVCenter];

  DC := GetDC(Editor.Handle);
  try
    OldFont := SelectObject(DC, Font);

    NewRect := RectArray.LabelRect;
    InflateRect(NewRect, -4, -2);

    NewRect := Listview.Scrollbars.MapViewRectToWindowRect(NewRect);

    DrawTextWEx(DC, NewText, NewRect, DrawFlags, 1);

    InflateRect(NewRect, H_STRINGEDITORMARGIN div 2, V_STRINGEDITORMARGIN);

    GetTextMetrics(DC, TextM);

    if NewRect.Right > Listview.ClientWidth - TextM.tmAveCharWidth + H_STRINGEDITORMARGIN then
      NewRect.Right := Listview.ClientWidth - TextM.tmAveCharWidth + H_STRINGEDITORMARGIN;
    if NewRect.Bottom > Listview.ClientHeight - V_STRINGEDITORMARGIN then
      NewRect.Bottom := Listview.ClientHeight - V_STRINGEDITORMARGIN;

    NewRect.Right := NewRect.Left + RectWidth(NewRect) + TextM.tmAveCharWidth;

  finally
    if OldFont <> 0 then
      SelectObject(DC, OldFont);
    ReleaseDC(Editor.Handle, DC);
  end
end;

procedure TEasyStringEditor.CreateEditor(var AnEditor: TWinControl; Column: TEasyColumn; Parent: TWinControl);
var
  Edit: TEasyEdit;
begin
  AnEditor := TEasyEdit.Create(nil);
  Edit := AnEditor as TEasyEdit;
  Edit.Visible := False;
  Edit.Parent := Listview; // Do this first or it resets at least BorderStyle to True
  Edit.Text := EditText(Item, Column);
  Edit.OnKeyDown := OnEditKeyDown;
  {$IFDEF USETHEMES}
  if not Listview.Themed then
  {$ENDIF USETHEMES}
  begin
    Edit.Ctl3D := False;
    Edit.BorderStyle := bsSingle;
    {$IFDEF COMPILER_6_UP}
    Edit.BevelInner := bvNone;
    Edit.BevelOuter := bvNone;
    Edit.BevelKind := bkNone;
    {$ENDIF}
  end
end;

function TEasyStringEditor.GetEditorFont: TFont;
begin
  Result := (Editor as TEasyEdit).Font
end;

function TEasyStringEditor.GetText: Variant;
begin
  Result := (Editor as TEasyEdit).Text
end;

procedure TEasyStringEditor.DoEditKeyDown(Sender: TObject; var Key: Word;
  Shift: TShiftState; var DoDefault: Boolean);
begin

end;

procedure TEasyStringEditor.OnEditKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
//
// Check to see if the user is finished, if not test to see if the edit needs
// to be resized to reflect the new text
//
var
  DoDefault: Boolean;
begin
  DoDefault := True;
  DoEditKeyDown(Sender, Key, Shift, DoDefault);
  if DoDefault then
  begin
    if Key = VK_RETURN then
    begin
      Key := 0;
      AcceptEdit;
    end else
    if Key = VK_F2 then
    begin
      SelectAll
    end else
      PostMessage(Editor.Handle, WM_EDITORRESIZE, 0, 0);
  end;
  FModified := True;
end;

procedure TEasyStringEditor.SelectAll;
begin
  (Editor as TEasyEdit).SelectAll;
end;

procedure TEasyStringEditor.SetEditorFocus;
begin
  inherited SetEditorFocus;
  SelectAll;
end;

{ TEasyTaskPanelBand }

constructor TEasyTaskPanelBand.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  Groups.FItemClass := TEasyGroupTaskPanel;
end;

function TEasyTaskPanelBand.CreateGroups: TEasyGroups;
begin
  Result := TEasyGroupsTaskPanel.Create(Self)
end;

function TEasyTaskPanelBand.GetGroups: TEasyGroupsTaskPanel;
begin
  Result := inherited Groups as TEasyGroupsTaskPanel
end;

procedure TEasyTaskPanelBand.CMMouseWheel(var Msg: TCMMouseWheel);
begin
  inherited;
  PositionPanels
end;

procedure TEasyTaskPanelBand.DoCustomGrid(Group: TEasyGroup; ViewStyle: TEasyListStyle; var Grid: TEasyGridGroupClass);
begin
  if Assigned(OnCustomGrid) then
    OnCustomGrid(Self, Group, ViewStyle, Grid);
  if not Assigned(Grid) then
   Grid := TEasyGridGroupTaskPanel
end;

procedure TEasyTaskPanelBand.DoGroupCustomView(Group: TEasyGroup; ViewStyle: TEasyListStyle; var View: TEasyViewGroupClass);
begin
  if Assigned(OnGroupCustomView) then
    OnGroupCustomView(Self, Group, ViewStyle, View);
  if not Assigned(View) then
    View := TEasyViewTaskPanelGroup
end;

procedure TEasyTaskPanelBand.DoGetTaskPanel(Sender: TEasyGroupTaskPanel; var TaskPanel: TEasyTaskPanelFormClass);
begin
  if Assigned(OnGetTaskPanel) then
    OnGetTaskPanel(Self, Sender, TaskPanel)
end;

procedure TEasyTaskPanelBand.DoGroupExpand(Group: TEasyGroup);
begin
  inherited DoGroupExpand(Group);
  PositionPanels;
end;

procedure TEasyTaskPanelBand.PositionPanels;
var
  i: Integer;
  DeferHandle: THandle;
  TaskPanel: TEasyTaskPanelForm;
  MappedR: TRect;
begin
  SendMessage(Handle, WM_SETREDRAW, 0, 0);
  try
    Groups.Rebuild(True);
    // MUST MAKE SURE NEVER TO MAKE THE PANELS NOT VISIBLE FOR THIS TO WORK
    DeferHandle := BeginDeferWindowPos(Groups.Count);
    for i := 0 to Groups.Count - 1 do
    begin
      if not (Groups[i] as TEasyGroupTaskPanel).TaskWindowCreated then
        (Groups[i] as TEasyGroupTaskPanel).CreateTaskWindow;
      TaskPanel := (Groups[i] as TEasyGroupTaskPanel).TaskPanel;
      MappedR := Scrollbars.MapViewRectToWindowRect(Groups[i].BoundsRectBkGnd, True);
      if (DeferHandle <> 0) and Assigned(TaskPanel) and not IsRectEmpty(MappedR) then
      begin
        if not TaskPanel.Themed then
          InflateRect(MappedR, -1, -1); // Make sure the frame shows

        TaskPanel.Visible := True;
        if (MappedR.Left <> TaskPanel.Left) or (MappedR.Top <> TaskPanel.Top) or
          (TaskPanel.Width <> RectWidth(MappedR)) or (TaskPanel.Height <> RectHeight(MappedR)) then
        begin
          DeferHandle := DeferWindowPos(DeferHandle, TaskPanel.Handle, 0,
            MappedR.Left, MappedR.Top,
            RectWidth(MappedR), RectHeight(MappedR),
            SWP_NOACTIVATE	or SWP_NOOWNERZORDER or SWP_NOZORDER);
        end
      end
    end;
    if (DeferHandle <> 0) then
      EndDeferWindowPos(DeferHandle)
  finally
    SendMessage(Handle, WM_SETREDRAW, 1, 0);
    RedrawWindow(Handle, nil, 0, RDW_ERASE or RDW_INVALIDATE or RDW_ERASENOW or
      RDW_UPDATENOW or RDW_ALLCHILDREN or RDW_FRAME);
  end
end;

procedure TEasyTaskPanelBand.SetGroups(const Value: TEasyGroupsTaskPanel);
begin
  inherited Groups := Value
end;

procedure TEasyTaskPanelBand.WMHScroll(var Msg: TWMHScroll);
begin
  inherited;
  PositionPanels;
end;

procedure TEasyTaskPanelBand.WMVScroll(var Msg: TWMVScroll);
begin
  inherited;
  PositionPanels;
end;

procedure TEasyTaskPanelBand.WMWindowPosChanging(var Msg: TWMWindowPosChanging);
begin
  inherited;
  PositionPanels
end;

{ TEasyGroupsTaskPanel }

constructor TEasyGroupsTaskPanel.Create(AnOwner: TCustomEasyListview);
begin
  inherited Create(AnOwner);
end;

function TEasyGroupsTaskPanel.GetGroup(Index: Integer): TEasyGroupTaskPanel;
begin
  Result := inherited Groups[Index] as TEasyGroupTaskPanel
end;

procedure TEasyGroupsTaskPanel.SetGroup(Index: Integer; Value: TEasyGroupTaskPanel);
begin
  inherited Groups[Index] := Value
end;

{ TEasyGridGroupPanel }

constructor TEasyGridGroupTaskPanel.Create(AnOwner: TCustomEasyListview; AnOwnerGroup: TEasyGroup);
begin
  inherited Create(AnOwner, AnOwnerGroup);
  FCellSize := TEasyCellSize.Create(AnOwner);
end;

destructor TEasyGridGroupTaskPanel.Destroy;
begin
  FreeAndNil(FCellSize);
  inherited Destroy;
end;

function TEasyGridGroupTaskPanel.GetCellSize: TEasyCellSize;
begin
  Result := FCellSize
end;

function TEasyGridGroupTaskPanel.StaticTopMargin: Integer;
begin
  Result := 10;
end;

procedure TEasyGridGroupTaskPanel.Rebuild(PrevGroup: TEasyGroup; var NextVisibleItemIndex: Integer);
var
  Top: Integer;
  TaskGroup: TEasyGroupTaskPanel;
begin
  AutoSizeCells;

  if Assigned(PrevGroup) then
    Top := PrevGroup.DisplayRect.Bottom
  else
    Top := StaticTopMargin;

  TaskGroup := FOwnerGroup as TEasyGroupTaskPanel;
  if FOwnerGroup.Expanded then
    TaskGroup.DisplayRect := Rect(0, Top, OwnerListview.ClientWidth, Top + TaskGroup.Height + TaskGroup.MarginBottom.Size + TaskGroup.MarginTop.Size)
  else
    FOwnerGroup.DisplayRect := Rect(0, Top, OwnerListview.ClientWidth, Top + TaskGroup.MarginTop.Size + TaskGroup.MarginBottom.Size);
end;

procedure TEasyGridGroupTaskPanel.SetCellSize(Value: TEasyCellSize);
begin
  CellSize.Width := Value.Width;
  CellSize.Height := Value.Height
end;

{ TEasyGroupTaskPanel }

constructor TEasyGroupTaskPanel.Create(ACollection: TEasyCollection);
begin
  inherited Create(ACollection);
  FHeight := 200;
end;

destructor TEasyGroupTaskPanel.Destroy;
begin
  if Assigned(TaskPanel) then
    TaskPanel.Free; //Close;
  FTaskPanel := nil;
  inherited Destroy;
end;

function TEasyGroupTaskPanel.GetTaskWindowCreated: Boolean;
begin
  Result := Assigned(FTaskPanel)
end;

procedure TEasyGroupTaskPanel.CreateTaskWindow;
var
  TaskPanelClass: TEasyTaskPanelFormClass;
begin
  if (not Assigned(TaskPanel)) and not (csDesigning in OwnerListview.ComponentState) then
  begin
    TaskPanelClass := nil;
    TEasyTaskPanelBand(OwnerListview).DoGetTaskPanel(Self, TaskPanelClass);
    if Assigned(TaskPanelClass) then
    begin
      FTaskPanel := TaskPanelClass.Create(nil); // the Group owns the panel
      TForm( TaskPanel).AutoScroll := (OwnerListview as TEasyTaskPanelBand).AutoScrollPanels;
      TaskPanel.Themed := OwnerListview.Themed;
      TaskPanel.Width := RectWidth(BoundsRectBkGnd);
      TaskPanel.Height := RectHeight(BoundsRectBkGnd);
      TaskPanel.Top := BoundsRectBkGnd.Top;
      TaskPanel.Left := BoundsRectBkGnd.Left;
      TaskPanel.Visible := False;
      TaskPanel.Parent := OwnerListview;
      // Make it visible when the grid is rebuilt so it won't flicker
    end
  end;
end;

procedure TEasyGroupTaskPanel.LoadFromStream(S: TStream; var Version: Integer);
begin
  inherited LoadFromStream(S, Version);
  S.Read(FHeight, SizeOf(FHeight));

  // For new objects test the stream version first
  // if Version > X then
  // begin
  //   ReadStream....
  // end
end;

procedure TEasyGroupTaskPanel.SaveToStream(S: TStream; Version: Integer = EASYLISTVIEW_STREAM_VERSION);
begin
  inherited SaveToStream(S);
  S.Write(FHeight, SizeOf(FHeight));

  // For new objects test the stream version first
  // if Version > X then
  // begin
  //   WriteStream....
  // end
end;

procedure TEasyGroupTaskPanel.SetExpanded(Value: Boolean);
begin
  inherited SetExpanded(Value);
  if Assigned(TaskPanel) then
  begin
    if Expanded then
      TaskPanel.Height := Height
    else
      TaskPanel.Height := 0
  end
end;

procedure TEasyGroupTaskPanel.SetHeight(const Value: Integer);
begin
  if FHeight <> Value then
  begin
    FHeight := Value;
    OwnerGroups.Rebuild(True)
  end
end;

procedure TEasyGroupTaskPanel.SetInitialized(Value: Boolean);
begin
  inherited SetInitialized(Value);
end;

{ TEasyViewTaskPanelGroup }

procedure TEasyViewTaskPanelGroup.PaintBefore(Group: TEasyGroup; ACanvas: TCanvas; MarginEdge: TEasyGroupMarginEdge; ObjRect: TRect; RectArray: TEasyRectArrayObject);
begin
  inherited PaintBefore(Group, ACanvas, MarginEdge, ObjRect, RectArray);
end;

{ TEasyTaskBand }
procedure TEasyTaskBand.DoGroupCollapse(Group: TEasyGroup);
begin
//  PositionPanels;
  inherited DoGroupCollapse(Group);
end;

procedure TEasyTaskBand.DoGroupExpand(Group: TEasyGroup);
begin
//  PositionPanels;
  inherited DoGroupExpand(Group);
end;

{ TEasyGestureManager }
constructor TEasyGestureManager.Create(AnOwner: TCustomEasyListview);
begin
  inherited Create(AnOwner);
  FTolerance := 3;
  FButton := [cmbRight]
end;

{ TEasyColumnDropDownButton}
constructor TEasyColumnDropDownButton.Create(AnOwner: TEasyColumn);
begin
  inherited Create;
  FOwner := AnOwner;
  FEnabled := True
end;

procedure TEasyColumnDropDownButton.SetAlwaysShow(const Value: Boolean);
begin
  if Value <> FAlwaysShow then
  begin
    FAlwaysShow := Value;
    Owner.Invalidate(False)
  end;
end;

procedure TEasyColumnDropDownButton.SetEnabled(const Value: Boolean);
begin
  if Value <> FEnabled then
  begin
    FEnabled := Value;
    Owner.Invalidate(False)
  end;
end;

procedure TEasyColumnDropDownButton.SetVisible(const Value: Boolean);
begin
  if Value <> FVisible then
  begin
    FVisible := Value;
    Owner.Invalidate(False)
  end;
end;

initialization
  OleInitialize(nil);
  Screen.Cursors[crVHeaderSplit] := LoadCursor(hInstance, CURSOR_VHEADERSPLIT);
  HeaderClipboardFormat := RegisterClipboardFormat(EASYLISTVIEW_HEADER_CLIPFORMAT);
  AlphaBlender := TEasyAlphaBlender.Create;
  RegisterClass(TEasyGroup);
  RegisterClass(TEasyGroupVirtual);
  RegisterClass(TEasyGroupInterfaced);
  RegisterClass(TEasyGroupStored);
  RegisterClass(TEasyGroupTaskPanel);
  RegisterClass(TEasyItem);
  RegisterClass(TEasyItemVirtual);
  RegisterClass(TEasyItemInterfaced);
  RegisterClass(TEasyItemStored);
  RegisterClass(TEasyColumn);
  RegisterClass(TEasyColumnVirtual);
  RegisterClass(TEasyColumnInterfaced);
  RegisterClass(TEasyColumnStored);   

finalization
  FreeAndNil(AlphaBlender);
  OleUnInitialize();

end.
