/
kn_TreeNoteMng.pas
2699 lines (2340 loc) · 84.9 KB
/
kn_TreeNoteMng.pas
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
unit kn_TreeNoteMng;
(****** LICENSE INFORMATION **************************************************
- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/.
------------------------------------------------------------------------------
(c) 2007-2023 Daniel Prado Velasco <dprado.keynote@gmail.com> (Spain) [^]
(c) 2000-2005 Marek Jedlinski <marek@tranglos.com> (Poland)
[^]: Changes since v. 1.7.0. Fore more information, please see 'README.md'
and 'doc/README_SourceCode.txt' in https://github.com/dpradov/keynote-nf
*****************************************************************************)
interface
uses
Winapi.Windows,
Winapi.Messages,
System.SysUtils,
System.Contnrs,
System.Classes,
Vcl.Forms,
Vcl.Dialogs,
Vcl.Controls,
Vcl.Graphics,
Vcl.Clipbrd,
TreeNT,
RxRichEd,
kn_Info,
kn_Const,
kn_noteObj,
kn_nodeList
;
var
TransferNodes : TNodeList; // for data transfer (copy tree nodes between tabs)
DraggedTreeNode : TTreeNTNode;
MirrorNodes: TBucketList;
_LAST_NODE_SELECTED : TTreeNTNode;
_OLD_NODE_NAME : string;
// treenote-related methods:
function AddNodeToTree( aInsMode : TNodeInsertMode ) : TTreeNTNode;
function TreeNoteNewNode( const aTreeNote : TTreeNote; aInsMode : TNodeInsertMode; const aOriginNode : TTreeNTNode; const aNewNodeName : string; const aDefaultNode : boolean ) : TTreeNTNode;
procedure TreeNodeSelected( Node : TTreeNTNode; OnDeletingNode: boolean= false );
procedure DeleteTreeNode( const DeleteFocusedNode : boolean );
function MoveSubtree( myTreeNode : TTreeNTNode ): boolean;
procedure UpdateTreeNode( const aTreeNode : TTreeNTNode );
procedure CreateMasterNode;
procedure OutlineNumberNodes;
procedure CreateNodefromSelection;
function GetCurrentNoteNode : TNoteNode;
function GetCurrentTreeNode : TTreeNTNode;
procedure SelectIconForNode( const myTreeNode : TTreeNTNode; const IconKind : TNodeIconKind );
procedure UpdateTreeChrome( const myNote : TTreeNote );
procedure UpdateTreeOptions( const myNote : TTreeNote );
procedure MoveTreeNode( MovingNode : TTreeNTNode; const aDir : TDirection );
procedure PasteNodeName( const PasteMode : TPasteNodeNameMode );
procedure CopyNodeName( const IncludeNoteText : boolean );
procedure CopyNodePath( const InsertInEditor : boolean );
procedure ShowOrHideIcons( const tNote : TTreeNote; const UpdateNodes : boolean );
procedure ShowOrHideCheckBoxes( const tNote : TTreeNote );
procedure SetTreeNodeColor( const UseColorDlg, AsTextColor, ResetDefault, DoChildren : boolean );
procedure SetTreeNodeBold( const DoChildren : boolean );
procedure SetTreeNodeCustomImage;
procedure SetTreeNodeFontFace( const ResetDefault, DoChildren : boolean );
procedure SetTreeNodeFontSize( const ResetDefault, DoChildren : boolean );
procedure NavigateInTree( NavDirection : TNavDirection );
function GetNodePath( aNode : TTreeNTNode; const aDelimiter : string; const TopToBottom : boolean ) : string;
procedure ConvertStreamContent(Stream: TMemoryStream; FromFormat, ToFormat: TRichStreamFormat; RTFAux : TRxRichEdit);
function TreeTransferProc( const XferAction : integer; const PasteTargetNote : TTreeNote; const Prompt : boolean ; const PasteAsVirtualKNTNode: boolean; const MovingSubtree: boolean) : boolean;
procedure HideChildNodesUponCheckState (note: TTreeNote; ParentNode: TTreeNTNode; CheckState: TCheckState); // [dpv]
procedure ShowCheckedNodes (note: TTreeNote; ParentNode: TTreeNTNode); // [dpv]
procedure ShowOrHideChildrenCheckBoxes( const tnode : TTreeNTNode ); // [dpv]
function IsAnyNodeMoving: boolean; // [dpv]
procedure MarkAllFiltered (note: TTreeNote); // [dpv]
procedure MarkAllUnfiltered (note: TTreeNote); // [dpv]
procedure RemoveFilter (note: TTreeNote); // [dpv]
procedure HideFilteredNodes (note: TTreeNote); // [dpv]
function GetTreeNode (NoteID: integer; NodeID: integer): TTreeNTNode;
procedure ChangeCheckedState(TV: TTreeNT; Node: TTreeNTNode; Checked: Boolean; CalledFromMirrorNode: Boolean);
function GetMirrorNodes(originalNode: TTreeNTNode): Pointer;
procedure AddMirrorNode(MainNode: TTreeNTNode; Mirror_Node: TTreeNTNode);
procedure RemoveMirrorNode(MainNode: TTreeNTNode; mirror_Node: TTreeNTNode);
procedure ReplaceNonVirtualNode(MainNode: TTreeNTNode; newNode: TTreeNTNode);
implementation
uses
gf_strings,
gf_streams,
gf_miscvcl,
gf_misc,
kn_Global,
kn_EditorUtils,
kn_Chest,
kn_clipUtils,
kn_ImagePicker,
kn_NodeNum,
kn_Macro,
kn_Main,
kn_LinksMng,
kn_VirtualNodeMng,
kn_MacroMng,
kn_NoteFileMng
;
resourcestring
STR_01 = 'Error creating node: ';
STR_02 = ' Virtual: ';
STR_03 = 'Auto-detect and strip numbering from all nodes?';
STR_04 = 'Initial node not assigned - select a node and retry.';
STR_05 = 'cannot be ';
STR_06 = 'Error moving node: ';
STR_07 = 'Node "%s" %smoved %s';
STR_08 = #13#10 + 'This operation cannot be undone.';
STR_09 = 'Node "%s" has %d child nodes. Delete these child nodes too?';
STR_10 = 'Warning';
STR_11 = 'OK to delete node "%s"?';
STR_12 = 'OK to delete %d CHILD NODES of node "%s"?';
STR_13 = 'Selected node has no children.';
STR_14 = 'Error deleting node: ';
STR_15 = 'No tree node available for copying or pasting data.';
STR_16 = 'OK to move %d nodes from node "%s" to current node "%s"?';
STR_17 = ' No node is selected';
STR_18 = 'OK to forget %d copied nodes?';
STR_19 = 'No nodes were copied.';
STR_20 = ' %d nodes copied for transfer';
STR_21 = 'No data to paste. Select "Transfer|Copy Subtree" first.';
STR_22 = 'One or more nodes being transferred is a Virtual Node. Each such node will be pasted as normal (non-virtual) node, if another virtual node in this file is already linked to the same file.' + #13#13 + 'Continue?';
STR_23 = 'OK to paste %d nodes below current node "%s"?';
STR_24 = ' Pasted %d nodes';
STR_25 = '%d virtual nodes have been converted to normal nodes, because other virtual nodes in current file already link to the same files.';
STR_26 = 'OK to paste %d nodes as mirror nodes below current node "%s"?' + #13#10 + '(Only not hidden nodes will be pasted)';
STR_27 = 'Node not found (Note ID/Node ID): %d/%d';
var
__NodeChangeCounter : longint;
FNodeMoving: boolean; // [dpv]
function IsAnyNodeMoving: boolean; // [dpv]
begin
result:= FNodeMoving;
end;
function AddNodeToTree( aInsMode : TNodeInsertMode ) : TTreeNTNode;
begin
result := TreeNoteNewNode( nil, aInsMode, nil, '', false );
if ( KeyOptions.RunAutoMacros and assigned( result )) then
ExecuteMacro( _MACRO_AUTORUN_NEW_NODE, '' );
end; // AddNodeToTree
function TreeNoteNewNode(
const aTreeNote : TTreeNote;
aInsMode : TNodeInsertMode;
const aOriginNode : TTreeNTNode;
const aNewNodeName : string;
const aDefaultNode : boolean ) : TTreeNTNode;
var
myNode, myParentNode : TNoteNode;
myTreeNode, myOriginNode, mySiblingNode : TTreeNTNode;
myNote : TTreeNote;
myName : string;
p : integer;
AddingFirstNode, addnumber : boolean;
begin
result := nil;
if ( aTreeNote = nil ) then
begin
if ( not ( assigned( ActiveNote ) and ( ActiveNote.Kind = ntTree ))) then exit;
myNote := TTreeNote( ActiveNote );
end
else
begin
myNote := aTreeNote;
end;
if Form_Main.NoteIsReadOnly( myNote, true ) then exit;
myTreeNode := nil; { just to avoid }
myNode := nil; { compiler warning }
addnumber := myNote.AutoNumberNodes;
if ( aNewNodeName <> '' ) then
begin
myName := aNewNodeName;
end
else
begin
myName := myNote.DefaultNodeName;
// check for special tokens
p := pos( NODEINSDATE, myName );
if ( p > 0 ) then
begin
delete( myName, p, length( NODEINSDATE ));
insert( FormatDateTime( KeyOptions.DateFmt, now ), myName, p );
addnumber := false;
end;
p := pos( NODEINSTIME, myName );
if ( p > 0 ) then
begin
delete( myName, p, length( NODEINSTIME ));
insert( FormatDateTime( KeyOptions.TimeFmt, now ), myName, p );
addnumber := false;
end;
p := pos( NODECOUNT, myName );
if ( p > 0 ) then
begin
delete( myName, p, length( NODECOUNT ));
insert( inttostr( succ( myNote.TV.Items.Count )), myName, p );
addnumber := false;
end;
if addnumber then
myName := myNote.DefaultNodeName + #32 + inttostr( succ( myNote.TV.Items.Count ));
end;
if ( myNote.TV.Items.Count = 0 ) or ( not assigned( myNote.TV.Selected )) then
begin
AddingFirstNode := true;
aInsMode := tnTop; // no children or siblings if no nodes
end
else
begin
AddingFirstNode := false;
end;
try
try
// myNote.TV.OnChange := nil;
if ( aOriginNode = nil ) then
myOriginNode := myNote.TV.Selected
else
myOriginNode := aOriginNode;
case aInsMode of
tnTop : begin
myTreeNode := myNote.TV.Items.AddFirst( nil, myName );
end;
tnInsertBefore : begin
myTreeNode := myNote.TV.Items.Insert( myOriginNode, myName );
end;
tnAddLast : begin
myTreeNode := myNote.TV.Items.Add( myOriginNode, myName );
end;
tnAddChild : begin
myTreeNode := myNote.TV.Items.AddChild( myOriginNode, myName );
end;
tnAddAfter : begin
mySiblingNode := myOriginNode.GetNextSibling;
if assigned( mySiblingNode ) then
myTreeNode := myNote.TV.Items.Insert( mySiblingNode, myName )
else
myTreeNode := myNote.TV.Items.Add( myOriginNode, myName );
end;
end;
result := myTreeNode;
// these tokens can be expanded only after the node was created
if ( aNewNodeName = '' ) then
begin
if ( pos( '%', myName ) > 0 ) then
begin
p := pos( NODELEVEL, myName );
if ( p > 0 ) then
begin
delete( myName, p, length( NODELEVEL ));
insert( inttostr( myTreeNode.Level ), myName, p );
end;
p := pos( NODEINDEX, myName );
if ( p > 0 ) then
begin
delete( myName, p, length( NODEINDEX ));
insert( inttostr( succ( myTreeNode.Index )), myName, p );
end;
p := pos( NODEABSINDEX, myName );
if ( p > 0 ) then
begin
delete( myName, p, length( NODEABSINDEX ));
insert( inttostr( succ( myTreeNode.AbsoluteIndex )), myName, p );
end;
p := pos( NODEPARENT, myName );
if ( p > 0 ) then
begin
delete( myName, p, length( NODEPARENT ));
if assigned( myTreeNode.Parent ) then
insert( myTreeNode.Parent.Text, myName, p )
else
insert( '<NONE>', myName, p );
end;
p := pos( NODENOTENAME, myName );
if ( p > 0 ) then
begin
delete( myName, p, length( NODENOTENAME ));
insert( RemoveAccelChar( ActiveNote.Name ), myName, p );
end;
p := pos( NODEFILENAME, myName );
if ( p > 0 ) then
begin
delete( myName, p, length( NODEFILENAME ));
insert( ExtractFilename( NoteFile.FileName ), myName, p );
end;
end;
end;
if assigned( myOriginNode ) then
myParentNode := TNoteNode( myOriginNode.Data )
else
myParentNode := nil;
myNode := myNote.NewNode( myParentNode, myName, TreeOptions.InheritNodeProperties );
myTreeNode.Data := myNode;
if AddingFirstNode then
begin
ShowOrHideIcons( myNote, true );
ShowOrHideCheckBoxes( myNote );
end;
SelectIconForNode( myTreeNode.Parent, myNote.IconKind );
SelectIconForNode( myTreeNode, myNote.IconKind );
UpdateTreeNode( myTreeNode );
// assign the color of the currently displayed node
if TreeOptions.InheritNodeBG then
myNode.RTFBGColor := ActiveNote.Editor.Color;
myNote.SelectedNode := myNode;
myNote.DataStreamToEditor;
myTreeNode.MakeVisible;
myNote.TV.Selected := myTreeNode;
if _LastZoomValue <> 100 then
SetEditorZoom(myNote.Editor, _LastZoomValue, '' );
except
on E : Exception do
begin
messagedlg( STR_01 + E.Message, mtError, [mbOK], 0 );
// if assigned( myTreeNode ) then myTreeNode.Free;
// if assigned( myNode ) then myNode.Free;
end;
end;
finally
myNote.SelectedNode := myNode;
myNote.TV.OnChange := Form_Main.TVChange;
VirtualNodeUpdateMenu( false, false );
NoteFile.Modified := true;
UpdateNoteFileState( [fscModified] );
end;
if (( not aDefaultNode ) and assigned( myTreeNode ) and TreeOptions.EditNewNodes ) then
begin
Form_Main.MMRenamenodeClick( nil );
end;
end; // TreeNoteNewNode
function GetCurrentNoteNode : TNoteNode;
begin
result := nil;
if ( not ( assigned( ActiveNote ) and ( ActiveNote.Kind = ntTree ))) then exit;
if assigned( TTreeNote( ActiveNote ).TV.Selected ) then
result := TNoteNode( TTreeNote( ActiveNote ).TV.Selected.Data );
end; // GetCurrentNoteNode
function GetCurrentTreeNode : TTreeNTNode;
begin
result := nil;
if ( not (( assigned( NoteFile ) and assigned( ActiveNote )) and ( ActiveNote.Kind = ntTree ))) then exit;
result := TTreeNote( ActiveNote ).TV.Selected;
end; // GetCurrentTreeNode
procedure TreeNodeSelected( Node : TTreeNTNode; OnDeletingNode: boolean= false );
var
myTreeNote : TTreeNote;
myNode : TNoteNode;
KeepModified: boolean;
{$IFDEF WITH_IE}
NodeControl : TNodeControl;
{$ENDIF}
begin
with Form_Main do begin
if ( not assigned( Node )) then exit;
if ( not ( assigned( ActiveNote ) and ( ActiveNote.Kind = ntTree ))) then exit;
myTreeNote := TTreeNote( ActiveNote );
KeepModified:= false;
if assigned(myTreeNote.SelectedNode) then
myTreeNote.SelectedNode.ScrollPosInEditor:= ActiveNote.Editor.GetScrollPosInEditor;
if ( not _Executing_History_Jump ) and (not _Executing_JumpToKNTLocation_ToOtherNote) then begin
AddHistoryLocation( myTreeNote, false); // Add to history the location of current node, before the new node comes to be the selected node
_LastMoveWasHistory := false;
UpdateHistoryCommands;
end;
myTreeNote.TV.OnChange := nil;
ActiveNote.Editor.OnChange := nil;
ActiveNote.Editor.Lines.BeginUpdate;
{
if KeyOptions.FixScrollBars then
ActiveNote.Editor.ScrollBars := low( TScrollStyle );
}
try
try
if not OnDeletingNode then
ActiveNote.EditorToDataStream;
ActiveNote.Editor.Clear;
ActiveNote.Editor.ClearUndo;
myNode := TNoteNode( Node.Data );
myTreeNote.SelectedNode := myNode;
if assigned( myNode ) then
begin
case myNode.WordWrap of
wwAsNote : ActiveNote.Editor.WordWrap := ActiveNote.WordWrap;
wwYes : ActiveNote.Editor.WordWrap := true;
wwno : ActiveNote.Editor.WordWrap := false;
end;
ActiveNote.DataStreamToEditor;
{ The normal thing is to set Editor.Modified = False at the end of the DataStreamToEditor method
But if hidden marks to be eliminated have been identified (and corrected), it will have been kept as Modified,
to ensure that this correction ends up persisting. Here we will do the same }
if ActiveNote.Editor.Modified then
KeepModified:= True;
if ( myNode.VirtualMode = vmNone ) then
begin
VirtualNodeUpdateMenu( false, false );
if ( not EditorOptions.TrackStyle ) then
begin
if TreeOptions.ShowFullPath then
StatusBar.Panels[PANEL_HINT].Text := GetNodePath( Node, TreeOptions.NodeDelimiter, TreeOptions.PathTopToBottom ) // {N}
else
StatusBar.Panels[PANEL_HINT].Text := Node.Text; // {N}
end;
end
else
begin
VirtualNodeUpdateMenu( true, myNode.VirtualMode = vmKNTNode );
if ( not EditorOptions.TrackStyle ) then
StatusBar.Panels[PANEL_HINT].Text := STR_02 + myNode.VirtualFN;
end;
TVCheckNode.Checked := myNode.Checked;
TVBoldNode.Checked := myNode.Bold;
TVChildrenCheckbox.Checked:= myNode.ChildrenCheckbox; // [dpv]
if myTreeNote.Checkboxes or (assigned(node.Parent) and (node.Parent.CheckType =ctCheckBox)) then // [dpv]
TVCheckNode.Enabled := true
else
TVCheckNode.Enabled := false;
ShowAlarmStatus;
UpdateShowImagesState;
end
else
begin
VirtualNodeUpdateMenu( false, false );
if ( not EditorOptions.TrackStyle ) then
StatusBar.Panels[PANEL_HINT].Text := '';
end;
except
On E : Exception do
begin
TTreeNote( ActiveNote ).SelectedNode := nil;
messagedlg( E.Message, mtError, [mbOK], 0 );
exit;
end;
end;
finally
{
if KeyOptions.FixScrollBars then
ActiveNote.Editor.ScrollBars := ssBoth;
}
if _LastZoomValue <> 100 then
SetEditorZoom(ActiveNote.Editor, _LastZoomValue, '' );
if assigned(myNode) then
ActiveNote.Editor.SetScrollPosInEditor(myNode.ScrollPosInEditor);
ActiveNote.Editor.Lines.EndUpdate;
CheckWordCount(true);
UpdateWordWrap;
if KeyOptions.FixScrollBars then
ActiveNote.Editor.Invalidate; // [x] [?]
if not KeepModified then
ActiveNote.Editor.Modified := false;
RxRTFSelectionChange( ActiveNote.Editor );
RxRTFChange( ActiveNote.Editor );
ActiveNote.Editor.OnChange := RxRTFChange;
TTreeNote( ActiveNote ).TV.OnChange := TVChange;
// inc( __NodeChangeCounter );
// statusbar.panels[0].text := Format( ' %d', [__NodeChangeCounter ]);
end;
end;
end; // TreeNodeSelected
procedure CreateMasterNode;
var
myNode, nextnode, masternode : TTreeNTNode;
myNote : TTreeNote;
begin
if ( not ( assigned( ActiveNote ) and ( ActiveNote.Kind = ntTree ))) then exit;
if Form_Main.NoteIsReadOnly( ActiveNote, true ) then exit;
ActiveNote.FocusMemory := focTree;
myNote := TTreeNote( ActiveNote );
if ( myNote.TV.Items.Count > 0 ) then
myNote.TV.Selected := myNote.TV.Items.GetFirstNode;
masternode := TreeNoteNewNode( nil, tnInsertBefore, nil, '', true );
if assigned( masternode ) then
begin
myNote.TV.Items.BeginUpdate;
try
myNode := masternode.GetNext;
while assigned( myNode ) do
begin
nextnode := myNode.GetNextSibling;
myNode.MoveTo( masternode, naAddChild );
SelectIconForNode( myNode, myNote.IconKind );
myNode := nextnode;
end;
finally
SelectIconForNode( masternode, myNote.IconKind );
myNote.TV.Items.EndUpdate;
myNote.TV.Selected := masternode;
NoteFile.Modified := true;
UpdateNoteFileState( [fscModified] );
end;
end;
end; // CreateMasterNode
procedure OutlineNumberNodes;
type
TExistingNumbers = ( enNo, enYes, enAuto );
var
Form_NodeNum : TForm_NodeNum;
StartNode, myTreeNode, ParentNode : TTreeNTNode;
SubtreeOnly : boolean;
StripNames : boolean;
ExistingNumbers : TExistingNumbers;
myTNote : TTreeNote;
myNoteNode : TNoteNode;
StartNodeLevel, LastNodeLevel, thisNodeLevel : integer;
StartNumber, thisNumber : integer;
ParentLevelStr, LevelStr : string;
DepthLimit : integer;
ModalResponse : Word;
function ExtractNodeNumber( const aNode : TTreeNTNode ) : string;
var
p : integer;
begin
result := '';
if assigned( aNode ) then
begin
if StripNames then
begin
result := aNode.Text;
end
else
begin
p := pos( #32, aNode.Text );
if ( p > 1 ) then
begin
result := copy( aNode.Text, 1, pred( p ));
end;
end;
end;
end; // ExtractNodeNumber
procedure AddNumberToNode;
var
tmpstr : string;
i, SpacePos : integer;
begin
if StripNames then
begin
myNoteNode.Name := LevelStr;
end
else
begin
case ExistingNumbers of
enNo : begin
myNoteNode.Name := LevelStr + #32 + myNoteNode.Name;
end;
enYes : begin
SpacePos := AnsiPos( #32, myNoteNode.Name );
if ( SpacePos > 0 ) then
begin
tmpstr := myNoteNode.Name;
delete( tmpstr, 1, SpacePos );
myNoteNode.Name := LevelStr + #32 + tmpstr;
end
else
begin
myNoteNode.Name := LevelStr;
end;
end;
enAuto : begin
// check if node name begins with a number
// and if so, strip it
SpacePos := -1; // flag: node has NO number
for i := 1 to length( myNoteNode.Name ) do
begin
if ( i = 1 ) then
begin
if ( AnsiChar(myNoteNode.Name[1]) in ['0'..'9'] ) then
SpacePos := 0 // flag: node HAS number
else
break; // node does NOT begin with a number
end
else
begin
if ( AnsiChar(myNoteNode.Name[i]) in ['0'..'9', '.'] ) then
begin
continue;
end
else
if ( myNoteNode.Name[i] = #32 ) then
begin
SpacePos := i;
break;
end
else
begin
SpacePos := pred( i );
break;
end;
end;
end;
if ( SpacePos < 0 ) then
begin
// node name does not have a number
if ( ModalResponse = mrOK ) then
myNoteNode.Name := LevelStr + #32 + myNoteNode.Name;
end
else
if ( SpacePos = 0 ) then
begin
// whole node name is a number
if ( ModalResponse = mrOK ) then
myNoteNode.Name := LevelStr;
end
else
begin
// node has a number followed by text
tmpstr := myNoteNode.Name;
delete( tmpstr, 1, SpacePos );
if ( ModalResponse = mrOK ) then
myNoteNode.Name := LevelStr + #32 + tmpstr
else
myNoteNode.Name := tmpstr;
end;
end;
end;
end;
myTreeNode.Text := myNoteNode.Name;
end; // AddNumberToNode
begin
if ( not ( assigned( ActiveNote ) and ( ActiveNote.Kind = ntTree ))) then exit;
if Form_Main.NoteIsReadOnly( ActiveNote, true ) then exit;
myTNote := TTreeNote( ActiveNote );
if ( myTNote.TV.Items.Count = 0 ) then exit;
Form_NodeNum := TForm_NodeNum.Create( Form_Main );
try
ModalResponse := Form_NodeNum.ShowModal;
if ( ModalResponse in [mrOK, mrYesToAll] ) then
begin
if ( ModalResponse = mrYesToAll ) then
begin
if ( messagedlg( STR_03, mtConfirmation, [mbOK,mbCancel], 0 ) <> mrOK ) then
exit;
end;
with Form_NodeNum do
begin
SubtreeOnly := ( RG_Scope.ItemIndex > 0 );
if SubtreeOnly then
StartNode := myTNote.TV.Selected
else
StartNode := myTNote.TV.Items.GetFirstNode;
StripNames := ( RG_Method.ItemIndex > 0 );
ExistingNumbers := TExistingNumbers( RG_CurNum.ItemIndex );
StartNumber := Spin_StartNum.Value;
if CB_FullDepth.Checked then
DepthLimit := 0 // add numbers to all levels
else
DepthLimit := Spin_Depth.Value; // descend only DepthLimit levels
end;
if ( not assigned( StartNode )) then
begin
messagedlg( STR_04, mtError, [mbOK], 0 );
exit;
end;
myTNote.TV.Items.BeginUpdate;
try
try
myTreeNode := StartNode;
StartNodeLevel := StartNode.Level;
LastNodeLevel := StartNodeLevel;
ThisNumber := StartNumber;
LevelStr := '';
if ( ModalResponse = mrYesToAll ) then
begin
StripNames := false;
SubtreeOnly := false;
ExistingNumbers := enAuto;
while assigned( myTreeNode ) do
begin
myNoteNode := TNoteNode( myTreeNode.Data );
AddNumberToNode;
myTreeNode := myTreeNode.GetNext;
end;
exit;
end;
// first process starting level nodes,
// because they need different treatment
// (numbering starts with StartNumber and is not based on .index property)
while assigned( myTreeNode ) do
begin
myNoteNode := TNoteNode( myTreeNode.Data );
LevelStr := inttostr( ThisNumber );
inc( ThisNumber );
AddNumberToNode;
if SubtreeOnly then
break; // do not process sibling nodes, we only descend the subtree
myTreeNode := myTreeNode.GetNextSibling;
end;
myTreeNode := StartNode;
if ( DepthLimit <> 1 ) then // only if applying numbers more than 1 level deep
begin
StartNodeLevel := StartNode.Level; // return to original starting position
// from now on, we only need to number nodes which are below the
// initial numbering level and up to the max numbering level
ParentLevelStr := '';
LevelStr := '';
ParentNode := nil;
while assigned( myTreeNode ) do
begin
thisNodeLevel := myTreeNode.Level;
myNoteNode := TNoteNode( myTreeNode.Data );
if (( DepthLimit = 0 ) or ( thisNodeLevel < ( StartNodeLevel + DepthLimit ))) then
begin
if ( thisNodeLevel > StartNodeLevel ) then
begin
if ( ParentNode <> myTreeNode.Parent ) then
ParentLevelStr := ExtractNodeNumber( myTreeNode.Parent );
ParentNode := myTreeNode.Parent;
LevelStr := Format(
'%s.%d',
[ParentLevelStr, succ( myTreeNode.Index )]
);
AddNumberToNode;
end;
end;
// get next node, or bail out
LastNodeLevel := myTreeNode.Level;
myTreeNode := myTreeNode.GetNext;
if SubtreeOnly then
begin
// bail out, we have finished the subtree
if ( assigned( myTreeNode ) and ( myTreeNode.Level = StartNodeLevel )) then
break;
end;
end;
end;
except
on E : Exception do
begin
messagedlg( e.message, mtError, [mbOK], 0 );
end;
end;
finally
myTNote.TV.Items.EndUpdate;
NoteFile.Modified := true;
UpdateNoteFileState( [fscModified] );
end;
end;
finally
Form_NodeNum.Free;
end;
end; // OutlineNumberNodes
procedure UpdateTreeChrome( const myNote : TTreeNote );
var
myTreeNode, selectedTreeNode: TTreeNTNode;
myNode: TNoteNode;
FOrig: TFont;
FDest: TFontInfo;
FontChange: boolean;
BGColorChange: boolean;
Styles: TFontStyles;
begin
{ See comments in the methods TCustomTreeNT.RecreateTreeWindow and TCustomTreeNT.DestroyWnd (TreeNT.pas)
The indicated explains why font changes in the tree (from 'Note Properties...') were not reflected on it
on many occasions, precisely those in which we had not modified the background color.
And besides, it was also happening that the font changes, when they were shown, did not give rise to a correct
resizing of the height of each item.
* These problems were of tree refresh. Saving and reopening the file would show up correctly.
}
FOrig:= myNote.TV.Font;
FDest:= myNote.TreeChrome.Font;
FontChange:= (FOrig.Color <> FDest.Color) or (FOrig.Size <> FDest.Size) or
(FOrig.Name <> FDest.Name) or (FOrig.Style <> FDest.Style) or
(FOrig.Charset <> FDest.Charset);
BGColorChange:= (myNote.TV.Color <> myNote.TreeChrome.BGColor);
if not FontChange and not BGColorChange then
Exit;
if FontChange then begin
Log_StoreTick('');
Log_StoreTick( 'UpdateTreeChrome - Begin changes', 2, +1);
FontInfoToFont( myNote.TreeChrome.Font, myNote.TV.Font ); // This doesn't force any update (it doesn't end up calling RecreateWnd)
{ The above line does not affect nodes with any changes from the default values, we must explicitly update their size, style and font color.
The background color of the node and the its font.name do not need to be updated.
Also take into account: if the font of the tree includes the bold style, and we remove it from a node, it will not indicate Bold=True,
but for the tree we have explicitly modified it, so we need to also look at the property ParentFont of TTreeNTNode
}
Log_StoreTick( 'After FontInfoToFont', 2);
myTreeNode := myNote.TV.Items.GetFirstNode;
while assigned( myTreeNode ) do begin
myNode:= TNoteNode(myTreeNode.Data);
if assigned(myNode) then begin
if not myTreeNode.ParentFont
or myNode.Bold or myNode.HasNodeFontFace or myNode.HasNodeColor or myNode.HasNodeBGColor then begin
if not myNode.HasNodeFontFace then
myTreeNode.Font.Name := FDest.Name;
//if myNode.HasNodeBGColor then myTreeNode.Color := myNode.NodeBGColor; // Not necessary
myTreeNode.Font.Size := FDest.Size;
Styles:= FDest.Style;
if myNode.Bold then
Styles := Styles + [fsBold];
myTreeNode.Font.Style := Styles;
if myNode.HasNodeColor then
myTreeNode.Font.Color := myNode.NodeColor
else
myTreeNode.Font.Color := FDest.Color;
end;
end;
myTreeNode := myTreeNode.GetNext;
end;
end;
Log_StoreTick( 'After modified individual nodes', 2);
selectedTreeNode:= myNote.TV.Selected;
myNote.TV.OnChange := nil;
//myNote.TV.Items.BeginUpdate;
try
myNote.TV.Color := myNote.TreeChrome.BGColor; // It will do nothing if BGColorChange = False
Log_StoreTick( 'After changed TV.Color', 2);
if not BGColorChange and FontChange then begin
mynote.TV.RecreateTreeWindow;
Log_StoreTick( 'After RecreateTreeWindow', 2);
end;
finally
//myNote.TV.Items.EndUpdate;
myNote.TV.Selected:= selectedTreeNode;
myNote.TV.OnChange := Form_Main.TVChange;
end;
Log_StoreTick( 'UpdateTreeChrome - End changes', 2, -1);
Log_Flush();
end; // UpdateTreeChrome
procedure UpdateTreeOptions( const myNote : TTreeNote );
begin
// updates options for current note's tree
// based on global tree options
with myNote.TV do
begin
ColorDropSelected := clInfoBK;
ColorUnfocusedSelected := cl3DLight;
DragMode := dmAutomatic;
Options := Options + [toAutoExpand];
if TreeOptions.AutoScroll then
Options := Options + [toAutoScroll]
else
Options := Options - [toAutoScroll];
if TreeOptions.HotTrack then
Options := Options + [toHotTrack]
else
Options := Options - [toHotTrack];
if TreeOptions.EditInPlace then
Options := Options - [toReadOnly]
else
Options := Options + [toReadOnly];
if TreeOptions.ShowTooltips then
begin
Options := Options + [toInfoTip];
Options := Options + [toToolTips];
end
else
begin
Options := Options - [toInfoTip];
Options := Options - [toToolTips];
end;
end;
end; // UpdateTreeOptions
procedure MoveTreeNode( MovingNode : TTreeNTNode; const aDir : TDirection );
var