need help adding ctrl + c, x, v

weeshen

Member
Joined
Oct 1, 2006
Messages
13
Programming Experience
Beginner
So I have written a really really basic word processor and am having trouble figuring out how to add the keys ctrl+c, ctrl+x, ctrl+v so that i can highlight text copy, cut, or paste when i want without using the buttons i have created. Please help. Here is my code.
VB.NET:
Option Strict On
Option Explicit On 

Imports System
Imports System.ComponentModel
Imports System.Drawing
Imports System.Windows.Forms
Imports System.Collections
Imports System.Text

Public Class StyledTextArea : Inherits UserControl

    Private view As View          ' the view in the MVC pattern
    Private lineNumberView As LineNumberView
    Private model As model  ' the model in the MVC pattern
    Private selecting As Boolean  ' user selecting text
    Private panel As New Panel()  ' we use panel so we can set the borderstyle
    Private hScrollBar As New HScrollBar()
    Private vScrollBar As New VScrollBar()
    Private Const hScrollBarHeight As Integer = 15
    Private Const vScrollBarWidth As Integer = 15
    Public LineNumberWidth As Integer = 50
    Public Event ColumnChanged As ColumnEventHandler
    Public Event LineChanged As LineEventHandler


    ' indicates that the key press has been processed by ProcessDialogKey, 
    ' so KeyPressed does not have to process this.
    Private keyProcessed As Boolean
    Private caretLocation As New ColumnLine(1, 1)
    ' indicates whether the text has been edited
    Private editedField As Boolean

    Friend selectionStartLocation As New ColumnLine(0, 0)
    Friend selectionEndLocation As New ColumnLine(0, 0)
    ' represents the number of lines not displayed 
    ' because the screen was scrolled down
    Friend TopInvisibleLineCount As Integer

    Public Sub New()
        InitializeComponents()
    End Sub

    Public Property Edited() As Boolean
        Get
            Return editedField
        End Get
        Set(ByVal value As Boolean)
            'allows it to be reset/set, 
            'for example if the document using this control is saved
            editedField = value
        End Set
    End Property

    Public Property CaretColor() As Color
        Get
            Return view.CaretColor
        End Get
        Set(ByVal caretColor As Color)
            view.CaretColor = caretColor
        End Set
    End Property

    Public Property LineNumberForeColor() As Color
        Get
            Return lineNumberView.ForeColor
        End Get
        Set(ByVal color As Color)
            lineNumberView.ForeColor = color
        End Set
    End Property

    Public Property LineNumberBackColor() As Color
        Get
            Return lineNumberView.BackColor
        End Get
        Set(ByVal color As Color)
            lineNumberView.BackColor = color
        End Set
    End Property

    Public Property TextForeColor() As Color
        Get
            Return view.ForeColor
        End Get
        Set(ByVal color As Color)
            view.ForeColor = color
        End Set
    End Property

    Public Property TextBackColor() As Color
        Get
            Return view.BackColor
        End Get
        Set(ByVal color As Color)
            view.BackColor = color
        End Set
    End Property

    Public Property HighlightForeColor() As Color
        Get
            Return view.highlightForeColor
        End Get
        Set(ByVal color As Color)
            view.highlightForeColor = color
        End Set
    End Property

    Public Property HighlightBackColor() As Color
        Get
            Return view.highlightBackColor
        End Get
        Set(ByVal color As Color)
            view.highlightBackColor = color
        End Set
    End Property

    Public ReadOnly Property LineCount() As Integer
        Get
            Return model.LineCount
        End Get
    End Property

    Public Function GetLine(ByVal lineNo As Integer) As String
        Return model.GetLine(lineNo)
    End Function

    Friend Function HasSelection() As Boolean
        If selectionStartLocation.Equals(selectionEndLocation) Then
            Return False
        Else
            Return True
        End If
    End Function

    Private Sub InitializeComponents()
        model = New Model()
        view = New View(model)
        view.controller = Me
        view.ForeColor = Color.Black
        view.BackColor = Color.White
        view.Cursor = Cursors.IBeam

        lineNumberView = New LineNumberView(model)
        lineNumberView.controller = Me
        lineNumberView.BackColor = Color.AntiqueWhite
        lineNumberView.ForeColor = Color.Black

        lineNumberView.TabStop = False
        vScrollBar.TabStop = False
        hScrollBar.TabStop = False

        ResizeComponents()

        panel.Dock = DockStyle.Fill
        panel.Controls.Add(lineNumberView)
        panel.Controls.Add(view)
        panel.Controls.Add(hScrollBar)
        panel.Controls.Add(vScrollBar)
        panel.BorderStyle = BorderStyle.Fixed3D


        Me.Controls.Add(panel)
        AddHandler view.KeyPress, AddressOf view_KeyPress
        AddHandler view.MouseDown, AddressOf view_MouseDown
        AddHandler view.MouseUp, AddressOf view_MouseUp
        AddHandler view.MouseMove, AddressOf view_MouseMove

        AddHandler model.LineCountChanged, AddressOf Model_LineCountChanged
        AddHandler model.LongestLineCharCountChanged, _
          AddressOf model_LongestLineCharCountChanged
        AddHandler vScrollBar.Scroll, AddressOf vScrollBar_Scroll
        AddHandler hScrollBar.Scroll, AddressOf hScrollBar_Scroll
        AddHandler panel.Resize, AddressOf panel_Resize

        hScrollBar.Enabled = False
        hScrollBar.SmallChange = 1 ' 1 character
        hScrollBar.LargeChange = 2
        vScrollBar.Enabled = False
        vScrollBar.SmallChange = 1
        vScrollBar.LargeChange = 2
    End Sub

    Friend Property CaretLineNo() As Integer
        Get
            Return caretLocation.Line
        End Get
        Set(ByVal newLineNo As Integer)
            If newLineNo > 0 And newLineNo <= model.LineCount Then
                Dim oldLineNo As Integer = caretLocation.Line
                caretLocation.Line = newLineNo
                If oldLineNo <> newLineNo Then
                    OnLineChanged(New LineEventArgs(oldLineNo, newLineNo))
                End If
            End If
        End Set
    End Property

    Friend Property CaretColumnNo() As Integer
        Get
            Return caretLocation.Column
        End Get
        Set(ByVal newColumnNo As Integer)
            If newColumnNo > 0 And newColumnNo <= GetCurrentLine().Length + 1 Then
                Dim oldColumnNo As Integer = caretLocation.Column
                caretLocation.Column = newColumnNo
                If oldColumnNo <> newColumnNo Then
                    OnColumnChanged(New ColumnEventArgs(oldColumnNo, newColumnNo))
                End If
            End If
        End Set
    End Property

    Protected Overridable Sub OnColumnChanged(ByVal e As ColumnEventArgs)
        RaiseEvent ColumnChanged(Me, e)
    End Sub

    Protected Overridable Sub OnLineChanged(ByVal e As LineEventArgs)
        RaiseEvent LineChanged(Me, e)
    End Sub

    Public Function Find(ByVal userPattern As String, _
      ByVal startColumn As Integer, ByVal startLine As Integer, _
      ByVal caseSensitive As Boolean, _
      ByVal wholeWord As Boolean, ByVal goUp As Boolean) As ColumnLine

        'make sure startColumn and startLine is greater than 0
        startColumn = Math.Max(startColumn, 1)
        Dim pattern As String = userPattern.Trim()
        Dim patternLength As Integer = pattern.Length
        Dim lineCount As Integer = model.LineCount
        Dim direction As Integer = 1
        If goUp Then direction = -1

        startLine = Math.Max(startLine, 1)
        startLine = Math.Min(startLine, lineCount)
        Dim lineNo As Integer = startLine
        If Not caseSensitive Then
            pattern = pattern.ToUpper()
        End If
        While lineNo <= lineCount And lineNo > 0
            Dim thisLine As String = model.GetLine(lineNo)

            If Not caseSensitive Then
                thisLine = thisLine.ToUpper()
            End If

            Dim searchResult As Integer = -1
            If lineNo = startLine Then
                If startColumn - 1 < thisLine.Length Then
                    If goUp Then
                        searchResult = thisLine.LastIndexOf(pattern, startColumn - 1)
                    Else
                        searchResult = thisLine.IndexOf(pattern, startColumn - 1)
                    End If
                End If
            Else
                If goUp Then
                    searchResult = thisLine.LastIndexOf(pattern)
                Else
                    searchResult = thisLine.IndexOf(pattern)
                End If
            End If

            If searchResult <> -1 And wholeWord Then
                'search successful but now test if the found pattern is a
                'whole word by checking the characters after and before
                'the match
                If searchResult > 0 Then
                    'test the character before the match
                    If Char.IsLetterOrDigit( _
                      Convert.ToChar(thisLine.Substring(searchResult - 1, 1))) Then
                        searchResult = -1
                    End If
                End If
                If searchResult <> -1 And _
                  thisLine.Length > searchResult + patternLength Then
                    'test the character after the match
                    If Char.IsLetterOrDigit( _
                      Convert.ToChar(thisLine.Substring(searchResult + _
                      patternLength, 1))) Then
                        searchResult = -1
                    End If
                End If
            End If
            If searchResult <> -1 Then    'successful
                'move caret to new position
                CaretLineNo = lineNo
                CaretColumnNo = searchResult + patternLength + 1
                Highlight(searchResult + 1, lineNo, _
                  searchResult + patternLength + 1, lineNo)
                RedrawAll()
                Return New ColumnLine(searchResult + 1, lineNo)
            End If
            lineNo = lineNo + direction
        End While
        Return New ColumnLine(0, 0)
    End Function

    Private Sub ResizeComponents()
        lineNumberView.Size = New Size(LineNumberWidth, panel.Height - _
          hScrollBarHeight - 4)
        lineNumberView.Location = New Point(0, 0)
        view.Size = New Size(panel.Width - lineNumberView.Width - _
          vScrollBarWidth - 4, panel.Height - hScrollBarHeight - 4)
        view.Location = New Point(lineNumberView.Width, 0)
        vScrollBar.Location = New Point(view.Width + lineNumberView.Width, 0)
        vScrollBar.Size = New Size(vScrollBarWidth, view.Height)
        hScrollBar.Location = New Point(0, view.Height)
        hScrollBar.Size = _
          New Size(view.Width + lineNumberView.Width, hScrollBarHeight)
        AdjustVScrollBar()
        AdjustHScrollBar()

    End Sub

    Public Sub SelectLine(ByVal lineNo As Integer)
        If lineNo <= model.LineCount Then
            Dim thisLine As String = model.GetLine(lineNo)
            Dim length As Integer = thisLine.Length
            selectionStartLocation = New ColumnLine(1, lineNo)
            selectionEndLocation = New ColumnLine(length + 1, lineNo)
            caretLocation = selectionStartLocation
            RedrawAll()
        End If
    End Sub
    Private Sub panel_Resize(ByVal sender As Object, ByVal e As EventArgs)
        ResizeComponents()
    End Sub

    Private Sub hScrollBar_Scroll(ByVal sender As Object, _
      ByVal e As ScrollEventArgs)
        Select Case e.Type
            Case ScrollEventType.SmallIncrement
                If view.LeftInvisibleCharCount < _
                  model.LongestLineCharCount - view.VisibleCharCount + 1 Then
                    view.MoveScreen(hScrollBar.SmallChange)
                End If
            Case ScrollEventType.LargeIncrement
                If view.LeftInvisibleCharCount < _
                  model.LongestLineCharCount - view.VisibleCharCount Then
                    Dim maxIncrement As Integer = _
                      Math.Min(hScrollBar.LargeChange, model.LongestLineCharCount - _
                      view.VisibleCharCount - view.LeftInvisibleCharCount)
                    view.MoveScreen(maxIncrement)
                End If
            Case ScrollEventType.SmallDecrement
                view.MoveScreen(-hScrollBar.SmallChange)
            Case ScrollEventType.LargeDecrement
                view.MoveScreen(-hScrollBar.LargeChange)
            Case ScrollEventType.ThumbTrack
                view.LeftInvisibleCharCount = e.NewValue
                RedrawAll()
            Case ScrollEventType.ThumbPosition
                view.LeftInvisibleCharCount = e.NewValue
                RedrawAll()
        End Select
    End Sub

    Private Sub vScrollBar_Scroll(ByVal sender As Object, _
      ByVal e As ScrollEventArgs)
        Select Case e.Type
            Case ScrollEventType.SmallIncrement
                If TopInvisibleLineCount < model.LineCount - view.VisibleLineCount Then
                    view.Scroll(vScrollBar.SmallChange)
                    lineNumberView.RedrawAll()
                End If
            Case ScrollEventType.LargeIncrement
                If TopInvisibleLineCount < model.LineCount - view.VisibleLineCount Then
                    Dim maxIncrement As Integer = _
                      Math.Min(vScrollBar.LargeChange, _
                        model.LineCount - view.VisibleLineCount - TopInvisibleLineCount)
                    view.Scroll(maxIncrement)
                    lineNumberView.RedrawAll()
                End If
            Case ScrollEventType.SmallDecrement
                view.Scroll(-vScrollBar.SmallChange)
                lineNumberView.RedrawAll()
            Case ScrollEventType.LargeDecrement
                view.Scroll(-vScrollBar.LargeChange)
                lineNumberView.RedrawAll()
            Case ScrollEventType.ThumbTrack
                TopInvisibleLineCount = e.NewValue
                RedrawAll()
            Case ScrollEventType.ThumbPosition
                TopInvisibleLineCount = e.NewValue
                RedrawAll()
        End Select

    End Sub

    Private Sub Model_LineCountChanged(ByVal sender As Object, _
      ByVal e As LineCountEventArgs)
        AdjustVScrollBar()
    End Sub

    Private Sub AdjustHScrollBar()
        If view.Width < model.LongestLineCharCount * view.characterWidth + 1 Then
            hScrollBar.Enabled = True
            hScrollBar.Maximum = model.LongestLineCharCount - _
                view.VisibleCharCount + 1  '10 is the margin
            hScrollBar.Value = Math.Min(hScrollBar.Maximum, _
              view.LeftInvisibleCharCount)
        Else
            hScrollBar.Enabled = False
        End If
    End Sub

    Private Sub AdjustVScrollBar()
        'adjust vScrollBar
        If view.VisibleLineCount < model.LineCount Then
            vScrollBar.Enabled = True
            vScrollBar.Maximum = model.LineCount - view.VisibleLineCount + 1
            vScrollBar.Value = Math.Min(TopInvisibleLineCount, vScrollBar.Maximum)
        Else
            vScrollBar.Enabled = False
        End If

    End Sub

    Private Sub model_LongestLineCharCountChanged(ByVal sender As Object, _
      ByVal e As LongestLineEventArgs)
        If e.LongestLineCharCount < view.VisibleCharCount Then
            view.LeftInvisibleCharCount = 0
        End If
        AdjustHScrollBar()
    End Sub

    Private Sub view_MouseUp(ByVal sender As Object, ByVal e As MouseEventArgs)
        'move the caret
        If selecting And e.Button = MouseButtons.Left Then
            selecting = False 'reset selecting
            view.RepositionCaret(e.X, e.Y)
            RedrawAll()
        End If
    End Sub

    Private Sub view_MouseDown(ByVal sender As Object, ByVal e As MouseEventArgs)
        'move the caret
        If e.Button = MouseButtons.Left Then
            view.RepositionCaret(e.X, e.Y)
            selecting = True
            Dim cl As ColumnLine = view.TranslateIntoCaretLocation(e.X, e.Y)
            selectionStartLocation = cl
            selectionEndLocation = cl
            RedrawAll()
        End If
    End Sub

    Private Sub view_MouseMove(ByVal sender As Object, ByVal e As MouseEventArgs)
        'only respond when selecting, i.e. when the left button is pressed
        If selecting Then
            Dim cl As ColumnLine
            cl = view.TranslateIntoCaretLocation(e.X, e.Y)
            selectionEndLocation = cl
            RedrawAll()
        End If
    End Sub

    Private Sub view_KeyPress(ByVal sender As Object, ByVal e As KeyPressEventArgs)
        If Not keyProcessed Then
            Dim c As Char = e.KeyChar
            Dim convertedChar As Integer = Convert.ToInt32(c)
            RemoveSelection()
            ResetSelection()

            Select Case convertedChar
                Case Keys.Back   'backspace 
                    If Not (IsCaretOnFirstColumn() And IsCaretOnFirstLine()) Then
                        'not at beginning of Model
                        If IsCaretOnFirstColumn() Then
                            Dim oldLine As String = model.GetLine(CaretLineNo)
                            Dim prevLine As String = model.GetLine(CaretLineNo - 1)
                            Dim newLine As String = prevLine & oldLine
                            model.SetLine(CaretLineNo - 1, newLine)
                            model.RemoveLine(CaretLineNo, True)
                            CaretLineNo = CaretLineNo - 1
                            CaretColumnNo = prevLine.Length + 1
                        Else
                            Dim oldLine As String = model.GetLine(CaretLineNo)
                            Dim newLine As String = oldLine.Remove(CaretColumnNo - 2, 1)
                            model.SetLine(CaretLineNo, newline)
                            CaretColumnNo = CaretColumnNo - 1
                        End If
                        editedField = True
                    End If
                Case Keys.Return ' return key
                    Dim oldLine As String = GetCurrentLine()
                    Dim newLine As String = oldLine.Substring(0, CaretColumnNo - 1)
                    Dim nextLine As String = oldLine.Substring(CaretColumnNo - 1)
                    model.SetLine(CaretLineNo, newline)
                    model.InsertLine(CaretLineNo + 1, nextLine)
                    Dim needToScroll As Boolean = IsCaretOnLastVisibleLine()
                    CaretColumnNo = 1
                    CaretLineNo = CaretLineNo + 1
                    If needToScroll Then
                        view.Scroll(1)
                    End If
                    editedField = True

                Case Keys.Escape 'Escape
                    'escape key, do nothing
                Case Else
                    model.InsertChar(c, caretLocation)
                    CaretColumnNo = CaretColumnNo + 1
                    editedField = True
            End Select

            ScrollToShowCaret()
            RedrawAll()
            e.Handled = True
        End If
    End Sub

    Public Sub ResetSelection()
        selectionStartLocation.Column = 0
        selectionStartLocation.Line = 0
        selectionEndLocation.Column = 0
        selectionEndLocation.Line = 0
    End Sub

    Public Sub RemoveSelection()
        If Not HasSelection() Then
            Return
        End If

        Dim initialCaretLocation As ColumnLine = caretLocation
        ' after selection is removed, adjust CaretX position.
        Dim x1, y1, x2, y2 As Integer
        x1 = selectionStartLocation.Column
        y1 = selectionStartLocation.Line
        x2 = selectionEndLocation.Column
        y2 = selectionEndLocation.Line

        If y1 > y2 Or (y1 = y2 And x1 > x2) Then
            'swap (x1, y1) and (x2, y2)
            Dim t As Integer
            t = x1 : x1 = x2 : x2 = t
            t = y1 : y1 = y2 : y2 = t
        End If
        If y1 = y2 Then
            Dim thisLine As String = model.GetLine(y1)
            model.SetLine(y1, thisLine.Substring(0, x1 - 1) & _
              thisLine.Substring(x2 - 1, thisLine.Length - x2 + 1))
            'it's okay if event is raised when CaretColumnNo is set
            CaretColumnNo = x1
        Else
            'delete lines between y1 and y2
            Dim j As Integer
            For j = 1 To (y2 - y1 - 1)
                model.RemoveLine(y1 + 1, False) 'false means "do not raise event"
            Next
            'merge line y1 with line y2 and delete the original line y2
            Dim thisLine As String = model.GetLine(y1)
            Dim nextLine As String = model.GetLine(y1 + 1)
            model.SetLine(y1, thisLine.Substring(0, x1 - 1) & _
              nextLine.Substring(x2 - 1, nextLine.Length - x2 + 1))
            model.RemoveLine(y1 + 1, True)
            ' CaretLineNo must be adjusted before CaretColumnNo because
            ' CaretColumnNo property will use CaretLineNo. Therefore, it
            ' is important that CaretLineNo contains the correct value
            CaretLineNo = y1
            CaretColumnNo = x1
        End If
    End Sub

    Protected Overrides Function ProcessDialogKey(ByVal keyData As Keys) As Boolean
        keyProcessed = True
        Select Case keyData
            Case Keys.Down
                ResetSelection()
                If Not IsCaretOnLastLine() Then
                    If IsCaretOnLastVisibleLine() Then
                        view.Scroll(1)
                    End If
                    CaretLineNo = CaretLineNo + 1
                    If CaretColumnNo > GetCurrentLine().Length + 1 Then
                        CaretColumnNo = GetCurrentLine().Length + 1
                    End If
                    ScrollToShowCaret()
                    RedrawAll()
                End If
                Return True
            Case Keys.Up
                ResetSelection()
                If Not IsCaretOnFirstLine() Then
                    If IsCaretOnFirstVisibleLine() Then
                        view.Scroll(-1)
                    End If
                    CaretLineNo = CaretLineNo - 1
                    If CaretColumnNo > GetCurrentLine().Length + 1 Then
                        CaretColumnNo = GetCurrentLine().Length + 1
                    End If
                    ScrollToShowCaret()
                    RedrawAll()
                End If
                Return True
            Case Keys.Right
                ResetSelection()
                If IsCaretOnLastColumn() Then
                    If Not IsCaretOnLastLine() Then
                        If IsCaretOnLastVisibleLine() Then
                            view.Scroll(1)
                        End If
                        CaretLineNo = CaretLineNo + 1
                        CaretColumnNo = 1
                    End If
                Else
                    CaretColumnNo = CaretColumnNo + 1
                End If
                ScrollToShowCaret()
                RedrawAll()
                Return True
            Case Keys.Left
                ResetSelection()
                If IsCaretOnFirstColumn() Then
                    If Not IsCaretOnFirstLine() Then
                        If IsCaretOnFirstVisibleLine() Then
                            view.Scroll(-1)
                        End If
                        CaretLineNo = CaretLineNo - 1
                        CaretColumnNo = GetCurrentLine().Length + 1
                    End If
                Else
                    CaretColumnNo = CaretColumnNo - 1
                End If
                ScrollToShowCaret()
                RedrawAll()
                Return True
            Case Keys.Delete
                'Deleting character does not change caret position but
                'may change the longest line char count
                If HasSelection() Then
                    RemoveSelection()
                    ResetSelection()
                    ' then don't delete anything
                Else
                    If CaretColumnNo = GetCurrentLine().Length + 1 Then
                        ' at the end of line
                        If CaretLineNo < model.LineCount Then
                            'concatenate current line and next line
                            'and delete next line
                            Dim nextLine As String = model.GetLine(CaretLineNo + 1)
                            model.SetLine(CaretLineNo, GetCurrentLine() & nextLine)
                            model.RemoveLine(CaretLineNo + 1, True)
                        End If
                    Else
                        'delete one character
                        model.DeleteChar(caretLocation)
                    End If
                End If

                RedrawAll()
                Return True
            Case Else
                If CInt(Keys.Control And keyData) = 0 And _
                  CInt(Keys.Alt And keyData) = 0 Then
                    ' let KeyPress process the key
                    keyProcessed = False
                End If
                Return MyBase.ProcessDialogKey(keyData)
        End Select
    End Function

    Private Sub ScrollToShowCaret()
        If Not view.IsCaretVisible() Then
            If model.GetLine(CaretLineNo).Length > view.VisibleCharCount Then
                view.LeftInvisibleCharCount = GetCurrentLine().Length - _
                view.VisibleCharCount
            Else
                view.LeftInvisibleCharCount = 0
            End If
            If CaretLineNo > TopInvisibleLineCount + view.VisibleLineCount Then
                TopInvisibleLineCount = CaretLineNo - view.VisibleLineCount + 1
            End If
        End If
    End Sub

    Private Function IsCaretOnFirstColumn() As Boolean
        Return (CaretColumnNo = 1)
    End Function

    Private Function IsCaretOnLastColumn() As Boolean
        Return (CaretColumnNo = GetCurrentLine().Length + 1)
    End Function

    Private Function IsCaretOnLastVisibleLine() As Boolean
        Return (CaretLineNo = view.VisibleLineCount + TopInvisibleLineCount)
    End Function

    Private Function IsCaretOnLastLine() As Boolean
        Return (CaretLineNo = model.LineCount)
    End Function

    Private Function IsCaretOnFirstLine() As Boolean
        Return (CaretLineNo = 1)
    End Function

    Private Function IsCaretOnFirstVisibleLine() As Boolean
        Return (CaretLineNo = TopInvisibleLineCount + 1)
    End Function

    Private Sub RedrawAll()
        view.RedrawAll()
        lineNumberView.RedrawAll()
        AdjustVScrollBar()
        AdjustHScrollBar()
    End Sub

    Private Sub Highlight(ByVal x1 As Integer, ByVal y1 As Integer, _
      ByVal x2 As Integer, ByVal y2 As Integer)
        '(x1, y1) is the starting column,line of highlight
        '(x2, y2) is the end column,line of hightlight
        'swap (x1,y1) and (x2,y2) if necessary
        If y1 > y2 Or (y1 = y2 And x1 > x2) Then
            Dim t As Integer
            t = x1 : x1 = x2 : x2 = t
            t = y1 : y1 = y2 : y2 = t
        End If
        selectionStartLocation.Column = x1
        selectionStartLocation.Line = y1
        selectionEndLocation.Column = x2
        selectionEndLocation.Line = y2
    End Sub

    Public ReadOnly Property SelectedText() As String
        Get
            If HasSelection() Then
                Dim x1, y1, x2, y2 As Integer
                x1 = selectionStartLocation.Column
                y1 = selectionStartLocation.Line
                x2 = selectionEndLocation.Column
                y2 = selectionEndLocation.Line

                'swap if necessary
                If y1 > y2 Or (y1 = y2 And x1 > x2) Then
                    Dim t As Integer
                    t = x1 : x1 = x2 : x2 = t
                    t = y1 : y1 = y2 : y2 = t
                End If

                If y1 = y2 Then
                    Return model.GetLine(y1).Substring(x1 - 1, x2 - x1)
                Else
                    Dim sb As New StringBuilder(model.CharCount + 2 * model.LineCount)
                    Dim lineCount As Integer = model.LineCount
                    Dim i, lineNo As Integer
                    For i = y1 To y2
                        Dim thisLine As String = model.GetLine(i)
                        If i = y1 Then
                            sb.Append(thisLine.Substring(x1 - 1))
                        ElseIf i = y2 Then
                            sb.Append(Microsoft.VisualBasic.Constants.vbCrLf)
                            sb.Append(thisLine.Substring(0, x2 - 1))
                        Else
                            sb.Append(Microsoft.VisualBasic.Constants.vbCrLf)
                            sb.Append(thisLine)
                        End If
                    Next
                    Return sb.ToString()
                End If
            Else
                Return ""
            End If

        End Get
    End Property

    Public Overrides Property Text() As String
        Get
            Dim sb As New StringBuilder(model.CharCount + 2 * model.LineCount)
            Dim lineCount As Integer = model.LineCount
            Dim i As Integer
            For i = 1 To lineCount
                Dim thisLine As String = model.GetLine(i)
                sb.Append(thisLine)
                If i < lineCount Then
                    sb.Append(Microsoft.VisualBasic.Constants.vbCrLf)
                End If
            Next
            Return sb.ToString()
        End Get
        Set(ByVal s As String)
            If Not s Is Nothing Then
                Dim initialColumn As Integer = caretLocation.Column
                Dim initialLine As Integer = caretLocation.Line

                'remove all lines
                Dim i As Integer
                Dim lineCount As Integer = model.LineCount
                For i = 2 To lineCount
                    model.RemoveLine(2, False)
                Next
                model.SetLine(1, "") ' don't remove the first line
                caretLocation.Column = 1
                caretLocation.Line = 1
                caretLocation = model.InsertData(s, caretLocation)

                If caretLocation.Column <> initialColumn Then
                    OnColumnChanged(New _
                      ColumnEventArgs(initialColumn, caretLocation.Column))
                End If
                If caretLocation.Line <> initialLine Then
                    OnLineChanged(New LineEventArgs(initialLine, caretLocation.Line))
                End If
                ResetSelection()
                If Not view.IsCaretVisible() Then
                    ScrollToShowCaret()
                End If
                RedrawAll()
            End If
        End Set
    End Property

    Public Function IsInSelection(ByVal column As Integer, _
      ByVal line As Integer) As Boolean
        'indicate that the character at (column, line) is selected
        If Not HasSelection() Then
            Return False
        Else
            Dim x1, y1, x2, y2 As Integer
            x1 = selectionStartLocation.Column
            y1 = selectionStartLocation.Line
            x2 = selectionEndLocation.Column
            y2 = selectionEndLocation.Line

            'swap if necessary to make (x2, y2) below (x1, y1)
            If (y1 > y2) Or (y1 = y2 And x1 > x2) Then
                Dim t As Integer
                t = x2 : x2 = x1 : x1 = t
                t = y2 : y2 = y1 : y1 = t
            End If

            If y2 > model.LineCount Then
                y2 = model.LineCount
                If y1 = y2 And x1 > x2 Then
                    Dim t As Integer
                    t = x2 : x2 = x1 : x1 = t
                End If
            End If
            If line < y2 And line > y1 Then
                Return True
            ElseIf y1 = y2 And line = y1 And column >= x1 And column < x2 Then
                'selection in one line
                Return True
            ElseIf line = y1 And line <> y2 And column >= x1 Then
                Return True
            ElseIf line = y2 And line <> y1 And column < x2 Then
                Return True
            Else
                Return False
            End If
        End If
    End Function

    Public Sub Copy()
        'copy selected text to clipboard
        If HasSelection() Then
            Clipboard.SetDataObject(SelectedText)
        End If
    End Sub

    Public Sub Cut()
        If HasSelection() Then
            Copy()
            RemoveSelection()
            ResetSelection()
            RedrawAll()
        End If
    End Sub

    Public Sub Paste()
        ' In this method, CaretLocation's Line and Column fields
        ' are accessed without going through the CaretLineNo and CaretColumnNo
        ' properties so as not to raise the OnLineChanged and OnColumnChanged
        ' events repeatedly.
        Dim buffer As IDataObject = Clipboard.GetDataObject()
        If buffer.GetDataPresent(DataFormats.Text) Then

            Dim initialColumn As Integer = caretLocation.Column
            Dim initialLine As Integer = caretLocation.Line

            If HasSelection() Then
                RemoveSelection()
            End If

            Dim s As String = buffer.GetData(DataFormats.Text).ToString()
            caretLocation = model.InsertData(s, caretLocation)
            If caretLocation.Column <> initialColumn Then
                OnColumnChanged(New ColumnEventArgs(initialColumn, caretLocation.Column))
            End If
            If caretLocation.Line <> initialLine Then
                OnLineChanged(New LineEventArgs(initialLine, caretLocation.Line))
            End If
            If HasSelection() Then
                ResetSelection()
            End If
            If Not view.IsCaretVisible() Then
                ScrollToShowCaret()
            End If
            RedrawAll()
        Else
            'MsgBox("Incompatible data format")
        End If
    End Sub

    Public Sub SelectAll()
        selectionStartLocation.Column = 1
        selectionStartLocation.Line = 1
        selectionEndLocation.Column = model.GetLine(model.LineCount).Length + 1
        selectionEndLocation.Line = model.LineCount
        RedrawAll()
    End Sub

    Public Function GetCurrentLine() As String
        'return the line where the caret is on
        Return model.GetLine(CaretLineNo)
    End Function
    Protected Overrides Sub Finalize()
        MyBase.Finalize()
    End Sub


    Private Sub InitializeComponent()
        '
        'StyledTextArea
        '
        Me.Name = "StyledTextArea"

    End Sub

End Class

Option Strict On
Option Explicit On 

Public Class Model
    Private list As New ArrayList(1024)  ' number of initial lines
    Private longestLineCharCountField As Integer
    Public Event LineCountChanged As LineCountEventHandler
    Public Event LongestLineCharCountChanged As LongestLineEventHandler

    Public Sub New()
        list.Add("")
    End Sub

    Protected Overridable Sub OnLineCountChanged(ByVal e As LineCountEventArgs)
        RaiseEvent LineCountChanged(Me, e)
    End Sub

    Protected Overridable Sub OnLongestLineCharCountChanged( _
      ByVal e As LongestLineEventArgs)
        RaiseEvent LongestLineCharCountChanged(Me, e)
    End Sub

    Public ReadOnly Property LongestLineCharCount() As Integer
        Get
            Dim lineCount As Integer = list.Count
            If lineCount = 0 Then
                Return 0
            Else
                Dim i, max As Integer
                For i = 0 To lineCount - 1
                    Dim thisLineCharCount As Integer = CType(list.Item(i), String).Length
                    If thisLineCharCount > max Then
                        max = thisLineCharCount
                    End If
                Next
                Return max
            End If
        End Get
    End Property

    Public ReadOnly Property CharCount() As Integer
        ' the total of lengths of all lines, therefore newline is not counted
        Get
            Dim i, total As Integer
            For i = 0 To LineCount - 1
                total = total + list.Item(i).ToString().Length
            Next i
            Return total
        End Get
    End Property


    Public ReadOnly Property LineCount() As Integer
        Get
            Return list.Count
        End Get
    End Property

    Public Function GetLine(ByVal lineNo As Integer) As String 'lineNo is 1-based
        If lineNo > 0 And lineNo <= list.Count Then
            Return CType(list.Item(lineNo - 1), String)
        Else
            Throw New ArgumentOutOfRangeException()
        End If
    End Function

    Public Function InsertData(ByVal data As String, _
      ByVal insertLocation As ColumnLine) As ColumnLine

        'delete vbLf character
        data = data.Replace(Microsoft.VisualBasic.Constants.vbLf.ToString(), "")

        Dim initialLongestLineCharCount As Integer = LongestLineCharCount
        Dim x As Integer = insertLocation.Column
        Dim y As Integer = insertLocation.Line

        Dim returnInserted As Boolean
        ' data may contain carriage return character
        Dim thisLine As String = GetLine(y)
        Dim head As String = thisLine.Substring(0, x - 1)
        Dim tail As String = thisLine.Substring(x - 1)
        list.RemoveAt(y - 1)
        Dim startIndex As Integer
        Do While (startIndex >= 0)
            Dim endIndex As Integer = _
              data.IndexOf(Microsoft.VisualBasic.Constants.vbCr, startIndex)
            Dim line As String
            If endIndex = -1 Then
                line = data.Substring(startIndex)
                'don't use SetLine bec it can raise event
                Dim newLine As String = head & line & tail
                list.Insert(y - 1, newLine)
                x = head.Length + line.Length + 1
                startIndex = endIndex
            Else
                line = data.Substring(startIndex, endIndex - startIndex)
                list.Insert(y - 1, head & line)
                returnInserted = True
                y = y + 1
                x = 1
                head = ""
                startIndex = endIndex + 1 'without carriage return
            End If

        Loop

        Dim currentCharCount As Integer = LongestLineCharCount
        If initialLongestLineCharCount <> currentCharCount Then
            OnLongestLineCharCountChanged(New LongestLineEventArgs(currentCharCount))
        End If
        If returnInserted Then
            OnLineCountChanged(New LineCountEventArgs(LineCount))
        End If
        Return New ColumnLine(x, y)
    End Function

    Public Sub InsertLine(ByVal lineNo As Integer, ByVal line As String)
        If lineNo > 0 And lineNo <= list.Count + 1 Then
            Dim oldLongestLineCharCount As Integer = LongestLineCharCount
            list.Insert(lineNo - 1, line)
            OnLineCountChanged(New LineCountEventArgs(LineCount))
            Dim newLongestLineCharCount As Integer = LongestLineCharCount
            If oldLongestLineCharCount <> newLongestLineCharCount Then
                OnLongestLineCharCountChanged(New _
                  LongestLineEventArgs(newLongestLineCharCount))
            End If
        End If
    End Sub

    Public Sub InsertChar(ByVal c As Char, ByVal insertLocation As ColumnLine)
        Dim oldLongestLineCharCount As Integer = LongestLineCharCount
        Dim oldLine As String = list.Item(insertLocation.Line - 1).ToString()
        Dim newLine As String = _
          oldLine.Insert(insertLocation.Column - 1, c.ToString())
        list.Item(insertLocation.Line - 1) = newLine
        Dim newLongestLineCharCount As Integer = LongestLineCharCount
        If oldLongestLineCharCount <> newLongestLineCharCount Then
            OnLongestLineCharCountChanged(New _
              LongestLineEventArgs(newLongestLineCharCount))
        End If
    End Sub

    Public Sub RemoveLine(ByVal lineNo As Integer, ByVal triggerEvent As Boolean)
        If lineNo > 0 And lineNo <= list.Count Then
            Dim oldLongestLineCharCount As Integer = LongestLineCharCount
            list.RemoveAt(lineNo - 1)
            If triggerEvent Then
                Dim newLongestLineCharCount As Integer = LongestLineCharCount
                If oldLongestLineCharCount <> newLongestLineCharCount Then
                    OnLongestLineCharCountChanged(New _
                      LongestLineEventArgs(newLongestLineCharCount))
                End If
                OnLineCountChanged(New LineCountEventArgs(LineCount))
            End If
        End If
    End Sub

    Public Sub SetLine(ByVal lineNo As Integer, ByVal line As String)
        Dim oldLongestLineCharCount As Integer = LongestLineCharCount
        If (lineNo > 0 And lineNo <= list.Count) Then
            list.Item(lineNo - 1) = line
        End If
        Dim newLongestLineCharCount As Integer = LongestLineCharCount
        If oldLongestLineCharCount <> newLongestLineCharCount Then
            OnLongestLineCharCountChanged(New _
              LongestLineEventArgs(newLongestLineCharCount))
        End If
    End Sub

    Public Sub DeleteChar(ByVal deleteLocation As ColumnLine)
        Dim oldLongestLineCharCount As Integer = LongestLineCharCount
        list.Item(deleteLocation.Line - 1) = _
          GetLine(deleteLocation.Line).Remove(deleteLocation.Column - 1, 1)
        Dim newLongestLineCharCount As Integer = LongestLineCharCount
        If oldLongestLineCharCount <> newLongestLineCharCount Then
            OnLongestLineCharCountChanged(New _
              LongestLineEventArgs(newLongestLineCharCount))
        End If
    End Sub

End Class


Option Strict On
Option Explicit On 

Public Class LineNumberView : Inherits UserControl
    Private model As model
    Public controller As StyledTextArea
    Public lineSpace As Integer = 2
    Public fontFace As String = "Courier New"

    Public Sub New(ByRef model As model)
        Me.model = model
        fontHeight = 10
        Font = New Font(fontFace, fontHeight)
    End Sub

    Public ReadOnly Property VisibleLineCount() As Integer
        Get
            Return CInt(Me.Height / (lineSpace + FontHeight))
        End Get
    End Property

    Protected Overrides Sub OnPaint(ByVal e As PaintEventArgs)

        Dim graphics As Graphics = e.Graphics
        Dim textBrush As SolidBrush = New SolidBrush(ForeColor)
        Dim characterWidth As Integer = 8

        Dim i, visibleLine As Integer
        For i = 1 To Math.Min(model.LineCount, VisibleLineCount)
            Dim number As Integer = i + controller.TopInvisibleLineCount
            Dim x As Integer = Me.Width - characterWidth - _
              (number).ToString().Length * characterWidth
            Dim y As Integer = (i - 1) * (lineSpace + fontheight)
            graphics.DrawString(number.ToString(), Font, textBrush, x, y)
        Next i

    End Sub

    Public Sub RedrawAll()
        Me.Invalidate()
        Me.Update()
    End Sub
End Class

Imports System
Imports System.ComponentModel
Imports System.Drawing
Imports System.Windows.Forms

Public Class Form1 : Inherits Form
    Friend WithEvents textArea As StyledTextArea
    Friend WithEvents findPattern As TextBox
    Friend WithEvents findButton As Button
    Friend WithEvents copyButton As Button
    Friend WithEvents cutButton As Button
    Friend WithEvents pasteButton As Button
    Friend WithEvents selectButton As Button
    Friend WithEvents Label1 As Label
    Friend WithEvents caseCB As CheckBox
    Friend WithEvents wholeWordCB As CheckBox
    Friend WithEvents goUpCB As CheckBox

    Private x1 As Integer = 1
    Private y1 As Integer = 1
    Private column As Integer = 1
    Private line As Integer = 1

    Public Sub New()
        InitializeComponent()
    End Sub

    Private Sub textArea_ColumnChanged(ByVal sender As Object, _
      ByVal e As ColumnEventArgs) Handles textArea.ColumnChanged
        column = e.NewColumn
        UpdateLabel()
    End Sub

    Private Sub textArea_LineChanged(ByVal sender As Object, _
      ByVal e As LineEventArgs) Handles textArea.LineChanged
        line = e.NewLine
        UpdateLabel()
    End Sub

    Private Sub InitializeComponent()
        Me.textArea = New StyledTextArea
        Me.findPattern = New System.Windows.Forms.TextBox()
        Me.findButton = New System.Windows.Forms.Button()
        Me.caseCB = New System.Windows.Forms.CheckBox()
        Me.wholeWordCB = New System.Windows.Forms.CheckBox()
        Me.goUpCB = New System.Windows.Forms.CheckBox()
        Me.copyButton = New System.Windows.Forms.Button()
        Me.cutButton = New System.Windows.Forms.Button()
        Me.pasteButton = New System.Windows.Forms.Button()
        Me.selectButton = New System.Windows.Forms.Button()
        Me.Label1 = New System.Windows.Forms.Label()
        Me.SuspendLayout()
        '
        'textArea
        '
        Me.textArea.CaretColor = System.Drawing.Color.Red
        Me.textArea.Edited = False
        Me.textArea.HighlightBackColor = System.Drawing.Color.Black
        Me.textArea.HighlightForeColor = System.Drawing.Color.White
        Me.textArea.LineNumberBackColor = System.Drawing.Color.FromArgb(CType(240, Byte), CType(240, Byte), CType(240, Byte))
        Me.textArea.LineNumberForeColor = System.Drawing.Color.DarkCyan
        Me.textArea.Location = New System.Drawing.Point(5, 5)
        Me.textArea.Name = "textArea"
        Me.textArea.Size = New System.Drawing.Size(680, 345)
        Me.textArea.TabIndex = 0
        Me.textArea.TextBackColor = System.Drawing.Color.White
        Me.textArea.TextForeColor = System.Drawing.Color.Black
        '
        'findPattern
        '
        Me.findPattern.Location = New System.Drawing.Point(392, 400)
        Me.findPattern.Name = "findPattern"
        Me.findPattern.Size = New System.Drawing.Size(128, 20)
        Me.findPattern.TabIndex = 1
        Me.findPattern.Text = ""
        '
        'findButton
        '
        Me.findButton.Location = New System.Drawing.Point(536, 392)
        Me.findButton.Name = "findButton"
        Me.findButton.Size = New System.Drawing.Size(128, 32)
        Me.findButton.TabIndex = 2
        Me.findButton.Text = "Find"
        '
        'caseCB
        '
        Me.caseCB.Location = New System.Drawing.Point(390, 368)
        Me.caseCB.Name = "caseCB"
        Me.caseCB.Size = New System.Drawing.Size(123, 14)
        Me.caseCB.TabIndex = 3
        Me.caseCB.Text = "Case sensitive"
        '
        'wholeWordCB
        '
        Me.wholeWordCB.Location = New System.Drawing.Point(505, 368)
        Me.wholeWordCB.Name = "wholeWordCB"
        Me.wholeWordCB.Size = New System.Drawing.Size(84, 14)
        Me.wholeWordCB.TabIndex = 4
        Me.wholeWordCB.Text = "WholeWord"
        '
        'goUpCB
        '
        Me.goUpCB.Location = New System.Drawing.Point(600, 368)
        Me.goUpCB.Name = "goUpCB"
        Me.goUpCB.Size = New System.Drawing.Size(96, 14)
        Me.goUpCB.TabIndex = 5
        Me.goUpCB.Text = "Go Up"
        '
        'copyButton
        '
        Me.copyButton.Location = New System.Drawing.Point(4, 392)
        Me.copyButton.Name = "copyButton"
        Me.copyButton.Size = New System.Drawing.Size(80, 32)
        Me.copyButton.TabIndex = 6
        Me.copyButton.Text = "Copy"
        '
        'cutButton
        '
        Me.cutButton.Location = New System.Drawing.Point(90, 392)
        Me.cutButton.Name = "cutButton"
        Me.cutButton.Size = New System.Drawing.Size(80, 32)
        Me.cutButton.TabIndex = 7
        Me.cutButton.Text = "Cut"
        '
        'pasteButton
        '
        Me.pasteButton.Location = New System.Drawing.Point(176, 392)
        Me.pasteButton.Name = "pasteButton"
        Me.pasteButton.Size = New System.Drawing.Size(80, 32)
        Me.pasteButton.TabIndex = 8
        Me.pasteButton.Text = "Paste"
        '
        'selectButton
        '
        Me.selectButton.Location = New System.Drawing.Point(280, 392)
        Me.selectButton.Name = "selectButton"
        Me.selectButton.Size = New System.Drawing.Size(80, 32)
        Me.selectButton.TabIndex = 9
        Me.selectButton.Text = "Select All"
        '
        'Label1
        '
        Me.Label1.Location = New System.Drawing.Point(6, 367)
        Me.Label1.Name = "Label1"
        Me.Label1.Size = New System.Drawing.Size(160, 24)
        Me.Label1.TabIndex = 0
        '
        'Form1
        '
        Me.AutoScaleBaseSize = New System.Drawing.Size(5, 13)
        Me.ClientSize = New System.Drawing.Size(692, 433)
        Me.Controls.AddRange(New System.Windows.Forms.Control() {Me.Label1, Me.selectButton, Me.pasteButton, Me.cutButton, Me.copyButton, Me.goUpCB, Me.textArea, Me.wholeWordCB, Me.caseCB, Me.findButton, Me.findPattern})
        Me.Name = "Form1"
        Me.Text = "Using StyledTextArea"
        Me.ResumeLayout(False)

    End Sub

    Private Sub goUpCB_Click(ByVal sender As Object, ByVal e As EventArgs) _
      Handles goUpCB.CheckedChanged
        If goUpCB.Checked Then
            y1 = textArea.LineCount
            x1 = textArea.GetLine(y1).Length + 1
        Else
            x1 = 1
            y1 = 1
        End If

    End Sub

    Private Sub findButton_Click(ByVal sender As Object, ByVal e As EventArgs) _
      Handles findButton.Click
        Dim pattern As String = findPattern.Text
        Dim p As ColumnLine = textArea.Find(pattern, x1, y1, caseCB.Checked, _
          wholeWordCB.Checked, goUpCB.Checked)

        If p.Equals(New ColumnLine(0, 0)) Then
            'search not found
            If goUpCB.Checked Then
                y1 = textArea.LineCount
                x1 = textArea.GetLine(y1).Length + 1
            Else
                y1 = 1
                x1 = 1
            End If
        Else
            If Not goUpCB.Checked Then
                x1 = p.Column + 1
                y1 = p.Line
            Else
                x1 = p.Column - findPattern.Text.Length - 1
                If x1 <= 1 Then
                    x1 = 1
                    y1 = p.Line - 1
                Else
                    y1 = p.Line
                End If
            End If
        End If
    End Sub

    Private Sub copyButton_Click(ByVal sender As Object, ByVal e As EventArgs) _
      Handles copyButton.Click
        textArea.Copy()
        textArea.Focus()
    End Sub

    Private Sub cutButton_Click(ByVal sender As Object, ByVal e As EventArgs) _
      Handles cutButton.Click
        textArea.Cut()
        textArea.Focus()

    End Sub

    Private Sub pasteButton_Click(ByVal sender As Object, ByVal e As EventArgs) _
      Handles pasteButton.Click
        textArea.Paste()
        textArea.Focus()
    End Sub

    Private Sub selectButton_Click(ByVal sender As Object, ByVal e As EventArgs) _
      Handles selectButton.Click
        textArea.SelectAll()
        textArea.Focus()
    End Sub

    Private Sub UpdateLabel()
        Label1.Text = "Ln: " & line & "    Col: " & column
    End Sub

    <STAThread()> Shared Sub Main()
        System.Windows.Forms.Application.Run(New Form1())
    End Sub

End Class

Option Strict On
Option Explicit On 

Public Delegate Sub ColumnEventHandler(ByVal sender As Object, _
  ByVal e As ColumnEventArgs)

Public Class ColumnEventArgs : Inherits EventArgs
    Private oldColumnField, newColumnField As Integer

    Public Sub New(ByVal oldColumn As Integer, ByVal newColumn As Integer)
        oldColumnField = oldColumn
        newColumnField = newColumn
    End Sub

    Public ReadOnly Property OldColumn() As Integer
        Get
            Return oldColumnField
        End Get
    End Property

    Public ReadOnly Property NewColumn() As Integer
        Get
            Return newColumnField
        End Get
    End Property
End Class

Public Delegate Sub LineEventHandler(ByVal sender As Object, _
  ByVal e As LineEventArgs)

Public Class LineEventArgs : Inherits EventArgs
    Private oldLineField, newLineField As Integer

    Public Sub New(ByVal oldLine As Integer, ByVal newLine As Integer)
        oldLineField = oldLine
        newLineField = newLine
    End Sub

    Public ReadOnly Property OldLine() As Integer
        Get
            Return oldLineField
        End Get
    End Property

    Public ReadOnly Property NewLine() As Integer
        Get
            Return newLineField
        End Get
    End Property
End Class


Public Delegate Sub LineCountEventHandler(ByVal sender As Object, _
  ByVal e As LineCountEventArgs)

Public Class LineCountEventArgs : Inherits EventArgs
    Private lineCountField As Integer ' the current line count

    Public Sub New(ByVal lineCount As Integer)
        lineCountField = lineCount
    End Sub

    Public ReadOnly Property LineCount() As Integer
        Get
            Return lineCountField
        End Get
    End Property
End Class

Public Delegate Sub LongestLineEventHandler(ByVal sender As Object, _
  ByVal e As LongestLineEventArgs)

Public Class LongestLineEventArgs : Inherits EventArgs
    Private charCountField As Integer

    Public Sub New(ByVal charCount As Integer)
        charCountField = charCount
    End Sub

    Public ReadOnly Property LongestLineCharCount() As Integer
        Get
            Return charCountField
        End Get
    End Property
End Class

Option Strict On
Option Explicit On 

Imports System.Threading

Public Class View : Inherits UserControl

    Public controller As StyledTextArea
    Public lineSpace As Integer = 2  ' number of pixels between 2 lines
    Public fontFace As String = "Courier New"
    Public characterWidth As Integer = 8
    Public model As Model
    Private caretThread As Thread
    Private caretVisible As Boolean = True
    Private penWidth As Integer = 2
    Public highlightBackColor As Color = Color.DarkBlue
    Public highlightForeColor As Color = Color.White
    Public LeftInvisibleCharCount As Integer
    Private pen As New Pen(Color.Black, penWidth)

    Public Property CaretColor() As Color
        Get
            Return pen.Color
        End Get
        Set(ByVal Value As Color)
            pen.Color = Value
        End Set
    End Property

    Public Sub New(ByRef Model As Model)
        Me.model = Model
        fontHeight = 10
        Font = New Font(fontFace, fontHeight)

        caretThread = New Thread(New ThreadStart(AddressOf DisplayCaret))
        caretThread.Start()

    End Sub

    Public Function GetFontHeight() As Integer
        Return fontHeight
    End Function

    Public ReadOnly Property VisibleCharCount() As Integer
        Get
            Return CInt(Math.Floor(Me.Width / characterWidth)) - 1
        End Get
    End Property

    Public ReadOnly Property VisibleLineCount() As Integer
        Get
            Return CInt(Me.Height / (lineSpace + GetFontHeight()))
        End Get
    End Property

    Public Sub Scroll(ByVal increment As Integer)
        controller.TopInvisibleLineCount = _
          controller.TopInvisibleLineCount + increment
        If controller.TopInvisibleLineCount < 0 Then
            controller.TopInvisibleLineCount = 0
        End If
        RedrawAll()
    End Sub

    Public Sub MoveScreen(ByVal increment As Integer)
        'move screen horizontally by x character
        LeftInvisibleCharCount = Math.Max(LeftInvisibleCharCount + increment, 0)
        RedrawAll()
    End Sub

    Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean)
        caretThread.Abort()
        caretThread.Join()
        MyBase.Dispose(disposing)
    End Sub

    Protected Overrides Sub OnPaint(ByVal e As PaintEventArgs)

        Dim graphics As Graphics = e.Graphics
        Dim textBrush As SolidBrush = New SolidBrush(ForeColor)
        Dim highlightTextBrush As SolidBrush = New SolidBrush(highlightForeColor)

        If controller.HasSelection() Then
            PaintSelectionArea(graphics)
        End If

        Dim i, visibleLine As Integer
        Dim maxCharCount As Integer
        For i = 1 To model.LineCount
            Dim thisLine As String = model.GetLine(i)
            If i > controller.TopInvisibleLineCount Then
                Dim y As Integer = visibleLine * (lineSpace + fontHeight)
                Dim j As Integer
                For j = 1 To thisLine.Length
                    If j > LeftInvisibleCharCount And _
                      j <= LeftInvisibleCharCount + VisibleCharCount + 1 Then
                        Dim x As Integer = GetStringWidth(thisLine.Substring(0, j - 1)) - _
                          LeftInvisibleCharCount * characterWidth
                        If controller.IsInSelection(j, i) Then
                            graphics.DrawString(thisLine.Substring(j - 1, 1), Font, _
                              highlightTextBrush, x, y)
                        Else
                            graphics.DrawString(thisLine.Substring(j - 1, 1), Font, _
                              textBrush, x, y)
                        End If
                    End If
                Next j
                visibleLine = visibleLine + 1
            End If
        Next i

        'draw caret here
        DrawCaret(graphics)
    End Sub

    Protected Sub DrawCaret(ByRef graphics As Graphics)
        'it's protected so that it can be overriden by subclass
        If caretVisible And Me.Focused Then
            'Measure string
            Dim caretsLine As String = model.GetLine(controller.CaretLineNo)
            Dim x As Integer = GetStringWidth(caretsLine.Substring(0, _
              controller.CaretColumnNo - 1)) + _
              penWidth - (LeftInvisibleCharCount * characterWidth)
            Dim y As Integer = (controller.CaretLineNo - 1 - _
              controller.TopInvisibleLineCount) * (lineSpace + fontHeight)
            graphics.DrawLine(pen, x, y, x, y + lineSpace + fontHeight)
        End If
    End Sub

    Private Sub PaintSelectionArea(ByRef graphics As Graphics)
        Dim brush As New SolidBrush(highlightBackColor)
        ' representing start and end coordinates of selected text
        Dim x1, y1, x2, y2 As Integer
        Dim p1, p2 As ColumnLine
        p1 = controller.selectionStartLocation
        p2 = controller.selectionEndLocation
        x1 = p1.Column : y1 = p1.Line
        x2 = p2.Column : y2 = p2.Line

        If y1 > y2 Or (y1 = y2 And x1 > x2) Then
            'swap
            Dim t As Integer
            t = y1 : y1 = y2 : y2 = t
            t = x1 : x1 = x2 : x2 = t
        End If

        Dim i As Integer
        Dim beginLine As Integer = Math.Max(y1, 1)
        Dim endLine As Integer = Math.Min(y2, model.LineCount)

        If beginLine = endLine Then
            If x1 > x2 Then
                Dim t As Integer
                t = x1 : x1 = x2 : x2 = t
            End If
            Dim thisLine As String = model.GetLine(beginLine)
            graphics.FillRectangle(brush, _
              2 + GetStringWidth(thisLine.Substring(0, x1 - 1)) - _
              (LeftInvisibleCharCount * characterWidth), _
              (beginLine - 1 - controller.TopInvisibleLineCount) * _
              (lineSpace + fontHeight), _
              GetStringWidth(thisLine.Substring(x1 - 1, x2 - x1)), _
              (lineSpace + fontHeight))
        Else
            For i = beginLine To endLine
                Dim thisLine As String = model.GetLine(i)
                If i = beginLine Then
                    ' first line may not be the whole line, 
                    ' but from initial position of selection to end of string
                    graphics.FillRectangle(brush, _
                      2 + GetStringWidth(thisLine.Substring(0, x1 - 1)) - _
                      LeftInvisibleCharCount * characterWidth, _
                      (i - 1 - controller.TopInvisibleLineCount) * _
                      (lineSpace + fontHeight), GetStringWidth(thisLine) - _
                      GetStringWidth(thisLine.Substring(0, x1 - 1)), _
                      (lineSpace + fontHeight))

                ElseIf i = endLine Then
                    graphics.FillRectangle(brush, _
                      2 - LeftInvisibleCharCount * characterWidth, _
                      (i - 1 - controller.TopInvisibleLineCount) * _
                      (lineSpace + fontHeight), _
                      GetStringWidth(thisLine.Substring(0, x2 - 1)), _
                      (lineSpace + fontHeight))
                Else
                    ' last line may not be the whole line, 
                    ' but from first column to initial position of selection
                    graphics.FillRectangle(brush, _
                      2 - LeftInvisibleCharCount * characterWidth, _
                      (i - 1 - controller.TopInvisibleLineCount) * _
                      (lineSpace + fontHeight), GetStringWidth(thisLine), _
                      (lineSpace + fontHeight))
                End If
            Next i
        End If
        'don't dispose graphics!!    
    End Sub

    Private Function GetStringWidth(ByRef s As String) As Integer
        If Not s Is Nothing Then
            Return s.Length * characterWidth
        Else
            Return 0
        End If
    End Function


    Public Sub RedrawAll()
        'before redraw correct invisible line count
        controller.TopInvisibleLineCount = _
          Math.Min(controller.TopInvisibleLineCount, _
          model.LineCount - VisibleLineCount)
        If controller.TopInvisibleLineCount < 0 Then
            controller.TopInvisibleLineCount = 0
        End If
        LeftInvisibleCharCount = Math.Min(LeftInvisibleCharCount, _
          model.LongestLineCharCount - VisibleCharCount)
        If LeftInvisibleCharCount < 0 Then LeftInvisibleCharCount = 0
        Me.Invalidate()
        Me.Update()
    End Sub

    Private Sub DisplayCaret()
        Try
            While True
                ' call DrawCaret here
                Dim caretsLine As String = model.GetLine(controller.CaretLineNo)
                Dim x As Integer = GetStringWidth(caretsLine.Substring(0, _
                  controller.CaretColumnNo - 1)) + _
                  penWidth - (LeftInvisibleCharCount * characterWidth)
                Dim y As Integer = (controller.CaretLineNo - 1 - _
                  controller.TopInvisibleLineCount) * (lineSpace + fontHeight)
                Dim caretRectangle As New Rectangle( _
                  x - penWidth, y, 2 * penWidth, lineSpace + fontheight)

                Me.Invalidate(caretRectangle)
                Me.Update()
                If Not caretVisible Then
                    Thread.Sleep(150)
                Else
                    Thread.Sleep(350)
                End If
                caretVisible = Not caretVisible
            End While
        Catch
        End Try
    End Sub

    Public Function TranslateIntoCaretLocation( _
      ByVal x1 As Integer, ByVal y1 As Integer) As ColumnLine
        Dim column, line As Integer ' the coordinate for the returned Point

        'set lowest value for y1 in case the use keeps dragging above the control
        If y1 < 1 Then
            y1 = 1
        End If

        'Get the visible line number
        line = Math.Min(1 + controller.TopInvisibleLineCount + _
          CInt(y1 / (fontHeight + lineSpace)), model.LineCount)
        'Now calculate the closest position of the character in the current line
        Dim thisLine As String = model.GetLine(line)
        Dim i As Integer, minDistance As Single = Me.Width 'the width of this control
        Dim j As Integer = 0
        For i = 0 To thisLine.Length
            Dim distance As Single = _
              Math.Abs(x1 + LeftInvisibleCharCount * characterWidth - _
              GetStringWidth(thisLine.Substring(0, i)))
            If distance < minDistance Then
                minDistance = distance
                j = i
            End If
        Next i
        column = j + 1
        Return New ColumnLine(column, line)
    End Function

    Public Sub RepositionCaret(ByVal x As Integer, ByVal y As Integer)
        'Get the (visible) line number
        Dim lineNumber As Integer = 1 + CInt(y / (fontHeight + lineSpace))
        controller.CaretLineNo = Math.Min(lineNumber + _
          controller.TopInvisibleLineCount, model.LineCount)
        'Now calculate the closest position of the character in the current line
        Dim thisLine As String = controller.GetCurrentLine()
        Dim i As Integer, minDistance As Single = Width ' the width of this control
        Dim j As Integer = 0

        For i = 0 To thisLine.Length
            Dim distance As Integer = _
              Math.Abs(x + LeftInvisibleCharCount * characterWidth - _
              GetStringWidth(thisLine.Substring(0, i)))
            If distance < minDistance Then
                minDistance = distance
                j = i
            End If
        Next i
        controller.CaretColumnNo = j + 1
        RedrawAll()
    End Sub

    Public Function GetCaretXAbsolutePosition() As Integer
        Dim caretsLine As String = controller.GetCurrentLine()
        If Not caretsLine Is Nothing Then
            Return _
              GetStringWidth(caretsLine.Substring(0, controller.CaretColumnNo - 1))
        Else
            Return 0
        End If
    End Function

    Public Function IsCaretVisible() As Boolean
        Dim xPosition As Integer = GetCaretXAbsolutePosition()
        Dim leftInvisibleWidth As Integer = LeftInvisibleCharCount * characterWidth
        If xPosition < leftInvisibleWidth Or _
          xPosition > leftInvisibleWidth + Me.Width - 5 Or _
          controller.CaretLineNo > _
          controller.TopInvisibleLineCount + VisibleLineCount Then
            Return False
        Else
            Return True
        End If
    End Function

    Private Sub InitializeComponent()
        '
        'View
        '
        Me.Name = "View"
        controller.TopInvisibleLineCount = 10
    End Sub
End Class
 
Last edited by a moderator:
you set a boolean flag in textbox_keydown when CTRL is pressed
you reset your boolean flag in textbox_keyup when CTRL is released

then in textbox_keypress, if boolean flag is true and e.keychar = x or c or v then

'etc
 
Back
Top