Repeating Microsoft Word VBA until no search results found - vba

I've created a MS Word macro that searches for certain text (indicated by markup codes), cuts the text and inserts it into a new footnote, and then deletes the markup codes from the footnote. Now I want the macro to repeat until it doesn't find any more markup codes in the text.
Here's the macro below
Sub SearchFN()
'find a footnote
Selection.Find.ClearFormatting
With Selection.Find
.Text = "&&FB:*&&FE"
.Replacement.Text = ""
.Forward = True
.Wrap = wdFindContinue
.Format = False
.MatchCase = False
.MatchWholeWord = False
.MatchKashida = False
.MatchDiacritics = False
.MatchAlefHamza = False
.MatchControl = False
.MatchByte = False
.MatchAllWordForms = False
.MatchSoundsLike = False
.MatchFuzzy = False
.MatchWildcards = True
End With
Selection.Find.Execute
'cut the footnote from the text
Selection.Cut
'create a proper Word footnote
With Selection
With .FootnoteOptions
.Location = wdBottomOfPage
.NumberingRule = wdRestartContinuous
.StartingNumber = 1
.NumberStyle = wdNoteNumberStyleArabic
End With
.Footnotes.Add Range:=Selection.Range, Reference:=""
End With
'now paste the text into the footnote
Selection.Paste
'go to the beginning of the newly created footnote
'and find/delete the code for the start of the note (&&FB:)
Selection.Find.ClearFormatting
Selection.Find.Replacement.ClearFormatting
With Selection.Find
.Text = "&&FB:"
.Replacement.Text = ""
.Forward = False
.Wrap = wdFindContinue
.Format = False
.MatchCase = False
.MatchWholeWord = False
.MatchKashida = False
.MatchDiacritics = False
.MatchAlefHamza = False
.MatchControl = False
.MatchByte = False
.MatchAllWordForms = False
.MatchSoundsLike = False
.MatchFuzzy = False
.MatchWildcards = True
End With
Selection.Find.Execute
With Selection
If .Find.Forward = True Then
.Collapse Direction:=wdCollapseStart
Else
.Collapse Direction:=wdCollapseEnd
End If
.Find.Execute Replace:=wdReplaceOne
If .Find.Forward = True Then
.Collapse Direction:=wdCollapseEnd
Else
.Collapse Direction:=wdCollapseStart
End If
.Find.Execute
End With
'do same for ending code (&&FE)
With Selection.Find
.Text = "&&FE"
.Replacement.Text = ""
.Forward = True
.Wrap = wdFindContinue
.Format = False
.MatchCase = False
.MatchWholeWord = False
.MatchKashida = False
.MatchDiacritics = False
.MatchAlefHamza = False
.MatchControl = False
.MatchByte = False
.MatchAllWordForms = False
.MatchSoundsLike = False
.MatchFuzzy = False
.MatchWildcards = True
End With
Selection.Find.Execute
With Selection
If .Find.Forward = True Then
.Collapse Direction:=wdCollapseStart
Else
.Collapse Direction:=wdCollapseEnd
End If
.Find.Execute Replace:=wdReplaceOne
If .Find.Forward = True Then
.Collapse Direction:=wdCollapseEnd
Else
.Collapse Direction:=wdCollapseStart
End If
.Find.Execute
End With
Selection.HomeKey Unit:=wdStory
'now repeat--but how??
End Sub

Good question this one, you can loop through the whole document using the Selection.Find.Found result.
What you do is start a search and if you find a result go into a loop only while the Selection.Find.Found result is true. Once you've got through these, you're done. The following code should do the trick nicely for you.
Sub SearchFN()
Dim iCount As Integer
'Always start at the top of the document
Selection.HomeKey Unit:=wdStory
'find a footnote to kick it off
With Selection.Find
.ClearFormatting
.Text = "&&FB:*&&FE"
.Replacement.Text = ""
.Forward = True
.Wrap = wdFindContinue
.Format = False
.MatchCase = False
.MatchWholeWord = False
.MatchKashida = False
.MatchDiacritics = False
.MatchAlefHamza = False
.MatchControl = False
.MatchByte = False
.MatchAllWordForms = False
.MatchSoundsLike = False
.MatchFuzzy = False
.MatchWildcards = True
.Execute
End With
'If we find one then we can set off a loop to keep checking
'I always put a counter in to avoid endless loops for one reason or another
Do While Selection.Find.Found = True And iCount < 1000
iCount = iCount + 1
'Jump back to the start of the document. Since you remove the
'footnote place holder this won't pick up old results
Selection.HomeKey Unit:=wdStory
Selection.Find.Execute
'On the last loop you'll not find a result so check here
If Selection.Find.Found Then
''==================================
'' Do your footnote magic here
''==================================
'Reset the find parameters
With Selection.Find
.ClearFormatting
.Text = "&&FB:*&&FE"
.Replacement.Text = ""
.Forward = True
.Wrap = wdFindContinue
.Format = False
.MatchCase = False
.MatchWholeWord = False
.MatchKashida = False
.MatchDiacritics = False
.MatchAlefHamza = False
.MatchControl = False
.MatchByte = False
.MatchAllWordForms = False
.MatchSoundsLike = False
.MatchFuzzy = False
.MatchWildcards = True
End With
End If
Loop
End Sub

This can be done without using Do while(lots of extra lines, and space/time wastage), It could be as simple as follows:
Sub SearchFN()
'Start from The Top
Selection.HomeKey Unit:=wdStory
'Find the first search to start the loop
Do
With Selection.Find
.ClearFormatting
.Replacement.ClearFormatting
.Text = "&&FB:*&&FE"
.Replacement.Text = ""
.Forward = True
.Wrap = wdFindstop
.Format = False
.MatchCase = False
.MatchWholeWord = False
.MatchKashida = False
.MatchDiacritics = False
.MatchAlefHamza = False
.MatchControl = False
.MatchByte = False
.MatchAllWordForms = False
.MatchSoundsLike = False
.MatchFuzzy = False
.MatchWildcards = True
.Execute
End With
'If we found the result then loop started
If Selection.Find.Found Then
'' Do your work here
' Always end your work after the first found result
' else it will be endless loop
Else
'If we do not found any then it will exit the loop
Exit Do
End If
Loop
End Sub

The simplest way to do this is to make the function recursive (the function recalls itself). Add this one line to the bottom of your sub or function:
If (Selection.Find.Found = True) then call SearchFN

Related

Search & Replace 3 carriage returns by 2 until no more exist

I am trying to remove all the occurences of more than 2 carriage returns until no more is found. The text can contain 4 consecutive carriage returns or more, so it can be 5, 8, 10...
I tried this macro copied from this site.
Sub Search3Return()
Dim iCount As Integer
Selection.HomeKey Unit:=wdStory
With Selection.Find
.ClearFormatting
.Text = "^p^p^p"
.Replacement.Text = "^p^p"
.Forward = True
.Wrap = wdFindContinue
.Format = False
.MatchCase = False
.MatchWholeWord = False
.MatchKashida = False
.MatchDiacritics = False
.MatchAlefHamza = False
.MatchControl = False
.MatchByte = False
.MatchAllWordForms = False
.MatchSoundsLike = False
.MatchFuzzy = False
.MatchWildcards = False
.Execute
End With
'If we find one then we can set off a loop to keep checking
'put a counter in to avoid endless loops for one reason or another
Do While Selection.Find.Found = True And iCount < 1000
iCount = iCount + 1
'Jump back to the start of the document.
Selection.HomeKey Unit:=wdStory
Selection.Find.Execute
Loop
End Sub
I don't know why you even bother with VBA for this - unless it's part of a larger VBA project. All you need is a single wildcard Find/Replace, where:
Find = ^13{3,}
Replace = ^p^p
If you want the Find to not only find something but to replace it, you need to specify the Replace-parameter for the Execute-method, see https://learn.microsoft.com/en-us/office/vba/api/word.find
Sub Search3Return()
Dim iCount As Integer
Selection.HomeKey Unit:=wdStory
With Selection.Find
.ClearFormatting
.Text = "^p^p^p"
.Replacement.Text = "^p^p"
.Forward = True
.Wrap = wdFindContinue
.Format = False
.MatchCase = False
.MatchWholeWord = False
.MatchKashida = False
.MatchDiacritics = False
.MatchAlefHamza = False
.MatchControl = False
.MatchByte = False
.MatchAllWordForms = False
.MatchSoundsLike = False
.MatchFuzzy = False
.MatchWildcards = False
Do While iCount < 1000
.Execute Replace:=wdReplaceAll
If Not .Found Then Exit Do
'Jump back to the start of the document.
Selection.HomeKey Unit:=wdStory
iCount = iCount + 1
Loop
End With
End Sub

I want to highlight a word if it is not followed by another specific word using VB

So I'm a total newbie when it comes to using VB. I am trying to highlight a word when it is not followed by another specific word within the next two words. I tried the following code but it seems to just the first word. Many thanks in advance.
Sub fek()
'
'
'
'
Selection.Find.ClearFormatting
With Selection.Find
.Text = "n."
.Forward = True
.Wrap = wdFindContinue
.Format = False
.MatchCase = True
.MatchWholeWord = False
.MatchWildcards = False
.MatchSoundsLike = False
.MatchAllWordForms = False
End With
Selection.Find.Execute
If Selection.Find.Found = True Then
With Selection.Range
.MoveStart wdWord, 2
End With
With Selection.Find
.Text = "fek"
.Forward = True
.Wrap = wdFindContinue
.Format = False
.MatchCase = True
.MatchWholeWord = False
.MatchWildcards = False
.MatchSoundsLike = False
.MatchAllWordForms = False
End With
End If
If Selection.Find.Found = False Then
Selection.Range.HighlightColorIndex = wdYellow
End If
End Sub
The code below should do what you want. You need to bear in mind that what Word defines as a Word can be different to what a human would, e.g. an IP address is counted as 7 words!
Sub fek()
Dim findRange As Range
Dim nextWords As Range
Set findRange = ActiveDocument.Content
With findRange.Find
.ClearFormatting
.Text = "n."
.Forward = True
.Wrap = wdFindStop
.Format = False
.MatchCase = True
.MatchWholeWord = False
.MatchWildcards = False
.MatchSoundsLike = False
.MatchAllWordForms = False
Do While .Execute = True
'findRange is now the range of the match so set nextWords to the 2 next words
Set nextWords = findRange.Next(wdWord)
nextWords.MoveEnd wdWord, 3
'look for the specific text in the next two words
If InStr(nextWords.Text, "fek") = 0 Then findRange.HighlightColorIndex = wdYellow
'collapse and move findRange to the end of the match
findRange.Collapse wdCollapseEnd
findRange.Move wdWord, 4
Loop
End With
End Sub
The following would probably be significantly faster if there are many 'n.' strings in the document:
Sub Demo()
Application.ScreenUpdating = False
Dim i As Long
i = Options.DefaultHighlightColorIndex
Options.DefaultHighlightColorIndex = wdYellow
With ActiveDocument.Range
With .Find
.Forward = True
.Format = False
.MatchCase = False
.Wrap = wdFindContinue
.MatchWildcards = True
.ClearFormatting
.Replacement.ClearFormatting
.Replacement.Highlight = True
.Text = "n."
.Replacement.Text = "^&"
.Execute Replace:=wdReplaceAll
.Replacement.Highlight = False
.Text = "n.[^s ]#fek"
.Execute Replace:=wdReplaceAll
.Text = "n.[^s ]#[!^s ]#fek"
.Execute Replace:=wdReplaceAll
.Text = "n.[^s ]<[!^s ]#>[^s ]#fek"
.Execute Replace:=wdReplaceAll
.Text = "n.[^s ]<[!^s ]#>[^s ]#[!^s ]#fek"
.Execute Replace:=wdReplaceAll
End With
End With
Options.DefaultHighlightColorIndex = i
Application.ScreenUpdating = True
End Sub

add page break with macro (if there isnt any page break)

I want to add a page break before every heading 1 and evey \page bookmark.
This is my code that work :
Sub PageBreack(isok)
If isok <> True Then
Exit Sub
End If
Selection.GoTo What = wdGoToLine, Which = wdGoToFirst
Application.Browser.Target = wdBrowsePage
For i = 1 To ActiveDocument.BuiltInDocumentProperties("Number of Pages")
If i > 1 Then
ActiveDocument.Bookmarks("\page").Range.Select
Selection.InsertBreak Type:=wdSectionBreakContinuous 'wdSectionBreakNextPage
End If
Application.Browser.Next
Next i
For Each p In ActiveDocument.Paragraphs
If Left(p.Style, 9) = "Heading 1" Then
p.Range.Select
Selection.Previous.InsertBreak Type:=wdSectionBreakContinuous
End If
Next
Selection.Find.ClearFormatting
Selection.Find.Replacement.ClearFormatting
With Selection.Find
.Text = "^12": .Replacement.Text = "^m": .Forward = True: .Wrap = wdFindContinue: .Format = False: .MatchCase = False: .MatchWholeWord = False: .MatchKashida = False: .MatchDiacritics = False: .MatchAlefHamza = False: .MatchControl = False: .MatchWildcards = False: .MatchSoundsLike = False: .MatchAllWordForms = False
End With
Selection.Find.Execute Replace:=wdReplaceAll
End Sub
My problem is that this code doesnt check that exist any page break and add a new page break there.
how to change my code that macro check if there isnt any page break add a page break ?
(English is not my native language , i hope explain clearly)
you have blank pages by use page break macro
Here is a macro to delete all the blank pages in a Word Document
Sub Demo()
With ActiveDocument.Content.Find
.ClearFormatting
.Replacement.ClearFormatting
.Text = "^12[^12^13 ]{1,}"
.Replacement.Text = "^12"
.Forward = True
.Wrap = wdFindContinue
.Format = False
.MatchWildcards = True
.Execute Replace:=wdReplaceAll
End With
End Sub
reference:Remove Blank Pages from Docx using word interop
Sub PageBreack(isok)
If isok <> True Then
Exit Sub
End If
Selection.GoTo What = wdGoToLine, Which = wdGoToFirst
Application.Browser.Target = wdBrowsePage
For i = 1 To ActiveDocument.BuiltInDocumentProperties("Number of Pages")
If i > 1 Then
ActiveDocument.Bookmarks("\page").Range.Select
Selection.InsertBreak Type:=wdSectionBreakContinuous 'wdSectionBreakNextPage
End If
Application.Browser.Next
Next i
With ActiveDocument.Styles("Heading 1").ParagraphFormat
.PageBreakBefore = True
End With
Selection.Find.ClearFormatting
Selection.Find.Replacement.ClearFormatting
With Selection.Find
.Text = "^12": .Replacement.Text = "^m": .Forward = True: .Wrap = wdFindContinue: .Format = False: .MatchCase = False: .MatchWholeWord = False: .MatchKashida = False: .MatchDiacritics = False: .MatchAlefHamza = False: .MatchControl = False: .MatchWildcards = False: .MatchSoundsLike = False: .MatchAllWordForms = False
End With
Selection.Find.Execute Replace:=wdReplaceAll
With ActiveDocument.Content.Find
.ClearFormatting
.Replacement.ClearFormatting
.Text = "^12[^12^13 ]{1,}"
.Replacement.Text = "^12"
.Forward = True
.Wrap = wdFindContinue
.Format = False
.MatchWildcards = True
.Execute Replace:=wdReplaceAll
End With
End Sub
If no answers come, You can use your existing code and add one more step of checking for double page breaks.
Selection.Find.Replacement.ClearFormatting
With Selection.Find
.Text = "^m^m": .Replacement.Text = "^m": .Forward = True: .Wrap = wdFindContinue: .Format = False: .MatchCase = False: .MatchWholeWord = False: .MatchKashida = False: .MatchDiacritics = False: .MatchAlefHamza = False: .MatchControl = False: .MatchWildcards = False: .MatchSoundsLike = False: .MatchAllWordForms = False
End With
Selection.Find.Execute Replace:=wdReplaceAll

Macro creates multiple page breaks instead of just one

I created a macro that finds an instance of style H1 followed by style H2, and inserts a page break between them. And reiterates this till it gets to the end of the document.
However, the macro adds multiple page breaks (maybe 50 of them) instead of just one.
what did I do wrong and how to correct this?
The code:
Sub Force_page_break()
'
' Force_page_break Macro
'
'
Dim Eloop As Integer
Eloop = 1
Selection.HomeKey Unit:=wdStory
Do While Eloop = 1
Selection.Find.ClearFormatting
Selection.Find.Style = ActiveDocument.Styles("Heading 1")
With Selection.Find
.Text = ""
.Replacement.Text = ""
.Forward = True
.Wrap = wdFindContinue
.Format = True
.MatchCase = False
.MatchWholeWord = False
.MatchKashida = False
.MatchDiacritics = False
.MatchAlefHamza = False
.MatchControl = False
.MatchWildcards = False
.MatchSoundsLike = False
.MatchAllWordForms = False
End With
Selection.Find.Execute
If Selection.Find.Execute = False Then
Eloop = 0
Exit Do
End If
Selection.Find.ClearFormatting
Selection.Find.Style = ActiveDocument.Styles("Heading 2")
With Selection.Find
.Text = ""
.Replacement.Text = ""
.Forward = True
.Wrap = wdFindContinue
.Format = True
.MatchCase = False
.MatchWholeWord = False
.MatchKashida = False
.MatchDiacritics = False
.MatchAlefHamza = False
.MatchControl = False
.MatchWildcards = False
.MatchSoundsLike = False
.MatchAllWordForms = False
End With
Selection.Find.Execute
Selection.HomeKey Unit:=wdLine
Selection.InsertBreak Type:=wdPageBreak
Loop
ActiveDocument.Save
End Sub
Maybe you can add a line
Eloop = Eloop + 1
after the line
Selection.InsertBreak Type:=wdPageBreak
to get out of the loop?

Advanced find format, copy, and paste

I have a document that has a bunch of projects. I've gone through and certain projects are in red font if they are high priority.
I want to make a macro so that Word finds all instances of red font, saves those project names, and then pastes them in a list at the top of my document.
When I tried to do the Macro Recorder, this is what I got.
Sub HotTopics()
'
' HotTopics Macro
'
'
Selection.Find.ClearFormatting
Selection.Find.Font.Color = wdColorRed
With Selection.Find
.Text = ""
.Font.Color = wdColorRed
.Replacement.Text = ""
.Forward = True
.Wrap = wdFindContinue
.Format = True
.MatchCase = False
.MatchWholeWord = False
.MatchWildcards = False
.MatchSoundsLike = False
.MatchAllWordForms = False
End With
Selection.Copy
Selection.PasteAndFormat (wdFormatOriginalFormatting)
End Sub
When I try running the macro, it says there is an error with Selection.copy because there is nothing to copy
You didn't click Find while recording the macro. Note second last line.
Selection.Find.ClearFormatting
With Selection.Find
.Text = ""
.Replacement.Text = ""
.Forward = True
.Wrap = wdFindContinue
.Format = True
.MatchCase = False
.MatchWholeWord = False
.MatchKashida = False
.MatchDiacritics = False
.MatchAlefHamza = False
.MatchControl = False
.MatchByte = False
.CorrectHangulEndings = False
.MatchAllWordForms = False
.MatchSoundsLike = False
.MatchWildcards = False
.MatchFuzzy = False
End With
Selection.Find.Execute
Selection.Copy