Click here to Skip to main content
Licence 
First Posted 26 Mar 2001
Views 32,872
Bookmarked 9 times

Emacs-style Clipboard Ring

By | 26 Mar 2001 | Article
Enhanced multi-clipboard similar to the Emacs kill/yank ring.

Introduction

Reading CodeProject and seeing some of the cool VC++ plug-ins out there has almost convinced me to move my primary editor to VC++, away from (gasp! my friends at college would kill me!) Emacs. But I've been missing some stuff, the kill-ring with multi-yank being one of them.

The following is inspired by Emacs' kill-ring behavior and owes its existence to the code published by Michael Pelts in Multiple Clipboard Macros. That project showed me enough of VC++, VBScript to convince me to try this out. The current implementation addresses the two major issues I had with Mr. Pelts' code: incompatibility with "Virtual Space" mode, and incompatibility with the standard Windows clipboard. I believe that these issues are solved by the code below.

Description

Basically, this adds multiple clipboard functionality (a stack of past copy/cuts) to VC++ in a very clean manner. The only difference from normal usage is that if one does Shift + Paste (default key-binding), then past clipboard entries (well, ones from VC++, anyways) are available via subsequent Shift + Pastes. The extended-functionality via "Shift" is done so as not to interfere with normal pastes, which do not leave the pasted text selected. Note, just to emphasize: when text is added to the clipboard from an external application, it _is_ available to be Shift + Pasted.

MClipboardCut               ' Functionally equivalent to Cut, except
                            ' makes the text available for subsequent
                            ' MClipboardPastes.

MClipboardCopy              ' Ditto above, but with Copy.

MClipboardPaste             ' Pastes most recent Windows Clipboard
                            ' entry into the current text window,
                            ' leaving the selection active on the
                            ' paste.  Subsequent calls without changing
                            ' the selection replaces the selection with
                            ' the previous MCCut or MCCopy text.

MClipboardClear             ' Manually clear out the clipboard ring.

MClipboardDefaultBindings   ' Assigns keys as follows:
                            '     MCCut:   Ctrl+X,       Shift+Del
                            '     MCCopy:  Ctrl+C,       Ctrl+Ins
                            '     MCPaste: Ctrl+Shift+V, Ctrl+Shift+Ins
                            '     MCClear: Ctrl+Shift+X, Ctrl+Shift+Del

Notes

Two known issues (so far): The first is interaction with a column-mode selection (Alt + Mouse_drag). This does not MClipboardPaste the same way that standard Paste does. The second known issue occurs when doing a series of MClipboardPastes. If during the middle of this series, another application is used to add something to the clipboard, this clipboard text is not made available to the MClipboard ring. This shouldn't be a major issue, as the typical usage scenario is to do a series of MClipboardPastes and then move on.

The code has been roughly tested on VC++ 6.0 (and only VC++ 6.0). It might work on prior versions as well, although I don't have enough VBScript experience (this is my first go at it) to hazard a guess, and I don't have a prior version of VC++ to test with. Let me know if it does, however. =)

Have at it, folks, and enjoy!

'----------------------------------------------------------------------
'FILE DESCRIPTION: Multiple clipboard a-la Emacs kill-ring.
'----------------------------------------------------------------------
' File: MClipboard.dsm

' Concept code from Michael Pelts (mp_develop@hotmail.com)
' Sanded and polished up by: Damon Otoshi (damon@TheCrixa.net)


Dim nRingMaxSize
Dim nMaxValidExtract
Dim nRingInsertionPos
Dim nRingExtractionPos
Dim curCol
Dim curLine
Dim lastPaste
Dim lastPasteCol
Dim lastPasteLine

nRingMaxSize        = 10
Dim Ring(9)                 ' lame VBScript: mRingMaxSize-1
nMaxValidExtract    = 0     ' this -1 is index of max valid ring data
nRingInsertionPos   = -1
nRingExtractionPos  = 0
curCol              = 0
curLine             = 0
lastPaste           = ""
lastPasteCol        = 0
lastPasteLine       = 0

'==================================================================

Private Sub GetSelIntoRing()

    ' check for multi-insert, and ignore
    If (nRingInsertionPos <> -1) Then
        If (ActiveDocument.Selection = Ring(nRingInsertionPos)) Then
            Exit Sub
        End If
    End If

    nRingInsertionPos = (nRingInsertionPos + 1) Mod nRingMaxSize
    Ring(nRingInsertionPos) = ActiveDocument.Selection

    If (nMaxValidExtract < nRingMaxSize) Then
        nMaxValidExtract = nMaxValidExtract + 1
    End If

End Sub

'==================================================================

Private Sub SetNextExtractionPos()

    If (nMaxValidExtract = 0) Then
        nRingExtractionPos = 0
        Exit Sub
    End If

    If (nRingExtractionPos = 0) Then
        nRingExtractionPos = nMaxValidExtract - 1
        Exit Sub
    End If

    nRingExtractionPos = nRingExtractionPos - 1

End Sub

'==================================================================

Private Sub GetSelOnRecentYankAndUpdateLasts

    Dim col
    Dim line
    col  = ActiveDocument.Selection.CurrentColumn
    line = ActiveDocument.Selection.CurrentLine
    ActiveDocument.Selection.MoveTo curLine, curCol
    ActiveDocument.Selection.MoveTo line, col, dsExtend
    lastPaste = ActiveDocument.Selection
    lastPasteCol = curCol
    lastPasteLine = curLine

End Sub

'==================================================================

Sub MClipboardCopy ()
'DESCRIPTION: Multiple clipboard - copy

    If (ActiveDocument Is Nothing) Then
        Exit Sub
    ElseIf (ActiveDocument.Type <> "Text") Then
        Exit Sub
    ElseIf (Len(ActiveDocument.Selection) = 0) Then
        Exit Sub
    End If

    GetSelIntoRing()
    nRingExtractionPos = nRingInsertionPos
    ActiveDocument.Selection.Copy

End Sub

'==================================================================

Sub MClipboardCut ()
'DESCRIPTION: Multiple clipboard - cut

    If (ActiveDocument Is Nothing) Then
        Exit Sub
    ElseIf (ActiveDocument.Type <> "Text") Then
        Exit Sub
    ElseIf (Len(ActiveDocument.Selection) = 0) Then
        Exit Sub
    End If

    GetSelIntoRing()
    nRingExtractionPos = nRingInsertionPos
    ActiveDocument.Selection.Cut
End Sub

'==================================================================

Sub MClipboardPaste ()
'DESCRIPTION: Multiple clipboard - paste

    If (ActiveDocument Is Nothing) Then
        Exit Sub
    ElseIf (ActiveDocument.Type <> "Text") Then
        Exit Sub
    End If

    Dim currentSel
    currentSel = ActiveDocument.Selection
    ActiveDocument.Selection = ""

    curCol = ActiveDocument.Selection.CurrentColumn
    curLine = ActiveDocument.Selection.CurrentLine

    ' empty ring
    If  (nMaxValidExtract = 0) Then
        ' nothing in ring, try from clipboard, stick in ring
        ActiveDocument.Selection.Paste
        GetSelOnRecentYankAndUpdateLasts
        GetSelIntoRing
        Exit Sub
    End If

    ' recursion case, lotsa "And"s look ugly
    If  (currentSel = lastPaste) Then
        If  (curCol = lastPasteCol) Then
            If  (curLine = lastPasteLine) Then
                ' we just did a MClipboardPaste
                ActiveDocument.Selection = Ring(nRingExtractionPos)
                GetSelOnRecentYankAndUpdateLasts
                SetNextExtractionPos
                Exit Sub
            End If
        End If
    End If

    ' Last command wasn't a MClipboardPaste.  Yank from clipboard.
    ActiveDocument.Selection.Paste
    GetSelOnRecentYankAndUpdateLasts
    If (lastPaste <> Ring(nRingInsertionPos)) Then
        ' this is something we didn't insert into ring, grab it
        GetSelIntoRing
    Else
        ' this was something just inserted into ring, decrement ring
        SetNextExtractionPos
    End If

End Sub

'==================================================================

Sub MClipboardClear ()
'DESCRIPTION: Resets the multiClipboardBuffer.
    nMaxValidExtract    = 0
    nRingInsertionPos   = -1
    nRingExtractionPos  = 0
    curCol              = 0
    curLine             = 0
    lastPaste           = ""
    lastPasteCol        = 0
    lastPasteLine       = 0

    Dim i
    i = 0
    Do Until (i = nRingMaxSize)
        Ring(i) = ""
        i = i + 1
    Loop
End Sub

'==================================================================

Sub MClipboardDefaultBindings ()
'DESCRIPTION: Sets default key bindings.

    AddKeyBinding "CTRL+X",         "MClipboardCut",   "Text"
    AddKeyBinding "SHIFT+DEL",      "MClipboardCut",   "Text"

    AddKeyBinding "CTRL+C",         "MClipboardCopy",  "Text"
    AddKeyBinding "CTRL+INS",       "MClipboardCopy",  "Text"

    AddKeyBinding "CTRL+SHIFT+V",   "MClipboardPaste", "Text"
    AddKeyBinding "CTRL+SHIFT+INS", "MClipboardPaste", "Text"

    AddKeyBinding "CTRL+SHIFT+X",   "MClipboardClear", "Text"
    AddKeyBinding "CTRL+SHIFT+DEL", "MClipboardClear", "Text"

End Sub

'==================================================================
' eof

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

About the Author

Damon Otoshi



United States United States

Member



Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board. (secure sign-in)
 
Search this forum  
 FAQ
    Noise  Layout  Per page   
  Refresh
-- There are no messages in this forum --
Permalink | Advertise | Privacy | Mobile
Web01 | 2.5.120515.1 | Last Updated 27 Mar 2001
Article Copyright 2001 by Damon Otoshi
Everything else Copyright © CodeProject, 1999-2012
Terms of Use
Layout: fixed | fluid