tags:

views:

1239

answers:

1

I've got an old, legacy VB6 application that uses the DHTML editing control as an HTML editor. The Microsoft DHTML editing control, a.k.a. DHTMLEd, is probably nothing more than an IE control using IE's own native editing capability internally.

I'd like to modify the app to implement smart quotes like Word. Specifically, " is replaced with or and ' is replaced with or as appropriate as it is typed; and if the user presses Ctrl+Z immediately after the replacement, it goes back to being a straight quote.

Does anyone have code that does that?

If you don't have code for DHTML/VB6, but do have JavaScript code that works in a browser with contentEditable regions, I could use that, too

+12  A: 

Here's the VB6 version:

Private Sub DHTMLEdit1_onkeypress()
    Dim e As Object
    Set e = DHTMLEdit1.DOM.parentWindow.event
    'Perform smart-quote replacement'
    Select Case e.keyCode
    Case 34: 'Double-Quote'
        e.keyCode = 0
        If IsAtWordEnd Then
            InsertDoubleUndo ChrW$(8221), ChrW$(34)
        Else
            InsertDoubleUndo ChrW$(8220), ChrW$(34)
        End If
    Case 39: 'Single-Quote'
        e.keyCode = 0
        If IsAtWordEnd Then
            InsertDoubleUndo ChrW$(8217), ChrW$(39)
        Else
            InsertDoubleUndo ChrW$(8216), ChrW$(39)
        End If
    End Select
End Sub

Private Function IsLetter(ByVal character As String) As Boolean
    IsLetter = UCase$(character) <> LCase$(character)
End Function

Private Sub InsertDoubleUndo(VisibleText As String, HiddenText As String)
    Dim selection As Object
    Set selection = DHTMLEdit1.DOM.selection.createRange()
    selection.Text = HiddenText
    selection.moveStart "character", -Len(HiddenText)
    selection.Text = VisibleText
End Sub

Private Function IsAtWordEnd() As Boolean

    Dim ch As String
    ch = PreviousChar
    IsAtWordEnd = (ch <> " ") And (ch <> "")

End Function

Private Function PreviousChar() As String

    Dim selection As Object
    Set selection = m_dom.selection.createRange()
    selection.moveStart "character", -1
    PreviousChar = selection.Text

End Function

Note: this solution inserts an additional level in the undo chain. For example, typing "This is a test" gives a chain of “This is a test” -> “This is a test" -> “This is a test -> “ -> " (extra level in bold). To remove this extra level you'd have to implement some sort of PostMessage+subclassing solution that doesn't involve cancelling the native keypress

edit: Don't forget to include the DHTML Editing Control redistributable if you are targeting Windows Vista.

rpetrich
Wow! Thanks! Well done! Worked the first time. I'll still tweak it a tiny bit to handle a few weird cases, but this is terrific.
Joel Spolsky
By the way, I love the implementation of IsLetter()... a true programming pearl!
Joel Spolsky
Thanks for the praise; never thought I'd hear something like that from a programming legend :) Regarding IsLetter: Use IsCharAlphaW or another unicode-aware method instead. My implementation was quick and dirty so as not to detract from the meat of my answer :)
rpetrich