Indent multiple lines the same way as plain text editor - vba

Sometimes I have code blocks in my Word documents, and I want to work with them without copying to plain text editor.
Namely, I want to have an ability to indent/unindent multiple lines of code using "Tab" character. This task is very simple in any plain text editor or IDE, but for the sake of clarity, I will show it here. Tabs are shown as black arrows:
Initial state
Using the Shift key or mouse, I selected a part of JavaScript function
Then I pressed Tab key on my keyboard
Selected lines were indented by inserting tab character on each line.
How it could be done with VBA?
Since I don't post any code (as evidence of my own efforts), I don't expect to get something completely working. But at least, I hope to get an understanding "how" it could be done.
As David suggested, I recorded a macro. Here how it looks:
Sub Indentator()
Selection.TypeText Text:=vbTab
End Sub
The problem is, that I don't understand how to get it work for multiple lines. If I select them, this macro (and it was not surprise for me) just inserts "Tab" instead of selection.

Insert a tab character at the start of each paragraph in the selection:
Sub Indentator()
Dim para As Paragraph
For Each para In Selection.Paragraphs
para.Range.InsertBefore vbTab
Next
End Sub
(This assumes that each of your code "lines" is a new "paragraph" in Word, which it usually would be if you are intending to copy/paste this to/from actual code.)

If the macros are named IncreaseIndent and DecreaseIndent, they can be run using the Increase and Decrease Indent buttons on the Home tab.
Sub IncreaseIndent()
If Selection.Start = Selection.End Then
Selection.InsertBefore vbTab
Selection.Start = Selection.End
Else
Dim p As Paragraph
For Each p In Selection.Paragraphs
p.Range.InsertBefore vbTab
Next
End If
End Sub
Sub DecreaseIndent()
If Selection.Start = Selection.Paragraphs(1).Range.Start Then
Selection.Start = Selection.Start + 1
End If
Dim p As Paragraph, c As Range
For Each p In Selection.Paragraphs
Set c = p.Range.Characters(1)
If c.Text = vbTab Then c.Delete
Next
End Sub
Reference https://wordmvp.com/FAQs/MacrosVBA/InterceptSavePrint.htm

Related

Selection.style applied to wrong paragraph (and other issues)

I have multiple documents coming from the same template. Each of them starts with a chapter number, so that I can build a table of contents that includes all of them. Since chapters have sections and subsections, I created a multilevel list style called MyList. Since they are different documents and all lists would start at 1, I need to specify the chapter number manually.
This can be achieved by writing {LISTNUM MyList \l 1 \s x} on the first line, x being chapter number. I wrote a macro to prompt for the chapter number, insert a carriage return and apply a new paragraph style to type the chapter name:
Chapter 1 (style: ChapNum)
Chapter name here (style: ChapName)
Normal text blah blah
The (working) code I have right now is:
Sub ChapterNumber()
Selection.Style = ActiveDocument.Styles("ChapNum")
ActiveWindow.View.ShowFieldCodes = True
Num = InputBox("Chapter number", "Random title")
SendKeys "^{F9}"
Selection.InsertBefore Text:="LISTNUM MyList \l 1 \s " + Num
SendKeys "%{F9}{RIGHT}{ENTER}"
End Sub
I have encountered a few issues until I finally got there, which are:
I was unable to apply a different paragraph style after the last Sendkeys using Selection.Style = ActiveDocument.Styles("ChapName") since the whole thing converted to ChapName style, not only the paragraph where the cursor was pointing.
I've read Sendkeys is not that reliable so, initially, instead of SendKeys "%{F9}{RIGHT}{ENTER}", I tried
ActiveWindow.View.ShowFieldCodes = False
Selection.EndKey Unit:=wdLine
Sendkeys "{ENTER}"
Neither of the first two commands worked, and I don't know why; I have used them in other macros and never had any problem. Would you please be so kind to clarify? Please take in mind my knowledge of vba is very limited.
Thank you.
Add the text "Chapter " to your template and apply the correct style. Add empty paragraphs in the other two required styles and your template is already setup for you to start typing. All that is then required is to add the LISTNUM field which can be done as shown below.
Sub ChapterNumber()
Dim Num As Long
Num = InputBox("Chapter number", "Random title")
Dim location As Range
With ActiveDocument
Set location = .Paragraphs(1).Range.Characters.Last
location.Move wdCharacter, -1
.Fields.Add Range:=location, Text:="LISTNUM MyList \l 1 \s " & Num
End With
End Sub
EDIT IN RESPONSE TO COMMENTS:
As you appear to have defined numbered styles in your template you do need to add the LISTNUM field. Instead you should set the start at number for the list template attached to the style.
Sub ChapterNumber()
Dim Num As Long
Num = InputBox("Chapter number", "Random title")
ActiveDocument.Styles("ChapNum").ListTemplate.ListLevels(2).StartAt = Num
End Sub

I'm trying to create a simple userform that adds and deletes a block of text in Word

I have a userform with a bunch of checkboxes. I want the VBA code to add a block of text (defined as a variable) if the checkbox is true and remove that block of text if it gets unchecked. As an example, this is what I have for one of the checkboxes:
Private Sub CheckBox1_Click()
Dim Text1 As String
Text1 = "Text test"
If CheckBox1.Value = True Then
Selection.TypeText Text:=Text1
Selection.InsertParagraph
End If
If CheckBox1.Value = False Then
Selection.Delete Text:=Text1
End If
End Sub
First of all, the Selection.Delete Text:=Text1 part is completely wrong. I've tried to google something similar and have been unable to find anything that deletes the content of a variable.
Second of all, there seems to be an error with the Selection.InsertParagraph code. I want it to add a new paragraph between each block of text/variable, however with the way that the code is now, it adds the text block and the paragraphs separately like this if I were to activate the macro 3 times:
Text testText testText test
(new paragraph)
(new paragraph)
(new paragraph)
What I want instead is this:
Text test
(new paragraph)
Text test
(new paragraph)
Text test
(new paragraph)
Answering the first question, for which there is sufficient information to provide an answer...
The best control of where something is inserted and foramtted in a Word document is to use Range objects. There can be only one Selection, but code can work with multiple Ranges.
For inserting a new paragraph immediately following text it's possible to append the new paragraph at the end of the text using the ANSI 13 character, which can be represented in VBA code using vbCr.
Example:
Private Sub CheckBox1_Click()
Dim Text1 As String
Dim rngTarget as Range
Text1 = "Text test"
Set rngTarget = Selection.Range
If CheckBox1.Value = True Then
rngTarget.Text = Text1 & vbCr
End If
'
'If CheckBox1.Value = False Then
' Selection.Delete Text:=Text1
'End If
'''Move to the end of the range and select that for the next iteration
rngTarget.Collapse wdCollapseEnd
rngTarget.Select
End Sub

Macro for Adding Text To Begining of Every Paragraph

I am trying to create a Word macro that will go through a large document that I have and add the text "SAMPLE" to the beginning of every paragraph.
The document contains a Title page, Table of Contents and Headings throughout and I would prefer none of these have the "SAMPLE" text on them, just the paragraphs.
Below is some macro code I have found on various sites and kind of pieced together to do somewhat of what I want. It does place the "SAMPLE" text at the beginning of some paragraphs but not all, usually only the first paragraph of a new section within my document. And it also places it at the end of the Table of Contents and Beginning of the Title page.
I am brand new to macros in Word so any help is appreciated or if there is a better way of doing this perhaps? There might even be some unnecessary bits in this code since it is pieced together from other samples.
Sub SAMPLE()
Application.ScreenUpdating = False
Dim Par As Paragraph, Rng As Range
For Each Par In ActiveDocument.Paragraphs
If Par.Style = "Normal" Then
If Rng Is Nothing Then
Set Rng = Par.Range
Else
Rng.End = Par.Range.End
End If
Else
Call RngFmt(Rng)
End If
If Par.Range.End = ActiveDocument.Range.End Then
Call RngFmt(Rng)
End If
Next
Application.ScreenUpdating = True
End Sub
Sub RngFmt(Rng As Range)
If Not Rng Is Nothing Then
With Rng
.End = .End - 1
.InsertBefore "SAMPLE"
End With
Set Rng = Nothing
End If
End Sub
Provided your Title, Table of Contents and Headings etc. don't use the Normal Style - as they shouldn't - you really don't need a macro for this - all you need is a wildcard Find/Replace where:
Find = [!^13]*^13
Replace = SAMPLE: ^&
and you specify the Normal Style as a Find formatting parameter. You could, of course, record the above as a macro, but that seems overkill unless you're doing this often.

Testing for "hard" page break

How can I test whether the insertion point is at the start of a new page created by a manual page break? It seems like it should be as simple as checking if the preceding character is CHR(12), but that doesn't seem to work.
If Selection.Type = CHR(12) Then
Selection.TypeText Text:="HARD PAGE"
Else
Selection.TypeText Text:="NO HARD PAGE"
End If
Is it just a syntax error or do I have the wrong approach here?
You have to move the selection (or the range) backwards. Selection.Text (or Range.Text) always returns the character following the IP. Of course, you may not want to actually move the selection. That means you can work with a Range object to do the testing.
Since you have to move backwards, anyway, to test whether there's a hard pagebreak, I've put it in a loop so that the selection can be anywhere on the page, to begin with.
Also, I've added a check whether the macro has started on the first page, since you'd otherwise go into an infinite loop, moving backwards from the Selection to the next page.
Sub CheckWhetherHardPageBreak()
Dim rngToCheck As word.Range
Dim pgNr As Long
Dim pgNrChange As Long
Set rngToCheck = Selection.Range
pgNr = rngToCheck.Information(wdActiveEndPageNumber)
If pgNr = 1 Then
MsgBox "Can't start on Page 1"
Exit Sub
End If
pgNrChange = pgNr
Do While pgNrChange = pgNr
rngToCheck.MoveEnd wdCharacter, -1
pgNrChange = rngToCheck.Information(wdActiveEndPageNumber)
Loop
'Extend the selection to include the following character
'So that ASC() works
rngToCheck.MoveEnd wdCharacter, 1
If Asc(rngToCheck.Text) <> 12 Then
'Move it back before the previous character
'as the character immediately following a hard page break is Chr(13)
rngToCheck.MoveEnd wdCharacter, -2
End If
rngToCheck.MoveEnd wdCharacter, 1
If Asc(rngToCheck) = 12 Then
Selection.TypeText Text:="HARD PAGE"
Else
Selection.TypeText Text:="NO HARD PAGE"
End If
End Sub
I think you might be intending to use Chr(13) or Chr(10).
More information here:
Stack Overflow: What are carriage return, linefeed, and form feed?

How can I automatically highlight and extract colored text in MS Word?

I have a bunch of documents that need to be edited. The authors use blue text in some parts of the documents to indicate that those words need to be linked.
Thank you cornelius for the highlight text code:
Sub HighlightNotBlack()
Dim char As Range
For Each char In ActiveDocument.Characters
If char.Font.Color <> wdColorAutomatic And char.Font.Color <> wdColorBlack Then
char.HighlightColorIndex = wdYellow
End If
Next
End Sub
I would like to expand on this question. Is it possible to extract out only the highlighted text into a new word file? It should also be smart enough to know that two or more consecutive words in a sentence would all appear on one line in the new document as opposed to each word getting its own line in the new document. I figure it could look for all highlighted selections and only bring those over since the whole block of text would be entirely highlighted
I came with something like this. It highlights all non-black and non-automatic characters in active document.
Sub HighlightNotBlack()
Dim char As Range
For Each char In ActiveDocument.Characters
If char.Font.Color <> wdColorAutomatic And char.Font.Color <> wdColorBlack Then
char.HighlightColorIndex = wdYellow
End If
Next
End Sub