Stuart Radcliffe

One step at a time
posts - 196, comments - 69, trackbacks - 6

Copying HTML Link to the Clipboard

I've been using OneNote for a while now and its great.  In fact I'm typing this into OneNote right now.  The one thing that really annoys me, though, is the lack of an insert hyperlink command.  Now, this does not need to be as advanced as Word, although as common parts of the Office System it would have been nice, but even some simple functionality would have been a huge improvement over none.

I have been using BlogJet as a way of doing this but it’s a little bit clumsy having to open BlogJet, create the hyperlink then copy and paste it into OneNote.  I thought that I could make the process a bit easier.  And the BlogJet beta had run out and it looked like I would have to pay to continue to use it.

So, I thought, "This will be pretty easy.  I don't need much.  Just something to pop up and let me type in a url and some text for it and let me paste that into OneNote."

It wasn't quite as straightforward as that but it wasn't too bad either.  The first try at it got me to the user interface and some knowledge of the Clipboard object and it looked pretty straightforward.

 

Dim strURL As String As String
strURL = "" & _
LinkText.Text & "
"

Clipboard.SetDataObject(strURL)

This worked fine to paste the actual text but wasn't what I was looking for.  I wanted the text showing with the URL hidden but available.  So the result of the HTML rather than the code.  I knew this was possible as you can paste from Internet Explorer and it will paste quite happily into OneNote in the format that I wanted.  OneNote would try to be clever here and put in the source as well but the basic principle was that it should work.

So with a little more investigation I discovered the different data formats that could be contained in the clipboard and updated my code again.

Dim strURL As String, strHTML As String

strURL = "" & _
LinkText.Text & "
"

Dim data_object As New DataObject
data_object.SetData(DataFormats.Text, True, strURL)
data_object.SetData(DataFormats.Html, True, strURL)
data_object.SetData(DataFormats.Rtf, True, LinkText.Text)

Clipboard.SetDataObject(data_object, True)
Me.Close()

At first glance this seemed to be moving backwards as I could no longer paste it into OneNote at all.  So back to researching the clipboard and I came across ClipSpy on The Code Project.  This is a great tool from Michael Dunn that lets you see the various formats stored in the clipboard at any time.  Well, looking at the HTML format, it was clear there was more going on here than it first appeared.  So, after a bit of hacking and a pretty much ripped-off piece of Delphi code I found on Newsnet I came up with a workable solution.  I even added some sensible behaviour if there is text already in the clipboard when you run it.  If you have Version 1.1 of the .NET framework you can get the executable here.

Public Class EnterLink

Inherits System.Windows.Forms.Form

#Region " Windows Form Designer generated code "

Public Sub New()

MyBase.New()

'This call is required by the Windows Form Designer.

InitializeComponent()

'Add any initialization after the InitializeComponent() call

End Sub

'Form overrides dispose to clean up the component list.

Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean)

If disposing Then

If Not (components Is Nothing) Then

components.Dispose()

End If

End If

MyBase.Dispose(disposing)

End Sub

'Required by the Windows Form Designer

Private components As System.ComponentModel.IContainer

'NOTE: The following procedure is required by the Windows Form Designer

'It can be modified using the Windows Form Designer.

'Do not modify it using the code editor.

Friend WithEvents Label1 As System.Windows.Forms.Label

Friend WithEvents Label2 As System.Windows.Forms.Label

Friend WithEvents OKButton As System.Windows.Forms.Button

Friend WithEvents LinkText As System.Windows.Forms.TextBox

Friend WithEvents LinkURL As System.Windows.Forms.TextBox

Friend WithEvents ForgetItButton As System.Windows.Forms.Button

Private Sub InitializeComponent()

Me.Label1 = New System.Windows.Forms.Label

Me.OKButton = New System.Windows.Forms.Button

Me.LinkText = New System.Windows.Forms.TextBox

Me.Label2 = New System.Windows.Forms.Label

Me.LinkURL = New System.Windows.Forms.TextBox

Me.ForgetItButton = New System.Windows.Forms.Button

Me.SuspendLayout()

'

'Label1

'

Me.Label1.Location = New System.Drawing.Point(8, 16)

Me.Label1.Name = "Label1"

Me.Label1.Size = New System.Drawing.Size(48, 23)

Me.Label1.TabIndex = 0

Me.Label1.Text = "Text"

'

'OKButton

'

Me.OKButton.Location = New System.Drawing.Point(312, 8)

Me.OKButton.Name = "OKButton"

Me.OKButton.TabIndex = 2

Me.OKButton.Text = "OK"

'

'LinkText

'

Me.LinkText.Location = New System.Drawing.Point(72, 8)

Me.LinkText.Name = "LinkText"

Me.LinkText.Size = New System.Drawing.Size(208, 20)

Me.LinkText.TabIndex = 0

Me.LinkText.Text = "Link"

'

'Label2

'

Me.Label2.Location = New System.Drawing.Point(8, 56)

Me.Label2.Name = "Label2"

Me.Label2.Size = New System.Drawing.Size(48, 23)

Me.Label2.TabIndex = 3

Me.Label2.Text = "URL"

'

'LinkURL

'

Me.LinkURL.Location = New System.Drawing.Point(72, 48)

Me.LinkURL.Name = "LinkURL"

Me.LinkURL.Size = New System.Drawing.Size(208, 20)

Me.LinkURL.TabIndex = 1

Me.LinkURL.Text = "http://"

'

'ForgetItButton

'

Me.ForgetItButton.DialogResult = System.Windows.Forms.DialogResult.Cancel

Me.ForgetItButton.Location = New System.Drawing.Point(312, 48)

Me.ForgetItButton.Name = "ForgetItButton"

Me.ForgetItButton.TabIndex = 3

Me.ForgetItButton.Text = "Cancel"

'

'EnterLink

'

Me.AcceptButton = Me.OKButton

Me.AutoScaleBaseSize = New System.Drawing.Size(5, 13)

Me.CancelButton = Me.ForgetItButton

Me.ClientSize = New System.Drawing.Size(416, 101)

Me.Controls.Add(Me.ForgetItButton)

Me.Controls.Add(Me.LinkURL)

Me.Controls.Add(Me.Label2)

Me.Controls.Add(Me.LinkText)

Me.Controls.Add(Me.OKButton)

Me.Controls.Add(Me.Label1)

Me.Name = "EnterLink"

Me.Text = "Enter Link"

Me.ResumeLayout(False)

End Sub

#End Region

 

Private Sub OKButton_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles OKButton.Click

Dim strURL As String, strHTML As String

strURL = "" & _

LinkText.Text & ""

strHTML = MakeFragment(strURL)

Dim data_object As New DataObject

data_object.SetData(DataFormats.Text, True, strURL)

data_object.SetData(DataFormats.Html, True, strHTML)

data_object.SetData(DataFormats.Rtf, True, LinkText.Text)

Clipboard.SetDataObject(data_object, True)

Me.Close()

End Sub

Private Function MakeFragment(ByVal HTML As String) As String

' Helper routine to build a properly-formatted HTML fragment.

Const Version As String = "Version:1.0" & vbCrLf

Const StartHTML As String = "StartHTML:"

Const EndHTML As String = "EndHTML:"

Const StartFragment As String = "StartFragment:"

Const EndFragment As String = "EndFragment:"

Const DocType = "

Const HTMLIntro As String = "

Const HTMLExtro As String = ""

Const NumberLengthAndCR As Integer = 10

' Let the compiler determine the description length.

Dim DescriptionLength As Integer = Version.Length + StartHTML.Length + _

EndHTML.Length + StartFragment.Length + _

EndFragment.Length + 4 * NumberLengthAndCR

Dim Description As String

Dim StartHTMLIndex, EndHTMLIndex, StartFragmentIndex, EndFragmentIndex As Integer

' The HTML clipboard format is defined by using byte positions in the

'entire block where HTML text and

'fragments start and end. These positions are written in a

'description. Unfortunately the positions depend on the

'length of the description but the description may change with

'varying positions.

'To solve this dilemma the offsets are converted into fixed length

'strings which makes it possible to know

'the description length in advance.

StartHTMLIndex = DescriptionLength

StartFragmentIndex = StartHTMLIndex + DocType.Length + HTMLIntro.Length

EndFragmentIndex = StartFragmentIndex + HTML.Length

EndHTMLIndex = EndFragmentIndex + HTMLExtro.Length

Dim sb As New System.Text.StringBuilder(Version)

sb.Append(StartHTML)

sb.Append(StartHTMLIndex.ToString.PadLeft(8, "0"))

sb.AppendFormat(vbCrLf)

sb.Append(EndHTML)

sb.Append(EndHTMLIndex.ToString.PadLeft(8, "0"))

sb.Append(vbCrLf)

sb.Append(StartFragment)

sb.Append(StartFragmentIndex.ToString.PadLeft(8, "0"))

sb.Append(vbCrLf)

sb.Append(EndFragment)

sb.Append(EndFragmentIndex.ToString.PadLeft(8, "0"))

sb.Append(vbCrLf)

sb.Append(DocType)

sb.Append(HTMLIntro)

sb.Append(HTML)

sb.Append(HTMLExtro)

Return sb.ToString

End Function

Private Sub CancelButton_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ForgetItButton.Click

Me.Close()

End Sub

Private Sub InsertExistingLink(ByVal HTML As String, ByVal TextLink As String)

Dim LinkStartIndex As Integer = HTML.ToUpper.IndexOf("

If LinkStartIndex > 0 Then

Dim LinkEndIndex As Integer = HTML.ToUpper.IndexOf("") + 4

Dim FullLink As String = HTML.Substring(LinkStartIndex, LinkEndIndex - LinkStartIndex)

Dim hrefStartIndex As Integer = FullLink.IndexOf(Chr(34)) + 1

Dim hrefEndIndex As Integer = FullLink.IndexOf(Chr(34), hrefStartIndex + 1) - 1

Dim LinkTextStartIndex As Integer = FullLink.IndexOf(">", hrefEndIndex + 1) + 1

Dim LinkTextEndIndex As Integer = FullLink.ToUpper.IndexOf("

LinkText.Text = FullLink.Substring(LinkTextStartIndex, LinkTextEndIndex - LinkTextStartIndex + 1)

Dim Link As String = FullLink.Substring(hrefStartIndex, hrefEndIndex - hrefStartIndex + 1)

LinkURL.Text = CheckRelativeLink(Link, HTML)

Else

InsertTextLink(TextLink)

End If

End Sub

Private Function CheckRelativeLink(ByVal URL As String, _

ByVal HTML As String) As String

If URL.ToUpper.StartsWith("HTTP") Then

Return URL

Else

Dim SourceStartIndex As Integer = HTML.ToUpper.IndexOf("SOURCEURL") + 10

Dim SourceEndIndex As Integer = HTML.IndexOf(Chr(13), SourceStartIndex) - 1

Dim Source As String = HTML.Substring(SourceStartIndex, SourceEndIndex - SourceStartIndex + 1)

If URL.StartsWith("/") AndAlso Source.EndsWith("/") Then

Source = Source.TrimEnd("/")

End If

Return Source & URL

End If

End Function

Private Sub InsertTextLink(ByVal Clip As String)

If Not Clip Is Nothing Then

If Clip.ToUpper.StartsWith("HTTP") Then

LinkURL.Text = Clip

ElseIf Clip.ToUpper.StartsWith("WWW") Then

LinkURL.Text &= Clip

Else

LinkText.Text = Clip

End If

End If

End Sub

Private Sub EnterLink_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load

Dim DataObj As DataObject

DataObj = Clipboard.GetDataObject()

If DataObj.GetDataPresent(DataFormats.Html) Then

InsertExistingLink(DataObj.GetData(DataFormats.Html), DataObj.GetData(DataFormats.Text))

ElseIf DataObj.GetDataPresent(DataFormats.Text) Then

InsertTextLink(DataObj.GetData(DataFormats.Text))

End If

End Sub

End Class

posted on Sunday, May 09, 2004 10:17 PM