参照元に参照先の通し行番号を書き込む(2)
参照元に参照先の通し行番号を書き込む(2)
たとえば、
みたいなドキュメントがあるとする。
参照指示性を高めるために、全ての行に通しで行番号を振っている。
これは、芦田宏直氏のアイディアで、詳しいことは氏の著書『シラバス論』(2019 晶文社)をご覧ください。
問題は、文中に「〇〇行目を参照」などと書いた場合。
「相互参照」機能を使えば、段落番号とかページ番号なんかは設定できるんだが、通しの行番号というのがない。
参照先の位置などというものは、編集の都合で揺れ動くものなので、「相互参照」的に設定できないのは非常につらい。
そこで、
このとき、ブックマークを利用して参照先の通しの行番号を割り出し、参照元の行番号の部分を書き換える方法を編み出した。
あとは、前回発覚した
ブックマーク部分の文字を書き換えたらブックマークが消滅する問題
を解消すればよろしい。
消滅するブックマークを復活させる考え方
まず、次のように考えた
- 参照元の
[Range].Text
を書き換える前に、参照元のRange
オブジェクトを取得しておく - 参照元の
[Bookmark].Range.Text
を書き換える Document.Bookmarks.Add
メソッドを、参照元のブックマーク名、1.で取得したRange
オブジェクトを渡して実行する
これでうまくいく、と思った。
しかし、これではうまくいかない。
2.で参照元の[Bookmark].Range.Text
を書き換えた時点で、1.で取得したRange
オブジェクトが潰れてしまっているのだ。
たとえば、「○行目」の「○」の部分(1文字選択の状態)だったはずのRange
オブジェクトが、2.で「48行目」というふうに書き換えたとすると、「48」の手前のカンチャン(0文字選択の状態)になってしまうのだ。
これではまずい。
そこで、次のような手順を踏むことにした。
- 参照元の
[Range].Text
を書き換える前に、参照元のRange
オブジェクトを取得しておく - 参照元の
[Bookmark].Range.Text
を書き換える - 1.で取得した
Range
オブジェクトについてSelect
メソッドを実行する。(書き換えた行番号の手前にカーソルが移動する。) Selection.MoveRight
メソッドを用いて、行番号の文字数分だけカーソルを右に動かす。(右にドラッグする。)Document.Bookmarks.Add
メソッドを、参照元のブックマーク名、4.で取得したSelection.Range
オブジェクトを渡して実行する
こんなふうにした。
参照先の行番号に応じて参照元を書き換えるメソッド
上記の考えに基づいて作成したメソッドがコチラ。
リスト1
Public Sub refreshLineNumberReference( _ ByVal TargetDocument As Document, _ ByVal ReferrerName As String, _ ByVal ReferenceName As String) '### 参照先ブックマークのある行番号を取得して、参照元 ###' '### ブックマークの箇所の行番号を書き換える ###' '///ReferrerName :参照元ブックマーク名' '///ReferenceName :参照先ブックマーク名' Dim Doc As Document Set Doc = TargetDocument '参照先、参照元ブックマークが存在しなかったらExit' If Not bookmarkExists(Doc, ReferenceName) Then Exit Sub If Not bookmarkExists(Doc, ReferrerName) Then Exit Sub 'メインの処理' '参照元ブックマークを取得' Dim bmFrom As Bookmark Set bmFrom = Doc.Bookmarks(ReferrerName) '参照先の行番号を取得' Dim lineNum As Long lineNum = getLineNumber(Doc.Bookmarks(ReferenceName).Range) '参照元のRangeオブジェクトを取得' Dim tgtRange As Range Set tgtRange = bmFrom.Range '参照元の行番号を書き換える' Dim tmp As String tmp = CStr(lineNum) bmFrom.Range.Text = tmp '参照元ブックマークが消滅しているので、復元する' 'Rangeオブジェクトが潰されてしまっているので、行番号を表す' '文字数分右に広げてRagneオブジェクトを取得し直す' Call tgtRange.Select Call Selection.MoveRight(wdCharacter, Len(tmp), wdExtend) Set tgtRange = Selection.Range '再度ブックマークを設定する' Call Doc.Bookmarks.Add(ReferrerName, tgtRange) End Sub Private Function bookmarkExists( _ ByVal tgtDoc As Document, _ ByVal tgtName As String) As Boolean '///ブックマーク名の存否を確認' bookmarkExists = True Dim i As Long For i = 1 To tgtDoc.Bookmarks.Count If tgtDoc.Bookmarks(i).Name = tgtName Then Exit Function End If Next bookmarkExists = False End Function Public Function getLineNumber( _ ByVal tgtRange As Range) As Long Dim ret As Long '///tgtRangeのあるページ番号を取得' Dim currPage As Long currPage = tgtRange.Information(wdActiveEndPageNumber) 'tgtRangeのあるページ内での行番号を取得' Dim currLine As Long currLine = tgtRange.Information(wdFirstCharacterLineNumber) 'tgtRangeが1ページ目にあるときは、その行番号を返す' If currPage = 1 Then ret = currLine GoTo Finalizer: End If '2ページ以上ある時は、手前のページまでの累計を足さなければいけない' Dim Doc As Document Set Doc = tgtRange.Parent 'カーソル位置を記録' Dim orgRange As Range Set orgRange = Selection.Range '文書の先頭にカーソルを置く' Call Doc.Range(0, 0).Select '1ページ目の最終位置を取得' Dim pageEnd As Long '1ページ目の最終位置を選択' Dim i As Long For i = 1 To currPage - 1 pageEnd = Doc.Bookmarks("\Page").End Call Doc.Range(pageEnd - 1, pageEnd - 1).Select ret = ret + Selection.Range.Information(wdFirstCharacterLineNumber) '次のページの先頭へ' Call Selection.MoveRight(wdCharacter, 1, wdMove) Next ret = ret + currLine 'カーソル位置を戻す' Call orgRange.Select Finalizer: getLineNumber = ret End Function
Range
オブジェクトのある通し行番号を返すメソッドのコードを再掲したので、異様にタテ長になってしまったが、気にしないでくだされ。
今回もかなり細かくコメントを入れたので、説明は省略。
使ってみる
このように、参照先と参照元にそれぞれ「参照先01」、「参照元01」という名前のブックマークを設定し、次のコードで使ってみる。
リスト2
Private Sub test00() Dim Doc As Document Set Doc = Application.ActiveDocument Call LineNumUtil.refreshLineNumberReference( _ Doc, _ "参照元01", _ "参照先01") End Sub
ほれ。このように、ちゃんと参照先の行番号に置き換わっておる。
さらに、参照先をテキトーに動かしてから実行しても、ちゃんと参照先の行番号に置き換わっておる。
おわりに
やはりWordのRange
オブジェクトは癖が強い。まだまだ理解が足りないな……。
もっとわかってきたら、洗練されてくると思う。