MMOutline.pas
上传用户:hylc_2004
上传日期:2014-01-23
资源大小:46800k
文件大小:71k
- {========================================================================}
- {= (c) 1995-98 SwiftSoft Ronald Dittrich =}
- {========================================================================}
- {= All Rights Reserved =}
- {========================================================================}
- {= D 01099 Dresden = Tel.: +0351-8012255 =}
- {= Loewenstr.7a = info@swiftsoft.de =}
- {========================================================================}
- {= Actual versions on http://www.swiftsoft.de/mmtools.html =}
- {========================================================================}
- {= This code is for reference purposes only and may not be copied or =}
- {= distributed in any format electronic or otherwise except one copy =}
- {= for backup purposes. =}
- {= =}
- {= No Delphi Component Kit or Component individually or in a collection=}
- {= subclassed or otherwise from the code in this unit, or associated =}
- {= .pas, .dfm, .dcu, .asm or .obj files may be sold or distributed =}
- {= without express permission from SwiftSoft. =}
- {= =}
- {= For more licence informations please refer to the associated =}
- {= HelpFile. =}
- {========================================================================}
- {= $Date: 20.01.1998 - 18:00:00 $ =}
- {========================================================================}
- unit MMOutline;
- {$R-}
- interface
- {$R OUTLINE}
- uses Windows, Messages, Forms, Classes, Graphics, Menus, StdCtrls, Grids,
- Controls, SysUtils;
- type
- OutlineError = class(TObject); { Raised by GetNodeAtIndex }
- EOutlineError = class(Exception);
- TOutlineNodeCompare = (ocLess, ocSame, ocGreater, ocInvalid);
- TAttachMode = (oaAdd, oaAddChild, oaInsert);
- TChangeRange = -1..1;
- TMMCustomOutline = class;
- { TOutlineNode }
- { The TOutlineNode is an encapsulation of an outliner item. Access
- to a TOutlineNode is via the container class TOutline. Each
- TOutlineNode contains user defined text and data.
- An item is also capable of containing up to 16368 sub-items.
- TOutlineNodes are also persistent.
- A TOutlineNode item can be interrogated about its current state :
- Expanded
- Whether the node is open or closed.
- Index
- The current Index of the node. This changes as items are inserted and
- deleted. The index will range from 1..n
- Level
- The current depth of the node with 1 being the top level
- HasItems
- Whether the item contains items
- IsVisible
- Whether the item is capable of being displayed. This value is only
- True if all its parent items are visible
- TopItem
- Obtains the parent of the item that resides at level 1
- FullPath
- Returns the fully qualified name of the item starting from its
- level 1 parent. Each item is separated by the separator string
- specified in the TOutline Container
- Text
- Used to set and get the items text value
- Data
- Used to get and set the items data }
- TOutlineNode = class(TPersistent)
- private
- FList: TList;
- FText: string;
- FData: Pointer;
- FParent: TOutlineNode;
- FIndex: LongInt;
- FState: Boolean;
- FOutline: TMMCustomOutline;
- FExpandCount: LongInt;
- procedure ChangeExpandedCount(Value: LongInt);
- procedure CloseNode;
- procedure Clear;
- procedure Error(const ErrorString: string);
- function GetExpandedNodeCount: LongInt;
- function GetFullPath: string;
- function GetIndex: LongInt;
- function GetLastIndex: LongInt;
- function GetLevel: Cardinal;
- function GetList: TList;
- function GetMaxDisplayWidth(Value: Cardinal): Cardinal;
- function GetNode(Index: LongInt): TOutlineNode;
- function GetTopItem: Longint;
- function GetVisibleParent: TOutlineNode;
- function HasChildren: Boolean;
- function HasVisibleParent: Boolean;
- function IsEqual(Value: TOutlineNode): Boolean;
- procedure ReIndex(StartNode, EndNode: TOutlineNode; NewIndex: LongInt;
- IncludeStart: Boolean);
- procedure Repaint;
- function Resync(var NewIndex: LongInt; EndNode: TOutlineNode): Boolean;
- procedure SetExpandedState(Value: Boolean);
- procedure SetGoodIndex;
- procedure SetHorzScrollBar;
- procedure SetLevel(Level: Cardinal);
- procedure SetText(const Value: string);
- protected
- constructor Create(AOwner: TMMCustomOutline);
- destructor Destroy; override;
- function GetVisibleNode(TargetCount: LongInt): TOutlineNode;
- function AddNode(Value: TOutlineNode): LongInt;
- function InsertNode(Index: LongInt; Value: TOutlineNode): LongInt;
- function GetNodeAtIndex(TargetIndex: LongInt): TOutlineNode;
- function GetDataItem(Value: Pointer): LongInt;
- function GetTextItem(const Value: string): LongInt;
- function HasAsParent(Value: TOutlineNode): Boolean;
- function GetRowOfNode(TargetNode: TOutlineNode;
- var RowCount: Longint): Boolean;
- procedure InternalRemove(Value: TOutlineNode; Index: Integer);
- procedure Remove(Value: TOutlineNode);
- procedure WriteNode(Buffer: PChar; Stream: TStream);
- property Outline: TMMCustomOutline read FOutline;
- property List: TList read GetList;
- property ExpandCount: LongInt read FExpandCount;
- property Items[Index: LongInt]: TOutlineNode read GetNode; default;
- public
- procedure ChangeLevelBy(Value: TChangeRange);
- procedure Collapse;
- procedure Expand;
- procedure FullExpand;
- function GetDisplayWidth: Integer;
- function GetFirstChild: LongInt;
- function GetLastChild: LongInt;
- function GetNextChild(Value: LongInt): LongInt;
- function GetPrevChild(Value: LongInt): LongInt;
- procedure MoveTo(Destination: LongInt; AttachMode: TAttachMode);
- property Parent: TOutlineNode read FParent;
- property Expanded: Boolean read FState write SetExpandedState;
- property Text: string read FText write SetText;
- property Data: Pointer read FData write FData;
- property Index: LongInt read GetIndex;
- property Level: Cardinal read GetLevel write SetLevel;
- property HasItems: Boolean read HasChildren;
- property IsVisible: Boolean read HasVisibleParent;
- property TopItem: Longint read GetTopItem;
- property FullPath: string read GetFullPath;
- end;
- { TCustomOutline }
- { The TCustomOutline object is a container class for TOutlineNodes.
- All TOutlineNodes contained within a TOutline are presented
- to the user as a flat array of TOutlineNodes, with a parent
- TOutlineNode containing an index value that is one less than
- its first child (if it has any children).
- Interaction with a TOutlineNode is typically accomplished through
- the TCustomOutline using the following properties:
- CurItem
- Reads and writes the current item
- ItemCount
- Returns the total number of TOutlineNodes with the TCustomOutline.
- Note this can be computationally expensive as all indexes will
- be forced to be updated!!
- Items
- Allows Linear indexing into the hierarchical list of TOutlineNodes
- SelectedItem
- Returns the Index of the TOutlineNode which has the focus or 0 if
- no TOutlineNode has been selected
- The TCustomOutline has a number of properties which will affect all
- TOutlineNodes owned by the TCustomOutline:
- OutlineStyle
- Sets the visual style of the outliner
- ItemSeparator
- Sets the delimiting string for all TOutlineNodes
- PicturePlus, PictureMinus, PictureOpen, PictureClosed, PictureLeaf
- Sets custom bitmaps for these items }
- TBitmapArrayRange = 0..4;
- EOutlineChange = procedure (Sender: TObject; Index: LongInt) of object;
- TOutlineStyle = (osText, osPlusMinusText, osPictureText,
- osPlusMinusPictureText, osTreeText, osTreePictureText);
- TOutlineBitmap = (obPlus, obMinus, obOpen, obClose, obLeaf);
- TOutlineBitmaps = set of TOutlineBitmap;
- TBitmapArray = array[TBitmapArrayRange] of TBitmap;
- TOutlineType = (otStandard, otOwnerDraw);
- TOutlineOption = (ooDrawTreeRoot, ooDrawFocusRect, ooStretchBitmaps);
- TOutlineOptions = set of TOutlineOption;
- TMMCustomOutline = class(TCustomGrid)
- private
- FBlockInsert: Boolean;
- FRootNode: TOutlineNode;
- FGoodNode: TOutlineNode;
- UpdateCount: Integer;
- FCurItem: TOutlineNode;
- FSeparator: string;
- FFontSize: Integer;
- FStrings: TStrings;
- FUserBitmaps: TOutlineBitmaps;
- FOldBitmaps: TOutlineBitmaps;
- FPictures: TBitmapArray;
- FOnExpand: EOutlineChange;
- FOnCollapse: EOutlineChange;
- FOutlineStyle: TOutlineStyle;
- FMaskColor: TColor;
- FItemHeight: Integer;
- FChildItemHeight: integer;
- FStyle: TOutlineType;
- FOptions: TOutlineOptions;
- FIgnoreScrollResize: Boolean;
- FSelectedItem: TOutlineNode;
- FOnDrawItem: TDrawItemEvent;
- FSettingWidth: Boolean;
- FSettingHeight: Boolean;
- procedure WMSize(var Message: TWMSize); message WM_SIZE;
- function GetItemCount: LongInt;
- function AttachNode(Index: LongInt; Str: string;
- Ptr: Pointer; AttachMode: TAttachMode): LongInt;
- function Get(Index: LongInt): TOutlineNode;
- function GetSelectedItem: LongInt;
- procedure SetSelectedItem(Value: Longint);
- function CompareNodes(Value1, Value2: TOutlineNode): TOutlineNodeCompare;
- procedure Error(const ErrorString: string);
- procedure CMFontChanged(var Message: TMessage); message CM_FONTCHANGED;
- function ResizeGrid: Boolean;
- procedure DoExpand(Node: TOutlineNode);
- procedure Init;
- procedure MoveNode(Destination, Source: LongInt;
- AttachMode: TAttachMode);
- procedure ClearBitmap(var Bitmap: TBitmap; Kind: TOutlineBitmap);
- procedure ChangeBitmap(Value: TBitmap; Kind: TOutlineBitmap);
- procedure SetRowHeight;
- procedure SetCurItem(Value: LongInt);
- procedure CreateGlyph;
- procedure SetStrings(Value: TStrings);
- function GetStrings: TStrings;
- function IsCurItem(Value: LongInt): Boolean;
- procedure SetPicture(Index: Integer; Value: TBitmap);
- function GetPicture(Index: Integer): TBitmap;
- procedure DrawPictures(BitMaps: array of TBitmap; ARect: TRect);
- procedure DrawText(Node: TOutlineNode; Rect: TRect);
- procedure SetOutlineStyle(Value: TOutlineStyle);
- procedure DrawTree(ARect: TRect; Node: TOutlineNode);
- procedure SetMaskColor(Value: TColor);
- procedure SetItemHeight(Value: Integer);
- procedure SetChildItemHeight(Value: integer);
- procedure SetStyle(Value: TOutlineType);
- procedure SetOutlineOptions(Value: TOutlineOptions);
- function StoreBitmap(Index: Integer): Boolean;
- procedure ReadBinaryData(Stream: TStream);
- procedure WriteBinaryData(Stream: TStream);
- procedure SetHorzScrollBar;
- procedure ResetSelectedItem;
- procedure SetRowFromNode(Node: TOutlineNode);
- protected
- procedure Loaded; override;
- procedure Click; override;
- procedure KeyDown(var Key: Word; Shift: TShiftState); override;
- procedure KeyPress(var Key: Char); override;
- function SetGoodIndex(Value: TOutlineNode): TOutlineNode;
- procedure DrawCell(ACol, ARow: Longint; ARect: TRect;
- AState: TGridDrawState); override;
- procedure DblClick; override;
- procedure SetLevel(Node: TOutlineNode; CurLevel, NewLevel: Cardinal);
- function BadIndex(Value: TOutlineNode): Boolean;
- procedure DeleteNode(Node: TOutlineNode; CurIndex: LongInt);
- procedure Expand(Index: LongInt); dynamic;
- procedure Collapse(Index: LongInt); dynamic;
- procedure DefineProperties(Filer: TFiler); override;
- procedure MouseDown(Button: TMouseButton; Shift: TShiftState;
- X, Y: Integer); override;
- procedure Move(Destination, Source: LongInt; AttachMode: TAttachMode);
- procedure SetDisplayWidth(Value: Integer);
- property Lines: TStrings read GetStrings write SetStrings;
- property OutlineStyle: TOutlineStyle read FOutlineStyle write SetOutlineStyle default osTreePictureText;
- property OnExpand: EOutlineChange read FOnExpand write FOnExpand;
- property OnCollapse: EOutlineChange read FOnCollapse write FOnCollapse;
- property Options: TOutlineOptions read FOptions write SetOutlineOptions
- default [ooDrawTreeRoot, ooDrawFocusRect];
- property Style: TOutlineType read FStyle write SetStyle default otStandard;
- property ItemHeight: Integer read FItemHeight write SetItemHeight;
- property ChildItemHeight: Integer read FChildItemHeight write SetChildItemHeight;
- property OnDrawItem: TDrawItemEvent read FOnDrawItem write FOnDrawItem;
- property ItemSeparator: string read FSeparator write FSeparator;
- property PicturePlus: TBitmap index 0 read GetPicture write SetPicture stored StoreBitmap;
- property PictureMinus: TBitmap index 1 read GetPicture write SetPicture stored StoreBitmap;
- property PictureOpen: TBitmap index 2 read GetPicture write SetPicture stored StoreBitmap;
- property PictureClosed: TBitmap index 3 read GetPicture write SetPicture stored StoreBitmap;
- property PictureLeaf: TBitmap index 4 read GetPicture write SetPicture stored StoreBitmap;
- public
- constructor Create(AOwner: TComponent); override;
- destructor Destroy; override;
- function Add(Index: LongInt; const Text: string): LongInt;
- function AddChild(Index: LongInt; const Text: string): LongInt;
- function AddChildObject(Index: LongInt; const Text: string; const Data: Pointer): LongInt;
- function AddObject(Index: LongInt; const Text: string; const Data: Pointer): LongInt;
- function Insert(Index: LongInt; const Text: string): LongInt;
- function InsertObject(Index: LongInt; const Text: string; const Data: Pointer): LongInt;
- procedure Delete(Index: LongInt);
- function GetDataItem(Value: Pointer): Longint;
- function GetItem(X, Y: Integer): LongInt;
- function GetNodeDisplayWidth(Node: TOutlineNode): Integer;
- function GetTextItem(const Value: string): Longint;
- function GetVisibleNode(Index: LongInt): TOutlineNode;
- procedure FullExpand;
- procedure FullCollapse;
- procedure LoadFromFile(const FileName: string);
- procedure LoadFromStream(Stream: TStream);
- procedure SaveToFile(const FileName: string);
- procedure SaveToStream(Stream: TStream);
- procedure BeginUpdate;
- procedure EndUpdate;
- procedure SetUpdateState(Value: Boolean);
- procedure Clear;
- property ItemCount: LongInt read GetItemCount;
- property Items[Index: LongInt]: TOutlineNode read Get; default;
- property SelectedItem: Longint read GetSelectedItem write SetSelectedItem;
- property Row;
- property Canvas;
- end;
- TMMOutline = class(TMMCustomOutline)
- published
- property Lines;
- property OutlineStyle;
- property OnExpand;
- property OnCollapse;
- property Options;
- property Style;
- property ItemHeight;
- property ChildItemHeight;
- property OnDrawItem;
- property Align;
- property Enabled;
- property Font;
- property Color;
- property ParentColor;
- property ParentCtl3D;
- property Ctl3D;
- property TabOrder;
- property TabStop;
- property Visible;
- property OnClick;
- property DragMode;
- property DragCursor;
- property OnDragDrop;
- property OnDragOver;
- property OnEndDrag;
- property OnStartDrag;
- property OnEnter;
- property OnExit;
- property OnMouseDown;
- property OnMouseMove;
- property OnMouseUp;
- property OnDblClick;
- property OnKeyDown;
- property OnKeyPress;
- property OnKeyUp;
- property BorderStyle;
- property ItemSeparator;
- property PicturePlus;
- property PictureMinus;
- property PictureOpen;
- property PictureClosed;
- property PictureLeaf;
- property ParentFont;
- property ParentShowHint;
- property ShowHint;
- property PopupMenu;
- property ScrollBars;
- end;
- procedure register;
- implementation
- uses Consts;
- const
- MaxLevels = 255;
- TAB = Chr(9);
- InvalidIndex = -1;
- BitmapWidth = 16;
- BitmapHeight = 16;
- type
- { TOutlineStrings }
- TOutlineStrings = class(TStrings)
- private
- Outline: TMMCustomOutline;
- procedure ReadData(Reader: TReader);
- procedure WriteData(Writer: TWriter);
- protected
- procedure DefineProperties(Filer: TFiler); override;
- function Get(Index: Integer): string; override;
- function GetCount: Integer; override;
- public
- function Add(const S: string): Integer; override;
- procedure Clear; override;
- procedure Delete(Index: Integer); override;
- procedure Insert(Index: Integer; const S: string); override;
- procedure PutObject(Index: Integer; AObject: TObject); override;
- function GetObject(Index: Integer): TObject; override;
- end;
- function GetBufStart(Buffer: PChar; var Level: Cardinal): PChar;
- begin
- Level := 0;
- while Buffer^ in [' ', #9] do
- begin
- Inc(Buffer);
- Inc(Level);
- end;
- Result := Buffer;
- end;
- function PutString(BufPtr: PChar; const S: string): PChar;
- var
- I: Integer;
- begin
- for I := 1 to Length(S) do
- begin
- BufPtr^ := S[I];
- Inc(BufPtr);
- end;
- Word(Pointer(BufPtr)^) := $0A0D;
- Inc(BufPtr, 2);
- Result := BufPtr;
- end;
- {TOutlineNode}
- constructor TOutlineNode.Create(AOwner: TMMCustomOutline);
- begin
- FOutline := AOwner;
- end;
- destructor TOutlineNode.Destroy;
- var
- CurIndex: LongInt;
- LastNode: Boolean;
- begin
- with Outline do
- if FRootNode = Self then FIgnoreScrollResize := True;
- try
- CurIndex := 0;
- if Parent <> nil then CurIndex := Outline.FCurItem.Index;
- if FList <> nil then Clear;
- if Outline.FSelectedItem = Self then Outline.ResetSelectedItem;
- if Parent <> nil then
- begin
- LastNode := Parent.List.Last = Self;
- Parent.Remove(Self);
- if Parent.List.Count = 0 then
- Outline.SetRowFromNode(Parent)
- else if LastNode then
- Outline.SetRowFromNode(TOutlineNode(Parent.List.Last));
- Outline.DeleteNode(Self, CurIndex);
- end;
- finally
- with Outline do
- if FRootNode = Self then FIgnoreScrollResize := False;
- end;
- inherited Destroy;
- end;
- procedure TOutlineNode.Clear;
- var
- I: Integer;
- Node: TOutlineNode;
- begin
- for I := 0 to FList.Count - 1 do
- begin
- Node := FList.Items[I];
- Node.FParent := nil;
- Node.Destroy;
- end;
- FList.Destroy;
- FList := nil;
- end;
- procedure TOutlineNode.SetHorzScrollBar;
- begin
- if (Parent <> nil) and Parent.Expanded then
- Outline.SetHorzScrollBar;
- end;
- function TOutlineNode.GetList: TList;
- begin
- if FList = nil then FList := TList.Create;
- Result := FList;
- end;
- function TOutlineNode.GetNode(Index: LongInt): TOutlineNode;
- begin
- Result := List[Index];
- end;
- function TOutlineNode.GetLastIndex: LongInt;
- begin
- if List.Count <> 0 then
- Result := TOutlineNode(List.Last).GetLastIndex
- else
- Result := Index;
- end;
- procedure TOutlineNode.SetText(const Value: string);
- var
- NodeRow: LongInt;
- begin
- FText := Value;
- if not Assigned(FParent) then Exit;
- if Parent.Expanded then
- begin
- NodeRow := 0;
- with Outline do
- begin
- FRootNode.GetRowOfNode(Self, NodeRow);
- InvalidateCell(0, NodeRow - 2);
- end;
- end;
- SetHorzScrollBar;
- end;
- procedure TOutlineNode.ChangeExpandedCount(Value: LongInt);
- begin
- if not Expanded then Exit;
- Inc(FExpandCount, Value);
- if Parent <> nil then Parent.ChangeExpandedCount(Value);
- end;
- function TOutlineNode.GetIndex: LongInt;
- begin
- if Outline.BadIndex(Self) then SetGoodIndex;
- Result := FIndex;
- end;
- function TOutlineNode.GetLevel: Cardinal;
- var
- Node: TOutlineNode;
- begin
- Result := 0;
- Node := Parent;
- while Node <> nil do
- begin
- Inc(Result);
- Node := Node.Parent;
- end;
- end;
- procedure TOutlineNode.SetLevel(Level: Cardinal);
- var
- CurLevel: Cardinal;
- begin
- CurLevel := GetLevel;
- if Level = CurLevel then Exit;
- Outline.SetLevel(Self, CurLevel, Level);
- end;
- procedure TOutlineNode.ChangeLevelBy(Value: TChangeRange);
- begin
- Level := Level + Value;
- end;
- function TOutlineNode.GetDisplayWidth: Integer;
- begin
- Result := Outline.GetNodeDisplayWidth(Self);
- end;
- function TOutlineNode.HasVisibleParent: Boolean;
- begin
- Result := (Parent <> nil) and (Parent.Expanded);
- end;
- function TOutlineNode.GetVisibleParent: TOutlineNode;
- begin
- Result := Self;
- while (Result.Parent <> nil) and not Result.Parent.Expanded do
- Result := Result.Parent;
- end;
- function TOutlineNode.GetFullPath: string;
- begin
- if Parent <> nil then
- if Parent.Parent <> nil then
- Result := Parent.GetFullPath + Outline.ItemSeparator + Text
- else
- Result := Text
- else Result := EmptyStr;
- end;
- function TOutlineNode.HasAsParent(Value: TOutlineNode): Boolean;
- begin
- if Self = Value then
- Result := True
- else if Parent <> nil then Result := Parent.HasAsParent(Value)
- else Result := False;
- end;
- function TOutlineNode.GetTopItem: Longint;
- var
- Node: TOutlineNode;
- begin
- Result := 0;
- if Parent = nil then Exit;
- Node := Self;
- while Node.Parent <> nil do
- begin
- if Node.Parent.Parent = nil then
- Result := Node.FIndex;
- Node := Node.Parent;
- end;
- end;
- function TOutlineNode.GetFirstChild: LongInt;
- begin
- if List.Count > 0 then Result := Items[0].Index
- else Result := InvalidIndex;
- end;
- function TOutlineNode.GetLastChild: LongInt;
- begin
- if List.Count > 0 then Result := Items[List.Count - 1].Index
- else Result := InvalidIndex;
- end;
- function TOutlineNode.GetNextChild(Value: LongInt): LongInt;
- var
- I: Integer;
- begin
- Result := InvalidIndex;
- for I := 0 to List.Count - 1 do
- begin
- if Items[I].Index = Value then
- begin
- if I < List.Count - 1 then Result := Items[I + 1].Index;
- Break;
- end;
- end;
- end;
- function TOutlineNode.GetPrevChild(Value: LongInt): LongInt;
- var
- I: Integer;
- begin
- Result := InvalidIndex;
- for I := List.Count - 1 downto 0 do
- begin
- if Items[I].Index = Value then
- begin
- if I > 0 then Result := Items[I - 1].Index;
- Break;
- end;
- end;
- end;
- procedure TOutlineNode.MoveTo(Destination: LongInt; AttachMode: TAttachMode);
- begin
- Outline.Move(Destination, Index, AttachMode);
- end;
- procedure TOutlineNode.FullExpand;
- var
- I: Integer;
- begin
- if HasItems then
- begin
- Expanded := True;
- for I := 0 to List.Count - 1 do
- Items[I].FullExpand;
- end;
- end;
- function TOutlineNode.GetRowOfNode(TargetNode: TOutlineNode;
- var RowCount: Longint): Boolean;
- var
- I: Integer;
- begin
- Inc(RowCount);
- if TargetNode = Self then
- begin
- Result := True;
- Exit;
- end;
- Result := False;
- if not Expanded then Exit;
- for I := 0 to List.Count - 1 do
- begin
- Result := Items[I].GetRowOfNode(TargetNode, RowCount);
- if Result then Exit
- end;
- end;
- function TOutlineNode.GetVisibleNode(TargetCount: LongInt): TOutlineNode;
- var
- I, J: Integer;
- ExpandedCount, NodeCount, NodesParsed: LongInt;
- Node: TOutlineNode;
- Count: Integer;
- begin
- if TargetCount = 0 then
- begin
- Result := Self;
- Exit;
- end;
- Result := nil;
- Count := List.Count;
- NodesParsed := 0;
- { Quick exit if we are lucky }
- if ExpandCount = Count then
- begin
- Result := Items[TargetCount - 1];
- Exit;
- end;
- I := 0;
- while I <= Count - 1 do
- begin
- for J := I to Count - 1 do
- if Items[J].Expanded then Break;
- if J > I then
- begin
- if J - I >= TargetCount then
- begin
- Result := Items[I + TargetCount - 1];
- Break;
- end;
- Dec(TargetCount, J - I);
- end;
- Node := Items[J];
- NodeCount := Node.ExpandCount + 1;
- ExpandedCount := NodeCount + J - I;
- Inc(NodesParsed, ExpandedCount);
- if NodeCount >= TargetCount then
- begin
- Result := Node.GetVisibleNode(Pred(TargetCount));
- Break;
- end
- else if ExpandCount - NodesParsed = Count - (J + 1) then
- begin
- Result := Items[TargetCount - NodeCount + J];
- Exit;
- end
- else begin
- Dec(TargetCount, NodeCount);
- I := J;
- end;
- Inc(I);
- end;
- if Result = nil then Error(SOutlineIndexError);
- end;
- function TOutlineNode.GetNodeAtIndex(TargetIndex: LongInt): TOutlineNode;
- var
- I: Integer;
- Node: TOutlineNode;
- Lower: Integer;
- Upper: Integer;
- function RecurseNode: TOutlineNode;
- begin
- if Node.Index = TargetIndex then
- Result := Node
- else
- Result := Node.GetNodeAtIndex(TargetIndex);
- end;
- begin
- if TargetIndex = Index then
- begin
- Result := Self;
- Exit;
- end;
- Lower := 0;
- Upper := List.Count - 1;
- Result := nil;
- while Upper >= Lower do
- begin
- I := (Lower + Upper) div 2;
- Node := Items[I];
- if Lower = Upper then
- begin
- Result := RecurseNode;
- Break;
- end
- else if Node.Index > TargetIndex then Upper := Pred(I)
- else if (Node.Index < TargetIndex) and (I < Upper) and
- (Items[I + 1].Index <= TargetIndex) then Lower := Succ(I)
- else begin
- Result := RecurseNode;
- Break;
- end;
- end;
- if Result = nil then Raise OutlineError.Create;
- end;
- function TOutlineNode.GetDataItem(Value: Pointer): LongInt;
- var
- I: Integer;
- begin
- if Value = Data then
- begin
- Result := Index;
- Exit;
- end;
- Result := 0;
- for I := 0 to List.Count - 1 do
- begin
- Result := Items[I].GetDataItem(Value);
- if Result <> 0 then Break;
- end;
- end;
- function TOutlineNode.GetTextItem(const Value: string): LongInt;
- var
- I: Integer;
- begin
- if Value = Text then
- begin
- Result := Index;
- Exit;
- end;
- Result := 0;
- for I := 0 to List.Count - 1 do
- begin
- Result := Items[I].GetTextItem(Value);
- if Result <> 0 then Break;
- end;
- end;
- procedure TOutlineNode.Expand;
- begin
- Expanded := True;
- end;
- procedure TOutlineNode.Collapse;
- begin
- Expanded := False;
- end;
- procedure TOutlineNode.SetExpandedState(Value: Boolean);
- var
- ParentNode: TOutlineNode;
- begin
- if FState <> Value then
- begin
- if Value then
- begin
- ParentNode := Self.Parent;
- while ParentNode <> nil do
- begin
- if not ParentNode.Expanded then Error(SOutlineExpandError);
- ParentNode := ParentNode.Parent;
- end;
- Outline.Expand(Index);
- FState := True;
- ChangeExpandedCount(List.Count);
- end
- else begin
- CloseNode;
- if List.Count > 0 then ChangeExpandedCount(-List.Count);
- if Outline.ResizeGrid then Outline.Invalidate;
- Outline.Collapse(Index);
- FState := False;
- end;
- SetHorzScrollBar;
- Repaint;
- end;
- end;
- procedure TOutlineNode.CloseNode;
- var
- I: Integer;
- begin
- for I := 0 to List.Count - 1 do
- Items[I].CloseNode;
- if List.Count > 0 then ChangeExpandedCount(-List.Count);
- FState := False;
- end;
- procedure TOutlineNode.Repaint;
- begin
- if Outline <> nil then
- if Outline.ResizeGrid then Outline.Invalidate;
- end;
- procedure TOutlineNode.SetGoodIndex;
- var
- StartNode: TOutlineNode;
- ParentNode: TOutlineNode;
- begin
- StartNode := Outline.SetGoodIndex(Self);
- ParentNode := StartNode.Parent;
- if ParentNode <> nil then
- ParentNode.ReIndex(StartNode, Self, StartNode.FIndex, True)
- else if Self <> Outline.FRootNode then
- FIndex := Succ(StartNode.FIndex);
- Outline.FGoodNode := Self;
- end;
- function TOutlineNode.AddNode(Value: TOutlineNode): LongInt;
- begin
- List.Add(Value);
- Value.FParent := Self;
- ChangeExpandedCount(Value.ExpandCount + 1);
- if not Outline.FBlockInsert then Value.SetGoodIndex;
- with Value do
- begin
- Result := FIndex;
- SetHorzScrollBar;
- end;
- end;
- function TOutlineNode.InsertNode(Index: LongInt; Value: TOutlineNode): LongInt;
- var
- CurIndex: LongInt;
- I: Integer;
- begin
- for I := 0 to List.Count - 1 do
- begin
- CurIndex := Items[I].FIndex;
- if CurIndex = Index then
- begin
- List.Insert(I, Value);
- Value.FParent := Self;
- Break;
- end;
- end;
- ChangeExpandedCount(Value.ExpandCount + 1);
- if not Outline.FBlockInsert then Value.SetGoodIndex;
- with Value do
- begin
- Result := FIndex;
- SetHorzScrollBar;
- end;
- end;
- procedure TOutlineNode.InternalRemove(Value: TOutlineNode; Index: Integer);
- begin
- if Index <> 0 then
- Outline.SetGoodIndex(Items[Index - 1]) else
- Outline.SetGoodIndex(Self);
- List.Delete(Index);
- ChangeExpandedCount(-(Value.ExpandCount + 1));
- if (List.Count = 0) and (Parent <> nil) then Expanded := False;
- SetHorzScrollBar;
- end;
- procedure TOutlineNode.Remove(Value: TOutlineNode);
- begin
- InternalRemove(Value, List.IndexOf(Value));
- end;
- procedure TOutlineNode.ReIndex(StartNode, EndNode: TOutlineNode;
- NewIndex: LongInt; IncludeStart: Boolean);
- var
- I: Integer;
- begin
- for I := List.IndexOf(StartNode) to List.Count - 1 do
- begin
- if IncludeStart then
- begin
- if Items[I].Resync(NewIndex, EndNode) then Exit;
- end
- else
- IncludeStart := True;
- end;
- if Parent <> nil then
- Parent.ReIndex(Self, EndNode, NewIndex, False);
- end;
- function TOutlineNode.Resync(var NewIndex: LongInt; EndNode: TOutlineNode): Boolean;
- var
- I: Integer;
- begin
- FIndex := NewIndex;
- if EndNode = Self then
- begin
- Result := True;
- Exit;
- end;
- Result := False;
- Inc(NewIndex);
- for I := 0 to List.Count - 1 do
- begin
- Result := Items[I].Resync(NewIndex, EndNode);
- if Result then Exit;
- end;
- end;
- function TOutlineNode.GetExpandedNodeCount: LongInt;
- var
- I : Integer;
- begin
- Result := 1;
- if Expanded then
- for I := 0 to List.Count - 1 do
- Inc(Result, Items[I].GetExpandedNodeCount);
- end;
- function TOutlineNode.GetMaxDisplayWidth(Value: Cardinal): Cardinal;
- var
- I : Integer;
- Width: Cardinal;
- begin
- Width := GetDisplayWidth;
- if Width > Value then Result := Width
- else Result := Value;
- if Expanded then
- for I := 0 to List.Count - 1 do
- Result := Items[I].GetMaxDisplayWidth(Result);
- end;
- procedure TOutlineNode.Error(const ErrorString: string);
- begin
- raise EOutlineError.Create(ErrorString);
- end;
- function TOutlineNode.HasChildren: Boolean;
- begin
- Result := List.Count > 0;
- end;
- procedure TOutlineNode.WriteNode(Buffer: PChar; Stream: TStream);
- var
- BufPtr: PChar;
- NodeLevel: Word;
- I: Integer;
- begin
- if Parent <> nil then
- begin
- BufPtr := Buffer;
- NodeLevel := Level;
- while NodeLevel > 1 do
- begin
- BufPtr^ := Tab;
- Dec(NodeLevel);
- Inc(BufPtr);
- end;
- BufPtr := PutString(BufPtr, Text);
- Stream.WriteBuffer(Buffer[0], BufPtr - Buffer);
- end;
- for I := 0 to List.Count - 1 do
- Items[I].WriteNode(Buffer, Stream);
- end;
- function TOutlineNode.IsEqual(Value: TOutlineNode): Boolean;
- begin
- Result := (Text = Value.Text) and (Data = Value.Data) and
- (ExpandCount = Value.ExpandCount);
- end;
- { TOutlineStrings }
- function TOutlineStrings.Get(Index: Integer): string;
- var
- Node: TOutlineNode;
- Level: Word;
- I: Integer;
- begin
- Node := Outline[Index + 1];
- Level := Node.Level;
- Result := EmptyStr;
- for I := 0 to Level - 2 do
- Result := Result + TAB;
- Result := Result + Node.Text;
- end;
- function TOutlineStrings.GetCount: Integer;
- begin
- Result := Outline.ItemCount;
- end;
- procedure TOutlineStrings.Clear;
- begin
- Outline.Clear;
- end;
- procedure TOutlineStrings.DefineProperties(Filer: TFiler);
- function WriteNodes: Boolean;
- var
- I: Integer;
- Ancestor: TOutlineStrings;
- begin
- Ancestor := TOutlineStrings(Filer.Ancestor);
- if (Ancestor <> nil) and (Ancestor.Outline.ItemCount = Outline.ItemCount) and
- (Ancestor.Outline.ItemCount > 0) then
- for I := 1 to Outline.ItemCount - 1 do
- begin
- Result := not Outline[I].IsEqual(Ancestor.Outline[I]);
- if Result then Break;
- end
- else Result := Outline.ItemCount > 0;
- end;
- begin
- Filer.DefineProperty('Nodes', ReadData, WriteData, WriteNodes);
- end;
- procedure TOutlineStrings.ReadData(Reader: TReader);
- var
- StringList: TStringList;
- MemStream: TMemoryStream;
- begin
- Reader.ReadListBegin;
- StringList := TStringList.Create;
- try
- while not Reader.EndOfList do StringList.Add(Reader.ReadString);
- MemStream := TMemoryStream.Create;
- try
- StringList.SaveToStream(MemStream);
- MemStream.Position := 0;
- Outline.LoadFromStream(MemStream);
- finally
- MemStream.Free;
- end;
- finally
- StringList.Free;
- end;
- Reader.ReadListEnd;
- end;
- procedure TOutlineStrings.WriteData(Writer: TWriter);
- var
- I: Integer;
- MemStream: TMemoryStream;
- StringList: TStringList;
- begin
- Writer.WriteListBegin;
- MemStream := TMemoryStream.Create;
- try
- Outline.SaveToStream(MemStream);
- MemStream.Position := 0;
- StringList := TStringList.Create;
- try
- StringList.LoadFromStream(MemStream);
- for I := 0 to StringList.Count - 1 do
- Writer.WriteString(StringList.Strings[I]);
- finally
- StringList.Free;
- end;
- finally
- MemStream.Free;
- end;
- Writer.WriteListEnd;
- end;
- function TOutlineStrings.Add(const S: string): Integer;
- var
- Level, OldLevel, I: Cardinal;
- NewStr: string;
- NumNodes: LongInt;
- LastNode: TOutlineNode;
- begin
- NewStr := GetBufStart(PChar(S), Level);
- NumNodes := Outline.ItemCount;
- if NumNodes > 0 then LastNode := Outline[Outline.ItemCount]
- else LastNode := Outline.FRootNode;
- OldLevel := LastNode.Level;
- if (Level > OldLevel) or (LastNode = Outline.FRootNode) then
- begin
- if Level - OldLevel > 1 then Outline.Error(SOutlineFileLoad);
- end
- else begin
- for I := OldLevel downto Level + 1 do
- begin
- LastNode := LastNode.Parent;
- if not Assigned(LastNode) then Outline.Error(SOutlineFileLoad);
- end;
- end;
- Result := Outline.AddChild(LastNode.Index, NewStr) - 1;
- end;
- procedure TOutlineStrings.Delete(Index: Integer);
- begin
- Outline.Delete(Index + 1);
- end;
- procedure TOutlineStrings.Insert(Index: Integer; const S: string);
- begin
- Outline.Insert(Index + 1, S);
- end;
- procedure TOutlineStrings.PutObject(Index: Integer; AObject: TObject);
- var
- Node: TOutlineNode;
- begin
- Node := Outline[Index + 1];
- Node.Data := Pointer(AObject);
- end;
- function TOutlineStrings.GetObject(Index: Integer): TObject;
- begin
- Result := TObject(Outline[Index + 1].Data);
- end;
- {TCustomOutline}
- const
- Images: array[TBitmapArrayRange] of PChar = ('PLUS', 'MINUS', 'OPEN', 'CLOSED', 'LEAF');
- constructor TMMCustomOutline.Create(AOwner: TComponent);
- begin
- inherited Create(AOwner);
- Width := 121;
- Height := 97;
- Color := clWindow;
- ParentColor := False;
- SetRowHeight;
- RowCount := 0;
- ColCount := 1;
- FixedCols := 0;
- FixedRows := 0;
- DefaultDrawing := False;
- Init;
- FStrings := TOutlineStrings.Create;
- TOutlineStrings(FStrings).Outline := Self;
- inherited Options := [];
- Options := [ooDrawTreeRoot, ooDrawFocusRect];
- ItemSeparator := '';
- FOutlineStyle := osTreePictureText;
- CreateGlyph;
- end;
- destructor TMMCustomOutline.Destroy;
- var
- I: Integer;
- begin
- FStrings.Free;
- FRootNode.Free;
- for I := Low(FPictures) to High(FPictures) do FPictures[I].Free;
- inherited Destroy;
- end;
- procedure TMMCustomOutline.Init;
- begin
- if FRootNode = nil then FRootNode := TOutlineNode.Create(Self);
- FRootNode.FState := True;
- ResetSelectedItem;
- FGoodNode := FRootNode;
- FCurItem := FRootNode;
- FBlockInsert := False;
- UpdateCount := 0;
- ResizeGrid;
- end;
- procedure TMMCustomOutline.CreateGlyph;
- var
- I: Integer;
- begin
- FUserBitmaps := [];
- FOldBitmaps := [];
- for I := Low(FPictures) to High(FPictures) do
- begin
- FPictures[I] := TBitmap.Create;
- FPictures[I].Handle := LoadBitmap(HInstance, Images[I]);
- end;
- end;
- procedure TMMCustomOutline.SetRowHeight;
- var
- ScreenDC: HDC;
- begin
- if Style <> otOwnerDraw then
- begin
- ScreenDC := GetDC(0);
- try
- FFontSize := MulDiv(Font.Size, GetDeviceCaps(ScreenDC, LOGPIXELSY), 72);
- DefaultRowHeight := MulDiv(FFontSize, 120, 100);
- FItemHeight := DefaultRowHeight;
- FChildItemHeight := FItemHeight;
- finally
- ReleaseDC(0, ScreenDC);
- end;
- end
- end;
- procedure TMMCustomOutline.Clear;
- begin
- FRootNode.Destroy;
- FRootNode := nil;
- Init;
- end;
- procedure TMMCustomOutline.DefineProperties(Filer: TFiler);
- function WriteOutline: Boolean;
- var
- Ancestor: TMMCustomOutline;
- begin
- Ancestor := TMMCustomOutline(Filer.Ancestor);
- if Ancestor <> nil then
- Result := (Ancestor.FUserBitmaps <> []) and
- (Ancestor.FUserBitmaps - FUserBitmaps <> [])
- else Result := FUserBitmaps <> [];
- end;
- begin
- inherited DefineProperties(Filer);
- Filer.DefineBinaryProperty('Data', ReadBinaryData, WriteBinaryData,
- WriteOutline);
- end;
- procedure TMMCustomOutline.ReadBinaryData(Stream: TStream);
- begin
- Stream.ReadBuffer(FOldBitmaps, SizeOf(FOldBitmaps));
- end;
- procedure TMMCustomOutline.WriteBinaryData(Stream: TStream);
- begin
- Stream.WriteBuffer(FuserBitmaps, SizeOf(FUserBitmaps));
- end;
- function TMMCustomOutline.IsCurItem(Value: LongInt): Boolean;
- begin
- Result := Value = FCurItem.Index;
- end;
- function TMMCustomOutline.GetItemCount: LongInt;
- begin
- Result := FRootNode.GetLastIndex;
- end;
- procedure TMMCustomOutline.MoveNode(Destination, Source: LongInt;
- AttachMode: TAttachMode);
- var
- SourceNode: TOutlineNode;
- DestNode: TOutLineNode;
- OldParent: TOutlineNode;
- OldIndex: Integer;
- begin
- if Destination = Source then Exit;
- if IsCurItem(Destination) then
- DestNode := FCurItem
- else
- try
- DestNode := FRootNode.GetNodeAtIndex(Destination);
- except
- on OutlineError do Error(SOutlineIndexError);
- end;
- if IsCurItem(Source) then
- SourceNode := FCurItem
- else
- try
- SourceNode := FRootNode.GetNodeAtIndex(Source);
- except
- on OutlineError do Error(SOutlineIndexError);
- end;
- if DestNode.HasAsParent(SourceNode) then Exit;
- if DestNode.GetLevel > MaxLevels then Error(SOutlineMaxLevels);
- if (FGoodNode = FRootNode) and (FRootNode.List.Count <> 0) then
- TOutlineNode(FRootNode[0]).SetGoodIndex;
- OldParent := SourceNode.Parent;
- OldIndex := -1;
- case AttachMode of
- oaInsert:
- begin
- if DestNode.Parent = OldParent then
- begin
- OldIndex := OldParent.List.IndexOf(SourceNode);
- if OldParent.List.IndexOf(DestNode) < OldIndex then
- OldIndex := OldIndex + 1 else
- OldIndex := -1;
- end;
- DestNode.Parent.InsertNode(DestNode.Index, SourceNode);
- end;
- oaAddChild: DestNode.AddNode(SourceNode);
- oaAdd: DestNode.Parent.AddNode(SourceNode);
- end;
- if OldIndex <> -1 then
- OldParent.InternalRemove(SourceNode, OldIndex) else
- OldParent.Remove(SourceNode);
- if not DestNode.Expanded then SourceNode.Expanded := False;
- if (FGoodNode = FRootNode) and (FRootNode.List.Count <> 0) then
- TOutlineNode(FRootNode[0]).SetGoodIndex;
- ResizeGrid;
- Invalidate;
- end;
- function TMMCustomOutline.AttachNode(Index: LongInt; Str: string;
- Ptr: Pointer; AttachMode: TAttachMode): LongInt;
- var
- NewNode: TOutlineNode;
- CurrentNode: TOutLineNode;
- begin
- Result := 0;
- NewNode := TOutlineNode.Create(Self);
- with NewNode do
- begin
- Text := Str;
- Data := Ptr;
- FIndex := InvalidIndex;
- end;
- try
- if IsCurItem(Index) then CurrentNode := FCurItem
- else
- try
- CurrentNode := FRootNode.GetNodeAtIndex(Index);
- except
- on OutlineError do Error(SOutlineIndexError);
- end;
- if AttachMode = oaAdd then
- begin
- CurrentNode := CurrentNode.Parent;
- if CurrentNode = nil then Error(SOutlineError);
- AttachMode := oaAddChild;
- end;
- with CurrentNode do
- begin
- case AttachMode of
- oaInsert: Result := Parent.InsertNode(Index, NewNode);
- oaAddChild:
- begin
- if GetLevel > MaxLevels then Error(SOutlineMaxLevels);
- Result := AddNode(NewNode);
- end;
- end;
- end;
- if ResizeGrid then Invalidate;
- except
- NewNode.Destroy;
- Application.HandleException(Self);
- end;
- end;
- function TMMCustomOutline.Get(Index: LongInt): TOutlineNode;
- begin
- if IsCurItem(Index) then Result := FCurItem
- else
- try
- Result := FRootNode.GetNodeAtIndex(Index);
- except
- on OutlineError do Error(SOutlineIndexError);
- end;
- if Result = FRootNode then Error(SOutlineError);
- end;
- function TMMCustomOutline.GetSelectedItem: LongInt;
- begin
- if FSelectedItem <> FRootNode then
- begin
- if not FSelectedItem.IsVisible then
- FSelectedItem := FSelectedItem.GetVisibleParent;
- end
- else if FRootNode.List.Count > 0 then
- FSelectedItem := FRootNode.GetVisibleNode(Row + 1);
- Result := FSelectedItem.Index
- end;
- procedure TMMCustomOutline.ResetSelectedItem;
- begin
- FSelectedItem := FRootNode;
- end;
- procedure TMMCustomOutline.SetRowFromNode(Node: TOutlineNode);
- var
- RowValue: LongInt;
- begin
- if Node <> FRootNode then
- begin
- RowValue := 0;
- FRootNode.GetRowOfNode(Node, RowValue);
- Row := RowValue - 2;
- end;
- end;
- procedure TMMCustomOutline.SetSelectedItem(Value: Longint);
- var
- Node: TOutlineNode;
- begin
- if FBlockInsert then Exit;
- if (Value = 0) and (FRootNode.List.Count > 0) then Value := 1;
- if Value > 0 then
- begin
- if Value = FSelectedItem.Index then Node := FSelectedItem else
- try
- Node := FRootNode.GetNodeAtIndex(Value);
- except
- on OutlineError do Error(SOutlineIndexError);
- end;
- if not Node.IsVisible then Node := Node.GetVisibleParent;
- FSelectedItem := Node;
- SetRowFromNode(Node);
- end
- else Error(SOutlineSelection);
- end;
- function TMMCustomOutline.Insert(Index: LongInt; const Text: string): LongInt;
- begin
- Result := InsertObject(Index, Text, nil);
- end;
- function TMMCustomOutline.InsertObject(Index: LongInt; const Text: string; const Data: Pointer): LongInt;
- begin
- if Index > 0 then
- Result := AttachNode(Index, Text, Data, oaInsert)
- else if Index = 0 then AddChildObject(Index, Text, Data)
- else Error(SOutlineError);
- SetCurItem(Index);
- end;
- function TMMCustomOutline.Add(Index: LongInt; const Text: string): LongInt;
- begin
- Result := AddObject(Index, Text, nil);
- end;
- function TMMCustomOutline.AddObject(Index: LongInt; const Text: string; const Data: Pointer): LongInt;
- begin
- if Index > 0 then Result := AttachNode(Index, Text, Data, oaAdd)
- else If Index = 0 then Result := AddChildObject(Index, Text, Data)
- else Error(SOutlineError);
- SetCurItem(Index);
- end;
- function TMMCustomOutline.AddChild(Index: LongInt; const Text: string): LongInt;
- begin
- Result := AddChildObject(Index, Text, nil);
- end;
- function TMMCustomOutline.AddChildObject(Index: LongInt; const Text: string; const Data: Pointer): LongInt;
- begin
- if Index >= 0 then Result := AttachNode(Index, Text, Data, oaAddChild)
- else Error(SOutlineError);
- SetCurItem(Index);
- end;
- procedure TMMCustomOutline.Delete(Index: LongInt);
- begin
- if Index > 0 then
- begin
- try
- FRootNode.GetNodeAtIndex(Index).Free;
- except
- on OutlineError do Error(SOutlineIndexError);
- end;
- end
- else Error(SOutlineError);
- end;
- procedure TMMCustomOutline.Move(Destination, Source: LongInt; AttachMode: TAttachMode);
- begin
- if (AttachMode = oaAddChild) or (Destination > 0) then
- MoveNode(Destination, Source, AttachMode)
- else Error(SOutlineError);
- end;
- procedure TMMCustomOutline.DeleteNode(Node: TOutlineNode; CurIndex: LongInt);
- begin
- if (FGoodNode = FRootNode) and (FRootNode.List.Count <> 0) then
- FRootNode[0].SetGoodIndex;
- try
- FCurItem := FRootNode.GetNodeAtIndex(CurIndex);
- except
- on OutlineError do FCurItem := FRootNode;
- end;
- if (FSelectedItem = FRootNode) and (Node <> FRootNode) then
- GetSelectedItem;
- if ResizeGrid then Invalidate;
- end;
- procedure TMMCustomOutline.SetLevel(Node: TOutlineNode; CurLevel, NewLevel: Cardinal);
- var
- NumLevels: Integer;
- procedure MoveUp(Node: TOutlineNode; NumLevels: Cardinal);
- var
- Parent: TOutlineNode;
- I: Cardinal;
- Index: Integer;
- begin
- Parent := Node;
- for I := NumLevels downto 1 do
- Parent := Parent.Parent;
- Index := Parent.Parent.GetNextChild(Parent.Index);
- if Index = InvalidIndex then Node.MoveTo(Parent.Parent.Index, oaAddChild)
- else Node.MoveTo(Index, oaInsert);
- end;
- procedure MoveDown(Node: TOutlineNode; NumLevels: Cardinal);
- var
- Parent: TOutlineNode;
- I: Cardinal;
- begin
- while NumLevels > 0 do
- begin
- Parent := Node.Parent;
- for I := Parent.List.Count - 1 downto 0 do
- if Parent.Items[I].Index = Node.Index then Break;
- if I > 0 then
- begin
- Parent := Parent.Items[I - 1];
- Node.MoveTo(Parent.Index, oaAddChild);
- end else Error(SOutlineBadLevel);
- Dec(NumLevels);
- end;
- end;
- begin
- NumLevels := CurLevel - NewLevel;
- if (NewLevel > 0) then
- begin
- if (NumLevels > 0) then MoveUp(Node, NumLevels)
- else MoveDown(Node, ABS(NumLevels));
- end
- else Error(SOutlineBadLevel);
- end;
- procedure TMMCustomOutline.Click;
- begin
- if FRootNode.List.Count > 0 then
- SelectedItem := FRootNode.GetVisibleNode(Row + 1).Index;
- inherited Click;
- end;
- procedure TMMCustomOutline.WMSize(var Message: TWMSize);
- begin
- inherited;
- if FSettingWidth or FSettingHeight then Exit;
- if (ScrollBars in [ssNone, ssVertical]) or
- ((Style = otOwnerDraw) and Assigned(FOnDrawItem)) then
- DefaultColWidth := ClientWidth
- else SetHorzScrollBar;
- end;
- procedure TMMCustomOutline.KeyPress(var Key: Char);
- begin
- inherited KeyPress(Key);
- if FSelectedItem <> FRootNode then
- case Key of
- '+': FSelectedItem.Expanded := True;
- '-': FSelectedItem.Expanded := False;
- '*': FSelectedItem.FullExpand;
- end;
- end;
- procedure TMMCustomOutline.KeyDown(var Key: Word; Shift: TShiftState);
- var
- Node: TOutlineNode;
- begin
- inherited KeyDown(Key, Shift);
- if FRootNode.List.Count = 0 then Exit;
- Node := FRootNode.GetVisibleNode(Row + 1);
- case Key of
- VK_HOME:
- begin
- SelectedItem := TOutlineNode(FRootNode.List.First).Index;
- Exit;
- end;
- VK_END:
- begin
- Node := TOutlineNode(FRootNode.List.Last);
- while Node.Expanded and Node.HasItems do
- Node := TOutlineNode(Node.List.Last);
- SelectedItem := Node.Index;
- Exit;
- end;
- VK_RETURN:
- begin
- Node.Expanded := not Node.Expanded;
- Exit;
- end;
- VK_MULTIPLY:
- begin
- if ssCtrl in Shift then
- begin
- FullExpand;
- Exit;
- end;
- end;
- VK_RIGHT:
- begin
- if (not Node.HasItems) or (not Node.Expanded) then MessageBeep(0)
- else SelectedItem := SelectedItem + 1;
- Exit;
- end;
- VK_LEFT:
- begin
- if Node.Parent = FRootNode then MessageBeep(0)
- else SelectedItem := Node.Parent.Index;
- Exit;
- end;
- VK_UP:
- if ssCtrl in Shift then
- begin
- with Node.Parent do
- begin
- if List.First = Node then MessageBeep(0)
- else SelectedItem := Items[List.IndexOf(Node) - 1].Index;
- end;
- Exit;
- end;
- VK_DOWN:
- if ssCtrl in Shift then
- begin
- with Node.Parent do
- begin
- if List.Last = Node then MessageBeep(0)
- else SelectedItem := Items[List.IndexOf(Node) + 1].Index;
- end;
- Exit;
- end;
- end;
- SelectedItem := FRootNode.GetVisibleNode(Row + 1).Index;
- end;
- procedure TMMCustomOutline.DblClick;
- var
- Node: TOutlineNode;
- begin
- inherited DblClick;
- Node := FSelectedItem;
- if Node <> FRootNode then DoExpand(Node);
- end;
- procedure TMMCustomOutline.MouseDown(Button: TMouseButton; Shift: TShiftState;
- X, Y: Integer);
- begin
- inherited MouseDown(Button, Shift, X, Y);
- ResetSelectedItem;
- GetSelectedItem;
- end;
- procedure TMMCustomOutline.FullExpand;
- begin
- FRootNode.FullExpand;
- end;
- procedure TMMCustomOutline.FullCollapse;
- var
- I: Integer;
- begin
- for I := 0 to FRootNode.List.Count - 1 do
- FRootNode.Items[I].Expanded := False;
- end;
- procedure TMMCustomOutline.SetHorzScrollBar;
- begin
- if (ScrollBars in [ssHorizontal, ssBoth]) and
- (UpdateCount <= 0) and not FIgnoreScrollResize and
- not ((Style = otOwnerDraw) and Assigned(FOnDrawItem)) then
- SetDisplayWidth(FRootNode.GetMaxDisplayWidth(0));
- end;
- procedure TMMCustomOutline.DoExpand(Node: TOutlineNode);
- begin
- with Node do
- Expanded := not Expanded;
- end;
- procedure TMMCustomOutline.BeginUpdate;
- begin
- if UpdateCount = 0 then SetUpdateState(True);
- Inc(UpdateCount);
- end;
- procedure TMMCustomOutline.EndUpdate;
- begin
- Dec(UpdateCount);
- if UpdateCount = 0 then SetUpdateState(False);
- end;
- procedure TMMCustomOutline.SetUpdateState(Value: Boolean);
- begin
- if FBlockInsert <> Value then
- begin
- FBlockInsert := Value;
- if not FBlockInsert then
- begin
- if ResizeGrid then Invalidate;
- if FRootNode.List.Count > 0 then
- TOutlineNode(FRootNode.List.First).SetGoodIndex
- else
- FRootNode.SetGoodIndex;
- SetHorzScrollBar;
- end;
- end;
- end;
- function TMMCustomOutline.ResizeGrid: Boolean;
- var
- i: integer;
- OldRowCount: LongInt;
- begin
- Result := False;
- if not FBlockInsert then
- begin
- OldRowCount := RowCount;
- FSettingHeight := True;
- try
- i := FRootNode.ExpandCount;
- RowCount := i;
- if (i > 0) then
- for i := 0 to RowCount-1 do
- begin
- if GetVisibleNode(i).HasItems then
- RowHeights[i] := FItemHeight
- else
- RowHeights[i] := FChildItemHeight;
- end;
- finally
- FSettingHeight := False;
- end;
- Result := RowCount <> OldRowCount;
- if FSelectedItem <> FRootNode then SelectedItem := FSelectedItem.Index;
- end;
- end;
- function TMMCustomOutline.BadIndex(Value: TOutlineNode): Boolean;
- begin
- Result := CompareNodes(Value, FGoodNode) = ocGreater;
- end;
- function TMMCustomOutline.SetGoodIndex(Value: TOutlineNode): TOutlineNode;
- var
- ParentNode: TOutlineNode;
- Index: Integer;
- Compare: TOutlineNodeCompare;
- begin
- Compare := CompareNodes(FGoodNode, Value);
- case Compare of
- ocLess,
- ocSame:
- Result := FGoodNode;
- ocGreater:
- begin
- ParentNode := Value.Parent;
- Index := ParentNode.List.IndexOf(Value);
- if Index <> 0 then
- Result := ParentNode[Index - 1]
- else
- Result := ParentNode;
- end;
- ocInvalid:
- Result := FRootNode;
- end;
- FGoodNode := Result;
- end;
- function TMMCustomOutline.CompareNodes(Value1, Value2: TOutlineNode): TOutlineNodeCompare;
- var
- Level1: Integer;
- Level2: Integer;
- Index1: Integer;
- Index2: Integer;
- Value1ParentNode: TOutlineNode;
- Value2ParentNode: TOutlineNode;
- CommonNode: TOutlineNode;
- function GetParentNodeAtLevel(Value: TOutlineNode; Level: Integer): TOutlineNode;
- begin
- while Level > 0 do
- begin
- Value := Value.Parent;
- Dec(Level);
- end;
- Result := Value;
- end;
- begin
- if Value1 = Value2 then
- begin
- Result := ocSame;
- Exit;
- end;
- Value1ParentNode := Value1;
- Value2ParentNode := Value2;
- Level1 := Value1.GetLevel;
- Level2 := Value2.GetLevel;
- if Level1 > Level2 then
- Value1ParentNode := GetParentNodeAtLevel(Value1, Level1 - Level2)
- else if Level2 > Level1 then
- Value2ParentNode := GetParentNodeAtLevel(Value2, Level2 - Level1);
- while Value1ParentNode.Parent <> Value2ParentNode.Parent do
- begin
- Value1ParentNode := Value1ParentNode.Parent;
- Value2ParentNode := Value2ParentNode.Parent;
- end;
- CommonNode := Value1ParentNode.Parent;
- if CommonNode <> nil then
- begin
- Index1 := CommonNode.List.IndexOf(Value1ParentNode);
- Index2 := CommonNode.List.IndexOf(Value2ParentNode);
- if Index1 < Index2 then Result := ocLess
- else if Index2 < Index1 then Result := ocGreater
- else begin
- if Level1 > Level2 then Result := ocGreater
- else if Level1 = Level2 then Result := ocSame
- else Result := ocLess;
- end
- end
- else
- Result := ocInvalid;
- end;
- function TMMCustomOutline.GetDataItem(Value: Pointer): Longint;
- begin
- Result := FRootNode.GetDataItem(Value);
- end;
- function TMMCustomOutline.GetItem(X, Y: Integer): LongInt;
- var
- Value: TGridCoord;
- begin
- Result := -1;
- Value := MouseCoord(X, Y);
- with Value do
- if (Y > 0) or (FRootNode.List.Count > 0) then
- Result := FRootNode.GetVisibleNode(Y + 1).Index;
- end;
- function TMMCustomOutline.GetTextItem(const Value: string): Longint;
- begin
- Result := FRootNode.GetTextItem(Value);
- end;
- procedure TMMCustomOutline.SetCurItem(Value: LongInt);
- begin
- if Value < 0 then Error(SInvalidCurrentItem);
- if not IsCurItem(Value) then
- try
- FCurItem := FRootNode.GetNodeAtIndex(Value);
- except
- on OutlineError do Error(SOutlineIndexError);
- end;
- end;
- procedure TMMCustomOutline.SetOutlineStyle(Value: TOutlineStyle);
- begin
- if FOutlineStyle <> Value then
- begin
- FOutlineStyle := Value;
- SetHorzScrollBar;
- Invalidate;
- end;
- end;
- procedure TMMCustomOutline.CMFontChanged(var Message: TMessage);
- begin
- inherited;
- SetRowHeight;
- SetHorzScrollBar;
- end;
- procedure TMMCustomOutline.SetDisplayWidth(Value: Integer);
- begin
- FSettingWidth := True;
- try
- if DefaultColWidth <> Value then DefaultColWidth := Value;
- finally
- FSettingWidth := False;
- end;
- end;
- function TMMCustomOutline.GetNodeDisplayWidth(Node: TOutlineNode): Integer;
- var
- Delta: Integer;
- TextLength: Integer;
- begin
- Result := 0;
- Delta := (DefaultRowHeight - FFontSize) div 2;
- with Canvas do
- begin
- Font := Self.Font;
- TextLength := TextWidth(Node.Text) + 1;
- end;
- case OutlineStyle of
- osText: Inc(Result, DefaultRowHeight * (Node.Level - 1));
- osPlusMinusPictureText: Inc(Result, DefaultRowHeight * (Node.Level + 1));
- osPlusMinusText,
- osPictureText: Inc(Result, DefaultRowHeight * Node.Level);
- osTreeText:
- begin
- Inc(Result, DefaultRowHeight * (Node.Level - 1) - Delta);
- if ooDrawTreeRoot in Options then Inc(Result, DefaultRowHeight);
- end;
- osTreePictureText:
- begin
- Inc(Result, DefaultRowHeight * (Node.Level) - Delta);
- if ooDrawTreeRoot in Options then Inc(Result, DefaultRowHeight);
- end;
- end;
- Inc(Result, TextLength+2);
- if Result < 0 then Result := 0;
- end;
- function TMMCustomOutline.GetVisibleNode(Index: LongInt): TOutlineNode;
- begin
- Result := FRootNode.GetVisibleNode(Index + 1);
- end;
- procedure TMMCustomOutline.DrawCell(ACol, ARow: Longint; ARect: TRect; AState: TGridDrawState);
- var
- Node: TOutlineNode;
- Expanded: Boolean;
- HasChildren: Boolean;
- IndentLevel: Word;
- Bitmap1, Bitmap2: TBitmap;
- TextLength: Integer;
- Delta: Integer;
- InitialLeft: Integer;
- function GetBitmap(Value: TOutlineBitmap): TBitmap;
- begin
- Result := FPictures[Ord(Value)];
- end;
- procedure DrawFocusCell;
- begin
- Inc(ARect.Right, TextLength);
- if (Row = ARow) and (Node.Text <> '') then
- Canvas.FillRect(ARect);
- end;
- procedure DrawTheText;
- begin
- Inc(ARect.Left, DefaultRowHeight * (IndentLevel - 1));
- ARect.Right := ARect.Left;
- DrawFocusCell;
- DrawText(Node, ARect);
- end;
- procedure DrawPlusMinusPicture;
- begin
- Inc(ARect.Left, DefaultRowHeight * (IndentLevel - 1));
- if HasChildren then
- begin
- if Expanded then
- begin
- Bitmap1 := GetBitmap(obMinus);
- Bitmap2 := GetBitmap(obOpen);
- end
- else begin
- Bitmap1 := GetBitmap(obPlus);
- Bitmap2 := GetBitmap(obClose);
- end;
- end
- else begin
- Bitmap1 := nil;
- Bitmap2 := GetBitmap(obLeaf);
- end;
- ARect.Left := ARect.Left + DefaultRowHeight * 2;
- ARect.Right := ARect.Left;
- DrawFocusCell;
- DrawText(Node, ARect);
- Dec(ARect.Left, DefaultRowHeight * 2);
- DrawPictures([Bitmap1, Bitmap2], ARect);
- end;
- procedure DrawPictureText;
- var
- Style: TOutlineBitmap;
- begin
- Inc(ARect.Left, DefaultRowHeight * (IndentLevel - 1));
- if HasChildren then
- begin
- if Expanded then Style := obOpen
- else Style := obClose
- end
- else Style := obLeaf;
- Bitmap1 := GetBitmap(Style);
- ARect.Left := ARect.Left + DefaultRowHeight;
- ARect.Right := ARect.Left;
- DrawFocusCell;
- DrawText(Node, ARect);
- Dec(ARect.Left, DefaultRowHeight);
- DrawPictures([Bitmap1], ARect);
- end;
- procedure DrawPlusMinusText;
- var
- Style: TOutlineBitmap;
- begin
- Inc(ARect.Left, DefaultRowHeight * IndentLevel);
- ARect.Right := ARect.Left;
- DrawFocusCell;
- DrawText(Node, ARect);
- if HasChildren then
- begin
- if Expanded then Style := obMinus
- else Style := obPlus;
- Bitmap1 := GetBitmap(Style);
- Dec(ARect.Left, DefaultRowHeight);
- DrawPictures([Bitmap1], ARect);
- end;
- end;
- procedure DrawTheTree;
- begin
- DrawTree(ARect, Node);
- Inc(ARect.Left, DefaultRowHeight * (IndentLevel - 1) - Delta);
- if ooDrawTreeRoot in Options then Inc(ARect.Left, DefaultRowHeight);
- ARect.Right := ARect.Left + Delta;
- DrawFocusCell;
- Inc(ARect.Left, Delta);
- DrawText(Node, ARect);
- end;
- procedure DrawTreePicture;
- var
- Style: TOutlineBitmap;
- begin
- //DrawTree(ARect, Node);
- Inc(ARect.Left, DefaultRowHeight * (IndentLevel - 1) - Delta);
- if ooDrawTreeRoot in Options then Inc(ARect.Left, DefaultRowHeight);
- ARect.Left := 2+ARect.Left + DefaultRowHeight;
- ARect.Right := ARect.Left + Delta;
- DrawFocusCell;
- DrawText(Node, ARect);
- ARect.Left := ARect.Left-2;
- Dec(ARect.Left, DefaultRowHeight - Delta);
- if HasChildren then
- begin
- if Expanded then Style := obOpen
- else Style := obClose;
- end
- else Style := obLeaf;
- Bitmap1 := GetBitmap(Style);
- // Canvas.Brush.Color := clRed;
- // Canvas.FillRect(aRect);
- DrawPictures([Bitmap1], ARect);
- end;
- begin
- if FRootNode.List.Count = 0 then
- begin
- with Canvas do
- begin
- Brush.Color := Color;
- FillRect(ARect);
- end;
- Exit;
- end;
- if (Style = otOwnerDraw) and Assigned(FOnDrawItem) then
- begin
- if Row = ARow then
- begin
- if GetFocus = Self.Handle then
- begin
- FOnDrawItem(Self, ARow, ARect, [odFocused, odSelected]);
- if ooDrawFocusRect in Options then
- DrawFocusRect(Canvas.Handle, ARect);
- end
- else FOnDrawItem(Self, ARow, ARect, [odSelected])
- end
- else OnDrawItem(Self, ARow, ARect, []);
- Exit;
- end;
- InitialLeft := ARect.Left;
- Node := GetVisibleNode(ARow);
- Delta := (ARect.Bottom - ARect.Top - FFontSize) div 2;
- with Canvas do
- begin
- {!!!!!!!!!!!!!!!!!!}
- (*
- InflateRect(ARect,-1,-1);
- Brush.Color := clRed;
- FillRect(ARect);
- exit;*)
- Font := Self.Font;
- Brush.Color := Color;
- FillRect(ARect);
- TextLength := TextWidth(Node.Text) + 1;
- if Row = ARow then
- begin
- Brush.Color := clHighlight;
- Font.Color := clHighlightText;
- end;
- end;
- Expanded := Node.Expanded;
- HasChildren := Node.HasItems;
- IndentLevel := Node.GetLevel;
- case OutlineStyle of
- osText: DrawTheText;
- osPlusMinusText: DrawPlusMinusText;
- osPlusMinusPictureText: DrawPlusMinusPicture;
- osPictureText: DrawPictureText;
- osTreeText: DrawTheTree;
- osTreePictureText: DrawTreePicture;
- end;
- exit;
- if (Row = ARow) and (Node.Text <> '') then
- begin
- ARect.Left := InitialLeft + DefaultRowHeight * (IndentLevel - 1);
- if OutlineStyle >= osTreeText then
- begin
- Dec(ARect.Left, Delta);
- if ooDrawTreeRoot in Options then Inc(ARect.Left, DefaultRowHeight);
- end;
- if (OutlineStyle <> osText) and (OutlineStyle <> osTreeText) then
- Inc(ARect.Left, DefaultRowHeight);
- if OutlineStyle = osPlusMinusPictureText then
- Inc(ARect.Left, DefaultRowHeight);
- if (GetFocus = Self.Handle) and (ooDrawFocusRect in Options) then
- DrawFocusRect(Canvas.Handle, ARect);
- end;
- end;
- procedure TMMCustomOutline.DrawTree(ARect: TRect; Node: TOutlineNode);
- var
- Offset: Word;
- Height: Word;
- OldPen: TPen;
- I: Integer;
- ParentNode: TOutlineNode;
- IndentLevel: Integer;
- begin
- Offset := DefaultRowHeight div 2;
- Height := ARect.Bottom;
- IndentLevel := Node.GetLevel;
- I := IndentLevel - 3;
- if ooDrawTreeRoot in Options then Inc(I);
- OldPen := TPen.Create;
- try
- OldPen.Assign(Canvas.Pen);
- with Canvas do
- begin
- Pen.Color := clBlack;
- Pen.Width := 1;
- try
- ParentNode := Node.Parent;
- while (ParentNode.Parent <> nil) and
- ((ooDrawTreeRoot in Options) or
- (ParentNode.Parent.Parent <> nil)) do
- begin
- with ParentNode.Parent do
- begin
- if List.IndexOf(ParentNode) < List.Count - 1 then
- begin
- Canvas.MoveTo(ARect.Left + DefaultRowHeight * I + Offset, ARect.Top);
- Canvas.LineTo(ARect.Left + DefaultRowHeight * I + Offset, Height);
- end;
- end;
- ParentNode := ParentNode.Parent;
- Dec(I);
- end;
- with Node.Parent do
- if List.IndexOf(Node) = List.Count - 1 then
- Height := ARect.Top + Offset;
- if (ooDrawTreeRoot in Options) or (IndentLevel > 1) then
- begin
- if not (ooDrawTreeRoot in Options) then Dec(IndentLevel);
- with ARect do
- begin
- Inc(Left, DefaultRowHeight * (IndentLevel - 1));
- MoveTo(Left + Offset, Top);
- LineTo(Left + Offset, Height);
- MoveTo(Left + Offset, Top + Offset);
- LineTo(Left + Offset + FFontSize div 2, Top + Offset);
- end;
- end;
- finally
- Pen.Assign(OldPen);
- end;
- end;
- finally
- OldPen.Destroy;
- end;
- end;
- procedure TMMCustomOutline.DrawPictures(BitMaps: array of TBitmap; ARect: TRect);
- var
- I: Word;
- Rect: TRect;
- Value: TBitmap;
- Offset: Word;
- Delta: Integer;
- OldTop: Integer;
- OldColor: TColor;
- begin
- OldColor := Canvas.Brush.Color;
- Canvas.Brush.Color := Color;
- Offset := (DefaultRowHeight - FFontSize) div 2;
- //Rect.Top := ARect.Top + Offset;
- //Rect.Bottom := Rect.Top + FFontSize;
- for I := Low(Bitmaps) to High(Bitmaps) do
- begin
- Value := BitMaps[I];
- Rect.Left := ARect.Left + Offset;
- Rect.Right := Rect.Left + Value.Width;
- Inc(ARect.Left, DefaultRowHeight);
- if Value <> nil then
- begin
- if not (ooStretchBitmaps in Options) then
- begin
- //if Rect.Top + Value.Height < Rect.Bottom then
- // Rect.Bottom := Rect.Top + Value.Height;
- //if Rect.Left + Value.Width < Rect.Right then
- // Rect.Right := Rect.Left + Value.Width;
- //Delta := (FFontSize - (Rect.Bottom - Rect.Top)) div 2;
- //if Delta > 0 then
- //begin
- // Delta := (DefaultRowHeight - (Rect.Bottom - Rect.Top)) div 2;
- // OldTop := Rect.Top;
- // Rect.Top := ARect.Top + Delta;
- // Rect.Bottom := Rect.Bottom - OldTop + Rect.Top;
- //end;
- Rect.Top := aREct.Top+((aRect.Bottom-aRect.Top)-Value.Height)div 2;
- Rect.Bottom := Rect.Top + Value.Height;
- Canvas.BrushCopy(Rect, Value,
- Bounds(0, 0, Rect.Right - Rect.Left, Rect.Bottom - Rect.Top),
- Value.TransparentColor);
- end
- else
- Canvas.BrushCopy(Rect, Value,
- Bounds(0, 0, Value.Width, Value.Height),
- Value.TransparentColor);
- end;
- end;
- Canvas.Brush.Color := OldColor;
- end;
- procedure TMMCustomOutline.DrawText(Node: TOutlineNode; Rect: TRect);
- begin
- Windows.DrawText(Canvas.Handle, PChar(Node.Text), Length(Node.Text), Rect,
- DT_LEFT or DT_SINGLELINE or DT_VCENTER or DT_NOPREFIX);
- end;
- function TMMCustomOutline.StoreBitmap(Index: Integer): Boolean;
- begin
- Result := TOutlineBitmap(Index) in FUserBitmaps;
- end;
- procedure TMMCustomOutline.ClearBitmap(var Bitmap: TBitmap; Kind: TOutlineBitmap);
- begin
- if Bitmap <> nil then
- begin
- Bitmap.Free;
- Bitmap := nil;
- end;
- end;
- procedure TMMCustomOutline.ChangeBitmap(Value: TBitmap; Kind: TOutlineBitmap);
- var
- Bitmap: ^TBitmap;
- begin
- Bitmap := @FPictures[Ord(Kind)];
- Include(FUserBitmaps, Kind);
- if Value = nil then ClearBitmap(Bitmap^, Kind)
- else Bitmap^.Assign(Value);
- Invalidate;
- end;
- procedure TMMCustomOutline.SetPicture(Index: Integer; Value: TBitmap);
- begin
- ChangeBitmap(Value, TOutlineBitmap(Index));
- end;
- function TMMCustomOutline.GetPicture(Index: Integer): TBitmap;
- begin
- if csLoading in ComponentState then
- Include(FUserBitmaps, TOutlineBitmap(Index));
- Result := FPictures[Index];
- end;
- procedure TMMCustomOutline.LoadFromFile(const FileName: string);
- var
- Stream: TStream;
- begin
- Stream := TFileStream.Create(FileName, fmOpenRead);
- try
- LoadFromStream(Stream);
- finally
- Stream.Free;
- end;
- end;
- procedure TMMCustomOutline.SetMaskColor(Value: TColor);
- begin
- FMaskColor := Value;
- Invalidate;
- end;
- procedure TMMCustomOutline.SetItemHeight(Value: Integer);
- begin
- FItemHeight := Value;
- if Style <> otOwnerDraw then SetRowHeight
- else
- begin
- DefaultRowHeight := ItemHeight;
- FFontSize := MulDiv(ItemHeight, 100, 120);
- Invalidate;
- end;
- end;
- procedure TMMCustomOutline.SetChildItemHeight(Value: Integer);
- begin
- if (FChildItemHeight <> Value) then
- begin
- FChildItemHeight := Value;
- if not (csLoading in ComponentState) then
- begin
- ResizeGrid;
- Invalidate;
- end;
- end;
- end;
- procedure TMMCustomOutline.SetStyle(Value: TOutlineType);
- begin
- if Style <> Value then
- begin
- FStyle := Value;
- if Value = otStandard then SetRowHeight;
- end;
- end;
- procedure TMMCustomOutline.SetOutlineOptions(Value: TOutlineOptions);
- begin
- if Value <> FOptions then
- begin
- FOptions := Value;
- Invalidate;
- end;
- end;
- function LineStart(Buffer, BufPos: PChar): PChar;
- begin
- if BufPos - Buffer - 2 > 0 then
- begin
- Dec(BufPos, 2);
- while (BufPos^ <> #$0D) and (BufPos > Buffer) do Dec(BufPos);
- if BufPos > Buffer then
- begin
- Inc(BufPos);
- if BufPos^ = #$0A then Inc(BufPos);
- end;
- Result := BufPos;
- end
- else Result := Buffer;
- end;
- function GetString(BufPtr: PChar; var S: string): PChar;
- var
- Start: PChar;
- begin
- Start := BufPtr;
- while not (BufPtr^ in [#13, #26]) do Inc(BufPtr);
- SetString(S, Start, Integer(BufPtr - Start));
- if BufPtr^ = #13 then Inc(BufPtr);
- if BufPtr^ = #10 then Inc(BufPtr);
- Result := BufPtr;
- end;
- procedure TMMCustomOutline.LoadFromStream(Stream: TStream);
- const
- EOF = Chr($1A);
- BufSize = 4096;
- var
- Count: Integer;
- Buffer, BufPtr, BufEnd, BufTop: PChar;
- ParentNode, NewNode: TOutlineNode;
- Str: string;
- Level, OldLevel: Cardinal;
- I: Integer;
- begin
- GetMem(Buffer, BufSize);
- try
- OldLevel := 0;
- Clear;
- ParentNode := FRootNode;
- BufEnd := Buffer + BufSize;
- BufTop := BufEnd;
- repeat
- Count := BufEnd - BufTop;
- if Count <> 0 then System.Move(BufTop[0], Buffer[0], Count);
- BufTop := Buffer + Count;
- Inc(BufTop, Stream.Read(BufTop[0], BufEnd - BufTop));
- if BufTop < BufEnd then BufTop[0] := EOF else
- begin
- BufTop := LineStart(Buffer, BufTop);
- if BufTop = Buffer then Error(SOutlineLongLine);
- end;
- BufPtr := Buffer;
- while (BufPtr < BufTop) and (BufPtr[0] <> EOF) do
- begin
- BufPtr := GetBufStart(BufPtr, Level);
- BufPtr := GetString(BufPtr, Str);
- NewNode := TOutlineNode.Create(Self);
- try
- NewNode.Text := Str;
- if (Level > OldLevel) or (ParentNode = FRootNode) then
- begin
- if Level - OldLevel > 1 then Error(SOutlineFileLoad);
- end
- else
- begin
- for I := OldLevel downto Level do
- begin
- ParentNode := ParentNode.Parent;
- if ParentNode = nil then Error(SOutlineFileLoad);
- end;
- end;
- ParentNode.List.Add(NewNode);
- NewNode.FParent := ParentNode;
- ParentNode := NewNode;
- OldLevel := Level;
- except
- NewNode.Free;
- Raise;
- end;
- end;
- until (BufPtr < BufEnd) and (BufPtr[0] = EOF);
- finally
- FreeMem(Buffer, BufSize);
- if not (csLoading in ComponentState) then Loaded;
- end;
- end;
- procedure TMMCustomOutline.Loaded;
- var
- Item: TOutlineBitmap;
- begin
- inherited Loaded;
- with FRootNode do
- begin
- FExpandCount := List.Count;
- Row := 0;
- ResetSelectedItem;
- if ResizeGrid then Invalidate;
- if List.Count > 0 then
- begin
- TOutlineNode(List.First).SetGoodIndex;
- FSelectedItem := List.First;
- end;
- if csDesigning in ComponentState then FullExpand;
- end;
- for Item := obPlus to obLeaf do
- if (Item in FOldBitmaps) and not (Item in FUserBitmaps) then
- ChangeBitmap(nil, Item);
- FOldBitmaps := [];
- SetHorzScrollBar;
- end;
- procedure TMMCustomOutline.SaveToFile(const FileName: string);
- var
- Stream: TStream;
- begin
- Stream := TFileStream.Create(FileName, fmCreate);
- try
- SaveToStream(Stream);
- finally
- Stream.Free;
- end;
- end;
- procedure TMMCustomOutline.SaveToStream(Stream: TStream);
- const
- BufSize = 4096;
- var
- Buffer: PChar;
- begin
- GetMem(Buffer, BufSize);
- try
- FRootNode.WriteNode(Buffer, Stream);
- finally
- FreeMem(Buffer, BufSize);
- end;
- end;
- procedure TMMCustomOutline.SetStrings(Value: TStrings);
- begin
- FStrings.Assign(Value);
- if csDesigning in ComponentState then FRootNode.FullExpand;
- SetHorzScrollBar;
- end;
- function TMMCustomOutline.GetStrings: TStrings;
- begin
- Result := FStrings;
- end;
- procedure TMMCustomOutline.Error(const ErrorString: string);
- begin
- Raise EOutlineError.Create(ErrorString);
- end;
- procedure TMMCustomOutline.Expand(Index: LongInt);
- begin
- if Assigned(FOnExpand) then FOnExpand(Self, Index);
- end;
- procedure TMMCustomOutline.Collapse(Index: LongInt);
- begin
- if Assigned(FOnCollapse) then FOnCollapse(Self, Index);
- end;
- procedure Register;
- begin
- RegisterComponents('Samples', [TMMOutLine]);
- end;
- end.