Repository navigation
Expand file tree
/
Copy pathGradTextBox.ctl
More file actions
2057 lines (1731 loc) · 80.4 KB
/
Copy pathGradTextBox.ctl
File metadata and controls
2057 lines (1731 loc) · 80.4 KB
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 5.00
Begin VB.UserControl MorphTextBox
AutoRedraw = -1 'True
BackColor = &H00C0E0FF&
ClientHeight = 825
ClientLeft = 0
ClientTop = 0
ClientWidth = 1830
ScaleHeight = 55
ScaleMode = 3 'Pixel
ScaleWidth = 122
End
Attribute VB_Name = "MorphTextBox"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = True
Attribute VB_PredeclaredId = False
Attribute VB_Exposed = False
'Download by http://www.codefans.net
Option Explicit
Private Declare Function DeleteObject Lib "gdi32" (ByVal hObject As Long) As Long
Private Declare Function CreateRoundRectRgn Lib "gdi32.dll" (ByVal X1 As Long, ByVal Y1 As Long, ByVal X2 As Long, ByVal Y2 As Long, ByVal X3 As Long, ByVal Y3 As Long) As Long
Private Declare Function FillRgn Lib "gdi32.dll" (ByVal hdc As Long, ByVal hRgn As Long, ByVal hBrush As Long) As Long
Private Declare Function CreateSolidBrush Lib "gdi32.dll" (ByVal crColor As Long) As Long
Private Declare Function CombineRgn Lib "gdi32.dll" (ByVal hDestRgn As Long, ByVal hSrcRgn1 As Long, ByVal hSrcRgn2 As Long, ByVal nCombineMode As Long) As Long
Private Declare Function SetWindowRgn Lib "user32" (ByVal hWnd As Long, ByVal hRgn As Long, ByVal bRedraw As Long) As Long
Private Declare Function CreatePen Lib "gdi32" (ByVal nPenStyle As Long, ByVal nWidth As Long, ByVal crColor As Long) As Long
Private Declare Function LineTo Lib "gdi32" (ByVal hdc As Long, ByVal X As Long, ByVal Y As Long) As Long
Private Declare Function MoveTo Lib "gdi32" Alias "MoveToEx" (ByVal hdc As Long, ByVal X As Long, ByVal Y As Long, lpPoint As Any) As Long
Private Declare Function SelectObject Lib "gdi32" (ByVal hdc As Long, ByVal hObject As Long) As Long
Private Declare Function OleTranslateColor Lib "olepro32.dll" (ByVal OLE_COLOR As Long, ByVal hPalette As Long, pccolorref As Long) As Long
Private Declare Function DrawText Lib "user32.dll" Alias "DrawTextA" (ByVal hdc As Long, ByVal lpStr As String, ByVal nCount As Long, ByRef lpRect As RECT, ByVal wFormat As Long) As Long
' declares for Carles P.V.'s gradient paint routine.
Private Type BITMAPINFOHEADER
biSize As Long
biWidth As Long
biHeight As Long
biPlanes As Integer
biBitCount As Integer
biCompression As Long
biSizeImage As Long
biXPelsPerMeter As Long
biYPelsPerMeter As Long
biClrUsed As Long
biClrImportant As Long
End Type
Private Declare Function StretchDIBits Lib "gdi32" (ByVal hdc As Long, ByVal X As Long, ByVal Y As Long, ByVal dx As Long, ByVal dy As Long, ByVal SrcX As Long, ByVal SrcY As Long, ByVal wSrcWidth As Long, ByVal wSrcHeight As Long, lpBits As Any, lpBitsInfo As Any, ByVal wUsage As Long, ByVal dwRop As Long) As Long
Private Const DIB_RGB_COLORS As Long = 0
Private Const PI As Single = 3.14159265358979
Private Const TO_DEG As Single = 180 / PI
Private Const TO_RAD As Single = PI / 180
Private Const INT_ROT As Long = 1000
' rectangle structure for API drawing of text onto control.
Private Type RECT
Left As Long
Top As Long
Right As Long
Bottom As Long
End Type
' structure that will hold the display range of the text in the textbox.
Private Type TextRange
FirstCharacter As Long
LastCharacter As Long
End Type
Private DisplayRange As TextRange ' overall text display range.
Private SelectedRange As TextRange ' selected text display range.
' default property values
Private Const m_def_SelText = ""
Private Const m_def_SelStart = 0
Private Const m_def_SelLength = 0
Private Const m_def_SelColor1 = &H7000
Private Const m_def_SelColor2 = &H50FF40
Private Const m_def_PasswordChar = ""
Private Const m_def_MaxLength = 256
Private Const m_def_Locked = False
Private Const m_def_SideScroll = 5
Private Const m_def_DisabledColor1 = &H808080
Private Const m_def_DisabledColor2 = &HC0C0C0
Private Const m_def_DisabledMiddleOut = True
Private Const m_def_DisabledAngle = 90
Private Const m_def_DisabledTextColor = &H808080
Private Const m_def_DisabledBorderColor = &H808080
Private Const m_def_DisabledBorderWidth = 1
Private Const m_def_CursorColor = &H0
Private Const m_def_Enabled = True
Private Const m_def_Text = ""
Private Const m_def_DefaultTextColor = &H0
Private Const m_def_DefaultColor1 = &H7F90
Private Const m_def_DefaultColor2 = &H60F0FF
Private Const m_def_DefaultMiddleOut = True
Private Const m_def_DefaultAngle = 90
Private Const m_def_DefaultBorderColor = &H0
Private Const m_def_DefaultBorderWidth = 1
Private Const m_def_FocusColor1 = &H907000
Private Const m_def_FocusColor2 = &HFFEF1F
Private Const m_def_FocusMiddleOut = True
Private Const m_def_FocusAngle = 90
Private Const m_def_FocusTextColor = &H0
Private Const m_def_FocusBorderColor = &H0
Private Const m_def_FocusBorderWidth = 2
' property variables
Private m_SelText As String ' the selected text.
Private m_SelStart As Long ' the start character position of the selected text.
Private m_SelLength As Long ' the length of the selected text.
Private m_SelColor1 As OLE_COLOR ' selected text first gradient color.
Private m_SelColor2 As OLE_COLOR ' selected text second gradient color.
Private m_Picture As Picture ' if set, supercedes regular gradient background.
Private m_PasswordChar As String ' when set, all typed chars appear as this character.
Private m_MaxLength As Long ' when 0, no limit to number of characters in textbox.
Private m_Locked As Boolean ' when true, text cannot be changed.
Private m_SideScroll As Long ' # of chars to scroll when cursor passes end of textbox display.
Private m_DisabledColor1 As OLE_COLOR ' first gradient color when textbox is disabled.
Private m_DisabledColor2 As OLE_COLOR ' second gradient color when textbox is disabled.
Private m_DisabledMiddleOut As Boolean ' middle-out gradient display flag when textbox is disabled.
Private m_DisabledAngle As Single ' gradient angle when textbox is disabled.
Private m_DisabledTextColor As OLE_COLOR ' text color when textbox is disabled.
Private m_DisabledBorderColor As OLE_COLOR ' border color when textbox is disabled.
Private m_DisabledBorderWidth As Integer ' border width when textbox is disabled.
Private m_CursorColor As OLE_COLOR ' cursor color (contrast with focus gradient and text colors).
Private m_Enabled As Boolean ' control enabled flag.
Private m_Font As Font ' the font to write the text with.
Private m_Text As String ' the text contents of the control.
Private m_DefaultTextColor As OLE_COLOR ' text color when textbox is enabled, no focus.
Private m_DefaultColor1 As OLE_COLOR ' first gradient color when textbox is enabled, no focus.
Private m_DefaultColor2 As OLE_COLOR ' second gradient color when textbox is enabled, no focus.
Private m_DefaultMiddleOut As Boolean ' middle-out gradient flag when textbox is enabled, no focus.
Private m_DefaultAngle As Single ' gradient angle when textbox is enabled, no focus.
Private m_DefaultBorderColor As OLE_COLOR ' border color when textbox is enabled, no focus.
Private m_DefaultBorderWidth As Integer ' border width when textbox is enabled, no focus.
Private m_FocusColor1 As OLE_COLOR ' first gradient color when textbox is enabled, has focus.
Private m_FocusColor2 As OLE_COLOR ' second gradient color when textbox is enabled, has focus.
Private m_FocusMiddleOut As Boolean ' middle-out gradient flag when textbox is enabled, has focus.
Private m_FocusAngle As Single ' gradient angle when textbox is enabled, has focus.
Private m_FocusTextColor As OLE_COLOR ' text color when textbox is enabled, has focus.
Private m_FocusBorderColor As OLE_COLOR ' border color when textbox is enabled, has focus.
Private m_FocusBorderWidth As Integer ' border width when textbox is enabled, has focus.
' event declarations
Event DblClick()
Attribute DblClick.VB_Description = "Occurs when the user presses and releases a mouse button and then presses and releases it again over an object."
Event Click()
Attribute Click.VB_Description = "Occurs when the user presses and then releases a mouse button over an object."
Event KeyDown(KeyCode As Integer, Shift As Integer)
Attribute KeyDown.VB_Description = "Occurs when the user presses a key while an object has the focus."
Event KeyPress(KeyAscii As Integer)
Attribute KeyPress.VB_Description = "Occurs when the user presses and releases an ANSI key."
Event KeyUp(KeyCode As Integer, Shift As Integer)
Attribute KeyUp.VB_Description = "Occurs when the user releases a key while an object has the focus."
Event MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single)
Attribute MouseDown.VB_Description = "Occurs when the user presses the mouse button while an object has the focus."
Event MouseMove(Button As Integer, Shift As Integer, X As Single, Y As Single)
Attribute MouseMove.VB_Description = "Occurs when the user moves the mouse."
Event MouseUp(Button As Integer, Shift As Integer, X As Single, Y As Single)
Attribute MouseUp.VB_Description = "Occurs when the user releases the mouse button while an object has the focus."
' miscellaneous control variables
Private Const BACKSPACE As Integer = 8 ' editing key.
Private HasFocus As Boolean ' reflects focus status of textbox.
Private CursorPos As Long ' character position of the cursor line.
Private MouseIsDown As Boolean ' for selecting text
Private CharMap() As Long ' holds the X coordinate of left edge of each char in textbox
Private WordMap() As Long ' byte position of the first character in each word of text.
Private WordCount As Long ' the number of separate words in the text.
Private Clearance As Long ' distance from left edge of textbox to start drawing text.
Private SelectModeActive As Boolean ' lets control know text is being selected (shift key down).
Private CharactersMapped As Boolean ' lets control know positions of each character are known.
'<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
'<<<<<<<<<<<<<<<<<<<<<<<<<<< Event-Handling Routines >>>>>>>>>>>>>>>>>>>>>>>>>>>>
'<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
Private Sub UserControl_Initialize()
'*************************************************************************
'* The first event in the textbox life cycle. *
'*************************************************************************
CursorPos = 1
DisplayRange.FirstCharacter = 1
SelectModeActive = False
End Sub
Private Sub UserControl_Show()
'*************************************************************************
'* sets up the text mapping and displays text for the first time. *
'*************************************************************************
ReDim WordMap(1 To m_MaxLength)
RedrawControl
MapCharacters DisplayRange
End Sub
Private Sub UserControl_KeyDown(KeyCode As Integer, Shift As Integer)
'*************************************************************************
'* handles cursor movement and text selection. *
'*************************************************************************
Dim ShiftDown As Boolean ' shift status flag.
Dim CtrlDown As Boolean ' ctrl key status flag.
Dim i As Long ' loop variable.
Dim TextLen As Long ' for optimizing.
Dim sTemp As String ' holds either text or len(text) worth of password char.
' determine shift key status. This lets us know if user is selecting text.
ShiftDown = (Shift And vbShiftMask) > 0
If ShiftDown Then
If KeyCode = vbKeyLeft Or KeyCode = vbKeyUp Or KeyCode = vbKeyRight Or _
KeyCode = vbKeyDown Or KeyCode = vbKeyHome Or KeyCode = vbKeyEnd Then
If SelectModeActive Then
' .LastCharacter can actually be less than .FirstCharacter. That's why
' you'll see me swapping the two values in certain places in this code.
SelectedRange.LastCharacter = CursorPos - 1
End If
If Not SelectModeActive Then
SelectModeActive = True
SelectedRange.FirstCharacter = CursorPos
End If
End If
Else
' set SelectMode Active to False, but keep any SelectedRange values. That way when we
' press delete, backspace, or an alphanumeric, appropriate actions will still take place.
SelectModeActive = False
End If
' determine ctrl key status.
CtrlDown = (Shift And vbCtrlMask) > 0
Select Case KeyCode
Case vbKeyLeft, vbKeyUp ' left or up arrow key.
' this helps emulate vb textbox functionality - when cursor is at beginning
' of text and a non-shifted arrow key is pressed, selection mode turns off.
If CursorPos = 1 And Not ShiftDown Then
SelectModeActive = False
RedrawControl
Exit Sub
End If
If CursorPos > 1 Then
' if control key is not down deal with text one character at a time.
If Not CtrlDown Then
CursorPos = CursorPos - 1
If SelectModeActive Then
SelectedRange.LastCharacter = CursorPos
End If
' if the cursor goes left past the clearance, we must scroll the
' text left by the number of characters in the SideScroll property.
If CharMap(CursorPos) < Clearance Then
DisplayRange.FirstCharacter = DisplayRange.FirstCharacter - m_SideScroll
' a necessary safety net.
If DisplayRange.FirstCharacter < 1 Then
DisplayRange.FirstCharacter = 1
End If
End If
MapCharacters DisplayRange
RedrawControl
Else
' if ctrl key is down, move cursor to beginning of previous word (or beginning of text).
' for password character mode, just move to beginning of text. That way, the
' individual word segments in the text are concealed.
If m_PasswordChar <> "" Then
CursorPos = 1
' check for shift pressed as well as ctrl key.
If SelectModeActive Then
SelectedRange.LastCharacter = 1
End If
CharactersMapped = False
DisplayRange.FirstCharacter = 1
RedrawControl
Else
' otherwise go to beginning of previous word or beginning of text.
For i = WordCount To 1 Step -1
If CursorPos > WordMap(i) Then
CursorPos = WordMap(i)
Exit For
End If
Next i
' this is for when both Ctrl and Shift keys are down while pressing Left (or Up) arrow.
If SelectModeActive Then
SelectedRange.LastCharacter = CursorPos
End If
' if the cursor went past the left edge of the textbox,
' re-range the text from the cursor position.
If CharMap(CursorPos) < Clearance Then
CharactersMapped = False
DisplayRange.FirstCharacter = CursorPos
End If
RedrawControl
End If
End If
End If
Case vbKeyRight, vbKeyDown ' right or down arrow key.
' this helps emulate vb textbox functionality - when cursor is at end
' of text and a non-shifted arrow key is pressed, selection mode turns off.
If CursorPos = Len(m_Text) + 1 And Not ShiftDown Then
SelectModeActive = False
RedrawControl
Exit Sub
End If
If CursorPos <= Len(m_Text) Then
' if control key is not down deal with text one character at a time.
If Not CtrlDown Then
CursorPos = CursorPos + 1
' if selection mode is on (shift is down), adjust the selected range.
If SelectModeActive Then
SelectedRange.LastCharacter = CursorPos
End If
' if the cursor goes right past the last displayed character, we must scroll the
' text right by the number of characters in the SideScroll property.
If CharMap(CursorPos) > (ScaleWidth - m_FocusBorderWidth - 1) Then
CharactersMapped = False
DisplayRange.FirstCharacter = DisplayRange.FirstCharacter + m_SideScroll
End If
RedrawControl
Else
' if ctrl key is down, move cursor to start of next word (or end of text).
' for password character mode, just move to end of text. That way the
' individual word segments in the text are concealed.
If m_PasswordChar <> "" Then
CursorPos = Len(m_Text) + 1
' check for shift pressed as well as ctrl key.
If SelectModeActive Then
SelectedRange.LastCharacter = CursorPos
End If
CharactersMapped = False
' must map password chars, not the actual text, for display.
SetTextDisplayRangeRev String(Len(m_Text), Left(m_PasswordChar, 1)), CursorPos
RedrawControl
Else
' otherwise, move cursor to beginning of next word (or end of text).
For i = 1 To WordCount
If CursorPos < WordMap(i) Then
CursorPos = WordMap(i)
Exit For
End If
Next i
' this is for when both Ctrl and Shift keys are down while pressing right (or down) arrow.
If SelectModeActive Then
SelectedRange.LastCharacter = CursorPos
End If
' if the cursor went past the edge of the textbox,
' re-range the text from the cursor position backwards.
If CharMap(CursorPos) > (ScaleWidth - m_FocusBorderWidth - 1) Then
SetTextDisplayRangeRev m_Text, CursorPos
CharactersMapped = False
End If
RedrawControl
End If
End If
End If
Case vbKeyHome ' home key.
' this helps emulate vb textbox functionality - if already at first
' character position and text is selected, remove selection.
If CursorPos = 1 And Not ShiftDown Then
SelectModeActive = False
RedrawControl
Exit Sub
End If
If CursorPos <> 1 Then
' if we are selecting text, we highlight from the beginning to the cursor position.
If SelectModeActive Then
' this will be swapped in the selection gradient paint routine.
' doing it this way helps me emulate vb textbox behavior more accurately.
SelectedRange.LastCharacter = 1
End If
CursorPos = 1
DisplayRange.FirstCharacter = 1
MapCharacters DisplayRange
RedrawControl
End If
Case vbKeyEnd ' end key.
' if cursor is already at end of control, exit.
If CursorPos = Len(m_Text) + 1 Then
' takes care of when text is selected and cursor's at end of text.
If Not ShiftDown Then
SelectModeActive = False
RedrawControl
End If
Exit Sub
End If
' for textwidth calculating purposes.
If m_PasswordChar = "" Then
sTemp = m_Text
Else
sTemp = String(Len(m_Text), Left(m_PasswordChar, 1))
End If
If SelectModeActive Then
' SelectedRange.FirstCharacter was already set when we pressed 'Shift'.
SelectedRange.LastCharacter = Len(m_Text) + 1
End If
' if the whole text fits in control, just place cursor at end.
If TextWidth(sTemp) < ScaleWidth - Clearance - 2 Then
If CursorPos <> Len(m_Text) + 1 Then
CursorPos = Len(m_Text) + 1
RedrawControl
End If
Else
' otherwise, find the last 'n' characters that fill the control and place cursor at end.
SetTextDisplayRangeRev sTemp, Len(m_Text)
CursorPos = Len(m_Text) + 1
' if the cursor goes right past the last displayed character, we must scroll the
' text right by the number of characters in the SideScroll property.
If CharMap(CursorPos) > (ScaleWidth - m_FocusBorderWidth - 1) Then
DisplayRange.FirstCharacter = DisplayRange.FirstCharacter + m_SideScroll
End If
CharactersMapped = False
RedrawControl
End If
Case vbKeyDelete ' delete key.
If Not m_Locked Then
TextLen = Len(m_Text)
If TextLen > 0 And CursorPos <= TextLen + 1 Then
' if there is a selection made, we delete that.
If SelectedRange.FirstCharacter > 0 Then ' that's enough to determine a selection exists.
DeleteSelection
Else
' otherwise we delete just the character at the cursor position.
If CursorPos <= TextLen Then
DeleteCharacterAtCursorPosition
End If
End If
RedrawControl
MapCharacters DisplayRange
End If
End If
Case BACKSPACE ' backspace key.
If Not m_Locked Then
If Len(m_Text) > 0 And CursorPos >= 1 Then
' delete selection if text has been selected.
If Len(m_SelText) > 0 Then
DeleteSelection
Else
' otherwise, treat as a regular backspace.
If CursorPos > 1 Then
DeleteCharacterAtPreviousCursorPosition
End If
End If
If Len(m_Text) = 0 Then
CursorPos = 1
End If
RedrawControl
MapCharacters DisplayRange
End If
End If
End Select
RaiseEvent KeyDown(KeyCode, Shift)
End Sub
Private Sub UserControl_KeyPress(KeyAscii As Integer)
'*************************************************************************
'* process regular alphanumeric keystrokes into the textbox. *
'*************************************************************************
' if invalid character, or textbox is locked, pass along the event and exit.
If KeyAscii < 32 Or m_Locked Then
RaiseEvent KeyPress(KeyAscii)
Exit Sub
End If
' if the text is equal to the MaxLength property value, exit.
If Len(m_Text) = m_MaxLength Then
Exit Sub
End If
' if any text is selected, delete selection and replace that text with the character typed.
If Len(m_SelText) > 0 Then
DeleteSelection
End If
' insert the alphanumeric character at the cursor position and move the cursor right.
m_Text = Left(m_Text, CursorPos - 1) & Chr(KeyAscii) & Right(m_Text, Len(m_Text) - CursorPos + 1)
CursorPos = CursorPos + 1
' map the character positions now that the text has been altered.
MapCharacters DisplayRange
' if the typing goes past the right edge, we must scroll the text to the left
' by a couple of characters to help mimic intrinsic vb textbox behavior.
If CharMap(CursorPos) > (ScaleWidth - m_FocusBorderWidth - 1) Then
CharactersMapped = False
DisplayRange.FirstCharacter = DisplayRange.FirstCharacter + 3
End If
RedrawControl
DoEvents
RaiseEvent KeyPress(KeyAscii)
End Sub
Private Sub UserControl_KeyUp(KeyCode As Integer, Shift As Integer)
'*************************************************************************
'* not used, but the event is passed on to the programmer. *
'*************************************************************************
RaiseEvent KeyUp(KeyCode, Shift)
End Sub
Private Sub UserControl_GotFocus()
'*************************************************************************
'* performs any necessary actions when the textbox receives the focus. *
'*************************************************************************
HasFocus = True
RedrawControl
End Sub
Private Sub UserControl_LostFocus()
'*************************************************************************
'* sets focus flag, sets selected text to null, and draws box as default.*
'*************************************************************************
HasFocus = False
' turn off selection mode.
SelectModeActive = False
SelectedRange.FirstCharacter = 0
SelectedRange.LastCharacter = 0
RedrawControl
End Sub
Private Sub UserControl_DblClick()
'*************************************************************************
'* when a word is double-clicked, it is selected. When a space is *
'* double-clicked, all the way to beginning of previous word is selected.*
'*************************************************************************
Dim i As Long ' loop variable.
Dim SpaceFound As Boolean ' flag indicating a space has been encountered.
Dim NonSpaceFound As Boolean ' flag indicating a non-space has been encountered.
' if the character x-position map needs updating (if text typed or deleted, for example), do it.
If Not CharactersMapped Then
MapCharacters DisplayRange
End If
SelectModeActive = True
' for password character mode, just select all text. That way,
' the individual word segments in the text are concealed.
If m_PasswordChar <> "" Then
SelectedRange.FirstCharacter = 1
SelectedRange.LastCharacter = Len(m_Text) + 1
RedrawControl
RaiseEvent DblClick
Exit Sub
End If
' for when a non-space character is clicked.
If Mid(m_Text, CursorPos, 1) <> " " Then
' find the beginning of the word.
For i = CursorPos - 1 To 1 Step -1
If Mid(m_Text, i, 1) = " " Then
SpaceFound = True
SelectedRange.FirstCharacter = i + 1 ' the start of the word.
Exit For
End If
Next i
' if there was no space, it's the first word.
If Not SpaceFound Then
SelectedRange.FirstCharacter = 1
End If
' find the end of the word.
SpaceFound = False
For i = CursorPos + 1 To Len(m_Text)
If Mid(m_Text, i, 1) = " " Then
SpaceFound = True
SelectedRange.LastCharacter = i
Exit For
End If
Next i
' if there was no space, it's the last word.
If Not SpaceFound Then
SelectedRange.LastCharacter = Len(m_Text) + 1
End If
Else
' a space was clicked. loop back to beginning of previous word.
' find the first non-space character, this is the end of the previous word.
If CursorPos > 1 Then
i = CursorPos - 1
Else
i = 1
End If
If Mid(m_Text, i, 1) <> " " Then
NonSpaceFound = True
SelectedRange.LastCharacter = i + 1
Else
While Mid(m_Text, i, 1) = " " And (i > 1)
i = i - 1
If Mid(m_Text, i, 1) <> " " Then
NonSpaceFound = True
SelectedRange.LastCharacter = i + 1
End If
Wend
End If
' if a non-space character was found, find the beginning of the word.
If NonSpaceFound Then
For i = SelectedRange.LastCharacter - 1 To 1 Step -1
If Mid(m_Text, i, 1) = " " Then
SpaceFound = True
SelectedRange.FirstCharacter = i + 1
Exit For
End If
Next i
' if a space character was not found, we've reached the beginning of the text.
If Not SpaceFound Then
SelectedRange.FirstCharacter = 1
End If
End If
End If
RedrawControl
' raise the DblClick event for the rest of the project.
RaiseEvent DblClick
End Sub
Private Sub UserControl_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single)
'*************************************************************************
'* sets mousedown flag and places cursor in appropriate position. *
'* Cursor placement mimics the intrinsic vb textbox method. *
'*************************************************************************
Dim i As Long ' loop index.
Dim CharMidPoint As Long ' the middle point of the clicked character.
' allow user of control to process a right-click in their own way (pop a menu, etc.).
If Button = vbRightButton Then
RaiseEvent MouseDown(Button, Shift, X, Y)
Exit Sub
End If
' set the mousedown flag.
MouseIsDown = True
' determine where to place the cursor.
' step 1. if the character x-position map needs updating (if text typed or deleted, for example), do it.
If Not CharactersMapped Then
MapCharacters DisplayRange
End If
' step 2. Place the cursor.
' 2a. special case - if text is empty, place cursor at end.
If Len(m_Text) = 0 Then
SetMouseDownShiftStatus Shift
CursorPos = 1
RedrawControl
RaiseEvent MouseDown(Button, Shift, X, Y)
Exit Sub
End If
' 2b. special case - if the mouse is clicked after the last character, place cursor at end.
If CharMap(Len(m_Text)) < X Then
SetMouseDownShiftStatus Shift
CursorPos = Len(m_Text) + 1
SelectedRange.LastCharacter = CursorPos
RedrawControl
RaiseEvent MouseDown(Button, Shift, X, Y)
Exit Sub
End If
' 2c. special case - if the mouse is clicked before the first character, place cursor at beginning.
If CharMap(1) > X Then
SetMouseDownShiftStatus Shift
CursorPos = 1
SelectedRange.LastCharacter = CursorPos
RedrawControl
RaiseEvent MouseDown(Button, Shift, X, Y)
Exit Sub
End If
' 2d. loop through the map and find out upon which character the mouse was clicked.
For i = 1 To Len(m_Text) - 1
If (X >= CharMap(i)) And (X < CharMap(i + 1)) Then
SetMouseDownShiftStatus Shift
CursorPos = i
SelectedRange.LastCharacter = CursorPos
Exit For
End If
Next i
' 2e. now that we know the character, we need to know where in the character the mouse
' was clicked. If the mouse was clicked in the left half of the character, that
' character gets the cursor. If the mouse was clicked in the right half of the character,
' the character to the right gets the cursor. (just like the vb textbox.)
CharMidPoint = (CharMap(CursorPos) + CharMap(CursorPos + 1)) / 2
If X > CharMidPoint Then
SetMouseDownShiftStatus Shift
CursorPos = CursorPos + 1
SelectedRange.LastCharacter = CursorPos
End If
RedrawControl
' raise the MouseDown event for the rest of the project.
RaiseEvent MouseDown(Button, Shift, X, Y)
End Sub
Private Sub UserControl_MouseMove(Button As Integer, Shift As Integer, X As Single, Y As Single)
'*************************************************************************
'* handles mouse drag selection of text. *
'************************************************************************
Dim i As Long ' loop variable.
' set the cursor to the familiar textbox i-beam.
UserControl.MousePointer = vbIbeam
' if there's no text, just pass along the event and exit.
If Len(m_Text) < 1 Then
RaiseEvent MouseMove(Button, Shift, X, Y)
Exit Sub
End If
' if the mouse button is down, then initiate or continue drag selection.
If MouseIsDown Then
If Not SelectModeActive Then
SelectModeActive = True
SelectedRange.FirstCharacter = CursorPos
Else
' need to find the character the cursor is over.
' cursor moved before first displayed character.
If X < Clearance And CursorPos > 1 Then
CharactersMapped = False
DisplayRange.FirstCharacter = DisplayRange.FirstCharacter - m_SideScroll
' a necessary safety net.
If DisplayRange.FirstCharacter < 1 Then
DisplayRange.FirstCharacter = 1
End If
CursorPos = DisplayRange.FirstCharacter
ElseIf X < Clearance Then
SelectedRange.LastCharacter = 1
ElseIf CharMap(CursorPos) > (ScaleWidth - m_FocusBorderWidth - 1) Then
' if the mouse goes past the right edge, we must scroll the text to the left
' by a couple of characters to help mimic intrinsic vb textbox behavior.
CharactersMapped = False
DisplayRange.FirstCharacter = DisplayRange.FirstCharacter + 3
ElseIf CharMap(Len(m_Text)) < X Then
' cursor moved past last character.
SelectedRange.LastCharacter = Len(m_Text) + 1
Else
' cursor is somewhere in the middle of the text.
For i = 1 To Len(m_Text) + 1
If (X >= CharMap(i)) And (X < CharMap(i + 1)) Then
CursorPos = i
SelectedRange.LastCharacter = CursorPos
Exit For
End If
Next i
End If
' cursor moved after last character.
If CharMap(Len(m_Text)) < X Then
CursorPos = Len(m_Text) + 1
End If
' if text display range has changed, remap displayed text.
If Not CharactersMapped Then
MapCharacters DisplayRange
End If
RedrawControl
' necessary for on-the-fly MouseMove textbox redraw.
UserControl.Refresh
End If
End If
' raise the event for the rest of the project.
RaiseEvent MouseMove(Button, Shift, X, Y)
End Sub
Private Sub UserControl_MouseUp(Button As Integer, Shift As Integer, X As Single, Y As Single)
'*************************************************************************
'* handles actions when mouse is "unclicked". *
'*************************************************************************
' if it's the right button, just pass the event along to the user and exit. The
' vb textbox would pop the popup menu at this point, but I ain't gonna do that.
If Button = vbRightButton Then
RaiseEvent MouseUp(Button, Shift, X, Y)
Exit Sub
End If
MouseIsDown = False
' raise appropriate events for user. Click event order is MouseDown, MouseUp, Click.
RaiseEvent MouseUp(Button, Shift, X, Y)
RaiseEvent Click
End Sub
Private Sub UserControl_Resize()
'*************************************************************************
'* just for resizing purposes in design mode. No need to raise event. *
'*************************************************************************
RedrawControl
End Sub
'<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
'<<<<<<<<<<<<<<<<<<<<<<< String Manipulation Routines >>>>>>>>>>>>>>>>>>>>>>>>>>>
'<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
Private Sub SetMouseDownShiftStatus(ByVal ShiftStatus As Integer)
'*************************************************************************
'* this is utilized by the MouseDown event handling routine. I made it *
'* separate since this is used in several MouseDown code points. *
'*************************************************************************
Dim ShiftDown As Boolean
' determine shift key status. This lets us know if user is selecting text.
ShiftDown = (ShiftStatus And vbShiftMask) > 0
If ShiftDown Then
If SelectModeActive Then
' if shift is down when mouse clicked, existing selection is extended to new cursor
' position, or a new selection is made from beginning of text to new cursor position.
SelectedRange.LastCharacter = CursorPos
End If
If Not SelectModeActive Then
SelectModeActive = True
SelectedRange.FirstCharacter = CursorPos
End If
Else
' set SelectMode Active to false, and also clear SelectedRange values. This is
' because a mouse click with no shift causes selection highlight to disappear.
SelectModeActive = False
SelectedRange.FirstCharacter = 0
SelectedRange.LastCharacter = 0
End If
End Sub
Private Sub SetTextDisplayRange(TextToRange As String)
'*************************************************************************
'* determines the first and last characters to display based on textbox *
'* width and accumulated text widths. *
'*************************************************************************
Dim i As Long ' loop variable.
Dim TextLen As Long ' holds length of text; used for speed optimizing purposes.
Dim SWLessBW As Long ' scalewidth - borderwidth; for righthand spacing purposes.
SWLessBW = ScaleWidth - m_FocusBorderWidth ' get the right-side clearance.
' time saver - if the whole string can fit in the textbox, bypass the loop and exit.
If TextWidth(TextToRange) < (SWLessBW - Clearance) Then
DisplayRange.LastCharacter = Len(TextToRange)
Exit Sub
End If
TextLen = Len(TextToRange) ' get total text length.
If Not CharactersMapped Then
MapCharacters DisplayRange
End If
' start accumulating character widths, stopping when xWidth
' exceeds the textbox scalewidth or we run out of characters.
For i = DisplayRange.FirstCharacter To TextLen
If CharMap(i) + TextWidth(Mid$(TextToRange, i, 1)) > SWLessBW Then
' since the character starts in textbox view, but exceeds the scalewidth
' less the border width, use the previous character as the last to display.
DisplayRange.LastCharacter = i - 1
Exit Sub
End If
Next i
' just a safety net - if we get here, the remainder of the string will fit in the box.
DisplayRange.LastCharacter = Len(TextToRange)
End Sub
Private Sub SetTextDisplayRangeRev(TextToRange As String, StartPos As Long)
'*************************************************************************
'* determines the first and last characters to display based on textbox *
'* width and accumulated text widths from the end of the text backwards. *
'* This is used only for 'End' key functionality. *
'*************************************************************************
Dim i As Long ' loop variable.
Dim xWidth As Long ' running accumulation of text character widths.
Dim TextLen As Long ' holds length of text; used for speed optimizing purposes.
Dim DisplayableArea As Long ' the width of the displayable area.
' get the displayable area, with a 1-letter right clearance.
DisplayableArea = ScaleWidth - m_FocusBorderWidth - Clearance - TextWidth("n")
TextLen = Len(TextToRange) ' get total text length.
xWidth = 0
' start accumulating character widths, stopping when xWidth
' exceeds the textbox scalewidth or we run out of characters.
For i = StartPos To 1 Step -1
xWidth = xWidth + TextWidth(Mid(TextToRange, i, 1))
If xWidth > DisplayableArea Then
DisplayRange.FirstCharacter = i + 1
DisplayRange.LastCharacter = Len(TextToRange)
Exit Sub
End If
Next i
End Sub
Private Sub MapCharacters(DispRange As TextRange)
'*************************************************************************
'* maps the X pixel position of the leftmost edge of each character, *
'* from the first displayed character to the end of the text string. *
'* these also are the cursor X positions. *
'*************************************************************************
Dim i As Long ' loop variable.
Dim X As Long ' holds accumulating x character positions.
Dim TextToMap As String ' holds either actual text or password char string.
Dim SpaceFound As Boolean ' flag for use during the word count routine.
' if there's no text, there's not much point in continuing.
If Len(m_Text) = 0 Then
' we do this even if the textbox has no text so that the cursor can be drawn.
ReDim CharMap(1 To 2)
CharMap(1) = Clearance
Exit Sub
End If
' sanity check.
If DisplayRange.FirstCharacter < 1 Then
DisplayRange.FirstCharacter = 1
End If
' resize the character X-address array to the length of the text.
ReDim CharMap(1 To Len(m_Text) + 2)
' get the start character position of each word. Used for ctrl-left and ctrl-right arrow cursor jumping.
' make sure the first entry is the first position so cursor can move to beginning of text.
WordCount = 1
WordMap(WordCount) = 1
SpaceFound = True
' loop through the text, adding the start byte position of each word in the text.
For i = 1 To Len(m_Text)
If Mid(m_Text, i, 1) = " " Then
SpaceFound = True
Else
If Mid(m_Text, i, 1) <> " " Then
If SpaceFound Then
' start of new word encountered.
WordCount = WordCount + 1
WordMap(WordCount) = i
SpaceFound = False
End If
End If
End If
Next i
' if there are any words, add a last entry to allow cursor to move to end of text.
If WordCount > 0 Then
WordCount = WordCount + 1
WordMap(WordCount) = Len(m_Text) + 1
End If
' determine what text to map - the actual text or password characters.
If m_PasswordChar = "" Then
TextToMap = m_Text
Else
TextToMap = String(Len(m_Text), Left(m_PasswordChar, 1))
End If