source: branches/2.20_branch/Components/Outline2.PAS@ 378

Last change on this file since 378 was 251, checked in by RBRi, 18 years ago

fix usage of StrStartsWithIgnoringCase

  • Property svn:eol-style set to native
File size: 54.2 KB
Line 
1Unit Outline2;
2
3Interface
4
5Uses
6 Classes,
7 Forms,
8 SysUtils,
9 BseDos;
10
11{
12History
13
14V1.4.13 20/1/1
15Standardised border, use DrawSystemBorder.
16Added smooth scrolling.
17
18V1.4.12 16/9/00
19Added recursive collapse functions. Incl TOutline2.CollapseAll;
20
21V1.4.11 9/8/00
22Small optimisation for adding nodes outside
23of Update...
24If the node is collapsed, don't check refresh
25
26V1.4.10 9/8/00
27Added level and parent functions to TNOde
28
29V1.4.9 9/8/00
30Fixed problem with not setting up scroll bars after update.?
31Draw selected node correctly, with inverted colors.
32
33V1.4.8 31/7/00
34Crap - I damaged the node setting.
35
36V1.4.7 30/7/00
37Improved handling during updates: if the
38selected node is set during update then the new node is
39just saved and applied at end update.
40
41V1.4.6 30/7/00
42When clearing a node's children do it last node first
43for efficiency in list handling.
44
45V1.4.5 30/7/00
46Minor bit of fiddling with child list allocation.
47
48V1.4.4 26/4/00
49Added goto next node up and down
50(includes non-visible nodes; renamed previous
51methods to gotonextVISIBLEnodeup/down)
52
53V1.4.3 26/4/00
54Added page up and down
55
56V1.4.2 26/4/00
57Fixed problem clearing corner rect between scrollbars.
58
59V1.4.1 26/4/00
60Added +/- keyboard commands
61If selected node would be hidden when collapsing then
62make the collapsing node the selected one.
63
64V1.4.0 26/4/00
65Hmm, did I really do nothing for all that time ?
66Anyway, now has keyboard support, home/end/cursor up/down
67
68V1.3.1 12/4/00
69Tree lines have a seperate color
70Limit plus minus size to min 7
71ScanExtents sets lineheight to textheight if textheight is bigger.
72
73V1.3.0 12/4/00
74Optimised a lot more. Now drawing can be just of a single node
75which eliminates most flickering. Made the plus/minus buttons
76show feedback when clicked, exactly like original TOutline.
77
78V1.2.0 11/4/00
79Worked some more on visual aspects:
80- can now draw 3d or flat plus/minus
81- optimise background clearing somewhat (draw per node)
82- can show selected node as whole line or just text
83
84THis reduces flicker but, it coudl be better.
85Main thing to do would be to seperate out the layout:
86
87Function Layout:
88Reads from root to last expanded node. Works out Y position of
89each node and stores in the node. (?)
90
91This would allow node drawing to be done basically independently...
92
93V1.1.0 ???
94Now can set topnode to anything. Will draw
95correctly.
96Added ExpandParents method to help
97}
98
99const
100 AllNodes = nil;
101 WM_MOUSEENTER = $041E;
102 WM_MOUSELEAVE = $041F;
103
104Type
105
106 TNodeFindFunction = function( Data: TObject ): boolean;
107
108 TOutline2 = class;
109
110 TNode = class
111 protected
112 _Text: ^string;
113 _Data: TObject;
114 _Children: TList;
115 _Parent: TNode; // nil if head
116 _Expanded: boolean; // only applicable if _Children.Count> 0
117 _Outline: TOutline2;
118 Function GetChild( index: longint ): TNode;
119 Function IsVisible: boolean;
120 procedure CheckChangeRefresh;
121 Function GetText: string;
122 procedure SetText( const NewText: string );
123 procedure FreeText;
124 public
125 property Children[ index: longint ]: TNode read GetChild;
126 property Text: string read GetText write SetText;
127 property Data: TObject read _Data write _Data;
128
129 constructor Create( Parent: TNode );
130
131 function AddChild( const TheText: string;
132 const TheData: TObject ): TNode;
133 function HasChildren: boolean;
134 function ChildCount: longint;
135
136 procedure Clear; // clear children
137
138 destructor Destroy; override;
139
140 function Level: longint; // 0 is top level.
141 function Parent: TNode; // nil if at top level
142 function FullPath( const Separator: string ): string;
143 function IndexOfChild( Child: TNode ): longint;
144 function IndexInParent: longint;
145 function FindData( DataToFind: TObject ): TNode;
146 function FindByFunction( FindFunction: TNodeFindFunction ): TNode;
147
148 // Returns true if the given node is a parent of
149 // this node
150 function NodeIsAParent( Node: TNode ): boolean;
151
152 property Expanded: boolean read _Expanded;
153 procedure Expand;
154 procedure ExpandChildren( Recursive: boolean );
155
156 // Expand all parents if needed
157 procedure ExpandParents;
158
159 procedure Collapse;
160 procedure CollapseChildren( Recursive: boolean );
161 end;
162
163 TNodePart =
164 (
165 npNothing,
166 npPlusMinus,
167 npBitmap, // not yet implemented
168 npText,
169 npOther
170 );
171
172 TPlusMinusStyle = ( pmFlat, pm3d );
173
174 TNodeEvent = procedure( Node: TNode ) of object;
175
176 TOutline2=Class(TControl)
177 Protected
178 _UpdateCount: integer;
179 _NewSelectedNode: TNode; // during update
180
181 _MouseDownNode: TNode;
182 _MouseDownPart: TNodePart;
183 _MouseDownFeedback: boolean;
184 _LastMouseMoveFeedback: boolean;
185
186 _LastMouseX: longint;
187 _LastMouseY: longint;
188
189 _HScrollbar: TScrollbar;
190 _VScrollbar: TScrollbar;
191 _NeedVScroll, _NeedHScroll: boolean;
192
193 // Scroll information
194 // we use these rather than the scrollbar positions direct,
195 // since those are not updated during tracking
196 FXScroll: longint;
197 FYScroll: longint;
198
199 FLastXScroll: longint;
200 FLastYScroll: longint;
201
202 FSmoothScroll: boolean;
203
204 _Root: TNode;
205
206 _HoverTimer: TTimer;
207 _HintWindow: THintWindow;
208
209 // appearance controls
210 _BorderStyle: TBorderStyle;
211 _ScrollbarWidth: longint;
212 _LineHeight: longint;
213 _AutoLineHeight: boolean;
214 _Indent: longint; // X distance between branches
215 _LeftMargin: longint; // X distance to left of first plus/minus col
216 _TextIndent: longint; // pixels between stub end and text
217 _PlusMinusWidth: longint;
218 _PlusMinusHeight: longint;
219 _StubWidth: longint; // additional distance beyond indent to draw lines
220 _PlusMinusStyle: TPlusMinusStyle;
221 _SelectLine: boolean;
222 _TreeLineColor: TColor;
223
224 _PathSeparator: string;
225
226 _SelectedNode: TNode;
227
228 _OnItemDblClick: TNodeEvent;
229 _OnItemFocus: TNodeEvent;
230 _OnItemClick: TNodeEvent;
231 _OnExpand: TNodeEvent;
232 _OnCollapse: TNodeEvent;
233
234 _DrawNode: TNode; // nil for all
235
236 Procedure SetupComponent; Override;
237
238 Procedure ScanExtents;
239 Procedure ScanNodeExtents( Node: TNode;
240 X: longint;
241 Var MaxX: longint;
242 Var MaxY: longint );
243
244 Function RowIsVisible( Y: longint ): boolean;
245
246 function GetNodePosition( const Node: TNode;
247 Var NodeTop: longint ): boolean;
248 function NodeGetNodePosition( const NodeToFind: TNode;
249 const NodeToScan: TNode;
250 Var NodeTop: longint ): boolean;
251
252 procedure DrawNode( Node: TNode );
253
254 procedure Draw;
255 Procedure DrawBorder;
256 Procedure PaintNode( Node: TNode;
257 X: longint;
258 Var Y: longint );
259
260 procedure DrawPlusMinus( Rect: TRect;
261 Expanded: boolean;
262 Pressed: boolean );
263
264 Procedure TestHit( Point: TPoint;
265 Var Node: TNode;
266 Var Part: TNodePart );
267 Procedure TestNodeHit( Node: TNode;
268 Point: TPoint;
269 X: longint;
270 Var Y: longint;
271 Var HitNode: TNode;
272 Var Part: TNodePart );
273 Procedure SetupScrollbars;
274 function GetDrawRect: TRect;
275 function GetTextRect: TRect;
276 function DrawHeight: longint;
277 function GetLineRect( LineBottom: longint ): TRect;
278 Function GetPlusMinusRect( Left, LineBottom: longint ): TRect;
279
280 Procedure DoHorizontalScroll( NewX: longint );
281 Procedure DoVerticalScroll( NewY: longint );
282
283 // Property handlers
284 Procedure SetSelectedNode( Node: TNode );
285 Procedure SetTopNode( Node: TNode );
286
287 Function GetChild( index: longint ): TNode;
288 Function GetChildCount: longint;
289
290 Procedure SetLineHeight( LineHeight: longint );
291 Procedure SetAutoLineHeight( Value: boolean );
292 Procedure SetIndent( Indent: longint );
293 Procedure SetBorderStyle( BorderStyle: TBorderStyle );
294 Procedure SetScrollbarWidth( ScrollbarWidth: longint );
295 Procedure SetLeftMargin( LeftMargin: longint );
296 Procedure SetTextIndent( TextIndent: longint );
297 Procedure SetPlusMinusWidth( PlusMinusWidth: longint );
298 Procedure SetPlusMinusHeight( PlusMinusHeight: longint );
299 Procedure SetStubWidth( StubWidth: longint );
300 Procedure SetPlusMinusStyle( NewValue: TPlusMinusStyle );
301 Procedure SetSelectLine( NewValue: boolean );
302 Procedure SetTreeLineColor( NewValue: TColor );
303 Public
304 Procedure OnHoverTimeout( Sender: TObject );
305 // PM Events
306 Procedure Redraw( const rec: TRect ); Override;
307 Procedure Resize; override;
308 Procedure MouseDblClick( Button: TMouseButton;
309 ShiftState: TShiftState;
310 X, Y: Longint ); override;
311 Procedure MouseDown( Button: TMouseButton;
312 ShiftState: TShiftState;
313 X, Y: Longint ); override;
314 Procedure MouseUp( Button: TMouseButton;
315 ShiftState: TShiftState;
316 X, Y: Longint ); override;
317 Procedure MouseMove( ShiftState: TShiftState;
318 X, Y: Longint ); override;
319
320 Procedure Scroll( Sender: TScrollbar;
321 ScrollCode: TScrollCode;
322 Var ScrollPos: Longint ); override;
323
324 Procedure SetupShow; override;
325
326 Procedure ScanEvent( Var KeyCode: TKeyCode;
327 RepeatCount: Byte ); override;
328 Procedure CharEvent( Var Key: Char;
329 RepeatCount: Byte ); override;
330 Procedure WMMouseLeave( Var Msg: TMessage ); message WM_MOUSELEAVE;
331
332 Procedure FontChange; override;
333
334 Procedure SetFocus; override;
335 Procedure KillFocus; override;
336 published
337 property Align;
338 property Color;
339 property Font;
340 property ParentColor;
341 property ParentFont;
342 property ParentPenColor;
343 property ParentShowHint;
344 property PenColor;
345 property PopupMenu;
346 property ShowHint;
347 Property TabOrder;
348 Property TabStop;
349 property Visible;
350 property ZOrder;
351
352 property LineHeight: longint read _LineHeight write SetLineHeight;
353 property AutoLineHeight: boolean read _AutoLineHeight write SetAutoLineHeight;
354 property Indent: longint read _Indent write SetIndent;
355 property BorderStyle: TBorderStyle read _BorderStyle write SetBorderStyle;
356 property ScrollbarWidth: longint read _ScrollbarWidth write SetScrollbarWidth;
357 property LeftMargin: longint read _LeftMargin write SetLeftMargin;
358 property TextIndent: longint read _TextIndent write SetTextIndent;
359 property PlusMinusWidth: longint read _PlusMinusWidth write SetPlusMinusWidth;
360 property PlusMinusHeight: longint read _PlusMinusHeight write SetPlusMinusHeight;
361 property StubWidth: longint read _StubWidth write SetStubWidth;
362 property PlusMinusStyle: TPlusMinusStyle read _PlusMinusStyle write SetPlusMinusStyle;
363 property SelectLine: boolean read _SelectLine write SetSelectLine;
364 property TreeLineColor: TColor read _TreeLineColor write SetTreeLineColor;
365
366 property SmoothScroll: boolean read FSmoothScroll write FSmoothScroll;
367
368 property PathSeparator:string read _PathSeparator write _PathSeparator;
369
370 property OnItemDblClick: TNodeEvent read _OnItemDblClick write _OnItemDblClick;
371 property OnItemFocus: TNodeEvent read _OnItemFocus write _OnItemFocus;
372 property OnItemClick: TNodeEvent read _OnItemClick write _OnItemClick;
373 property OnExpand: TNodeEvent read _OnExpand write _OnExpand;
374 property OnCollapse: TNodeEvent read _OnCollapse write _OnCollapse;
375 property OnClick;
376 property OnDblClick;
377 property OnDragOver;
378 property OnDragDrop;
379 property OnEndDrag;
380 Property OnEnter;
381 Property OnExit;
382 Property OnFontChange;
383 Property OnHide;
384 property OnKeyPress;
385 Property OnResize;
386 Property OnScan;
387 Property OnSetupShow;
388 Property OnShow;
389
390 public
391
392 procedure Clear;
393
394 function AddChild( const TheText: string;
395 const TheObject: TObject ): TNode;
396 property Children[ index: longint ]: TNode read GetChild;
397 property ChildCount: longint read GetChildCount;
398
399 procedure BeginUpdate;
400 procedure EndUpdate;
401
402 Destructor Destroy; Override;
403
404 function NextNodeUp( Node: TNode;
405 VisibleOnly: boolean ): TNode;
406 function NextNodeDown( Node: TNode;
407 VisibleOnly: boolean ): TNode;
408 function LastVisibleNode: TNode;
409
410 Procedure GotoFirstNode;
411 Procedure GotoLastVisibleNode;
412
413 // The following 4 functions return false if
414 // they could not do the requested movement
415 function GotoNextVisibleNodeUp: boolean;
416 function GotoNextVisibleNodeDown: boolean;
417 function GotoNextNodeUp: boolean;
418 function GotoNextNodeDown: boolean;
419
420 Procedure PageUp;
421 Procedure PageDown;
422
423 Procedure ExpandAll;
424 Procedure CollapseAll;
425
426 // search for a node which starts with the given text
427 // Note: if you use VisibleOnly true, then make sure you
428 // start with a visible node!!
429 Function FindNextNodeByText( StartNode: TNode;
430 const S: string;
431 VisibleOnly: boolean ): TNode;
432
433{ Procedure ScrollUpLine;
434 Procedure ScrollDownLine;
435 Procedure ScrollUpPage;
436 Procedure ScrollDownPage;
437}
438{ Procedure ScrollLeftLine;
439 Procedure ScrollRightLine;
440 Procedure ScrollLeftPage;
441 Procedure ScrollRightPage;
442}
443 property SelectedNode: TNode read _SelectedNode write SetSelectedNode;
444 procedure SetSelectedObject( Data: TObject );
445 function NodeFromObject( Data: TObject ): TNode;
446 function FindByFunction( FindFunction: TNodeFindFunction ): TNode;
447
448// property TopNode: TNode read _TopNode write SetTopNode;
449 End;
450
451{Define components to export}
452{You may define a page of the component palette and a component bitmap file}
453Exports
454 TOutline2,'User','Outline2.bmp';
455
456Implementation
457
458uses
459 Messages,
460 ControlScrolling,
461 StringUtilsUnit;
462
463// ============================================================================
464// Node implementation
465// ============================================================================
466
467Function TNode.IsVisible: boolean;
468var
469 Node: TNode;
470begin
471 Result:= true;
472 Node:= self;
473 while Node._Parent <> nil do
474 begin
475 Node:= Node._Parent;
476 if not Node._Expanded then
477 Result:= false;
478 end;
479end;
480
481// Call when a node changes (add, delete, rename, collapse/expand )
482procedure TNode.CheckChangeRefresh;
483begin
484 if _Outline.Handle = 0 then
485 exit;
486
487 if _Outline._UpdateCount > 0 then
488 exit;
489
490 if IsVisible then
491 begin
492 _Outline.ScanExtents;
493 _Outline.Refresh;
494 end;
495end;
496
497function TNode.GetText: string;
498begin
499 Result:= _Text^;
500end;
501
502procedure TNode.FreeText;
503begin
504 if _Text <> nil then
505 FreeMem( _Text, Length( _Text^ ) + 1 );
506end;
507
508procedure TNode.SetText( const NewText: string );
509begin
510 FreeText;
511 GetMem( _Text, Length( NewText ) + 1 );
512 _Text^:= NewText;
513end;
514
515function TNode.AddChild( const TheText: string;
516 const TheData: TObject ): TNode;
517Var
518 NewNode: TNode;
519Begin
520 NewNode:= TNode.Create( self );
521 NewNode.Text:= TheText;
522 NewNode.Data:= TheData;
523 NewNode._Outline:= _Outline;
524
525 if _Children.Count = 0 then
526 _Children.Capacity:= 4;
527
528 _Children.Add( NewNode );
529 Result:= NewNode;
530
531 if _Expanded
532 or ( _Children.Count = 1 ) // we just added first child; need to display +
533 then
534 CheckChangeRefresh;
535End;
536
537function TNode.IndexOfChild( Child: TNode ): longint;
538begin
539 Result:= _Children.IndexOf( Child );
540end;
541
542function TNode.NodeIsAParent( Node: TNode ): boolean;
543begin
544 if _Parent = _Outline._Root then
545 Result:= false
546 else if _Parent = Node then
547 Result:= true
548 else
549 Result:= _Parent.NodeIsAParent( Node );
550end;
551
552function TNode.IndexInParent: longint;
553begin
554 if _Parent = nil then
555 result:= -1
556 else
557 Result:= _Parent.IndexOfChild( Self );
558end;
559
560procedure TNode.ExpandChildren( Recursive: boolean );
561Var
562 ChildIndex: longint;
563 Child: TNode;
564begin
565 _Outline.BeginUpdate;
566 for ChildIndex:= 0 to _Children.Count - 1 do
567 begin
568 Child:= _Children[ ChildIndex ];
569 Child.Expand;
570 if Recursive then
571 Child.ExpandChildren( true );
572 end;
573 _Outline.EndUpdate;
574end;
575
576procedure TNode.CollapseChildren( Recursive: boolean );
577Var
578 ChildIndex: longint;
579 Child: TNode;
580begin
581 _Outline.BeginUpdate;
582 for ChildIndex:= 0 to _Children.Count - 1 do
583 begin
584 Child:= _Children[ ChildIndex ];
585 Child.Collapse;
586 if Recursive then
587 Child.CollapseChildren( true );
588 end;
589 _Outline.EndUpdate;
590end;
591
592function TNode.FindData( DataToFind: TObject ): TNode;
593Var
594 ChildIndex: longint;
595 Child: TNode;
596begin
597 if _Parent <> nil then
598 // not root...
599 if Data = DataToFind then
600 begin
601 Result:= self;
602 exit;
603 end;
604 for ChildIndex:= 0 to _Children.Count - 1 do
605 begin
606 Child:= _Children[ ChildIndex ];
607 Result:= Child.FindData( DataToFind );
608 if Result <> nil then
609 exit;
610 end;
611 Result:= nil;
612end;
613
614function TNode.FindByFunction( FindFunction: TNodeFindFunction ): TNode;
615Var
616 ChildIndex: longint;
617 Child: TNode;
618 d: TObject;
619begin
620 if _Parent <> nil then
621 begin
622 // not root...
623 d:= Data;
624 if FindFunction( Data ) then
625 begin
626 Result:= self;
627 exit;
628 end;
629 end;
630 for ChildIndex:= 0 to _Children.Count - 1 do
631 begin
632 Child:= _Children[ ChildIndex ];
633 Result:= Child.FindByFunction( FindFunction );
634 if Result <> nil then
635 exit;
636 end;
637 Result:= nil;
638end;
639
640constructor TNode.Create( Parent: TNode );
641Begin
642 _Parent:= Parent;
643 _Children:= TList.Create;
644 _Expanded:= false;
645 _Text:= nil;
646End;
647
648procedure TNode.Clear;
649begin
650 // Delete children
651 // (They will remove themselves from us)
652 // Remove them from end of list first for efficiency.
653 while _Children.Count > 0 do
654 TNode( _Children[ _Children.Count - 1 ] ).Destroy;
655end;
656
657destructor TNode.Destroy;
658Begin
659 Clear;
660 _Children.Destroy;
661 // Delete self from parent
662 if _Parent <> nil then
663 _Parent._Children.Remove( self );
664
665 if _Outline._NewSelectedNode = self then
666 _Outline._NewSelectedNode := nil;
667
668 if _Outline._SelectedNode = self then
669 _Outline.SetSelectedNode( nil );
670
671 FreeText;
672 CheckChangeRefresh;
673End;
674
675function TNode.ChildCount: longint;
676Begin
677 Result:= _Children.Count;
678End;
679
680function TNode.HasChildren: boolean;
681Begin
682 Result:= _Children.Count > 0;
683End;
684
685Function TNode.GetChild( index: longint ): TNode;
686Begin
687 Result:= _Children[ index ];
688End;
689
690function TNode.Level: longint;
691Var
692 NextNode: TNode;
693Begin
694 Result:= -1; // since root is a special case
695 NextNode:= _Parent;
696 while NextNode <> nil do
697 begin
698 inc( Result );
699 NextNode:= NextNode._Parent;
700 end;
701End;
702
703Function TNode.Parent: TNode;
704begin
705 if _Parent = nil then
706 Result:= nil
707 else if _Parent._Parent = nil then
708 Result:= nil // parent is really root but we don't want to give access to root
709 else
710 Result:= _Parent;
711end;
712
713Function TNode.FullPath( const Separator: string ): string;
714Var
715 NextNode: TNode;
716Begin
717 Result:= Text;
718 NextNode:= _Parent;
719 while NextNode <> nil do
720 begin
721 Result:= NextNode.Text + Separator + Result;
722 NextNode:= NextNode._Parent;
723 end;
724End;
725
726procedure TNode.Expand;
727begin
728 if _Expanded then
729 exit;
730 _Expanded:= true;
731 if Assigned( _Outline._OnExpand ) then
732 _Outline._OnExpand( self );
733 CheckChangeRefresh;
734end;
735
736procedure TNode.Collapse;
737begin
738 if not _Expanded then
739 exit;
740 if _Outline.SelectedNode <> nil then
741 if _Outline.SelectedNode.NodeIsAParent( Self ) then
742 // selected node is in collapsed nodes, make ourself the selected one
743 _Outline._SelectedNode:= Self;
744 if Assigned( _Outline._OnCollapse ) then
745 _Outline._OnCollapse( self );
746 _Expanded:= false;
747 CheckChangeRefresh;
748end;
749
750// Returns true if a row starting at Y will be at least partially visible
751Function TOutline2.RowIsVisible( Y: longint ): boolean;
752begin
753 Result:= ( Y < Height + _LineHeight ) and ( Y >= 0 );
754end;
755
756// ============================================================================
757// Outline component implementation
758// ============================================================================
759
760Procedure TOutline2.ScanExtents;
761Var
762 ChildIndex: longint;
763 MaxWidth, MaxHeight: longint;
764 AvailableHeight: longint;
765 AvailableWidth: longint;
766 TextRect: TRect;
767 TextHeight: longint;
768Begin
769 if csDesigning in ComponentState then
770 exit;
771 if Handle = 0 then
772 exit;
773
774 MaxWidth:= 0;
775 MaxHeight:= 0;
776 _NeedVScroll:= false;
777 _NeedHScroll:= false;
778
779 TextRect:= GetTextRect;
780 AvailableWidth:= TextRect.Right - TextRect.Left;
781 AvailableHeight:= TextRect.Top - TextRect.Bottom;
782
783 TextHeight:= Canvas.TextHeight( 'H' );
784 if TextHeight > _LineHeight then
785 _LineHeight := TextHeight;
786
787 // draw children
788 for ChildIndex:= 0 to _Root.ChildCount - 1 do
789 begin
790 ScanNodeExtents( _Root.Children[ ChildIndex ],
791 0,
792 MaxWidth,
793 MaxHeight );
794 end;
795
796 if MaxWidth > AvailableWidth then
797 begin
798 // will need horizontal scroll bar...
799 _NeedHScroll:= true;
800 dec( AvailableHeight, _ScrollBarWidth );
801 end;
802
803 if MaxHeight > AvailableHeight then
804 begin
805 // will need vertical scroll bar
806 _NeedVScroll:= true;
807 dec( AvailableWidth, _ScrollBarWidth );
808 if _NeedHScroll = false then
809 // check if we need one now,
810 if MaxWidth > AvailableWidth then
811 begin
812 // will need horizontal scroll bar after all
813 dec( AvailableHeight, _ScrollBarWidth );
814 _NeedHScroll:= true;
815 end;
816 end;
817
818 if _NeedVScroll then
819 begin
820 if _VScrollBar.Position > ( MaxHeight - AvailableHeight ) then
821 _VScrollBar.Position:= MaxHeight - AvailableHeight;
822
823 _VScrollBar.Min:= 0;
824 _VScrollBar.Max:= MaxHeight;
825 _VScrollBar.SliderSize:= AvailableHeight;
826 _VScrollBar.SmallChange:= _LineHeight;
827 _VScrollBar.LargeChange:= AvailableHeight div 2;
828 end
829 else
830 // everything will fit vertically.
831 _VScrollBar.Position:= 0;
832
833 if _NeedHScroll then
834 begin
835 if _HScrollBar.Position > ( MaxWidth - AvailableWidth ) then
836 _HScrollBar.Position:= MaxWidth - AvailableWidth;
837
838 _HScrollBar.Min:= 0;
839 _HScrollBar.Max:= MaxWidth;
840 _HScrollBar.SliderSize:= AvailableWidth;
841 _HScrollBar.SmallChange:= _LineHeight;
842 _HScrollBar.LargeChange:= AvailableWidth div 2;
843 end
844 else
845 // everything will fit horizontally
846 _HScrollBar.Position:= 0; // no offset
847
848 SetupScrollBars;
849End;
850
851Procedure TOutline2.ScanNodeExtents( Node: TNode;
852 X: longint;
853 Var MaxX: longint;
854 Var MaxY: longint );
855Var
856 ChildIndex: longint;
857 TextW, TextH: longint;
858 TextX: longint;
859
860Begin
861 Canvas.GetTextExtent( Node.Text, TextW, TextH );
862 TextX:= X + _PlusMinusWidth + _StubWidth + _TextIndent;
863
864 if TextX + TextW > MaxX then
865 MaxX:= TextX + TextW;
866
867 inc( MaxY, _LineHeight );
868
869 if Node.ChildCount > 0 then
870 if Node._Expanded then
871 // check children
872 for ChildIndex:= 0 to Node.ChildCount-1 do
873 ScanNodeExtents( Node.Children[ ChildIndex ],
874 X + Indent,
875 MaxX,
876 MaxY );
877
878End;
879
880function TOutline2.GetDrawRect: TRect;
881begin
882 Result := ClientRect;
883 if _NeedHScroll then
884 inc( Result.Bottom, _ScrollbarWidth );
885 if _NeedVScroll then
886 dec( Result.Right, _ScrollbarWidth );
887end;
888
889function TOutline2.GetTextRect: TRect;
890begin
891 Result := GetDrawRect;
892 if _BorderStyle <> bsNone then
893 InflateRect( Result, -2, -2 );
894end;
895
896function TOutline2.DrawHeight: longint;
897begin
898 Result := ClientRect.Top - ClientRect.Bottom;
899 if _NeedHScroll then
900 dec( Result, _ScrollbarWidth );
901
902 if _BorderStyle <> bsNone then
903 begin
904 dec( Result, 2 ); // top border
905 if not _NeedHScroll then
906 dec( Result, 2 ); // bottom border
907 end;
908end;
909
910Procedure TOutline2.Redraw( const rec: TRect );
911Var
912 OldClip: TRect;
913 TextRect: TRect;
914Begin
915 OldClip:= Canvas.ClipRect;
916
917 if csDesigning in ComponentState then
918 begin
919 _NeedVScroll:= false;
920 _NeedHScroll:= false;
921 DrawBorder;
922 Canvas.FillRect( GetTextRect, Color );
923 exit;
924 end;
925
926 FYScroll:= _VScrollBar.Position;
927 FLastYScroll:= FYScroll;
928 FXScroll:= _HScrollBar.Position;
929 FLastXScroll:= FXScroll;
930
931 DrawBorder;
932
933 TextRect:= GetTextRect;
934 Canvas.ClipRect:= IntersectRect( rec, TextRect );
935
936 _DrawNode:= AllNodes;
937 Draw;
938
939 Canvas.ClipRect:= OldClip;
940
941end;
942
943procedure TOutline2.DrawNode( Node: TNode );
944var
945 OldClip: TRect;
946 TextRect: TRect;
947begin
948 if Node = nil then
949 exit;
950 if InDesigner then
951 exit;
952 if Handle = 0 then
953 exit;
954 _DrawNode:= Node;
955
956 TextRect:= GetTextRect;
957 OldClip:= Canvas.ClipRect;
958 Canvas.ClipRect:= TextRect;
959
960 Draw;
961
962 Canvas.ClipRect:= OldClip;
963end;
964
965procedure TOutline2.Draw;
966var
967 Y: longint;
968 ChildIndex: longint;
969 TextRect: Trect;
970begin
971 TextRect:= GetTextRect;
972
973 Y:= TextRect.Top + _VScrollBar.Position + 1;
974
975 // draw children
976 for ChildIndex:= 0 to _Root.ChildCount - 1 do
977 begin
978 PaintNode( _Root.Children[ ChildIndex ],
979 _LeftMargin - _HScrollBar.Position,
980 Y );
981 if y < 0 then
982 break;
983 end;
984
985 // clear remainder -
986 if Y>0 then
987 begin
988 TextRect.Top:= Y - 1;
989 Canvas.FillRect( TextRect, Color );
990 end;
991End;
992
993Procedure TOutline2.DrawBorder;
994var
995 Rect: TRect;
996 CornerRect: TRect;
997begin
998 Rect:= GetDrawRect;
999 DrawSystemBorder( Self, Rect, _BorderStyle );
1000 if _NeedHScroll and _NeedVScroll then
1001 begin
1002 CornerRect.Left:= Width - _ScrollBarWidth;
1003 CornerRect.Bottom:= 0;
1004 CornerRect.Right:= Width;
1005 CornerRect.Top:= _ScrollBarWidth;
1006 Canvas.ClipRect:= IntersectRect( Canvas.ClipRect, CornerRect );
1007 Canvas.FillRect( CornerRect, Parent.Color );
1008 end;
1009end;
1010
1011Procedure DrawDottedRect( Canvas: TCanvas; Rect: TRect );
1012var
1013 OldStyle: TPenStyle;
1014begin
1015 OldStyle:= Canvas.Pen.Style;
1016 Canvas.Pen.Style:= psInsideFrame; // psAlternate for SPCC v2.5+
1017 Canvas.MoveTo( Rect.Left, Rect.Bottom - 1 );
1018 Canvas.LineTo( Rect.Left, Rect.Top );
1019 Canvas.LineTo( Rect.Right, Rect.Top );
1020 Canvas.LineTo( Rect.Right, Rect.Bottom );
1021 Canvas.LineTo( Rect.Left, Rect.Bottom );
1022 Canvas.Pen.Style:= OldStyle;
1023end;
1024
1025procedure TOutline2.DrawPlusMinus( Rect: TRect;
1026 Expanded: boolean;
1027 Pressed: boolean );
1028var
1029 CentreY: integer;
1030 CentreX: integer;
1031begin
1032 CentreY:= ( Rect.Top + Rect.Bottom ) div 2;
1033 CentreX:= ( Rect.Left + Rect.Right ) div 2;
1034
1035 Canvas.Pen.Color:= clBlack;
1036
1037 case _PlusMinusStyle of
1038 pmFlat:
1039 begin
1040 if Pressed then
1041 Canvas.FillRect( Rect, clHighlight );
1042 DrawDottedRect( Canvas, Rect );
1043 end;
1044
1045 pm3d:
1046 begin
1047 Canvas.Rectangle( Rect );
1048
1049 InflateRect( Rect, -1, -1 );
1050 Canvas.FillRect( Rect, clLtGray );
1051 if Pressed then
1052 Canvas.ShadowedBorder( Rect,
1053 clDkGray,
1054 clWhite )
1055 else
1056 Canvas.ShadowedBorder( Rect,
1057 clWhite,
1058 clDkGray );
1059 end;
1060 end;
1061
1062 // draw minus
1063 Canvas.Line( Rect.Left + 2, CentreY,
1064 Rect.Right - 2, CentreY );
1065
1066 if not Expanded then
1067 // draw vertical stroke of +
1068 Canvas.Line( CentreX, Rect.Top - 2,
1069 CentreX, Rect.Bottom + 2 );
1070end;
1071
1072Function TOutline2.GetPlusMinusRect( Left, LineBottom: longint ): TRect;
1073Var
1074 CentreY: longint;
1075Begin
1076 CentreY:= LineBottom + _LineHeight div 2;
1077
1078 Result.Left:= Left;
1079 Result.Right:= Left + _PlusMinusWidth - 1;
1080 Result.Bottom:= CentreY - _PlusMinusHeight div 2;
1081 Result.Top:= Result.Bottom + _PlusMinusHeight - 1;
1082end;
1083
1084Function TOutline2.GetLineRect( LineBottom: longint ): TRect;
1085begin
1086 Result:= GetTextRect;
1087
1088 Result.Top:= LineBottom + _LineHeight - 1;
1089 Result.Bottom:= LineBottom;
1090end;
1091
1092Procedure TOutline2.PaintNode( Node: TNode;
1093 X: longint;
1094 Var Y: longint );
1095Var
1096 ChildIndex: longint;
1097 CentrePMX: longint;
1098 CentreY: longint;
1099 ChildCentreY: longint;
1100 TextW, TextH: longint;
1101 PlusMinusRect: TRect;
1102 TextX: longint;
1103 LineRect: TRect;
1104 SelectedRect: TRect;
1105Begin
1106 dec( Y, _LineHeight );
1107
1108 PlusMinusRect:= GetPlusMinusRect( X, Y );
1109
1110 CentrePMX:= X + _PlusMinusWidth div 2;
1111 CentreY:= Y + _LineHeight div 2;
1112
1113 if ( ( _DrawNode = AllNodes )
1114 or ( Node = _DrawNode ) )
1115 and ( Y + _LineHeight >= Canvas.ClipRect.Bottom ) // line top above bottom
1116 and ( Y <= Canvas.ClipRect.Top ) // line bottom below top
1117 then
1118 begin
1119 Canvas.GetTextExtent( Node.Text, TextW, TextH );
1120 TextX:= PlusMinusRect.Right + _StubWidth + _TextIndent;
1121
1122 LineRect:= GetLineRect( Y );
1123
1124 if Node = _SelectedNode then
1125 begin
1126 Canvas.Pen.Color:= clHighlightText;
1127 Canvas.Brush.Color:= clHighlight;
1128 if _SelectLine then
1129 SelectedRect:= LineRect
1130 else
1131 begin
1132 SelectedRect.Left:= TextX - _TextIndent div 2;
1133 SelectedRect.Top:= CentreY + TextH div 2 - 1;
1134 SelectedRect.Right:= TextX + TextW + _TextIndent div 2;
1135 SelectedRect.Bottom:= SelectedRect.Top - TextH + 1;
1136 // clear line
1137 Canvas.FillRect( LineRect, Color );
1138 end;
1139
1140 // Draw selected background (either whole line, or just text background)
1141 Canvas.FillRect( SelectedRect, clHighLight );
1142
1143 if HasFocus then
1144 Canvas.DrawFocusRect( SelectedRect );
1145 end
1146 else
1147 begin
1148 // Clear line
1149 Canvas.FillRect( LineRect, Color );
1150 Canvas.Pen.Color:= PenColor;
1151 Canvas.Brush.Color:= Color;
1152 end;
1153
1154 // Draw text
1155 Canvas.Brush.Mode:= bmTransparent;
1156 Canvas.TextOut( TextX,
1157 CentreY - TextH div 2,
1158 Node.Text );
1159
1160 if Node.ChildCount > 0 then
1161 begin
1162 // draw plus/minus
1163 DrawPlusMinus( PlusMinusRect,
1164 Node._Expanded,
1165 ( _MouseDownNode = Node )
1166 and ( _MouseDownPart = npPlusMinus )
1167 and ( _MouseDownFeedback ) );
1168 // Draw stub from plus/minus
1169 if _StubWidth > 0 then
1170 begin
1171 Canvas.Pen.Color:= _TreeLineColor;
1172 Canvas.Line( PlusMinusRect.Right + 1,
1173 CentreY,
1174 PlusMinusRect.Right + _StubWidth,
1175 CentreY );
1176 end;
1177
1178 end
1179 else
1180 begin
1181 // no children
1182 if Node._Parent <> _Root then
1183 begin
1184 Canvas.Pen.Color:= _TreeLineColor;
1185 // draw stub, no Plus minus
1186 Canvas.Line( X,
1187 CentreY,
1188 PlusMinusRect.Right + _StubWidth,
1189 CentreY );
1190 end;
1191 end;
1192 end;
1193
1194 if ( Node.ChildCount > 0 )
1195 and Node._Expanded then
1196 begin
1197 // draw children
1198 for ChildIndex:= 0 to Node.ChildCount-1 do
1199 begin
1200 // make sure truncation of the half pixel works same as for parent
1201 ChildCentreY:= Y - _LineHeight + _LineHeight div 2;
1202 if y < 0 then
1203 break;
1204 PaintNode( Node.Children[ ChildIndex ],
1205 X + Indent,
1206 Y );
1207 Canvas.Pen.Color:= _TreeLineColor;
1208 Canvas.Line( CentrePMX,
1209 ChildCentreY,
1210 X + _Indent,
1211 ChildCentreY );
1212 end;
1213
1214 // draw vertical branch line
1215 Canvas.Pen.Color:= _TreeLineColor;
1216 Canvas.Line( CentrePMX,
1217 PlusMinusRect.Bottom - 1,
1218 CentrePMX,
1219 ChildCentreY );
1220 end;
1221
1222End;
1223
1224// Outline component implementation
1225Procedure TOutline2.TestHit( Point: TPoint;
1226 Var Node: TNode;
1227 Var Part: TNodePart );
1228Var
1229 ChildIndex: longint;
1230 Y: longint;
1231 DrawRect: TRect;
1232Begin
1233 Node:= nil;
1234
1235 DrawRect:= GetDrawRect;
1236 if ( Point.X < DrawRect.Left )
1237 or ( Point.X > DrawRect.Right )
1238 or ( Point.Y > DrawRect.Top )
1239 or ( Point.Y < DrawRect.Bottom ) then
1240 exit;
1241
1242 Y:= DrawRect.Top + _VScrollBar.Position;
1243
1244 // test children
1245 for ChildIndex:= 0 to _Root.ChildCount - 1 do
1246 begin
1247 TestNodeHit( _Root.Children[ ChildIndex ],
1248 Point,
1249 _LeftMargin - _HScrollBar.Position,
1250 Y,
1251 Node,
1252 Part );
1253 if Node <> nil then
1254 break;
1255 if Y < 0 then
1256 break;
1257 end;
1258
1259End;
1260
1261Procedure TOutline2.TestNodeHit( Node: TNode;
1262 Point: TPoint;
1263 X: longint;
1264 Var Y: longint;
1265 Var HitNode: TNode;
1266 Var Part: TNodePart );
1267Var
1268 ChildIndex: longint;
1269 TextW, TextH: longint;
1270 TextX: longint;
1271 PlusMinusRect: TRect;
1272 CentreY: longint;
1273 TextRect: TRect;
1274Begin
1275
1276 dec( Y, _LineHeight );
1277
1278 if ( Point.Y >= Y )
1279 and ( Point.Y < Y + _LineHeight ) then
1280 begin
1281 // this node. What part?
1282
1283 HitNode:= Node;
1284
1285 CentreY:= Y + _LineHeight div 2;
1286
1287 PlusMinusRect:= GetPlusMinusRect( X, Y );
1288
1289 if PointInRect( Point, PlusMinusRect ) then
1290 begin
1291 Part:= npPlusMinus;
1292 exit;
1293 end;
1294
1295 Canvas.GetTextExtent( Node.Text, TextW, TextH );
1296 TextX:= X + _PlusMinusWidth + _StubWidth + _TextIndent;
1297 TextRect.Left:= TextX;
1298 TextRect.Right:= TextX + TextW;
1299 TextRect.Top:= CentreY + TextH div 2;
1300 TextRect.Bottom:= TextRect.Top + TextH;
1301
1302 if PointInRect( Point, TextRect ) then
1303 begin
1304 Part:= npText;
1305 exit;
1306 end;
1307
1308 // bitmap...
1309
1310 Part:= npOther;
1311 exit;
1312 end;
1313
1314 if Node.ChildCount > 0 then
1315 if Node._Expanded then
1316 // check children
1317 for ChildIndex:= 0 to Node.ChildCount-1 do
1318 begin
1319 TestNodeHit( Node.Children[ ChildIndex ],
1320 Point,
1321 X + Indent,
1322 Y,
1323 HitNode,
1324 Part );
1325 if HitNode <> nil then
1326 break;
1327 if y < 0 then
1328 break;
1329 end;
1330
1331End;
1332
1333// Finds the distance from the top of the given node.
1334function TOutline2.GetNodePosition( const Node: TNode;
1335 Var NodeTop: longint ): boolean;
1336Var
1337 ChildIndex: longint;
1338 DrawRect: TRect;
1339Begin
1340 DrawRect:= GetDrawRect;
1341
1342 NodeTop:= 0;
1343
1344 // test children
1345 for ChildIndex:= 0 to _Root.ChildCount - 1 do
1346 begin
1347 if NodeGetNodePosition( Node,
1348 _Root.Children[ ChildIndex ],
1349 NodeTop ) then
1350 begin
1351 Result:= true;
1352 NodeTop:= - NodeTop;
1353 exit;
1354 end;
1355 end;
1356 Result:= false;
1357End;
1358
1359function TOutline2.NodeGetNodePosition( const NodeToFind: TNode;
1360 const NodeToScan: TNode;
1361 Var NodeTop: longint ): boolean;
1362Var
1363 ChildIndex: longint;
1364Begin
1365 if NodeToFind = NodeToScan then
1366 begin
1367 Result:= true;
1368 exit;
1369 end;
1370
1371 dec( NodeTop, _LineHeight );
1372
1373 if NodeToScan.ChildCount > 0 then
1374 if NodeToScan._Expanded then
1375 // check children
1376 for ChildIndex:= 0 to NodeToScan.ChildCount-1 do
1377 begin
1378 if NodeGetNodePosition( NodeToFind,
1379 NodeToScan.Children[ ChildIndex ],
1380 NodeTop ) then
1381 begin
1382 Result:= true;
1383 exit;
1384 end;
1385 end;
1386 Result:= false;
1387End;
1388
1389Procedure TOutline2.SetSelectedNode( Node: TNode );
1390var
1391 OldSelectedNode: TNode;
1392 NodePosition: longint;
1393 DrawRect: TRect;
1394 DrawHeight: longint;
1395 OldScroll: longint;
1396begin
1397 if _UpdateCount > 0 then
1398 begin
1399 _NewSelectedNode:= Node;
1400 exit;
1401 end;
1402
1403 if _SelectedNode = Node then
1404 exit;
1405
1406 OldSelectedNode:= _SelectedNode;
1407
1408 _SelectedNode:= Node;
1409
1410 if Node = nil then
1411 begin
1412 DrawNode( OldSelectedNode ); // clear old
1413 exit;
1414 end;
1415
1416 if not Node.IsVisible then
1417 _SelectedNode.ExpandParents // this will refresh the whole thing...
1418 else
1419 DrawNode( OldSelectedNode ); // clear old
1420
1421 DrawRect:= GetDrawRect;
1422 DrawHeight:= DrawRect.Top - DrawRect.Bottom;
1423
1424 OldScroll:= _VScrollBar.Position;
1425 if GetNodePosition( Node, NodePosition ) then
1426 begin
1427 if NodePosition + _LineHeight > _VScrollBar.Position + DrawHeight then
1428 // Node is below bottom of control - scroll down
1429 _VScrollBar.Position:= NodePosition - DrawHeight + _LineHeight;
1430
1431 if NodePosition < _VScrollBar.Position then
1432 // Node is aobve top of control - scroll up
1433 _VScrollBar.Position:= NodePosition;
1434 end;
1435
1436
1437 // draw new
1438 if OldScroll = _VScrollBar.Position then
1439 DrawNode( _SelectedNode )
1440 else
1441 Refresh;
1442
1443 if _OnItemFocus <> nil then
1444 _OnItemFocus( Node );
1445
1446end;
1447
1448Procedure TOutline2.SetTopNode( Node: TNode );
1449var
1450 NodePosition: longint;
1451begin
1452 Node.ExpandParents;
1453
1454 if GetNodePosition( Node, NodePosition ) then
1455 begin
1456 _VScrollBar.Position:= NodePosition;
1457 end;
1458end;
1459
1460Procedure TOutline2.SetAutoLineHeight( Value: boolean );
1461begin
1462 _AutoLineHeight := Value;
1463 if Value then
1464 FontChange;
1465end;
1466
1467Procedure TOutline2.SetLineHeight( LineHeight: longint );
1468begin
1469 if LineHeight < _PlusMinusHeight + 1 then
1470 LineHeight := _PlusMinusHeight + 1;
1471 if _LineHeight = LineHeight then
1472 exit;
1473 _LineHeight:= LineHeight;
1474 ScanExtents;
1475 Refresh;
1476end;
1477
1478Procedure TOutline2.SetIndent( Indent: longint );
1479begin
1480 if _Indent = Indent then
1481 exit;
1482
1483 _Indent:= Indent;
1484 ScanExtents;
1485 Refresh;
1486end;
1487
1488Procedure TOutline2.SetBorderStyle( BorderStyle: TBorderStyle );
1489begin
1490 if _BorderStyle = BorderStyle then
1491 exit;
1492 _BorderStyle:= BorderStyle;
1493 ScanExtents;
1494 Refresh;
1495end;
1496
1497Procedure TOutline2.SetScrollbarWidth( ScrollbarWidth: longint );
1498begin
1499 _ScrollBarWidth:= ScrollBarWidth;
1500 ScanExtents;
1501 Refresh;
1502end;
1503
1504Procedure TOutline2.SetLeftMargin( LeftMargin: longint );
1505begin
1506 if _LeftMargin = LeftMargin then
1507 exit;
1508 _LeftMargin:= LeftMargin;
1509 ScanExtents;
1510 Refresh;
1511end;
1512
1513Procedure TOutline2.SetTextIndent( TextIndent: longint );
1514begin
1515 if _TextIndent = TextIndent then
1516 exit;
1517 _TextIndent:= TextIndent;
1518 ScanExtents;
1519 Refresh;
1520end;
1521
1522Procedure TOutline2.SetPlusMinusWidth( PlusMinusWidth: longint );
1523begin
1524 if _PlusMinusWidth = PlusMinusWidth then
1525 exit;
1526
1527 if PlusMinusWidth < 7 then
1528 PlusMinusWidth:= 7;
1529
1530 _PlusMinusWidth:= PlusMinusWidth;
1531 ScanExtents;
1532 Refresh;
1533end;
1534
1535Procedure TOutline2.SetPlusMinusHeight( PlusMinusHeight: longint );
1536begin
1537 if _PlusMinusHeight = PlusMinusHeight then
1538 exit;
1539 if PlusMinusHeight < 7 then
1540 PlusMinusHeight:= 7;
1541
1542 _PlusMinusHeight:= PlusMinusHeight;
1543 ScanExtents;
1544 Refresh;
1545end;
1546
1547Procedure TOutline2.SetStubWidth( StubWidth: longint );
1548begin
1549 if _StubWidth = StubWidth then
1550 exit;
1551 _StubWidth:= StubWidth;
1552 ScanExtents;
1553 Refresh;
1554end;
1555
1556Procedure TOutline2.SetPlusMinusStyle( NewValue: TPlusMinusStyle );
1557begin
1558 if _PlusMinusStyle = NewValue then
1559 exit;
1560
1561 _PlusMinusStyle:= NewValue;
1562 Refresh;
1563end;
1564
1565Procedure TOutline2.SetSelectLine( NewValue: boolean );
1566begin
1567 if _SelectLine = NewValue then
1568 exit;
1569 _SelectLine:= NewValue;
1570 DrawNode( _SelectedNode );
1571end;
1572
1573Procedure TOutline2.SetTreeLineColor( NewValue: TColor );
1574begin
1575 if _TreeLineColor = NewValue then
1576 exit;
1577 _TreeLineColor:= NewValue;
1578 Refresh;
1579end;
1580
1581procedure TNode.ExpandParents;
1582var
1583 AParent: TNode;
1584begin
1585 AParent:= self._Parent;
1586 while AParent._Parent <> nil do
1587 begin
1588 AParent.Expand;
1589 AParent:= AParent._Parent;
1590 end;
1591end;
1592
1593procedure TOutline2.Clear;
1594begin
1595 BeginUpdate;
1596 _Root.Clear;
1597 EndUpdate;
1598end;
1599
1600Function TOutline2.AddChild( const TheText: string;
1601 const TheObject: TObject ): TNode;
1602Begin
1603 Result := _Root.AddChild( TheText, TheObject );
1604End;
1605
1606Function TOutline2.GetChild( index: longint ): TNode;
1607Begin
1608 Result:= _Root.Children[ index ];
1609End;
1610
1611Function TOutline2.GetChildCount: longint;
1612begin
1613 Result:= _Root.ChildCount;
1614end;
1615
1616Procedure TOutline2.SetupScrollbars;
1617Begin
1618 _VScrollbar.Visible:= _NeedVScroll;
1619 if _NeedVScroll then
1620 begin
1621 _VScrollbar.Left:= Width - _ScrollbarWidth;
1622 _VScrollbar.Width:= _ScrollbarWidth;
1623
1624 if _NeedHScroll then
1625 begin
1626 _VScrollbar.Bottom:= _ScrollbarWidth;
1627 _VScrollbar.Height:= Height - _ScrollbarWidth
1628 end
1629 else
1630 begin
1631 _VScrollbar.Bottom:= 0;
1632 _VScrollbar.Height:= Height;
1633 end;
1634 end;
1635
1636 _HScrollbar.Visible:= _NeedHScroll;
1637 if _NeedHScroll then
1638 begin
1639 _HScrollbar.Left:= 0;
1640 _HScrollbar.Bottom:= 0;
1641 _HScrollbar.Height:= _ScrollbarWidth;
1642
1643 if _NeedVScroll then
1644 _HScrollbar.Width:= Width - _ScrollBarWidth
1645 else
1646 _HScrollbar.Width:= Width;
1647 end;
1648
1649End;
1650
1651Procedure TOutline2.Resize;
1652begin
1653 ScanExtents;
1654end;
1655
1656Procedure TOutline2.SetupComponent;
1657Begin
1658 Inherited SetupComponent;
1659 Name:= 'Outline2';
1660
1661 Width:= 100;
1662 Height:= 100;
1663
1664 _BorderStyle:= bsSingle;
1665 FSmoothScroll := true;
1666
1667 _StubWidth:= 0;
1668 _Indent:= 11;
1669 _PlusMinusWidth:= 11;
1670 _PlusMinusHeight:= 11;
1671 _AutoLineHeight := true;
1672 _LeftMargin:= 5;
1673 _TextIndent:= 5;
1674 _ScrollbarWidth:= 15;
1675 _PlusMinusStyle:= pm3d;
1676 _SelectLine:= false;
1677 _TreeLineColor:= clGray;
1678
1679 Color:= clEntryField;
1680 ParentPenColor:= true;
1681 Exclude( ComponentState, csAcceptsControls );
1682
1683 if not InDesigner then
1684 begin
1685 _Root:= TNode.Create( nil );
1686 _Root.Text:= 'Root';
1687 _Root._Outline:= self;
1688 _Root._Expanded:= true; // by definition
1689 _SelectedNode:= nil;
1690
1691 _MouseDownNode:= nil;
1692 _MouseDownPart:= npNothing;
1693
1694 _HScrollbar.Create( self );
1695 _HScrollbar.Kind:= sbHorizontal;
1696 Include( _HScrollbar.ComponentState, csDetail );
1697 _HScrollbar.Parent:= self;
1698
1699 _VScrollbar.Create( self );
1700 _VScrollbar.Kind:= sbVertical;
1701 Include( _VScrollbar.ComponentState, csDetail );
1702 _VScrollbar.Parent:= self;
1703
1704 _UpdateCount:= 0;
1705
1706 _HoverTimer := TTimer.Create( self );
1707 _HoverTimer.Interval := 500;
1708 _HoverTimer.OnTImer := OnHoverTimeout;
1709 _HintWindow:= THintWindow.Create( self );
1710 _HintWindow.CreateWnd;
1711 end;
1712
1713End;
1714
1715Destructor TOutline2.Destroy;
1716Begin
1717 if not InDesigner then
1718 begin
1719 _Root.Destroy;
1720 end;
1721 Inherited Destroy;
1722End;
1723
1724Procedure TOutline2.SetupShow;
1725begin
1726 Inherited SetupShow;
1727 _UpdateCount:= 0;
1728
1729 if not InDesigner then
1730 ScanExtents;
1731end;
1732
1733Procedure TOutline2.MouseDown( Button: TMouseButton;
1734 ShiftState: TShiftState;
1735 X, Y: Longint );
1736var
1737 Node: TNode;
1738 Part: TNodePart;
1739 Point: TPoint;
1740begin
1741 inherited MouseDown( Button, ShiftState, X, Y );
1742
1743 if InDesigner then
1744 exit;
1745
1746 Focus;
1747
1748 MouseCapture:= true;
1749 Point.X:= X;
1750 Point.Y:= Y;
1751 TestHit( Point,
1752 Node,
1753 Part );
1754 if Node <> nil then
1755 begin
1756 _MouseDownNode:= Node;
1757 _MouseDownPart:= Part;
1758
1759 _MouseDownFeedback:= true;
1760 _LastMouseMoveFeedback:= true;
1761
1762 DrawNode( _MouseDownNode );
1763 end;
1764end;
1765
1766Procedure TOutline2.MouseMove( ShiftState: TShiftState;
1767 X, Y: Longint );
1768var
1769 Node: TNode;
1770 Part: TNodePart;
1771 Point: TPoint;
1772begin
1773 if InDesigner then
1774 exit;
1775
1776 if not MouseCapture then
1777 begin
1778 _HoverTimer.Stop;
1779// _HoverTimer.Start;
1780 _LastMouseX := X;
1781 _LastMouseY := Y;
1782 exit;
1783 end;
1784
1785 Point.X:= X;
1786 Point.Y:= Y;
1787 TestHit( Point,
1788 Node,
1789 Part );
1790
1791 _MouseDownFeedback:=
1792 ( Node = _MouseDownNode )
1793 and ( Part = _MouseDownPart );
1794
1795 if _LastMouseMoveFeedback <> _MouseDownFeedback then
1796 DrawNode( _MouseDownNode );
1797
1798 _LastMouseMoveFeedback:= _MouseDownFeedback;
1799end;
1800
1801Procedure TOutline2.MouseUp( Button: TMouseButton;
1802 ShiftState: TShiftState;
1803 X, Y: Longint );
1804var
1805 Node: TNode;
1806 Part: TNodePart;
1807 Point: TPoint;
1808 MouseDownNode: TNode;
1809begin
1810 if InDesigner then
1811 exit;
1812
1813 if not MouseCapture then
1814 exit;
1815
1816 MouseDownNode:= _MouseDownNode;
1817 _MouseDownNode:= nil;
1818 MouseCapture:= false;
1819
1820 Point.X:= X;
1821 Point.Y:= Y;
1822 TestHit( Point,
1823 Node,
1824 Part );
1825
1826 if ( Node <> nil )
1827 and ( Node = MouseDownNode )
1828 and ( Part = _MouseDownPart )
1829 then
1830 begin
1831 if Part = npPlusMinus then
1832 begin
1833 if Node.Expanded then
1834 Node.Collapse
1835 else
1836 Node.Expand;
1837 end
1838 else
1839 begin
1840 SelectedNode:= Node;
1841
1842 if Assigned( _OnItemClick ) then
1843 _OnItemClick( Node );
1844 end;
1845
1846 end
1847 else
1848 begin
1849 DrawNode( MouseDownNode );
1850 end;
1851
1852end;
1853
1854Procedure TOutline2.MouseDblClick( Button: TMouseButton;
1855 ShiftState: TShiftState;
1856 X, Y: Longint );
1857var
1858 Node: TNode;
1859 Part: TNodePart;
1860 Point: TPoint;
1861begin
1862 if InDesigner then
1863 exit;
1864
1865 Point.X:= X;
1866 Point.Y:= Y;
1867 TestHit( Point,
1868 Node,
1869 Part );
1870 if Node <> nil then
1871 begin
1872 if Part = npPlusMinus then
1873 if Node.Expanded then
1874 Node.Collapse
1875 else
1876 Node.Expand
1877 else
1878 begin
1879 SelectedNode:= Node;
1880 if Assigned( _OnItemDblClick ) then
1881 _OnItemDblClick( Node );
1882 end;
1883
1884 end;
1885end;
1886
1887procedure TOutline2.BeginUpdate;
1888begin
1889 if _UpdateCount = 0 then
1890 _NewSelectedNode:= _SelectedNode;
1891
1892 inc( _UpdateCount );
1893end;
1894
1895procedure TOutline2.EndUpdate;
1896begin
1897 dec( _UpdateCount );
1898 if _UpdateCount = 0 then
1899 begin
1900 ScanExtents;
1901 Refresh;
1902 SetSelectedNode( _NewSelectedNode );
1903 end
1904 else if _UpdateCount < 0 then
1905 raise Exception.Create( 'Too many EndUpdates in TOutline2!' );
1906end;
1907
1908procedure TOutline2.SetSelectedObject( Data: TObject );
1909var
1910 Node: TNode;
1911begin
1912 Node:= _Root.FindData( Data );
1913 SelectedNode:= Node;
1914end;
1915
1916function TOutline2.NodeFromObject( Data: TObject ): TNode;
1917begin
1918 Result:= _Root.FindData( Data );
1919end;
1920
1921function TOutline2.FindByFunction( FindFunction: TNodeFindFunction ): TNode;
1922begin
1923 Result:= _Root.FindByFunction( FindFunction );
1924end;
1925
1926Procedure TOutline2.GotoFirstNode;
1927begin
1928 if ChildCount > 0 then
1929 SelectedNode:= Children[ 0 ];
1930end;
1931
1932Procedure TOutline2.GotoLastVisibleNode;
1933var
1934 Node: TNode;
1935begin
1936 Node:= LastVisibleNode;
1937 if Node <> nil then
1938 SelectedNode:= Node;
1939end;
1940
1941function TOutline2.LastVisibleNode: TNode;
1942begin
1943 Result:= nil;
1944 if ChildCount > 0 then
1945 begin
1946 Result:= _Root;
1947 while Result.HasChildren do
1948 begin
1949 if Result.Expanded then
1950 Result:= Result.Children[ Result.ChildCount - 1 ]
1951 else
1952 break;
1953 end;
1954 end;
1955end;
1956
1957function TOutline2.NextNodeUp( Node: TNode;
1958 VisibleOnly: boolean ): TNode;
1959var
1960 NodeIndexInParent: longint;
1961begin
1962 NodeIndexInParent:= Node.IndexInParent;
1963 if NodeIndexInParent = 0 then
1964 begin
1965 if Node._Parent = _Root then
1966 Result:= nil // nothing above this one
1967 else
1968 Result:= Node._Parent
1969 end
1970 else
1971 begin
1972 Result:= Node._Parent.Children[ NodeIndexInParent - 1 ];
1973 while Result.HasChildren
1974 and ( Result.Expanded or ( not VisibleOnly ) ) do
1975 begin
1976 Result:= Result.Children[ Result.ChildCount - 1 ];
1977 end;
1978 end;
1979
1980end;
1981
1982function TOutline2.NextNodeDown( Node: TNode;
1983 VisibleOnly: boolean ): TNode;
1984var
1985 NodeIndexInParent: longint;
1986begin
1987 if Node.HasChildren
1988 and ( Node.Expanded or ( not VisibleOnly ) ) then
1989 Result:= Node.Children[ 0 ]
1990 else
1991 begin
1992 Result:= node;
1993 while Result <> _Root do
1994 begin
1995 NodeIndexInParent:= Result.IndexInParent;
1996 if NodeIndexInParent < Result._Parent.ChildCount - 1 then
1997 begin
1998 Result:= Result._Parent.Children[ NodeIndexInParent + 1 ];
1999 break;
2000 end;
2001 Result:= Result._Parent;
2002 end;
2003 if Result = _Root then
2004 begin
2005 Result:= nil;
2006 // already at bottom
2007 exit;
2008 end;
2009 end;
2010end;
2011
2012function TOutline2.GotoNextVisibleNodeUp: boolean;
2013var
2014 NextNode: TNode;
2015begin
2016 Result := true;
2017 if SelectedNode <> nil then
2018 begin
2019 NextNode:= NextNodeUp( SelectedNode, true );
2020 if NextNode <> nil then
2021 SelectedNode:= NextNode
2022 else
2023 Result := false;
2024 end
2025 else
2026 GotoFirstNode;
2027end;
2028
2029function TOutline2.GotoNextVisibleNodeDown: boolean;
2030var
2031 NextNode: TNode;
2032begin
2033 Result := true;
2034 if SelectedNode <> nil then
2035 begin
2036 NextNode:= NextNodeDown( SelectedNode, true );
2037 if NextNode <> nil then
2038 SelectedNode:= NextNode
2039 else
2040 Result := false;
2041 end
2042 else
2043 GotoFirstNode;
2044end;
2045
2046function TOutline2.GotoNextNodeUp: boolean;
2047var
2048 NextNode: TNode;
2049begin
2050 Result := true;
2051 if SelectedNode <> nil then
2052 begin
2053 NextNode:= NextNodeUp( SelectedNode, false );
2054 if NextNode <> nil then
2055 SelectedNode:= NextNode
2056 else
2057 Result := false;
2058 end
2059 else
2060 GotoFirstNode;
2061end;
2062
2063function TOutline2.GotoNextNodeDown: boolean;
2064var
2065 NextNode: TNode;
2066begin
2067 Result := true;
2068 if SelectedNode <> nil then
2069 begin
2070 NextNode:= NextNodeDown( SelectedNode, false );
2071 if NextNode <> nil then
2072 SelectedNode:= NextNode
2073 else
2074 Result := false;
2075 end
2076 else
2077 GotoFirstNode;
2078end;
2079
2080Procedure TOutline2.PageUp;
2081var
2082 NextNode: TNode;
2083 Y: longint;
2084begin
2085 if SelectedNode <> nil then
2086 begin
2087 NextNode:= NextNodeUp( SelectedNode, true );
2088 Y:= _LineHeight;
2089 while ( NextNode <> nil )
2090 and ( Y < DrawHeight ) do
2091 begin
2092 inc( Y, _LineHeight );
2093 NextNode:= NextNodeUp( NextNode, true );
2094 end;
2095 if NextNode = nil then
2096 GotoFirstNode
2097 else
2098 SelectedNode:= NextNode;
2099
2100 end
2101 else
2102 GotoFirstNode;
2103end;
2104
2105Procedure TOutline2.PageDown;
2106var
2107 NextNode: TNode;
2108 Y: longint;
2109begin
2110 if SelectedNode <> nil then
2111 begin
2112 NextNode:= NextNodeDown( SelectedNode, true );
2113 Y:= _LineHeight;
2114 while ( NextNode <> nil )
2115 and ( Y < DrawHeight ) do
2116 begin
2117 inc( Y, _LineHeight );
2118 NextNode:= NextNodeDown( NextNode, true );
2119 end;
2120 if NextNode = nil then
2121 GotoLastVisibleNode
2122 else
2123 SelectedNode:= NextNode;
2124
2125 end
2126 else
2127 GotoFirstNode;
2128end;
2129
2130Procedure TOutline2.CollapseAll;
2131begin
2132 _Root.CollapseChildren( true );
2133end;
2134
2135Procedure TOutline2.ExpandAll;
2136begin
2137 _Root.ExpandChildren( true );
2138end;
2139
2140Procedure TOutline2.SetFocus;
2141begin
2142 inherited SetFocus;
2143 if _SelectedNode <> nil then
2144 DrawNode( _SelectedNode );
2145end;
2146
2147Procedure TOutline2.KillFocus;
2148begin
2149 inherited KillFocus;
2150 if _SelectedNode <> nil then
2151 DrawNode( _SelectedNode );
2152end;
2153
2154Procedure TOutline2.ScanEvent( Var KeyCode: TKeyCode;
2155 RepeatCount: Byte );
2156Begin
2157 if InDesigner then
2158 begin
2159 inherited ScanEvent( KeyCode, RepeatCount );
2160 exit;
2161 end;
2162
2163 Case KeyCode Of
2164 kbHome, kbCtrlHome, kbCtrlPageUp:
2165 GotoFirstNode;
2166
2167 kbEnd, kbCtrlEnd, kbCtrlPageDown:
2168 GotoLastVisibleNode;
2169
2170 kbCUp:
2171 begin
2172 GotoNextVisibleNodeUp;
2173 KeyCode := kbNull; // prevent sibyl changing focus
2174 end;
2175
2176 kbCDown:
2177 begin
2178 GotoNextVisibleNodeDown;
2179 KeyCode := kbNull; // prevent sibyl changing focus
2180 end;
2181
2182 kbPageUp:
2183 PageUp;
2184
2185 kbPageDown:
2186 PageDown;
2187
2188 End;
2189
2190 inherited ScanEvent( KeyCode, RepeatCount );
2191
2192End;
2193
2194Function TOutline2.FindNextNodeByText( StartNode: TNode;
2195 const S: string;
2196 VisibleOnly: boolean ): TNode;
2197
2198var
2199 Node: TNode;
2200begin
2201 Result := nil;
2202
2203 if StartNode = nil then
2204 exit;
2205
2206 Node := StartNode;
2207
2208 while true do
2209 begin
2210 Node := NextNodeDown( Node, VisibleOnly );
2211 if Node = nil then
2212 // reached bottom, start from top again.
2213 Node := Children[ 0 ];
2214
2215 if Node = StartNode then
2216 // searched all nodes, done.
2217 exit;
2218
2219 if StrStartsWithIgnoringCase(Node.Text, S) then
2220 begin
2221 // found
2222 Result := Node;
2223 exit;
2224 end;
2225 end;
2226end;
2227
2228Procedure TOutline2.CharEvent( Var Key: Char;
2229 RepeatCount: Byte );
2230var
2231 Node: TNode;
2232 NextNode: TNode;
2233begin
2234 if InDesigner then
2235 begin
2236 inherited CharEvent( Key, RepeatCount );
2237 exit;
2238 end;
2239
2240 if SelectedNode <> nil then
2241 begin
2242 case Key of
2243 '+', '=': // = to catch non-shifted + on most keyboards
2244 SelectedNode.Expand;
2245
2246 '-':
2247 SelectedNode.Collapse;
2248
2249 ' ': // space toggles expanded/collapsed
2250 if SelectedNode.Expanded then
2251 SelectedNode.Collapse
2252 else
2253 SelectedNode.Expand;
2254
2255 '*': // Alberto thinks this would be nice to expand the tree...
2256 begin
2257 SelectedNode.Expand;
2258 SelectedNode.ExpandChildren( true );
2259 end;
2260
2261 else
2262 begin
2263 // search by first letter, in visible nodes.
2264
2265 if ChildCount = 0 then
2266 // no nodes to search in
2267 exit;
2268
2269 if SelectedNode = nil then
2270 Node := _Root.Children[ 0 ]
2271 else
2272 Node := SelectedNode;
2273
2274 NextNode := FindNextNodeByText( Node, key, true ); // visible only
2275 if NextNode <> nil then
2276 SelectedNode := NextNode;
2277
2278 end;
2279 end;
2280 end;
2281
2282 inherited CharEvent( Key, RepeatCount );
2283end;
2284
2285Procedure TOutline2.OnHoverTimeout( Sender: TObject );
2286var
2287 R: TRect;
2288begin
2289 R.Left := _LastMouseX;
2290 R.Bottom := _LastMouseY;
2291 R.Right := R.Left + 100;
2292 R.Top := R.Bottom + 50;
2293 // show hint
2294 R.LeftBottom := ClientToScreen( R.LeftBottom );
2295 R.RightTop := ClientToScreen( R.RightTop );
2296 _HintWindow.Parent := Parent;
2297 _HintWindow.ActivateHint( R, 'Yees' );
2298 _HoverTimer.Stop;
2299end;
2300
2301Procedure TOutline2.WMMouseLeave( Var Msg: TMessage );
2302begin
2303 if InDesigner then
2304 exit;
2305 _HoverTimer.Stop;
2306// _HintWindow.DeactivateHint;
2307end;
2308
2309Procedure TOutline2.FontChange;
2310begin
2311 inherited FontChange;
2312
2313 if _AutoLineHeight then
2314 begin
2315 LineHeight := Canvas.TextHeight( 'H' ) + 1;
2316 Refresh; // lineheight change alone may not be enough.
2317 end;
2318end;
2319
2320Procedure TOutline2.DoVerticalScroll( NewY: longint );
2321var
2322 ScrollDistance: longint;
2323begin
2324 if not Visible then
2325 exit;
2326
2327 FYScroll := NewY;
2328 ScrollDistance:= FYScroll - FLastYScroll;
2329
2330 ScrollControlRect( Self,
2331 GetTextRect,
2332 0,
2333 ScrollDistance,
2334 Color,
2335 FSmoothScroll );
2336
2337 FLastYScroll:= FYScroll;
2338 PostMsg( Handle, WM_PAINT, 0, 0 );
2339end;
2340
2341Procedure TOutline2.DoHorizontalScroll( NewX: longint );
2342var
2343 ScrollDistance: longint;
2344begin
2345 if not Visible then
2346 exit;
2347
2348 FXScroll := NewX;
2349 ScrollDistance:= FXScroll - FLastXScroll;
2350
2351 ScrollControlRect( Self,
2352 GetTextRect,
2353 - ScrollDistance,
2354 0,
2355 Color,
2356 FSmoothScroll );
2357
2358 FLastXScroll:= FXScroll;
2359 PostMsg( Handle, WM_PAINT, 0, 0 );
2360end;
2361
2362Procedure TOutline2.Scroll( Sender: TScrollbar;
2363 ScrollCode: TScrollCode;
2364 Var ScrollPos: Longint );
2365
2366begin
2367 case ScrollCode of
2368// scVertEndScroll,
2369 scVertPosition,
2370 scPageUp,
2371 scPageDown,
2372 scVertTrack,
2373 scLineDown,
2374 scLineUp:
2375 begin
2376 DoVerticalScroll( ScrollPos );
2377 end;
2378
2379 scHorzPosition,
2380 scPageRight,
2381 scPageLeft,
2382 scHorzTrack,
2383 scColumnRight,
2384 scColumnLeft:
2385 begin
2386 DoHorizontalScroll( ScrollPos );
2387 end;
2388 end;
2389end;
2390
2391Initialization
2392 {Register classes}
2393 RegisterClasses([TOutline2]);
2394End.
2395
2396
Note: See TracBrowser for help on using the repository browser.