A VBA Macro that doesn't know when to stop.. :)

C

Cumulous

I needed a Macro for Word 2003 that would process a selected block of
text (with or without line breaks) - and prefix every line with "> ", to
emulate what happens when an email program Quotes a message you are replying
to.

With the help of Doug Robbins, I ended up with the code listed below.

---------------------------------------------

Sub QuoteSelection()
Dim oRng As Range
Set oRng = Selection.Range
Selection.Collapse
While Selection.Range.End < oRng.End
Selection.HomeKey Unit:=wdLine
Selection.EndKey Unit:=wdLine, Extend:=wdExtend
If Asc(Selection.Text) <> 13 Then
Selection.HomeKey Unit:=wdLine
Selection.TypeText "> "
Selection.MoveDown
Selection.HomeKey Unit:=wdLine
Else
' Empty line with just a paragraph mark
Selection.HomeKey Unit:=wdLine
Selection.TypeText "> "
Selection.MoveDown
End If
Wend
End Sub

----------------------------------------------


However, there is a problem with this revised code.

When the initial selection of text goes to the very end of the
document - the
Macro keeps adding "> " to the last line in an endless cycle until Word
crashes.

Basically, the code tells it to move down a line - but it has nowhere to
go, so the characters keep being entered endlessly.

How can this be altered so that for a selection that goes to the end of
a document, it knows when to stop?


Cumulous
 
E

Ed

You might want to check out "predefined bookmarks" in VBA help. Word
automatically establishes several predefined bookmarks in every document;
one of them is "\EndOfDoc". Is there some way you could use this? Like
maybe:
Selection.EndKey Unit:=wdLine
If ActiveDocument.Bookmarks("\EndOfDoc").Exists = False Then
' back to the start of the line and run code

Ed

PS: Please note: I've never used this, so I'm not sure the way I've
presented it is correct. I ran across it the other day and thought it might
be useful for some things I have in the works. Hopefully, someone else will
chime in.
 
J

JGM

Hi Cumulous,

Try this slightly (Well, OK, quite different!) different code:
'_______________________________________
Sub QuoteSelection()

Dim oRng As Range
Dim CountEnd As Integer

CountEnd = 0
Set oRng = Selection.Range
Selection.Collapse

'check if only last line selected and
'if it is not a lone paragraph mark
Selection.EndKey Unit:=wdLine
If Selection.Range.End + 1 = ActiveDocument.Range.End _
And Asc(oRng.Text) <> 13 Then
CountEnd = 1
End If
Selection.Start = oRng.Start
Selection.Collapse

'Add "> "
Do While Selection.Range.End < oRng.End
If Selection.Range.End + 1 = ActiveDocument.Range.End Then
CountEnd = CountEnd + 1
End If
If CountEnd > 1 Then Exit Do
Selection.HomeKey Unit:=wdLine
Selection.TypeText "> "
Selection.MoveDown
Selection.EndKey Unit:=wdLine
Loop

oRng.Select

End Sub
'_______________________________________

HTH
Cheers!
 
C

Cumulous

Hi, JGM - thank you for replying!

Well, to begin with, your code works pretty well - and it most certainly
deals with the problem of a selection that extends to the end of the
document.

However, it does introduce one interesting bug - that shows itself in
two ways:


---------------------------------------------

Situation 1:

Conditionals:

1. If you have a single line of text selected
2. If that single line of selected text does NOT end in a Line
Break/Carriage Return/etc... (it wraps)
3. If the line directly above it DOES end in a Line Break/Carriage
Return/etc...

Result:

The line above your selected line gets the inserted "> ". The
selected line gets nothing.

--------------------

Situation 2:

Conditionals:

1. If you have a block of text selected (more than one line)
2. If Conditionals 2 and 3 from Situation 1 applies to the TOP line
of this selected block of text

Result:

The entire selected block of text is properly processed - however,
the line directly above the selected block is ALSO processed.

-----------------------------------------------


What I can see is that when you have the top (or only) line of the
selection meeting Conditionals 2 and 3 of Situation 1 - the problem is
taking place within this section of code:

---------------------------------------
Selection.Start = oRng.Start
Selection.Collapse
---------------------------------------

Apparently, when a line meeting the conditions above is selected via the
method of having the cursor at the end of the line and is then processed by
"Selection.Start=oRng.Start" - when the "Selection.Collapse" statement goes
through, the cursor ends up on the line above, instead of at the beginning
of the selected line.

The first time "Selection.Collapse" is used, the text in question was
selected via a different method, therefore - there was no problem.

But the Cursor-At-End "Selection.Start=oRng.Start" method seems to be
causing the cursor to end up on the line above when we have that set of
circumstances in front of us.

That can't be difficult to correct, can it? (By the way, thank you
again for helping me!) :)



Cumulous
 
C

Cumulous

Thanks, Ed. You provided a good lead. JGM seems to have used something
equivalent.

Thank you for helping!



Cumulous
 
J

JGM

Hi Cumulous,

Sorry about that... I just happened to check the code I posted on text that
had a line feed at the end of every line.... If I had tested it on wrapped
text.... I may have given up because it is quite difficult... at least to
me!
The problem is that you add 2 characters to every line, this makes the line
longer and depending on the line content (and also depending on the previous
line content), the added "> " may end up wrapped at the end or in the middle
of the previous line and the result is a mess if you have a larged wrapped
selection. This is complicated largely due to the fact that there is no line
object as such in Word. There is a word object and a paragraph object, no
line object...

I think I have found a way around all that... check out my code below... I
hope it is not overkill... but in my twisted mind I could not see any other
way of doing this...
If someone comes along with a very simple solution I will first be grateful
for the learning experience, and then I will go and hang myself ! ;-) !

I tried to explain my code with numerous comments... I hope it all makes
sense to you.

'_______________________________________
Sub QuoteSelection()

'Constant, symbol to signify "Quotation"
Const QuoteMark As String = "> "

'User selected range
Dim oRng As Range

'Each line in range converted in a paragraph
Dim MyPara As Paragraph

'In case range changes when adding paragrpah marks
Dim oRngStart As Integer

'To make sure we do not have an endless loop
'when adding paragraph marks
Dim Count1 As Integer

'To make sure we do not have an endless loop
'when adding QuoteMark
Dim Count2 As Integer

'To check whether paragraph marks have been added,
'in which case the user range has changed
Dim AddedChar As Boolean

'Set initial values
AddedChar = False
Count1 = 0
Count2 = 0
Set oRng = Selection.Range
oRngStart = oRng.Start
Selection.Collapse

'check if only last line selected and
'if it is a lone paragraph mark
Selection.EndKey Unit:=wdLine
If Selection.Range.End + 1 = ActiveDocument.Range.End _
And Asc(oRng.Text) = 13 Then
Count2 = 1
End If
Selection.HomeKey Unit:=wdLine

'If user selection is wrapped...
'Add ¶ to try to keep the text integrity
'as we will add 2 characters to the beginning of each line
'so the final text will be wrapped differently
'and the added QuoteMarks may be lost "inside" the text because
'of the wrapping
Do While Selection.Range.End <= oRng.End
Selection.EndKey Unit:=wdLine
If Selection.Range.End + 1 = ActiveDocument.Range.End Then
Count1 = Count1 + 1
End If
If Count1 > 1 Then Exit Do
Selection.HomeKey Unit:=wdLine
Selection.MoveLeft wdCharacter, 1
If Not Selection.Range.Start = 0 Then
If Asc(Selection.Text) <> 13 Then
Selection.Text = Chr(13)
AddedChar = True
End If
Selection.MoveRight wdCharacter, 1
If Asc(Selection.Text) = 11 Then Selection.Delete
End If
Selection.MoveDown
Loop

'Reset range to reflect user selection after adding ¶
If AddedChar Then
oRng.Start = oRngStart
If Not oRng.End = ActiveDocument.Range.End Then
oRng.SetRange oRng.Start, _
oRng.End - 1
End If
End If

'Add QuoteMark
oRng.Select
Selection.Collapse

For Each MyPara In oRng.Paragraphs
MyPara.Range.InsertBefore QuoteMark
Next MyPara

'Check whether the addition of Quotemark has forced lines to
'overflow onto a new line, i.e. a line has become two lines.
'If so, add QuoteMark to the beginning of the new "second" line
Do While Selection.Range.End <= oRng.End
If Selection.Range.End + 1 = ActiveDocument.Range.End Then
Count2 = Count2 + 1
End If
If Count2 > 1 Then Exit Do
Selection.HomeKey Unit:=wdLine
Selection.MoveRight wdCharacter, 2, wdExtend
If Not Selection.Text = QuoteMark Then
Selection.Collapse
Selection.TypeText Chr(13) & QuoteMark
End If
Selection.MoveDown
Selection.EndKey Unit:=wdLine
Loop

'Reselect original user selection
oRng.Select

End Sub
'_______________________________________

HTH
Cheers!
 
C

Cumulous

JGM, I am so sorry that you went to all that trouble! Thank you! But I
feel ashamed. :)

I almost hate saying this, but...

The issue you refer to - about sometimes having the "> " wrapping to the
end of the line above... I understood that that had to happen occasionally
for text that was wrapped. That is actually not a big problem.

But more to the point, that was not the problem I was referencing with
your original code. The problem was not that a line was processed - and the
inserted text wrapped to the line above. The problem is that the *wrong*
line was processed in the first place.

When you selected a line in one of those two situations - the method
your code selects that line and then collapses that selection moves the
cursor to the line *above* when it is supposed to just move it to the
beginning of that *same* line.

That has nothing to do with wrapping - it has to do with cursor
placement before the insert.

<duck> :)





Cumulous

p.s. Although this revised code attempts to deal with a different problem
than stated initially - I did give it a quick look. Although I did not test
it as thoroughly as I did the first version - I did happen to spot something
you should know about. When you select a block of text that includes a
blank line between two paragraphs - the code deletes the blank line and
merges the two paragraphs. <duck again!>
 
B

Bruce Brown

Cumulous

Your innocent-sounding request quickly turned into a nightmare. Here's
one more stab at a solution.

To avoid the endless loop at the end, the code puts in a hard return
at the end of the document before the loop and removes it after the
loop. That's probably cheating but it does seem to work.

What terminates the loop is an If statement immediately following the
MoveDown command. It looks strange to see Do and Loop by themselves,
with no conditions after them, but that's the only way it I could get
it to work.

If you don't whittle off a character from the range of selected text
at the start (X.End = X.End - 1), when the macro moves down to the
line after the last selected line, Word thinks the cursor is still in
the X range and adds a ">" where one doesn't belong. For sake of
appearances, the code adds back the shaved-off character so the
post-macro selection will look the same as the original.

Altogether a nasty business, Cumulous. I can't imagine what horrors
your next request will visit upon us. Bruce


Dim R As Range, X As Range, FirstLineDone As Boolean
Set X = Selection.Range
X.End = X.End - 1
ActiveDocument.Range.InsertAfter vbCr
Selection.Collapse
Do
If FirstLineDone Then Selection.MoveDown wdLine
FirstLineDone = True
If Selection.Range.End > X.End Or Selection.Range.End =
ActiveDocument.Range.End - 1 Then Exit Do
Selection.HomeKey wdLine
Set R = Selection.Range
R.MoveEnd wdCharacter
If Not R.Text = vbCr Then R.InsertBefore ">"
Loop
X.End = X.End + 1
X.Select
ActiveDocument.Range.Characters.Last.Delete
 
J

JGM

Hi again,


Cumulous said:
JGM, I am so sorry that you went to all that trouble! Thank you! But I
feel ashamed. :)

I almost hate saying this, but...

The issue you refer to - about sometimes having the "> " wrapping to the
end of the line above... I understood that that had to happen occasionally
for text that was wrapped. That is actually not a big problem.

How can that not be a problem? If you want, for example, something that
looks like this after you run the code:
Some text that was quoted by the macro
Some more text that was quoted by the macro
Yet again, some text that was quoted by the macro
The last line of this quoted text.

but instead you get somthing like:
Some text that was quoted by the macro > Some more
text that was quoted by the macro > Yet again, some text
that was quoted by the macro > The last line of this quoted text.

What is the point of the code in the first place?
I do not see how you can discard this as acceptable.... Maybe I am too much
of a maniac with details!
But more to the point, that was not the problem I was referencing with
your original code. The problem was not that a line was processed - and the
inserted text wrapped to the line above. The problem is that the *wrong*
line was processed in the first place.

When you selected a line in one of those two situations - the method
your code selects that line and then collapses that selection moves the
cursor to the line *above* when it is supposed to just move it to the
beginning of that *same* line.

That has nothing to do with wrapping - it has to do with cursor
placement before the insert.

I understood that right away, but as I tested and corrected that problem
(which was easy enough to fix), I realized the wrapping problem, which to my
eyes was a major problem...
That was the "not-so-easy" part to fix I mentionned in my previous post...
<duck> :)





Cumulous

p.s. Although this revised code attempts to deal with a different problem
than stated initially - I did give it a quick look. Although I did not
test

Again, the second version of the code did not just attempt to deal with it,
it solved the initial concern (which I had not noticed the first time
around...) you wrote about while adding a nice bonus...
it as thoroughly as I did the first version - I did happen to spot something
you should know about. When you select a block of text that includes a
blank line between two paragraphs - the code deletes the blank line and
merges the two paragraphs. <duck again!>

Actually, according to my tests, that happens only if the said line is made
up of a lone manual line feed charcter or line break - Chr(11) (created by
pressing SHIFT-ENTER, as opposed to a paragraph mark - Chr(13) or ENTER).
That was easy to fix, so here is a revised version of the code. I had not
tested for that possibility because I never use manual line break to create
spaces in my text. But, it is not because I do not do it that I have to
assume that nobody does! Mea culpa!

'_______________________________________
Sub QuoteSelection()

'Constant, symbol to signify "Quotation"
Const QuoteMark As String = "> "

'User selected range
Dim oRng As Range

'Each line in range converted in a paragraph
Dim MyPara As Paragraph

'In case range changes when adding paragrpah marks
Dim oRngStart As Integer

'To make sure we do not have an endless loop
'when adding paragraph marks
Dim Count1 As Integer

'To make sure we do not have an endless loop
'when adding QuoteMark
Dim Count2 As Integer

'To check whether paragraph marks have been added,
'in which case the user range has changed
Dim AddedChar As Boolean

'Set initial values
AddedChar = False
Count1 = 0
Count2 = 0
Set oRng = Selection.Range
oRngStart = oRng.Start
Selection.Collapse

'check if only last line selected and
'if it is a lone paragraph mark
Selection.EndKey Unit:=wdLine
If Selection.Range.End + 1 = ActiveDocument.Range.End _
And Asc(oRng.Text) = 13 Then
Count2 = 1
End If
Selection.HomeKey Unit:=wdLine

'If user selection is wrapped...
'Add ¶ to try to keep the text integrity
'as we will add 2 characters to the beginning of each line
'so the final text will be wrapped differently
'and the added QuoteMarks may be lost "inside" the text because
'of the wrapping
Do While Selection.Range.End <= oRng.End
Selection.EndKey Unit:=wdLine
If Selection.Range.End + 1 = ActiveDocument.Range.End Then
Count1 = Count1 + 1
End If
If Count1 > 1 Then Exit Do
Selection.HomeKey Unit:=wdLine

'in case line is made up of a lone line feed character:
If Asc(Selection.Text) = 11 Then
Selection.Text = Chr(13)
Selection.MoveRight wdCharacter, 1
Selection.Delete
End If

Selection.MoveLeft wdCharacter, 1
If Not Selection.Range.Start = 0 Then
If Asc(Selection.Text) <> 13 Then
Selection.Text = Chr(13)
AddedChar = True
End If
Selection.MoveRight wdCharacter, 1
If Asc(Selection.Text) = 11 Then Selection.Delete
End If
Selection.MoveDown
Loop

'Reset range to reflect user selection after adding ¶
If AddedChar Then
oRng.Start = oRngStart
If Not oRng.End = ActiveDocument.Range.End Then
oRng.SetRange oRng.Start, _
oRng.End - 1
End If
End If

'Add QuoteMark
oRng.Select
Selection.Collapse

For Each MyPara In oRng.Paragraphs
MyPara.Range.InsertBefore QuoteMark
Next MyPara

'Check whether the addition of Quotemark has forced lines to
'overflow onto a new line, i.e. a line has become two lines.
'If so, add QuoteMark to the beginning of the new "second" line
Do While Selection.Range.End <= oRng.End
If Selection.Range.End + 1 = ActiveDocument.Range.End Then
Count2 = Count2 + 1
End If
If Count2 > 1 Then Exit Do
Selection.HomeKey Unit:=wdLine
Selection.MoveRight wdCharacter, 2, wdExtend
If Not Selection.Text = QuoteMark Then
Selection.Collapse
Selection.TypeText Chr(13) & QuoteMark
End If
Selection.MoveDown
Selection.EndKey Unit:=wdLine
Loop

'Reselect original user selection
oRng.Select

End Sub
'_______________________________________

HTH
Cheers!
 
C

Cumulous

Hi, JGM!

Okay, well to begin with - the reason it was not a big issue for me was
because I had always been told that if we were going to be processing text
that was word wrapped - it would be a true nightmare and not generally worth
the effort and the extreme monetary expense of Advil that would result.

That being the case, I resigned myself to that limitation for two
reasons:

1. Everyone agreed that it wasn't worth the effort to get it right.

2. By taking the simpler route - I gained an additional benefit, which was
that text that was word-wrapped would remain word-wrapped. By putting
line-breaks at the end of each line, that has the potential of causing worse
problems depending on what else needs to be done with the text afterwards.



As such, I am content with your original code - but I would like to know
what needs to be changed to correct that small issue I initially mentioned.

Now, that having been said - your current code also has its uses, but it
does seem to be something of a nightmare to accomplish and polish - and I
hate to put anyone else through that immense effort. (In other words - if
you insist on persisting until you get it right, than count me in on being
on-board to help you test. But if you would rather just wash your hands of
the whole thing - you will get no argument from me. Only thanks for all the
effort you have put in. :)


Regardless of how you want to proceed - I did take a quick look at the
current revision of code (I did not do thorough testing yet, before seeing
how you wanted to proceed). However, I did find a couple of problems:


--------------------------------------------

1. When processing word-wrapped text:

This:

----------------

But more to the point, that was not the problem I was referencing with
your original code. The

problem was not that a line was processed - and the inserted text wrapped
to the line above. The

problem is that the *wrong* line was processed in the first place.



When you selected a line in one of those two situations - the method
your code selects that line

and then collapses that selection moves the cursor to the line *above* when
it is supposed to just

move it to the beginning of that *same* line.


----------------


Turns into this:

----------------
But more to the point, that was not the problem I was referencing with
your original code.
problem was not that a line was processed - and the inserted text wrapped
to the line above.
problem is that the *wrong* line was processed in the first place.

When you selected a line in one of those two situations - the method
your code selects that
and then collapses that selection moves the cursor to the line *above*
when it is supposed to
move it to the beginning of that *same* line.


----------------

So, every line seems to have the last word put onto a line of its own.


----------------------

2. When processing text that has line breaks at the end of every line,
*sometimes* it works properly - but I have seen a number of issues crop up:

2a. Additional lines being inserted in between paragraphs
2b. Blank lines are occasionally skipped insofar as having the "> "
inserted. (These may be the lines inserted listed in 2a)
3b. When selecting a text block that includes empty lines on the
bottom - an extra blank line is added before the last one, which is not
processed. Example:

This (last empty line is selected as well for processing):

----------------------
That has nothing to do with wrapping - it has to do with cursor

placement before the insert.



----------------------

Becomes this:

----------------------
That has nothing to do with wrapping - it has to do with cursor
placement before the insert.

----------------------


Now, once again - please do not try to perfect this version of code
unless you *really* want to. (I feel guilty already. :)

But whether you do or not, I would like to know how to change the
original revised version. :)

Thank you again!



Cumulous
 
J

JGM

Cumulous said:
Hi, JGM!

Okay, well to begin with - the reason it was not a big issue for me was
because I had always been told that if we were going to be processing text
that was word wrapped - it would be a true nightmare and not generally worth
the effort and the extreme monetary expense of Advil that would result.

Do not worry... I won't charge you and I enjoyed monkeying around with the
code! No Advil necessary!
That being the case, I resigned myself to that limitation for two
reasons:

1. Everyone agreed that it wasn't worth the effort to get it right.

Well, I maybe old fashioned, but I believe that if it is worth doing at all,
it should be done right!
2. By taking the simpler route - I gained an additional benefit, which was
that text that was word-wrapped would remain word-wrapped. By putting
line-breaks at the end of each line, that has the potential of causing worse
problems depending on what else needs to be done with the text afterwards.

Possibly... Then instead of adding characters to signify "user quoted" text,
it would be better to consider a different approach. Such as changing font
color, adding a border, highlighting, etc.
This way the original text/paragraphs retain their integrity. Otherwise,
whatever type or number of characters you decide to add at the beginning of
the line, as soon as you add characters it is impossible to retain the
orginal text integrity... in the latter case, I do not know which is worse:
Adding ¶ at the end of every line and a special character at the begining of
every line or having your text interspersed with quote characters but
retaining wrapping.... In both cases it is easy to revert to the original
with a find/replace, but the first case looks better visually and acts
exactly like an e-mail software that quotes lines and moves characters to
new lines as necessary... at least that is what my Outlook does...
As such, I am content with your original code - but I would like to know
what needs to be changed to correct that small issue I initially
mentioned.

Try replacing

Selection.Start = oRng.Start
Selection.Collapse

by

Selection.HomeKey Unit:=wdLine

in the code below.

'_______________________________________
CountEnd = 0
Set oRng = Selection.Range
Selection.Collapse

'check if only last line selected and
'if it is not a lone paragraph mark
Selection.EndKey Unit:=wdLine
If Selection.Range.End + 1 = ActiveDocument.Range.End _
And Asc(oRng.Text) <> 13 Then
CountEnd = 1
End If
Selection.Start = oRng.Start
Selection.Collapse
'_______________________________________
Now, that having been said - your current code also has its uses, but it
does seem to be something of a nightmare to accomplish and polish - and I
hate to put anyone else through that immense effort. (In other words - if
you insist on persisting until you get it right, than count me in on being
on-board to help you test. But if you would rather just wash your hands of
the whole thing - you will get no argument from me. Only thanks for all the
effort you have put in. :)


Regardless of how you want to proceed - I did take a quick look at the
current revision of code (I did not do thorough testing yet, before seeing
how you wanted to proceed). However, I did find a couple of problems:


--------------------------------------------

1. When processing word-wrapped text:

This:

----------------

But more to the point, that was not the problem I was referencing with
your original code. The

problem was not that a line was processed - and the inserted text wrapped
to the line above. The

problem is that the *wrong* line was processed in the first place.



When you selected a line in one of those two situations - the method
your code selects that line

and then collapses that selection moves the cursor to the line *above* when
it is supposed to just

move it to the beginning of that *same* line.


----------------


Turns into this:

----------------
with
your original code.

wrapped
to the line above.


your code selects that


when it is supposed to




----------------

So, every line seems to have the last word put onto a line of its own.


----------------------

1. Impossible to do otherwise, you add two characters to each line... The
line ends up being longer. So, either, as it is the case with my code, you
have extra lines where ever the added characters had an impact, or, as in my
first version, you have at the most only one extra line at the end of the
pargrpah but you have quote symbols throught out the quoted text.

2. When processing text that has line breaks at the end of every line,
*sometimes* it works properly - but I have seen a number of issues crop up:

2a. Additional lines being inserted in between paragraphs

As explained in 1. above.
2b. Blank lines are occasionally skipped insofar as having the "> "
inserted. (These may be the lines inserted listed in 2a)

I tried with all types of situations and could never reproduce this
behaviour.
3b. When selecting a text block that includes empty lines on the
bottom - an extra blank line is added before the last one, which is not
processed. Example:

This (last empty line is selected as well for processing):

----------------------
That has nothing to do with wrapping - it has to do with cursor

placement before the insert.

This is fixed with my fourth version provided below... do not worry it was
very easy to fix.
Now, once again - please do not try to perfect this version of code
unless you *really* want to. (I feel guilty already. :)

But whether you do or not, I would like to know how to change the
original revised version. :)

See above, I give you a hint.
Thank you again!



Cumulous

'_______________________________________
Sub QuoteSelection_v4()

'Constant, symbol to signify "Quotation"
Const QuoteMark As String = "> "

'User selected range
Dim oRng As Range

'Each line in range converted in a paragraph
Dim MyPara As Paragraph

'In case range changes when adding paragrpah marks
Dim oRngStart As Integer

'To make sure we do not have an endless loop
'when adding paragraph marks
Dim Count1 As Integer

'To make sure we do not have an endless loop
'when adding QuoteMark
Dim Count2 As Integer

'To check whether paragraph marks have been added,
'in which case the user range has changed
Dim AddedChar As Boolean

'Set initial values
AddedChar = False
Count1 = 0
Count2 = 0
Set oRng = Selection.Range
oRngStart = oRng.Start
Selection.Collapse

'check if only last line selected and
'if it is a lone paragraph mark
Selection.EndKey Unit:=wdLine
If Selection.Range.End + 1 = ActiveDocument.Range.End _
And Asc(oRng.Text) = 13 Then
Count2 = 1
End If
Selection.HomeKey Unit:=wdLine

'If user selection is wrapped...
'Add ¶ to try to keep the text integrity
'as we will add 2 characters to the beginning of each line
'so the final text will be wrapped differently
'and the added QuoteMarks may be lost "inside" the text because
'of the wrapping
Do While Selection.Range.End <= oRng.End
Selection.EndKey Unit:=wdLine
If Selection.Range.End + 1 = ActiveDocument.Range.End Then
Count1 = Count1 + 1
End If
If Count1 > 1 Then Exit Do
Selection.HomeKey Unit:=wdLine

'in case line is made up of a lone line feed character:
If Asc(Selection.Text) = 11 Then
Selection.Text = Chr(13)
Selection.MoveRight wdCharacter, 1
Selection.Delete
End If

Selection.MoveLeft wdCharacter, 1
If Not Selection.Range.Start = 0 Then
If Asc(Selection.Text) <> 13 Then
Selection.Text = Chr(13)
AddedChar = True
End If
Selection.MoveRight wdCharacter, 1
If Asc(Selection.Text) = 11 Then Selection.Delete
End If
Selection.MoveDown
Loop

'Reset range to reflect user selection after adding ¶
If AddedChar Then
oRng.Start = oRngStart
If Not oRng.End = ActiveDocument.Range.End Then
oRng.SetRange oRng.Start, _
oRng.End - 1
End If
End If

'Add QuoteMark
oRng.Select
Selection.Collapse

For Each MyPara In oRng.Paragraphs
MyPara.Range.InsertBefore QuoteMark
Next MyPara

'Check whether the addition of Quotemark has forced lines to
'overflow onto a new line, i.e. a line has become two lines.
'If so, add QuoteMark to the beginning of the new "second" line
Do While Selection.Range.End <= oRng.End
If Selection.Range.End + 1 = ActiveDocument.Range.End Then
Count2 = Count2 + 1
End If
If Count2 > 1 Then Exit Do
Selection.HomeKey Unit:=wdLine
'in case last line is a single ¶
If Asc(Selection.Text) = 13 Then
Selection.TypeText QuoteMark
Else
Selection.MoveRight wdCharacter, 2, wdExtend
If Not Selection.Text = QuoteMark Then
Selection.Collapse
Selection.TypeText Chr(13) & QuoteMark
End If
End If
Selection.MoveDown
Selection.EndKey Unit:=wdLine
Loop

'Reselect original user selection
oRng.Select

End Sub
'_______________________________________

HTH
Cheers!
 
C

Cumulous

JGM said:
Do not worry... I won't charge you and I enjoyed monkeying around with the
code! No Advil necessary!

Ah, a person who programs out of love! You bring inspiration to others.
:)

Well, I maybe old fashioned, but I believe that if it is worth doing at all,
it should be done right!


Hot damn! Get hired as a Product Development Manager at Microsoft!
afterwards.

Possibly... Then instead of adding characters to signify "user quoted" text,
it would be better to consider a different approach. Such as changing font
color, adding a border, highlighting, etc.
This way the original text/paragraphs retain their integrity. Otherwise,
whatever type or number of characters you decide to add at the beginning of
the line, as soon as you add characters it is impossible to retain the
orginal text integrity... in the latter case, I do not know which is worse:
Adding ¶ at the end of every line and a special character at the begining of
every line or having your text interspersed with quote characters but
retaining wrapping.... In both cases it is easy to revert to the original
with a find/replace, but the first case looks better visually and acts
exactly like an e-mail software that quotes lines and moves characters to
new lines as necessary... at least that is what my Outlook does...


Other options (such as color, font, etc...) don't apply when the text
format needs to remain Plain Text. :)

mentioned.

Try replacing

Selection.Start = oRng.Start
Selection.Collapse

by

Selection.HomeKey Unit:=wdLine

in the code below.


Thanks! That did the trick admirably.>
--------------------------------------------


1. Impossible to do otherwise, you add two characters to each line... The
line ends up being longer. So, either, as it is the case with my code, you
have extra lines where ever the added characters had an impact, or, as in my
first version, you have at the most only one extra line at the end of the
pargrpah but you have quote symbols throught out the quoted text.


Well, I would think that we could take care of all of the problems with
text flow and blank lines - if we could just get the code to work from top
to bottom. The way it works now, it's adding the line breaks first from top
to bottom, and then inserting the text from top to bottom.

However, if it inserted the text first on the first line, then added the
line break at the end of that same line - then moved to add the text to the
beginning of the second line, followed by the line break at the end of that
same second line, etc.... I would think that would keep the text flowing
perfectly.

Am I flawed in my thinking here?

This is fixed with my fourth version provided below... do not worry it was
very easy to fix

Jean-Guy Marcil
(e-mail address removed)


I discovered an interesting bug in the fourth version (it was probably
there in the earlier ones). When text is wrapped, trailing spaces are left
at the end of all the lines. However, due to how your code adds a line
break to the end of every line, what happens is that those trailing spaces
are all pushed to the beginning of the following line.

Thus:


Imagine this line wraps over
to this line here
as well as this one.


Becomes:

Imagine this line wraps over
to this line here
as well as this one.



The first line of the selection (and every paragraph in the selection)
doesn't have a space pushed to the front of it from the line above, so it is
the only line formatted correctly. Every other line looks like it has been
indented slightly.

However, deleting the now-leading spaces would make things difficult if
the text needed to be reverted via Find/Replace. Is it possible to get
around this problem by inserting the line-break *after* the trailing space
at the end of the lines which have them?




Cumulous
 
J

JGM

Cumulous said:
Ah, a person who programs out of love! You bring inspiration to others.


Hot damn! Get hired as a Product Development Manager at Microsoft!
Please! <grin>

I would probably get fired after a few days for not being "cost-efficient"!
 
J

JGM

Cumulous said:
Ah, a person who programs out of love! You bring inspiration to others.


Hot damn! Get hired as a Product Development Manager at Microsoft!
Please! <grin>

They would probably fire me after a few days for not being "cost-efficient"!

Thanks! That did the trick admirably.>

Glad I could help.
in


Well, I would think that we could take care of all of the problems with
text flow and blank lines - if we could just get the code to work from top
to bottom. The way it works now, it's adding the line breaks first from top
to bottom, and then inserting the text from top to bottom.

However, if it inserted the text first on the first line, then added the
line break at the end of that same line - then moved to add the text to the
beginning of the second line, followed by the line break at the end of that
same second line, etc.... I would think that would keep the text flowing
perfectly.

Am I flawed in my thinking here?

I am not sure that I understand what you are saying, but it is a difficult
process because the range and the number of lines change as we add
characters...
In this case, my code evolved from your comments, it was never rewritten,
just modified. In such cases you often end up with longish code... It would
be better it to rewrite the whole thing to make it shorter and more
efficient. Then something along the lines of what you suggest here would be
worth investigating.... I like monkeying with code... but I am not crazy! I
am sure you will understand that I do not feel like rewriting the whole
thing!

I discovered an interesting bug in the fourth version (it was probably
there in the earlier ones). When text is wrapped, trailing spaces are left
at the end of all the lines. However, due to how your code adds a line
break to the end of every line, what happens is that those trailing spaces
are all pushed to the beginning of the following line.

Thus:


Imagine this line wraps over
to this line here
as well as this one.


Becomes:





The first line of the selection (and every paragraph in the selection)
doesn't have a space pushed to the front of it from the line above, so it is
the only line formatted correctly. Every other line looks like it has been
indented slightly.

However, deleting the now-leading spaces would make things difficult if
the text needed to be reverted via Find/Replace. Is it possible to get
around this problem by inserting the line-break *after* the trailing space
at the end of the lines which have them?




Cumulous

The fourth-version bug is fixed in the code below... it was a 2 minute thing
to fix. I hope it did not bring about a bunch of new bugs!

Have a good day.

'_______________________________________
Sub QuoteSelection_v4()

'Constant, symbol to signify "Quotation"
Const QuoteMark As String = "> "

'User selected range
Dim oRng As Range

'Each line in range converted in a paragraph
Dim MyPara As Paragraph

'In case range changes when adding paragrpah marks
Dim oRngStart As Integer

'To make sure we do not have an endless loop
'when adding paragraph marks
Dim Count1 As Integer

'To make sure we do not have an endless loop
'when adding QuoteMark
Dim Count2 As Integer

'To check whether paragraph marks have been added,
'in which case the user range has changed
Dim AddedChar As Boolean

'Set initial values
AddedChar = False
Count1 = 0
Count2 = 0
Set oRng = Selection.Range
oRngStart = oRng.Start
Selection.Collapse

'check if only last line selected and
'if it is a lone paragraph mark
Selection.EndKey Unit:=wdLine
If Selection.Range.End + 1 = ActiveDocument.Range.End _
And Asc(oRng.Text) = 13 Then
Count2 = 1
End If
Selection.HomeKey Unit:=wdLine

'If user selection is wrapped...
'Add ¶ to try to keep the text integrity
'as we will add 2 characters to the beginning of each line
'so the final text will be wrapped differently
'and the added QuoteMarks may be lost "inside" the text because
'of the wrapping
Do While Selection.Range.End <= oRng.End
Selection.EndKey Unit:=wdLine
If Selection.Range.End + 1 = ActiveDocument.Range.End Then
Count1 = Count1 + 1
End If
If Count1 > 1 Then Exit Do
Selection.HomeKey Unit:=wdLine

'in case line is made up of a lone line feed character:
If Asc(Selection.Text) = 11 Then
Selection.Text = Chr(13)
Selection.MoveRight wdCharacter, 1
Selection.Delete
End If

Selection.MoveLeft wdCharacter, 1
Selection.EndKey Unit:=wdLine
If Not Selection.Range.Start = 0 Then
If Asc(Selection.Text) <> 13 Then
Selection.Text = Chr(13)
AddedChar = True
End If
Selection.MoveRight wdCharacter, 1
If Asc(Selection.Text) = 11 Then Selection.Delete
End If
Selection.MoveDown
Loop

'Reset range to reflect user selection after adding ¶
If AddedChar Then
oRng.Start = oRngStart
If Not oRng.End = ActiveDocument.Range.End Then
oRng.SetRange oRng.Start, _
oRng.End - 1
End If
End If

'Add QuoteMark
oRng.Select
Selection.Collapse

For Each MyPara In oRng.Paragraphs
MyPara.Range.InsertBefore QuoteMark
Next MyPara

'Check whether the addition of Quotemark has forced lines to
'overflow onto a new line, i.e. a line has become two lines.
'If so, add QuoteMark to the beginning of the new "second" line
Do While Selection.Range.End <= oRng.End
If Selection.Range.End + 1 = ActiveDocument.Range.End Then
Count2 = Count2 + 1
End If
If Count2 > 1 Then Exit Do
Selection.HomeKey Unit:=wdLine
'in case last line is a single ¶
If Asc(Selection.Text) = 13 Then
Selection.TypeText QuoteMark
Else
Selection.MoveRight wdCharacter, 2, wdExtend
If Not Selection.Text = QuoteMark Then
Selection.Collapse
Selection.TypeText Chr(13) & QuoteMark
End If
End If
Selection.MoveDown
Selection.EndKey Unit:=wdLine
Loop

'Reselect original user selection
oRng.Select

End Sub
'_______________________________________

--
'_______________________________________
Jean-Guy Marcil
(e-mail address removed)

p.s. I think my reply was sent before I was done writing it... Sorry for the
double post... I do not know what key I hit, but it was sent to soon....
 
C

Cumulous

JGM said:
They would probably fire me after a few days for not being "cost-efficient"!


A true shame. But probably true... :)

Glad I could help.

:)



I am not sure that I understand what you are saying, but it is a difficult
process because the range and the number of lines change as we add
characters...
In this case, my code evolved from your comments, it was never rewritten,
just modified. In such cases you often end up with longish code... It would
be better it to rewrite the whole thing to make it shorter and more
efficient. Then something along the lines of what you suggest here would be
worth investigating.... I like monkeying with code... but I am not crazy! I
am sure you will understand that I do not feel like rewriting the whole
thing!


I completely understand! Truthfully, I'm surprised you were able to
The fourth-version bug is fixed in the code below... it was a 2 minute thing
to fix. I hope it did not bring about a bunch of new bugs!


You fixed the leading/trailing space issue perfectly.

Alas.... I don't know if the 5th version caused this or not, but....

If the selection block has *only* one or two *blank* lines above it
before reaching the absolute beginning of the document - the code goes into
an endless loop moving the cursor back and forth between the line above the
selection, and the beginning and end of the first line of the selection
itself.. Needless to say, this crashes Word - as that is the only way to
stop the loop.


It's ironic that this started with fixing a problem that occured when
selecting the end of a document - and it is now coming to a close on a
problem that occurs when selecting close to the beginning of a document. :)

I never thought the macro I wanted would be so terribly complicated. It
always seemed like the absolute simplest of things to program. :)



Cumulous
 
J

JGM

Hi there,

I do not know what happened... I commented out two lines of code ("'
If Not Selection.Range.Start = 0 Then" and the corresponding "End If") and
that seemed to do it. Honestly, I do not remember why I had this line of
code in the procedure in the first place... I hope I did not recreate a
problem I had already solved! If so, let me now and I will know how to fix
it. This is what happens when you work on a code that you do not use
yourself... Anyway, this is what happens to me! I do not remember all the
details and the reasons for all the modifications as I do not document what
I do as thoroughly as if I were doing something for a client of mine...

Try this 6th version.

By the way, if you run code and see that you are in an endless loop, try
hitting CTRL-BREAK to stop the macro... do not wait for Word (or whatever
software is running the code) to crash.

Good luck.

'_______________________________________
Sub QuoteSelection_v6()

'Constant, symbol to signify "Quotation"
Const QuoteMark As String = "> "

'User selected range
Dim oRng As Range

'Each line in range converted in a paragraph
Dim MyPara As Paragraph

'In case range changes when adding paragrpah marks
Dim oRngStart As Integer

'To make sure we do not have an endless loop
'when adding paragraph marks
Dim Count1 As Integer

'To make sure we do not have an endless loop
'when adding QuoteMark
Dim Count2 As Integer

'To check whether paragraph marks have been added,
'in which case the user range has changed
Dim AddedChar As Boolean

'Set initial values
AddedChar = False
Count1 = 0
Count2 = 0
Set oRng = Selection.Range
oRngStart = oRng.Start
Selection.Collapse

'check if only last line selected and
'if it is a lone paragraph mark
Selection.EndKey Unit:=wdLine
If Selection.Range.End + 1 = ActiveDocument.Range.End _
And Asc(oRng.Text) = 13 Then
Count2 = 1
End If
Selection.HomeKey Unit:=wdLine

'If user selection is wrapped...
'Add ¶ to try to keep the text integrity
'as we will add 2 characters to the beginning of each line
'so the final text will be wrapped differently
'and the added QuoteMarks may be lost "inside" the text because
'of the wrapping
Do While Selection.Range.End <= oRng.End
Selection.EndKey Unit:=wdLine
If Selection.Range.End + 1 = ActiveDocument.Range.End Then
Count1 = Count1 + 1
End If
If Count1 > 1 Then Exit Do
Selection.HomeKey Unit:=wdLine

'in case line is made up of a lone line feed character:
If Asc(Selection.Text) = 11 Then
Selection.Text = Chr(13)
Selection.MoveRight wdCharacter, 1
Selection.Delete
End If

Selection.MoveLeft wdCharacter, 1
Selection.EndKey Unit:=wdLine
' If Not Selection.Range.Start = 0 Then
If Asc(Selection.Text) <> 13 Then
Selection.Text = Chr(13)
AddedChar = True
End If
Selection.MoveRight wdCharacter, 1
If Asc(Selection.Text) = 11 Then Selection.Delete
' End If
Selection.MoveDown
Loop

'Reset range to reflect user selection after adding ¶
If AddedChar Then
oRng.Start = oRngStart
If Not oRng.End = ActiveDocument.Range.End Then
oRng.SetRange oRng.Start, _
oRng.End - 1
End If
End If

'Add QuoteMark
oRng.Select
Selection.Collapse

For Each MyPara In oRng.Paragraphs
MyPara.Range.InsertBefore QuoteMark
Next MyPara

'Check whether the addition of Quotemark has forced lines to
'overflow onto a new line, i.e. a line has become two lines.
'If so, add QuoteMark to the beginning of the new "second" line
Do While Selection.Range.End <= oRng.End
If Selection.Range.End + 1 = ActiveDocument.Range.End Then
Count2 = Count2 + 1
End If
If Count2 > 1 Then Exit Do
Selection.HomeKey Unit:=wdLine
'in case last line is a single ¶
If Asc(Selection.Text) = 13 Then
Selection.TypeText QuoteMark
Else
Selection.MoveRight wdCharacter, 2, wdExtend
If Not Selection.Text = QuoteMark Then
Selection.Collapse
Selection.TypeText Chr(13) & QuoteMark
End If
End If
Selection.MoveDown
Selection.EndKey Unit:=wdLine
Loop

'Reselect original user selection
oRng.Select

End Sub
'_______________________________________
 
B

Bruce Brown

Cumulous, JGM

Wondering if either of you saw my message posted last Saturday. It's
been hiding at the end of the thread all weekend.

Anyway, here's the code again. Curious to know if you can make it
fail on wrapped text.

Did I misunderstand about leaving blank paragraph spaces as is,
without the ">"?I noticed that Jean-Guy's code doesn't leave blank
paragraph marks blank. If that's the way it's supposed to be, take
out these three lines . . .

Set R = Selection.Range
R.MoveEnd wdCharacter
If Not R.Text = vbCr Then R.InsertBefore ">"

.. . . and replace them with this line:

Selection.Range.InsertBefore ">"

Text must be selected first.


Dim R As Range, X As Range, FirstLineDone As Boolean
Set X = Selection.Range
X.End = X.End - 1
ActiveDocument.Range.InsertAfter vbCr
Selection.Collapse
Do
If FirstLineDone Then Selection.MoveDown wdLine
FirstLineDone = True
If Selection.Range.End > X.End Or Selection.Range.End =
ActiveDocument.Range.End - 1 Then Exit Do
Selection.HomeKey wdLine
Set R = Selection.Range
R.MoveEnd wdCharacter
If Not R.Text = vbCr Then R.InsertBefore ">"
Loop
X.End = X.End + 1
X.Select
ActiveDocument.Range.Characters.Last.Delete


P.S. Jean-Guy, you need to dim oRngStart as a long, not an integer.
As an integer, it gave me an error message at the end of a long
document. But other than that, it worked just fine.

- Bruce
 
J

JGM

Hi Bruce,

Yes, your code would be perfect and is much simpler than mine - and easier
to maintain too... execept that Cumulous wanted to prefix every selected
line with "> " and not ">". That extra space after the ">" is the one doing
the killing...

With your code, if we just add a space after the ">" in [ Range.InsertBefore
">" ] when we have wrapped text, all the available space left at the end of
line (because the last word is too long to fit at the end and is bumped to
the beginning of the next line), then, this end of line space from
justifying left, is filled with a bunch of "> ". Everytime the code adds
one, if there is space at at the end of the preceding line, it gets moved
there, along with the insertion point, which is brought back to the
beginning of the same line where a new "> " is added... And so on until the
end of the preceding line is filled with a bunch of "> ".

That is why I decided I had to put a hardbreak at the end of every line
(including the one preceding the fist selected line) before adding the "> ".

Thanks for the tip regarding the integer.. I should have thought of that...
I guess that when you have only a few variables it is not worth being too
stingy with memory.

Cheers.
 
B

Bruce Brown

Jean-Guy

Shame on me, I had not picked up on the requirement for the space
after >, which makes a nasty requirement completely fiendish.

I applaud your solution. It's not easy to understand but works like a
charm.

- Bruce


JGM said:
Hi Bruce,

Yes, your code would be perfect and is much simpler than mine - and easier
to maintain too... execept that Cumulous wanted to prefix every selected
line with "> " and not ">". That extra space after the ">" is the one doing
the killing...

With your code, if we just add a space after the ">" in [ Range.InsertBefore
">" ] when we have wrapped text, all the available space left at the end of
line (because the last word is too long to fit at the end and is bumped to
the beginning of the next line), then, this end of line space from
justifying left, is filled with a bunch of "> ". Everytime the code adds
one, if there is space at at the end of the preceding line, it gets moved
there, along with the insertion point, which is brought back to the
beginning of the same line where a new "> " is added... And so on until the
end of the preceding line is filled with a bunch of "> ".

That is why I decided I had to put a hardbreak at the end of every line
(including the one preceding the fist selected line) before adding the "> ".

Thanks for the tip regarding the integer.. I should have thought of that...
I guess that when you have only a few variables it is not worth being too
stingy with memory.

Cheers.

--
_______________________________________
Jean-Guy Marcil
(e-mail address removed)

Bruce Brown said:
Cumulous, JGM

Wondering if either of you saw my message posted last Saturday. It's
been hiding at the end of the thread all weekend.

Anyway, here's the code again. Curious to know if you can make it
fail on wrapped text.

Did I misunderstand about leaving blank paragraph spaces as is,
without the ">"?I noticed that Jean-Guy's code doesn't leave blank
paragraph marks blank. If that's the way it's supposed to be, take
out these three lines . . .

Set R = Selection.Range
R.MoveEnd wdCharacter
If Not R.Text = vbCr Then R.InsertBefore ">"

. . . and replace them with this line:

Selection.Range.InsertBefore ">"

Text must be selected first.


Dim R As Range, X As Range, FirstLineDone As Boolean
Set X = Selection.Range
X.End = X.End - 1
ActiveDocument.Range.InsertAfter vbCr
Selection.Collapse
Do
If FirstLineDone Then Selection.MoveDown wdLine
FirstLineDone = True
If Selection.Range.End > X.End Or Selection.Range.End =
ActiveDocument.Range.End - 1 Then Exit Do
Selection.HomeKey wdLine
Set R = Selection.Range
R.MoveEnd wdCharacter
If Not R.Text = vbCr Then R.InsertBefore ">"
Loop
X.End = X.End + 1
X.Select
ActiveDocument.Range.Characters.Last.Delete


P.S. Jean-Guy, you need to dim oRngStart as a long, not an integer.
As an integer, it gave me an error message at the end of a long
document. But other than that, it worked just fine.

- Bruce

"Cumulous" <[email protected]> wrote in message problems
with from
top from
top added
the to
the of
that would
be crazy!
I are
left
 
C

Cumulous

JGM said:
Hi there,

I do not know what happened... I commented out two lines of code ("'
If Not Selection.Range.Start = 0 Then" and the corresponding "End If") and
that seemed to do it. Honestly, I do not remember why I had this line of
code in the procedure in the first place... I hope I did not recreate a
problem I had already solved! If so, let me now and I will know how to fix
it. This is what happens when you work on a code that you do not use
yourself... Anyway, this is what happens to me! I do not remember all the
details and the reasons for all the modifications as I do not document what
I do as thoroughly as if I were doing something for a client of mine...

Try this 6th version.

By the way, if you run code and see that you are in an endless loop, try
hitting CTRL-BREAK to stop the macro... do not wait for Word (or whatever
software is running the code) to crash.

Good luck.


It works beautifully. Thank you again, for all of your help! I could
not have produced the code you did by myself (at least not yet). :)

I appreciate your attention to detail and determination to get the code
right. Thanks again! And have a wonderful week!




Cumulous
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top