-
Notifications
You must be signed in to change notification settings - Fork 2
/
CConsoleGrid.cls
2955 lines (2586 loc) · 83.8 KB
/
CConsoleGrid.cls
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
VERSION 1.0 CLASS
BEGIN
MultiUse = -1 'True
END
Attribute VB_Name = "CConsoleGrid"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = False
Attribute VB_Exposed = False
Option Compare Database
Option Explicit
Private Const NATIVE_FILE_MAGIC_TAG_V1 As Long = 3021968 'written in the native file format
Private Const NATIVE_FILE_MAGIC_TAG_V2 As Long = &H41534350
'V02.00.06 File Format Version 3, keeps V2 file magic tag
'Dropping support for file format less than previous version (< 2)
Private Const CLASS_VERSION As Long = 3& 'written in the native file format
Private Const MIN_SUPPORTED_VERSION As Long = 2&
'Rows and Cols for buffers
Private miRows As Integer
Private miCols As Integer
'Buffers
Private mfBuffersCreated As Boolean
Private mfDirty As Boolean 'if something changes, exposed via Dirty property
'Characters
Private masChars() As String
'Two dimesions (rows*cols)
Private malForeCol() As Long
Private mabForeCol() As Byte 'indicates if malForeCol item is used
Private malBackCol() As Long
Private mabBackCol() As Byte
Private maiAttribs() As Integer
' 11 bits for "attributes" (repeated declarations from MSupport, but private for easier integration)
Private Const ATTRIB_BOLDON As Integer = 1
Private Const ATTRIB_ITALICON As Integer = 2
Private Const ATTRIB_UNDLON As Integer = 4
Private Const ATTRIB_STRIKEON As Integer = 8
Private Const ATTRIB_INVERSEON As Integer = 16
Private Const ATTRIB_BOLDOFF As Integer = 32
Private Const ATTRIB_ITALICOFF As Integer = 64
Private Const ATTRIB_UNDLOFF As Integer = 128
Private Const ATTRIB_STRIKEOFF As Integer = 256
Private Const ATTRIB_INVERSEOFF As Integer = 512
Private Const ATTRIB_RESET As Integer = 1024
Private Const ATTRIB_SPILLON As Integer = 2048
Private Const ATTRIB_SPILLOFF As Integer = 4096
Private Const ATTRIB_BITS As Integer = 12 'do not include the reset "attribute", its not a toggle
Private maiAttribMask(0 To ATTRIB_BITS - 1) As Integer 'Will contain powers of 2 to avoid recalc
Private masAttribInject(0 To ATTRIB_BITS - 1) As String 'Initialized with VT100 escape codes per attribmask
' DrawText() Format Flags
Private Const DT_TOP = &H0
Private Const DT_LEFT = &H0
Private Const DT_CENTER = &H1
Private Const DT_RIGHT = &H2
Private Const DT_VCENTER = &H4
Private Const DT_BOTTOM = &H8
Private Const DT_WORDBREAK = &H10
Private Const DT_SINGLELINE = &H20
Private Const DT_EXPANDTABS = &H40
Private Const DT_TABSTOP = &H80
Private Const DT_NOCLIP = &H100
Private Const DT_EXTERNALLEADING = &H200
Private Const DT_CALCRECT = &H400
Private Const DT_NOPREFIX = &H800
Private Const DT_INTERNAL = &H1000
Private mfFragmentText As Boolean
Private miFragmentCharWidth As Integer
'We'll cache fully rendered VT100 lines, so we don't have to rebuild them
'on a subsequent call in GetLineVT100() method.
Private moVT100Cache As New CVT100Cache
'For rendering a line
Private Type TInjection
ID As Long 'needed to sort multiple entries at .iPos
iPos As Integer
sInject As String
End Type
Private matInjection() As TInjection
Private miInjectionCount As Integer
Private miInjectionSize As Integer
'Whether we want to show line numbers
Private mfShowBoundaries As Boolean 'whether we add "|" at end of lines and "---...--+" at end of output
'Selected item
Private miSelCol As Integer
Private miSelRow As Integer
Private miSelZone As Integer
Public Enum eClearDirection
eToCursorPosition = 0
eFromCursorPosition
End Enum
'search and replace char
Private miLastFindRow As Integer
Private miLastFindCol As Integer
Private msLastFindChar As String
Public Enum eReplaceCharScope
eToEndOfLine
eFullLine
eToEndOfText
eFullText
eSelection
End Enum
Public Enum eReplaceColorScope
eForeColor
eBackColor
eBoth
End Enum
' For VT100 parsing
Private Const LSCMD_NOOP As Integer = 0
Private Const LSCMD_TEXTOUT As Integer = 1
Private Const LSCMD_INVERSEON As Integer = 2
Private Const LSCMD_INVERSEOFF As Integer = 3
Private Const LSCMD_BOLDON As Integer = 4
Private Const LSCMD_ITALICON As Integer = 5
Private Const LSCMD_UNDERLINEON As Integer = 6
Private Const LSCMD_BOLDOFF As Integer = 7
Private Const LSCMD_ITALICOFF As Integer = 8
Private Const LSCMD_UNDERLINEOFF As Integer = 9
Private Const LSCMD_CROSSEDOUTON As Integer = 10
Private Const LSCMD_CROSSEDOUTOFF As Integer = 11
Private Const LSCMD_SETFORECOLOR As Integer = 20
Private Const LSCMD_SETBACKCOLOR As Integer = 21
Private Const LSCMD_BEGINZONE As Integer = 90
Private Const LSCMD_ENDZONE As Integer = 91
Private Const LSCMD_RESET As Integer = 99
Private Const LSCMD_SETWIDTH As Integer = 100
Private Const LSCMD_ADVANCEABS As Integer = 101
Private Const LSCMD_ADVANCEREL As Integer = 102
Private Const LSCMD_SAVEGPOS As Integer = 103
Private Const LSCMD_RESTOREGPOS As Integer = 104
Private Const LSCMD_DTFLAGS As Integer = 110
Private Const LSCMD_BKCOLSPILL As Integer = 111 'V01.02.04
Private Const VT100_RESET As String = "0"
Private Const VT100_INVERSEON As String = "7"
Private Const VT100_INVERSEOFF As String = "27"
Private Const VT100_BOLDON As String = "1"
Private Const VT100_BOLDOFF As String = "21"
Private Const VT100_ITALICON As String = "3"
Private Const VT100_ITALICOFF As String = "23"
Private Const VT100_UNDERLINEON As String = "4"
Private Const VT100_UNDERLINEOFF As String = "24"
Private Const VT100_CROSSEDOUT_ON As String = "9"
Private Const VT100_CROSSEDOUT_OFF As String = "29"
Private Const VT100_SETFGCOLOR As String = "38"
Private Const VT100_SETBKCOLOR As String = "48"
Private Const VT100X_BEGINZONE As String = "98"
Private Const VT100X_ENDZONE As String = "99"
Private Const VT100X_SETWIDTH As String = "100" 'V01.02.00 Adding support for setting width of next TEXT
Private Const VT100X_ADVANCEABS As String = "101"
Private Const VT100X_ADVANCEREL As String = "102"
Private Const VT100X_SAVEGPOS As String = "103"
Private Const VT100X_RESTOREGPOS As String = "104"
Private Const VT100X_DTFLAGS As String = "110" 'V01.02.03
Private Const VT100X_BKCOLSPILL As String = "111" 'V01.02.04
'V02.00.00 easier to raise an event than to chain code for dirty state
Event OnDirtyChange(ByVal pfDirty As Boolean)
Private Const SECTIONID_FORECOLOR_COLOR As Integer = 1
Private Const SECTIONID_FORECOLOR_ONOFF As Integer = 2
Private Const SECTIONID_BACKCOLOR_COLOR As Integer = 3
Private Const SECTIONID_BACKCOLOR_ONOFF As Integer = 4
Private Const SECTIONID_ATTRIBUTES As Integer = 5
Private Const SECTIONID_CHARWDATA As Integer = 6
'API
Private Declare PtrSafe Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As LongPtr)
' IClassError implementation
Implements IClassError
Private mlErrNo As Long
Private msErrCtx As String
Private msErrDesc As String
Private Sub ClearErr()
mlErrNo = 0&
msErrCtx = ""
msErrDesc = ""
End Sub
Private Sub SetErr(ByVal psErrCtx As String, ByVal plErrNum As Long, ByVal psErrDesc As String)
mlErrNo = plErrNum
msErrCtx = psErrCtx
msErrDesc = psErrDesc
End Sub
Public Property Get LastErr() As Long
LastErr = mlErrNo
End Property
Public Property Get LastErrDesc() As String
LastErrDesc = msErrDesc
End Property
Public Property Get IIClassError() As IClassError
Set IIClassError = Me
End Property
Private Property Get IClassError_LastErr() As Long
IClassError_LastErr = mlErrNo
End Property
Private Property Get IClassError_LastErrCtx() As String
IClassError_LastErrCtx = msErrCtx
End Property
Private Property Get IClassError_LastErrDesc() As String
IClassError_LastErrDesc = msErrDesc
End Property
' Class events
Private Sub Class_Initialize()
Dim i As Integer
'Precompute powers of 2
maiAttribMask(0) = ATTRIB_BOLDON
maiAttribMask(1) = ATTRIB_ITALICON
maiAttribMask(2) = ATTRIB_UNDLON
maiAttribMask(3) = ATTRIB_STRIKEON
maiAttribMask(4) = ATTRIB_INVERSEON
maiAttribMask(5) = ATTRIB_BOLDOFF
maiAttribMask(6) = ATTRIB_ITALICOFF
maiAttribMask(7) = ATTRIB_UNDLOFF
maiAttribMask(8) = ATTRIB_STRIKEOFF
maiAttribMask(9) = ATTRIB_INVERSEOFF
maiAttribMask(10) = ATTRIB_SPILLON
maiAttribMask(11) = ATTRIB_SPILLOFF
masAttribInject(0) = VT_BOLD_ON 'ATTRIB_BOLDON
masAttribInject(1) = VT_ITAL_ON 'ATTRIB_ITALICON
masAttribInject(2) = VT_UNDL_ON 'ATTRIB_UNDLON
masAttribInject(3) = VT_STRIKE_ON 'ATTRIB_STRIKEON
masAttribInject(4) = VT_INV_ON 'ATTRIB_INVERSEON
masAttribInject(5) = VT_BOLD_OFF 'ATTRIB_BOLDOFF
masAttribInject(6) = VT_ITAL_OFF 'ATTRIB_ITALICOFF
masAttribInject(7) = VT_UNDL_OFF 'ATTRIB_UNDLOFF
masAttribInject(8) = VT_STRIKE_OFF 'ATTRIB_STRIKEOFF
masAttribInject(9) = VT_INV_OFF 'ATTRIB_INVERSEOFF
masAttribInject(10) = VTX_SPILL(1) 'ATTRIB_SPILLON
masAttribInject(11) = VTX_SPILL(0) 'ATTRIB_SPILLOFF
'default rows/cols
miRows = 40
miCols = 80
mfShowBoundaries = True
End Sub
Private Sub Class_Terminate()
'nothing to do for now
End Sub
Public Function GetMemoryFootprint() As Double
Dim dblByteCt As Double
dblByteCt = dblByteCt + LenB(miRows)
dblByteCt = dblByteCt + LenB(miCols)
dblByteCt = dblByteCt + LenB(mfBuffersCreated)
dblByteCt = dblByteCt + LenB(mfDirty)
On Error Resume Next
Dim iLB1 As Integer
Dim iUB1 As Integer
Dim iLB2 As Integer
Dim iUB2 As Integer
Dim i As Long
Dim j As Long
'masChars
iLB1 = LBound(masChars)
iUB1 = UBound(masChars)
For i = iLB1 To iUB1
dblByteCt = dblByteCt + LenB(masChars(i))
Next i
'Debug.Print "After masChars(), bytes=" & dblByteCt
'Debug.Print "After masChars(), Kbytes=" & Format$(dblByteCt / 1024, "0.####")
'Debug.Print "After masChars(), Mbytes=" & Format$((dblByteCt / 1024) / 1024, "0.####")
'malForeCol
iLB1 = LBound(malForeCol, 1)
iUB1 = UBound(malForeCol, 1)
iLB2 = LBound(malForeCol, 2)
iUB2 = UBound(malForeCol, 2)
For i = iLB1 To iUB1
For j = iLB2 To iUB2
dblByteCt = dblByteCt + LenB(malForeCol(i, j))
Next j
Next i
'Debug.Print "After malForeCol(), bytes=" & dblByteCt
'Debug.Print "After malForeCol(), Kbytes=" & Format$(dblByteCt / 1024, "0.####")
'Debug.Print "After malForeCol(), Mbytes=" & Format$((dblByteCt / 1024) / 1024, "0.####")
'mabForeCol
iLB1 = LBound(mabForeCol, 1)
iUB1 = UBound(mabForeCol, 1)
iLB2 = LBound(mabForeCol, 2)
iUB2 = UBound(mabForeCol, 2)
For i = iLB1 To iUB1
For j = iLB2 To iUB2
dblByteCt = dblByteCt + LenB(mabForeCol(i, j))
Next j
Next i
'Debug.Print "After mabForeCol(), bytes=" & dblByteCt
'Debug.Print "After mabForeCol(), Kbytes=" & Format$(dblByteCt / 1024, "0.####")
'Debug.Print "After mabForeCol(), Mbytes=" & Format$((dblByteCt / 1024) / 1024, "0.####")
'malBackCol
iLB1 = LBound(malBackCol, 1)
iUB1 = UBound(malBackCol, 1)
iLB2 = LBound(malBackCol, 2)
iUB2 = UBound(malBackCol, 2)
For i = iLB1 To iUB1
For j = iLB2 To iUB2
dblByteCt = dblByteCt + LenB(malBackCol(i, j))
Next j
Next i
'Debug.Print "After malBackCol(), bytes=" & dblByteCt
'Debug.Print "After malBackCol(), Kbytes=" & Format$(dblByteCt / 1024, "0.####")
'Debug.Print "After malBackCol(), Mbytes=" & Format$((dblByteCt / 1024) / 1024, "0.####")
'mabBackCol
iLB1 = LBound(mabBackCol, 1)
iUB1 = UBound(mabBackCol, 1)
iLB2 = LBound(mabBackCol, 2)
iUB2 = UBound(mabBackCol, 2)
For i = iLB1 To iUB1
For j = iLB2 To iUB2
dblByteCt = dblByteCt + LenB(mabBackCol(i, j))
Next j
Next i
'Debug.Print "After mabBackCol(), bytes=" & dblByteCt
'Debug.Print "After mabBackCol(), Kbytes=" & Format$(dblByteCt / 1024, "0.####")
'Debug.Print "After mabBackCol(), Mbytes=" & Format$((dblByteCt / 1024) / 1024, "0.####")
'maiAttribs
iLB1 = LBound(maiAttribs, 1)
iUB1 = UBound(maiAttribs, 1)
iLB2 = LBound(maiAttribs, 2)
iUB2 = UBound(maiAttribs, 2)
For i = iLB1 To iUB1
For j = iLB2 To iUB2
dblByteCt = dblByteCt + LenB(maiAttribs(i, j))
Next j
Next i
'Debug.Print "After maiAttribs(), bytes=" & dblByteCt
'Debug.Print "After maiAttribs(), Kbytes=" & Format$(dblByteCt / 1024, "0.####")
'Debug.Print "After maiAttribs(), Mbytes=" & Format$((dblByteCt / 1024) / 1024, "0.####")
'maiAttribMask
iLB1 = LBound(maiAttribMask)
iUB1 = UBound(maiAttribMask)
For i = iLB1 To iUB1
dblByteCt = dblByteCt + LenB(maiAttribMask(i))
Next i
'Debug.Print "After maiAttribMask(), bytes=" & dblByteCt
'Debug.Print "After maiAttribMask(), Kbytes=" & Format$(dblByteCt / 1024, "0.####")
'Debug.Print "After maiAttribMask(), Mbytes=" & Format$((dblByteCt / 1024) / 1024, "0.####")
'masAttribInject
iLB1 = LBound(masAttribInject)
iUB1 = UBound(masAttribInject)
For i = iLB1 To iUB1
dblByteCt = dblByteCt + LenB(masAttribInject(i))
Next i
'Debug.Print "After masAttribInject(), bytes=" & dblByteCt
'Debug.Print "After masAttribInject(), Kbytes=" & Format$(dblByteCt / 1024, "0.####")
'Debug.Print "After masAttribInject(), Mbytes=" & Format$((dblByteCt / 1024) / 1024, "0.####")
'matInjection
iLB1 = LBound(matInjection)
iUB1 = UBound(matInjection)
For i = iLB1 To iUB1
dblByteCt = dblByteCt + LenB(matInjection(i))
Next i
'Debug.Print "After matInjection(), bytes=" & dblByteCt
'Debug.Print "After matInjection(), Kbytes=" & Format$(dblByteCt / 1024, "0.####")
'Debug.Print "After matInjection(), Mbytes=" & Format$((dblByteCt / 1024) / 1024, "0.####")
dblByteCt = dblByteCt + LenB(miInjectionCount)
dblByteCt = dblByteCt + LenB(miInjectionSize)
dblByteCt = dblByteCt + LenB(mfShowBoundaries)
dblByteCt = dblByteCt + LenB(miSelCol)
dblByteCt = dblByteCt + LenB(miSelRow)
dblByteCt = dblByteCt + LenB(miSelZone)
dblByteCt = dblByteCt + LenB(miLastFindRow)
dblByteCt = dblByteCt + LenB(miLastFindCol)
dblByteCt = dblByteCt + LenB(msLastFindChar)
dblByteCt = dblByteCt + LenB(mlErrNo)
dblByteCt = dblByteCt + LenB(msErrCtx)
dblByteCt = dblByteCt + LenB(msErrDesc)
'Debug.Print "Finally, bytes=" & dblByteCt
'Debug.Print "Finally, Kbytes=" & Format$(dblByteCt / 1024, "0.####")
'Debug.Print "Finally, Mbytes=" & Format$((dblByteCt / 1024) / 1024, "0.####")
GetMemoryFootprint = dblByteCt
End Function
'
' Public properties
'
Public Property Get Dirty() As Boolean
Dirty = mfDirty
End Property
Private Sub SetDirty(ByVal pfDirty As Boolean)
mfDirty = pfDirty
On Error Resume Next
RaiseEvent OnDirtyChange(mfDirty)
End Sub
Public Property Get Rows() As Integer
Rows = miRows
End Property
Public Property Get Cols() As Integer
Cols = miCols
End Property
Public Property Get CharAt(ByVal piRow As Integer, ByVal piCol As Integer) As String
If (piRow > 0) And (piRow <= miRows) And (piCol > 0) And (piCol <= miCols) Then
CharAt = Mid$(masChars(piRow), piCol, 1)
End If
End Property
Public Property Let CharAt(ByVal piRow As Integer, ByVal piCol As Integer, ByVal psChar As String)
If (Len(psChar) = 1) And (piRow > 0) And (piRow <= miRows) And (piCol > 0) And (piCol <= miCols) Then
Mid$(masChars(piRow), piCol, 1) = left$(psChar, 1)
SetDirty True
moVT100Cache.InvalidateLine piRow
End If
End Property
Public Property Get TextAt(ByVal piRow As Integer, ByVal piCol As Integer) As String
If (piRow > 0) And (piRow <= miRows) And (piCol > 0) And (piCol <= miCols) Then
TextAt = Right$(masChars(piRow), miCols - piCol + 1)
End If
End Property
Public Property Let TextAt(ByVal piRow As Integer, ByVal piCol As Integer, ByVal psText As String)
If (Len(psText) > 0) And (piRow > 0) And (piRow <= miRows) And (piCol > 0) And (piCol <= miCols) Then
Mid$(masChars(piRow), piCol, Len(psText)) = psText
If Len(masChars(piRow)) > miCols Then 'truncate to max number of columns
masChars(piRow) = left$(masChars(piRow), miCols)
End If
SetDirty True
moVT100Cache.InvalidateLine piRow
End If
End Property
Public Property Get CharForeCol(ByVal piRow As Integer, ByVal piCol As Integer) As Long
If (piRow > 0) And (piRow <= miRows) And (piCol > 0) And (piCol <= miCols) Then
CharForeCol = malForeCol(piRow, piCol)
End If
End Property
Public Property Let CharForeCol(ByVal piRow As Integer, ByVal piCol As Integer, ByVal plForeCol As Long)
If (piRow > 0) And (piRow <= miRows) And (piCol > 0) And (piCol <= miCols) Then
malForeCol(piRow, piCol) = plForeCol
mabForeCol(piRow, piCol) = 1
SetDirty True
moVT100Cache.InvalidateLine piRow
End If
End Property
Public Property Get HasForeCol(ByVal piRow As Integer, ByVal piCol As Integer) As Byte
If (piRow > 0) And (piRow <= miRows) And (piCol > 0) And (piCol <= miCols) Then
HasForeCol = mabForeCol(piRow, piCol)
End If
End Property
Public Property Let HasForeCol(ByVal piRow As Integer, ByVal piCol As Integer, ByVal pbHasForeCol As Byte)
If (piRow > 0) And (piRow <= miRows) And (piCol > 0) And (piCol <= miCols) Then
mabForeCol(piRow, piCol) = pbHasForeCol
SetDirty True
moVT100Cache.InvalidateLine piRow
End If
End Property
Public Property Get CharBackCol(ByVal piRow As Integer, ByVal piCol As Integer) As Long
If (piRow > 0) And (piRow <= miRows) And (piCol > 0) And (piCol <= miCols) Then
CharBackCol = malBackCol(piRow, piCol)
End If
End Property
Public Property Let CharBackCol(ByVal piRow As Integer, ByVal piCol As Integer, ByVal plBackCol As Long)
If (piRow > 0) And (piRow <= miRows) And (piCol > 0) And (piCol <= miCols) Then
malBackCol(piRow, piCol) = plBackCol
mabBackCol(piRow, piCol) = 1
SetDirty True
moVT100Cache.InvalidateLine piRow
End If
End Property
Public Property Get HasBackCol(ByVal piRow As Integer, ByVal piCol As Integer) As Byte
If (piRow > 0) And (piRow <= miRows) And (piCol > 0) And (piCol <= miCols) Then
HasBackCol = mabBackCol(piRow, piCol)
End If
End Property
Public Property Let HasBackCol(ByVal piRow As Integer, ByVal piCol As Integer, ByVal pbHasBackCol As Byte)
If (piRow > 0) And (piRow <= miRows) And (piCol > 0) And (piCol <= miCols) Then
mabBackCol(piRow, piCol) = pbHasBackCol
SetDirty True
moVT100Cache.InvalidateLine piRow
End If
End Property
Public Property Get CharAttribs(ByVal piRow As Integer, ByVal piCol As Integer) As Integer
If (piRow > 0) And (piRow <= miRows) And (piCol > 0) And (piCol <= miCols) Then
CharAttribs = maiAttribs(piRow, piCol)
End If
End Property
Public Property Let CharAttribs(ByVal piRow As Integer, ByVal piCol As Integer, ByVal piAttribs As Integer)
If (piRow > 0) And (piRow <= miRows) And (piCol > 0) And (piCol <= miCols) Then
maiAttribs(piRow, piCol) = piAttribs
SetDirty True
moVT100Cache.InvalidateLine piRow
End If
End Property
Public Property Get ShowBoundaries() As Boolean
ShowBoundaries = mfShowBoundaries
End Property
Public Property Let ShowBoundaries(ByVal pfShowBoundaries As Boolean)
If mfShowBoundaries <> pfShowBoundaries Then
mfShowBoundaries = pfShowBoundaries
moVT100Cache.Clear
End If
End Property
Public Property Get FragmentText() As Boolean
FragmentText = mfFragmentText
End Property
Public Sub SetFragmentText(ByVal pfAutoFragmentText As Boolean, ByVal piFragmentCharWidth As Integer)
mfFragmentText = pfAutoFragmentText
miFragmentCharWidth = piFragmentCharWidth
End Sub
'
' Public methods
'
Public Function Resize(ByVal piRows As Integer, ByVal piCols As Integer) As Boolean
Const LOCAL_ERR_CTX As String = "Resize"
On Error GoTo Resize_Err
Dim grdTemp As CConsoleGrid
Dim fOK As Boolean
Dim fCopyData As Boolean
ClearErr
If (piRows <= 0) Or (piCols <= 0) Then
SetErr "Resize", -1&, "Invalid size"
Exit Function
End If
'We have to copy the grid to a temporary new one
'then paste it back to the new canvas
'create a copy
fCopyData = mfBuffersCreated
If fCopyData Then
Set grdTemp = New CConsoleGrid
fOK = grdTemp.CreateFrom(Me, 1, miRows, 1, miCols)
If Not fOK Then
SetErr LOCAL_ERR_CTX, Err.Number, Err.Description
GoTo Resize_exit
End If
End If
'Create new grid in this instance
miRows = piRows
miCols = piCols
fOK = CreateBuffers()
If Not fOK Then
GoTo Resize_exit
End If
'Paste back
If fCopyData Then
fOK = Me.Paste(grdTemp, 1, 1, False)
End If
Resize_exit:
Resize = fOK
Set grdTemp = Nothing
Exit Function
Resize_Err:
SetErr LOCAL_ERR_CTX, Err.Number, Err.Description
Resume Resize_exit
End Function
Private Function CreateBuffers() As Boolean
Dim i As Integer
On Error GoTo CreateBuffers_Err
'(re)size buffers
ReDim masChars(1 To miRows) As String
ReDim malForeCol(1 To miRows, 1 To miCols) As Long
ReDim mabForeCol(1 To miRows, 1 To miCols) As Byte
ReDim malBackCol(1 To miRows, 1 To miCols) As Long
ReDim mabBackCol(1 To miRows, 1 To miCols) As Byte
ReDim maiAttribs(1 To miRows, 1 To miCols) As Integer
For i = 1 To miRows
masChars(i) = Space$(miCols)
Next i
moVT100Cache.Clear
mfBuffersCreated = True
CreateBuffers = True
CreateBuffers_exit:
Exit Function
CreateBuffers_Err:
SetErr "CreateBuffers", Err.Number, Err.Description
Resume CreateBuffers_exit
End Function
Public Function LoadConsole(poConsole As CConsoul, ByRef iiProgress As IProgressIndicator) As Boolean
Dim iRows As Integer
On Error GoTo LoadConsole_Err
ClearErr
poConsole.RenderMode = rmByLine
If Not mfBuffersCreated Then
If Not CreateBuffers() Then
Exit Function
End If
End If
Dim i As Integer
Dim sRender As String
Dim iExistingLines As Integer
If Not mfShowBoundaries Then
poConsole.Clear
End If
iRows = miRows
If poConsole.MaxCapacity < miRows Then
iRows = poConsole.MaxCapacity - 1
End If
iExistingLines = poConsole.LineCount
If Not iiProgress Is Nothing Then iiProgress.SetMax iRows
poConsole.AutoRedraw = False
For i = 1 To iRows
sRender = Me.GetLineVT100(i, mfShowBoundaries)
If i <= iExistingLines Then
poConsole.SetLine i, sRender
Else
poConsole.OutputLn sRender
iExistingLines = iExistingLines + 1
End If
If Not iiProgress Is Nothing Then iiProgress.SetValue i
'DbgWait 1
Next i
If Not iiProgress Is Nothing Then iiProgress.SetCaption "Finishing"
If mfShowBoundaries Then
sRender = String$(miCols, "-") & "+"
If iExistingLines < iRows + 1 Then
poConsole.OutputLn sRender
poConsole.OutputLn Space$(miCols + 1)
Else
poConsole.SetLine iRows + 1, sRender
End If
End If
LoadConsole = True
LoadConsole_exit:
poConsole.AutoRedraw = True
Exit Function
LoadConsole_Err:
SetErr "LoadConsole", Err.Number, Err.Description
Resume LoadConsole_exit
End Function
Private Sub InitInjectionsArray()
miInjectionSize = miCols * 2
ReDim matInjection(1 To miInjectionSize) As TInjection
miInjectionCount = 0
End Sub
Private Sub AddInjection(ByVal piPos As Integer, ByVal psInject As String)
Static lNextID As Long
lNextID = lNextID + 1
If lNextID > 2147483647 Then
lNextID = 1& '/**/this will break the sort order; no solution at this time.
End If
If miInjectionCount = miInjectionSize Then
ReDim Preserve matInjection(1 To miInjectionSize * 2) As TInjection
miInjectionSize = miInjectionSize * 2
End If
miInjectionCount = miInjectionCount + 1
With matInjection(miInjectionCount)
.ID = lNextID
.iPos = piPos
.sInject = psInject
End With
End Sub
Private Sub GenForeColInjections(ByVal piLine As Integer)
Dim i As Integer
Dim fColorChanged As Boolean
Dim lLastColor As Long
Dim iAttribs As Integer
Dim fReset As Boolean
Dim fFirstTime As Boolean
i = 1
lLastColor = malForeCol(piLine, 1)
fFirstTime = True
Do
iAttribs = maiAttribs(piLine, i)
fReset = CBool(iAttribs And ATTRIB_RESET)
If fReset Then fFirstTime = True
If mabForeCol(piLine, i) Then
fColorChanged = fFirstTime Or CBool(malForeCol(piLine, i) <> lLastColor)
If fColorChanged Then
AddInjection i, VT_FCOLOR(malForeCol(piLine, i))
lLastColor = malForeCol(piLine, i)
fFirstTime = False
End If
End If
i = i + 1
Loop Until i > miCols
End Sub
Private Sub GenBackColInjections(ByVal piLine As Integer)
Dim i As Integer
Dim fColorChanged As Boolean
Dim lLastColor As Long
Dim iAttribs As Integer
Dim fReset As Boolean
Dim fFirstTime As Boolean
i = 1
lLastColor = malForeCol(piLine, 1)
fFirstTime = True
Do
iAttribs = maiAttribs(piLine, i)
fReset = CBool(iAttribs And ATTRIB_RESET)
If fReset Then fFirstTime = True
If mabBackCol(piLine, i) Then
fColorChanged = fFirstTime Or CBool(malBackCol(piLine, i) <> lLastColor)
If fColorChanged Then
AddInjection i, VT_BCOLOR(malBackCol(piLine, i))
lLastColor = malBackCol(piLine, i)
fColorChanged = False
fFirstTime = False
End If
End If
i = i + 1
Loop Until i > miCols
End Sub
Private Sub GenAttribsInjections(ByVal piLine As Integer)
Dim i As Integer
Dim k As Integer
Dim iLastAttribs As Integer
Dim iAttribs As Integer
Dim iAtribsToSet As Integer
Dim fReset As Boolean
i = 1
Do
iAttribs = maiAttribs(piLine, i)
fReset = CBool(iAttribs And ATTRIB_RESET)
If fReset Then
'clear reset attribute
iAttribs = iAttribs And (Not ATTRIB_RESET)
iLastAttribs = 0
End If
If iAttribs > 0 Then
If iAttribs <> iLastAttribs Then
'only an attrib that wasn't set and that is now set generates an injection
'except for the reset attribute, handled separately
For k = 0 To ATTRIB_BITS - 1
If iAttribs And maiAttribMask(k) Then 'if the attrib is set now
If Not (iLastAttribs And maiAttribMask(k)) Then 'and it wasn't set before
AddInjection i, masAttribInject(k)
End If
End If
Next k
iLastAttribs = iAttribs
End If
End If 'iAttribs>0
i = i + 1
Loop Until i > miCols
End Sub
Private Sub GenResetsInjections(ByVal piLine As Integer)
Dim i As Integer
Dim iAttribs As Integer
Dim fReset As Boolean
i = 1
Do
iAttribs = maiAttribs(piLine, i)
fReset = CBool(iAttribs And ATTRIB_RESET)
If fReset Then
AddInjection i, VT_RESET()
End If
i = i + 1
Loop Until i > miCols
End Sub
Private Function GetSortKey(ByVal piPos As Integer, ByVal plID As Long) As String
'Debug.Print Format$(piPos, "0000000000") & Format$(plID, "0000000000")
GetSortKey = Format$(piPos, "0000000000") & Format$(plID, "0000000000")
End Function
Private Sub SortInjectionsArray(ByVal lower As Integer, ByVal upper As Integer)
Dim pivot As TInjection
Dim Temp As TInjection
Dim first As Integer
Dim last As Integer
Dim middle As Integer
'Locate pivot
first = lower
last = upper
middle = (first + last) / 2
pivot = matInjection(middle)
Do 'Move pointers against each other
While GetSortKey(matInjection(first).iPos, matInjection(first).ID) < GetSortKey(pivot.iPos, pivot.ID)
first = first + 1
Wend
While GetSortKey(matInjection(last).iPos, matInjection(last).ID) > GetSortKey(pivot.iPos, pivot.ID)
last = last - 1
Wend
If first <= last Then
Temp = matInjection(first)
matInjection(first) = matInjection(last)
matInjection(last) = Temp
first = first + 1&
last = last - 1&
End If
Loop Until first > last
If lower < last Then
Call SortInjectionsArray(lower, last)
End If
If first < upper Then
Call SortInjectionsArray(first, upper)
End If
End Sub
Public Function GetLineVT100(ByVal piLine As Integer, _
Optional ByVal pfAddBoundary As Boolean = True) As String
Dim i As Integer
Dim sLine As String
Dim iPos As Integer
Dim k As Integer
Dim sRender As String
Dim fInject As Boolean
Dim iLenLine As Integer
Dim sFragmentPre As String
Dim iFragmentSepLen As Integer
Dim sFragmentSep As String
Dim iLenInjects As Integer
Dim iBufLen As Integer
Dim iPoke As Integer
Dim iInjectLen As Integer
Dim iLenNOOP As Integer
Dim sTemp As String
On Error GoTo GetLineVT100_Err
If moVT100Cache.IsCached(piLine) Then
GetLineVT100 = moVT100Cache.GetLine(piLine)
Exit Function
End If
sFragmentPre = VTX_SETWIDTH(miFragmentCharWidth) & VTX_DTFLAGS(DT_SINGLELINE Or DT_NOPREFIX Or DT_CENTER)
sFragmentSep = VT_NOOP()
iFragmentSepLen = Len(sFragmentSep)
sLine = masChars(piLine) 'we start with row text
InitInjectionsArray
GenResetsInjections piLine
GenForeColInjections piLine
GenBackColInjections piLine
GenAttribsInjections piLine
If miInjectionCount > 1 Then
SortInjectionsArray 1, miInjectionCount
End If
'V02.00.06 optimisation, build srender with spaces then poke with mid$
'Get the total length of injections
If miInjectionCount > 0 Then
For i = 1 To miInjectionCount
iLenInjects = iLenInjects + Len(matInjection(i).sInject)
Next i
End If
iLenLine = Len(sLine)
iBufLen = Len(sLine) + iLenInjects
If mfFragmentText Then
iBufLen = iBufLen + (iLenLine - 1) * iFragmentSepLen
End If
sRender = Space$(iBufLen)
iPoke = 1
k = 1 'index in injections array
For i = 1 To iLenLine
fInject = False
If k <= miInjectionCount Then
If i = matInjection(k).iPos Then
fInject = True
End If
End If
If fInject Then
Do While fInject
'sRender = sRender & matInjection(k).sInject
iInjectLen = Len(matInjection(k).sInject)
Mid$(sRender, iPoke, iInjectLen) = matInjection(k).sInject
iPoke = iPoke + iInjectLen
If (k + 1) > miInjectionCount Then Exit Do
k = k + 1
fInject = CBool(matInjection(k).iPos = i)
Loop
End If
'sRender = sRender & Mid$(sLine, i, 1)
Mid$(sRender, iPoke, 1) = Mid$(sLine, i, 1)
iPoke = iPoke + 1
If mfFragmentText Then
If i < iLenLine Then
Mid$(sRender, iPoke, iFragmentSepLen) = sFragmentSep
iPoke = iPoke + iFragmentSepLen
End If
End If
Next i
'remaining injections after the last char
If k < miInjectionCount Then
For i = k To miInjectionCount
sRender = sRender & matInjection(k).sInject
Next i
End If
'boundary indicator
If pfAddBoundary Then
sRender = sRender & VT_RESET() & VTX_SETWIDTH(0) & "¦"
End If
If mfFragmentText Then
sRender = sFragmentPre & sRender
End If
GetLineVT100_Exit:
GetLineVT100 = sRender
Exit Function
GetLineVT100_Err:
SetErr "GetLineVT100", Err.Number, Err.Description
Resume GetLineVT100_Exit
End Function
Public Sub ClearChar(ByVal piRow As Integer, ByVal piCol As Integer, Optional ByVal pfClearAttribs As Boolean = True, Optional ByVal pfClearColors As Boolean = True)
If (piRow > 0) And (piRow <= miRows) And (piCol > 0) And (piCol <= miCols) Then
Mid$(masChars(piRow), piCol, 1) = " "
If pfClearColors Then
mabForeCol(piRow, piCol) = 0
mabBackCol(piRow, piCol) = 0
malForeCol(piRow, piCol) = 0
malBackCol(piRow, piCol) = 0
End If
If pfClearAttribs Then