Consulting

Page 4 of 5 FirstFirst ... 2 3 4 5 LastLast
Results 61 to 80 of 92

Thread: LinkToPrevious

  1. #61
    Microsoft Word MVP 2003-2009 VBAX Guru gmaxey's Avatar
    Joined
    Sep 2005
    Posts
    3,334
    Location
    glencoe,

    I can't explain the range issue because it isn't happening here.

    As for the wildcard search it works like this.

    The "( )" in the .Text string are wildcard grouping symbols. So we are searching for three groups 1) the opening tag, 2) any text 3) the closing tag.
    The "\2" in the .Replacement.Text string simply means to replace what was found (i.e., the opening tag, any text, and the closing tag) with the content of group 2 (i.e., just the text)

    The following code will find text tag like this "##Test**"

    Notice the closing tag string "\*\*" may look a little odd. This is because "*" in a wildcard search means "anything" where in this case we want to search for the literal "**" so we add the "\" before each literal character.

    Hope this helps.
    Sub ScratchMacro()
    'A basic Word macro coded by Greg Maxey
    Dim oSection As Section
    Dim oHF As HeaderFooter
    Dim oRng As Range, oTextRng As Range
    Dim strOTag As String, strCTag As String
      
      strOTag = "##"
      strCTag = "\*\*"
      For Each oSection In ActiveDocument.Sections
        For Each oHF In oSection.Headers
          With oHF
            If .LinkToPrevious = False Or oSection.Index = 1 Then
              Set oRng = oHF.Range
                With oRng.Find
                  .Text = "(" & strOTag & ")(*)(" & strCTag & ")"
                  .MatchWildcards = True
                  .Replacement.Text = "\2"
                  While .Execute(Replace:=wdReplaceOne)
                    oRng.HighlightColorIndex = wdBrightGreen
                    oRng.Collapse wdCollapseEnd
                  Wend
                End With
              End If
              End With
          Next oHF
      Next oSection
    End Sub
    Greg

    Visit my website: http://gregmaxey.com

  2. #62
    VBAX Regular
    Joined
    Nov 2011
    Posts
    71
    Location
    gmaxey,

    Thanks for the explanation. Makes more sense to me now! ;-)
    I also found a great page referencing in detail wildcards use in Word (not sure if there is a place on the forum to reference useful documentation) here: http://www.gmayor.com/replace_using_wildcards.htm

    Now, I also foresee a problem using wildcards search in this case: as you perfectly explained, we need to use a \ to really search the literal * character.
    In other words, all wildcards "special" characters should be avoided in the tags, or I would have to add a \ before any of them: ( [ ] { } < > ( ) - @ ? ! * \ )
    I assume this makes sense, right? I found this issue by using #< and ># as opening/closing tags.

    Now, though your code is great, it replaces all occurrences blindly without doing any order check (to make sure that there is no strOtag in between a correct pair). I am a bit concerned by this issue, and I'd like to find a nice fix for it. Maybe the solution would be the check in the .Find result if the string strOtag exists, in which case I could highlight it in red or any other color, and skip to the next occurrence... Any better idea?

  3. #63
    Microsoft Word MVP 2003-2009 VBAX Guru gmaxey's Avatar
    Joined
    Sep 2005
    Posts
    3,334
    Location
    glencoe,

    Yes using wildcards does present its problems. This doesn't look as clean, but you can avoid them if you want:

    Sub ScratchMacro2()
    'A basic Word macro coded by Greg Maxey
    Dim oSection As Section
    Dim oHF As HeaderFooter
    Dim oRng As Range, oTextRng As Range
    Dim oRng2 As Range
    Dim strOTag As String, strCTag As String
        
      strOTag = "##"
      strCTag = "**"
      For Each oSection In ActiveDocument.Sections
      For Each oHF In oSection.Headers
        With oHF
          If .LinkToPrevious = False Or oSection.Index = 1 Then
            Set oRng = oHF.Range
              With oRng.Find
                .Text = strOTag
                 While .Execute
                   Set oRng2 = oHF.Range
                   oRng2.Start = oRng.End
                   With oRng2.Find
                     .Text = strCTag
                     If .Execute Then
                       Set oTextRng = oRng.Duplicate
                       oTextRng.Collapse wdCollapseEnd
                       oTextRng.End = oRng2.Start
                       oRng.Delete
                       oRng2.Delete
                       If Not InStr(oTextRng.Text, strOTag) > 0 Then
                         oTextRng.HighlightColorIndex = wdBrightGreen
                       Else
                         oTextRng.HighlightColorIndex = wdRed
                       End If
                    End If
                   End With
                  Wend
                End With
              End If
            End With
        Next oHF
      Next oSection
    End Sub
    Last edited by gmaxey; 02-09-2014 at 02:26 PM.
    Greg

    Visit my website: http://gregmaxey.com

  4. #64
    VBAX Wizard
    Joined
    May 2004
    Posts
    6,713
    Location

    Yes, it is a training issue, but everyone makes mistakes as well, so even the most rigorous person may forget to close **some open bracket** in a sentence, and in my case, may forget to add the closing tag where it belongs... Got it?
    And, as I said, I do not want to use ONE type of "tag", as it might be used as an internal code by translation software. This is a risk I don't want to take, as I do not know/use/have control on the software used to produce the Word file. Let's assume one moment that ## ## is an internal tag used by such software. What would happen to the file once it is exported into Word? I have absolutely no idea!
    I will tell you one thing...you would be much much much much better off if none of this commenting "tagging" was done in your translation software, and ALL of it was done in Word.

    You could make a unique tagging string (or no tagging and just highlight, since that seems to be what you end up with), fired by a keyboard shortcut (or a icon click, whatever), that would ALWAYS use open and closing "tags". You would never, ever have to deal with checking for a closer, or if there was an extra one.
    Now, though your code is great, it replaces all occurrences blindly without doing any order check (to make sure that there is no strOtag in between a correct pair). I am a bit concerned by this issue, and I'd like to find a nice fix for it. Maybe the solution would be the check in the .Find result if the string strOtag exists, in which case I could highlight it in red or any other color, and skip to the next occurrence... Any better idea?
    I do. Do all your tagging in Word to start with.

  5. #65
    VBAX Wizard
    Joined
    May 2004
    Posts
    6,713
    Location
    Oh, and then you could do REAL comments. In any case, I will leave you in the capable hands of Greg, as he knows his stuff and is better suited to help you.

  6. #66
    VBAX Regular
    Joined
    Nov 2011
    Posts
    71
    Location
    Thank you Greg! Will check your code tomorrow.

    fumei, I understand your point, but this is a client's request... Later on, it may be a suggestion indeed to transfer those comments as real Word comments through the macro (but probably never commenting/highlighting manually directly in Word). There are a few good reasons to do it this way, but I don't think there is any need for me to explain them, as it would be totally off-topic here. Point is, I need to find a solution, and gmaxey seems to be guiding me on the right track... Plus, I like challenges! ;-)

  7. #67
    VBAX Regular
    Joined
    Nov 2011
    Posts
    71
    Location
    gmaxey, I love your solution! The only thing I would actually change is highlighting only the strOTag found in instr instead of highlighting the whole range, only to give the user less manipulations to do manually after (i.e. manually clear the highlighted part, add the missing tag, run the macro again). Highlighting only the tags only requires the user to add the missing tag and run the macro again.
    Anyway, I really took the time to consider your suggestion seriously, and while it is excellent, that is the only change I'd like to add. Now, don't just give me the answer yet, I want to do my homework before! ;-) Otherwise, I will never learn if you just give me everything on a plate... lolll

  8. #68
    Microsoft Word MVP 2003-2009 VBAX Guru gmaxey's Avatar
    Joined
    Sep 2005
    Posts
    3,334
    Location
    glencoe,

    Glad I could help and you are correct. Sometimes I give away too many fish. It is nice to know that you want to learn to catch your own fish.
    If you need some help though, just post back.
    Greg

    Visit my website: http://gregmaxey.com

  9. #69
    VBAX Regular
    Joined
    Nov 2011
    Posts
    71
    Location
    OK, I've got it...

    Now, I noticed something interesting: though the code is working exactly as expected, I didn't realize before running it through a test that it would actually erase the next correct pair of tags (following a bad pair)!

    e.g. original text with tags

    #<correct tags>#
    #<wrong closing tag#
    #<correct tags>#

    would give at the very end of the loop:

    correct tags
    #<wrong closing tag#
    correct tags

    And when I think about it, this makes more sense than what I actually wanted to accomplish... So I thought I could get the code to do it directly instead. The code is also simpler that way!
    I copied below both options (of course, it is either one or the other). The first one is the one I was talking about in my previous post. And I applied what I learnt with your help! Hopefully I got it right.

    I also commented each line to make sure I understood properly its meaning and action.
    I would really appreciate if you could double check both the code and my comments and let me know if I made a mistake somewhere!


    Dim oRng As Range, oRng2 As Range, fnd As Range, oTextRng As Range, oTextRng2 As Range
            
            For Each oSection In ActiveDocument.Sections
                For Each oHF In oSection.Headers
                    With oHF
    
    
                        '1st option
                        If .LinkToPrevious = False Or oSection.Index = 1 Then
                            Set oRng = oHF.Range 'Whole header
                            Set oRng2 = oHF.Range 'Whole header
                            With oRng.Find
                                .Text = strOTag 'Opening tag
                                While .Execute 'Find opening tag (oRng)
                                    oRng2.Start = oRng.End 'Starting after opening tag found above
                                    With oRng2.Find
                                        .Text = strCTag 'Closing tag
                                        If .Execute Then 'Find closing tag (oRng2)
                                            Set oTextRng = oRng.Duplicate 'Duplicating oRng. Now oTextRng = opening tag
                                            oTextRng.Collapse wdCollapseEnd 'Starting position of oTextRng is now the end of Opening tag
                                            oTextRng.End = oRng2.Start 'End of oTextRng = before Closing tag. Therefore oTextRng is the string between opening/closing tags
                                            If Not InStr(oTextRng.Text, strOTag) > 0 Then 'No extra opening tag found in oTextRng, therefore pair of tags is correct. Processing tags and highlighting text
                                                oRng.Delete 'Delete opening tag
                                                oRng2.Delete 'Delete closing tag
                                                oTextRng.HighlightColorIndex = Couleur1_Selectionnee 'Highlight string (this color is chosen by the user)
                                            Else 'opening tag found within the string. Highlighting tags only in another color.
                                                oRng.HighlightColorIndex = Couleur2_Selectionnee 'Highlight opening tag (this color is chosen by the user, though it will probably be red)
                                                oRng2.HighlightColorIndex = Couleur2_Selectionnee 'Highlight closing tag
                                                Set oTextRng2 = oTextRng.Duplicate 'Duplicate string range
                                                With oTextRng2.Find
                                                    .Text = strOTag 'Opening tag
                                                    If .Execute Then 'Find opening tag in string
                                                        oTextRng2.HighlightColorIndex = Couleur2_Selectionnee 'Highlight extra opening tag 
                                                    End If
                                                End With
                                            End If
                                        End If
                                    End With
                                Wend
                            End With
                        End If
                        
                        
                        '2nd solution
                        If .LinkToPrevious = False Or oSection.Index = 1 Then
                            Set oRng = oHF.Range 'Whole header
                            Set oRng2 = oHF.Range 'Whole header
                            With oRng.Find
                                .Text = strOTag 'Opening tag
                                While .Execute 'Find opening tag (oRng)
                                    oRng2.Start = oRng.End 'Starting after opening tag found above
                                    With oRng2.Find
                                        .Text = strCTag 'Closing tag
                                        If .Execute Then 'Find closing tag (oRng2)
                                            Set oTextRng = oRng.Duplicate 'Duplicating oRng. Now oTextRng = opening tag
                                            oTextRng.Collapse wdCollapseEnd 'Starting position of oTextRng is now the end of Opening tag
                                            oTextRng.End = oRng2.Start 'End of oTextRng = before Closing tag. Therefore oTextRng is the string between opening/closing tags
                                            If Not InStr(oTextRng.Text, strOTag) > 0 Then 'No extra opening tag found in oTextRng, therefore pair of tags is correct. Processing tags and highlighting text
                                                oRng.Delete 'Delete opening tag
                                                oRng2.Delete 'Delete closing tag
                                                oTextRng.HighlightColorIndex = Couleur1_Selectionnee 'Highlight string
                                            Else 'opening tag found within the string. Highlighting tags only in another color.
                                                oRng.HighlightColorIndex = Couleur2_Selectionnee 'Highlight opening tag
                                            End If
                                        End If
                                    End With
                                Wend
                            End With
                        End If
                    End With
                Next oHF
            Next oSection
    Also, I still haven't gone through all the process renaming variables in all of my code. If you have more advise about a good rule of thumb... string variables would start by "str". integers, by "int" I assume? any other suggestion?

  10. #70
    Microsoft Word MVP 2003-2009 VBAX Guru gmaxey's Avatar
    Joined
    Sep 2005
    Posts
    3,334
    Location
    Looks like you've achieved your goal and understand how you did it.

    As for variables, I suppose each person has their own style. I usually proceed all objects (things that must be set) with o (e.g., oRng, oFld, oShp, oBM, etc.)

    I use str text e.g., strText
    I rarely use integers and use longs instead lngCount, lngIndex etc.

    bSet, bState etc. for bollean

    Just for you to compare, this is what I thought you might be after:

    Sub ScratchMacro2()
    'A basic Word macro coded by Greg Maxey
    Dim oSection As Section
    Dim oHF As HeaderFooter
    Dim oRng As Range, oTextRng As Range
    Dim oRng2 As Range, oRngTag As Range
    Dim strOTag As String, strCTag As String
         
      strOTag = "##"
      strCTag = "**"
      For Each oSection In ActiveDocument.Sections
        For Each oHF In oSection.Headers
          With oHF
            If .LinkToPrevious = False Or oSection.Index = 1 Then
              Set oRng = oHF.Range
              With oRng.Find
                .Text = strOTag
                While .Execute
                    Set oRng2 = oHF.Range
                    oRng2.Start = oRng.End
                    With oRng2.Find
                      .Text = strCTag
                      If .Execute Then
                        Set oTextRng = oRng.Duplicate
                        oTextRng.Collapse wdCollapseEnd
                        oTextRng.End = oRng2.Start
                        oRng.Delete
                        oRng2.Delete
                        oTextRng.HighlightColorIndex = wdBrightGreen
                        If InStr(oTextRng.Text, strOTag) > 0 Then
                          Set oRngTag = oTextRng.Duplicate
                          With oRngTag.Find
                            .Text = strOTag
                            While .Execute
                              oRngTag.HighlightColorIndex = wdRed
                              oRngTag.Collapse wdCollapseEnd
                            Wend
                          End With
                        End If
                        oRng.Start = oTextRng.End
                      End If
                    End With
                  Wend
              End With
            End If
          End With
        Next oHF
      Next oSection
    End Sub
    Last edited by gmaxey; 02-11-2014 at 10:03 PM.
    Greg

    Visit my website: http://gregmaxey.com

  11. #71
    VBAX Regular
    Joined
    Nov 2011
    Posts
    71
    Location
    I'm glad you updated the oRng.Start to the end of the strings, as I was actually wondering yesterday if this was required or not in ranges! I was also wondering how the .Find function really works on ranges. Would the execution be faster if the position of the "cursor" is closer to the next result (rather than at the beginning of the range)? I know the cursor is not really involved here, though, but it is more the range being shortened.

    I also wonder what is the difference between oTextRng.Collapse wdCollapseEnd and oTextRng.Start = oTextRng.End. I assume both do the same? I only ask for my own information, as that question popped my mind when seeing the collapse function in your code.

    Also, you raised another good point I didn't think: checking if the opening string appears several times within the string. I forgot to implement that one indeed!

    Now, I don't fancy the idea of highlighting the whole string when extra opening tags exist, but that's only a matter of "taste"... I managed to find a good compromise, and the final code is both optimized and working well, due to your help! Thank you very much Greg! :-)

  12. #72
    Microsoft Word MVP 2003-2009 VBAX Guru gmaxey's Avatar
    Joined
    Sep 2005
    Posts
    3,334
    Location
    glencoe,

    The little I know about VBA is self-taught so anything I say or do is based just on experience and what I've picked up along the way. Jason is probably more qualified to answer the technical questions.

    You might find this interesting: http://gregmaxey.mvps.org/word_tip_p..._property.html

    I don't know if there is any performance difference between .Start = .End or .Collapse wdCollaspeEnd or not. If there is I wouldn't think it would matter a nit.

    Glad you have learned something from the experience.
    Greg

    Visit my website: http://gregmaxey.com

  13. #73
    VBAX Regular
    Joined
    Nov 2011
    Posts
    71
    Location
    Well, whatever your experience is in VBA, it certainly is much greater than mine. So, the "little" you know cannot be compared to the little I know! What I know is also pretty much self-taught, I am just 20 years behind you! :-p

    I will certainly check your page shortly! I also really appreciate learning more about ranges, as I do see the major benefits it brings to coding... It's like switching a horse for a Ferrari (ok, a Ferrari also has a "horse" logo, but you got my point).
    As we have gone extensively through the topic, I think I will post the thread as "solved" shortly (unless Jason wants to add something).
    I will certainly request more help when time comes. And hopefully I will be able to help others as well, once my own knowledge is more robust!

  14. #74
    VBAX Wizard
    Joined
    May 2004
    Posts
    6,713
    Location
    I don't know if there is any performance difference between .Start = .End or .Collapse wdCollaspeEnd or not. If there is I wouldn't think it would matter a nit.
    Not a nit, as they are numerically - and ranges are just numbers - identical. There is no change to the memory allocation. That is the advantage of ranges over Selection; a range of 1,000 characters (.Start = .End -1,000) uses exactly the same memory as a range of 5 characters (.Start = .End -5). Unlike Selections of the same number of characters, as Selection uses GUI memory allocations.
    I rarely use integers and use longs
    We should, as a best practise, only use Long as Integers are no longer used at all by VBA. You can declare them, but the parser converts them to Long on its initial parse pass. While I suppose you could say that the conversion itself takes time, and is true that the memory blocks (being larger for Long) need destroying and reallocation (and thus you really should never use integers), but I suspect that you would have to declare thousands and thousands of integers before you would be able to even detect anything. So yes you CAN declare integers, but why not just use what VBA uses...Longs.

  15. #75
    VBAX Regular
    Joined
    Nov 2011
    Posts
    71
    Location
    Very interesting, fumei! Thank you for sharing!

    Please all of you, give yourself a "good job tap on the shoulder" as you did convince me to forget (as much as possible) about Selection, and to move to Range instead... The memory allocation is just another extra benefit!

    And I will also move all Integers to Longs instead. Now, I mostly use Integers for loops like "For i=0 to MaxValue Next", where MaxValue would be a low number (e.g. less than 100). In this case, would you still recommend moving to Long as well?

    is true that the memory blocks (being larger for Long) need destroying and reallocation
    Is this an operation that is done by the parser (I assume so), or that we have to add in the code?

  16. #76
    VBAX Wizard
    Joined
    May 2004
    Posts
    6,713
    Location
    No the VBA garbage collector takes care of most memory allocations (including destroying). Allocation happens the moment you declare a variable such as a Long. You do not need to do anything else. It has been a hot debate for years whether you should explicitly destroy objects with a Set object = Nothing. For all "minor" objects IMO you do not need to, but it IS a good idea with ranges, and definitely a good idea with application objects. When you use application instances you should always destroy them when you are done. As for ranges, there are circumstances where it is best to explicitly destroy them as part of your code blocks. In theory, when you exit a routine with a Set object (for example Set oHF = a headerfooter object), that object is destroyed. But there is still persistent issues that pop up with application objects - i.e. "extra" instances of Word.

    For loops like that I still use Long. I never use Integers at all. Like I said, VBA converts all integers to long anyway.

  17. #77
    VBAX Regular
    Joined
    Nov 2011
    Posts
    71
    Location
    How about local variables declared in a sub routine? Is it still necessary to "destroy" them, knowing that they should disappear as soon as the code exits the sub routine, or is it possible that they still remain in memory, unless they have been set to Nothing through the code?

    There will be lots of changes to implement in my current macro in order to follow the proper "rules" and rules of thumb! And I'm not even talking about rethinking the code to optimize it with Range functions!

  18. #78
    VBAX Wizard
    Joined
    May 2004
    Posts
    6,713
    Location
    No, you can ignore local variables. VBA will tidy things up on its own. As stated, the only things of any realistic concern are application objects. You are not using any.

    The only things you can destroy on your own are objects you give a value to with the Set instruction. In Greg's code there a few, all Range objects. As is, there is no need to explicitly destroy them with a Set = Nothing. VBA can handle things. I only really mentioned this subject as a comment on the nada performance issue regarding .Start = .End versus .Collapse.

    Local variables (or even Public ones) such a Longs, or Strings are assigned a memory block when declared. For example, a Long is assigned 4 bytes. Once it is assigned, it remains (persists) until the routine that holds it terminates. So, regardless of VALUE ( 1 or 1,500 or 2,000,000), the declared Long uses 4 bytes for the Scope of the procedure. BTW, you should (if you have not done so) take a good look at what Scope means. When the procedure terminates, the block of 4 bytes is released by the VBA garbage collector. You do not have to do anything.

    Obviously with our modern computers with gobs of memory 4 bytes is almost meaningless. Thus it is true that the old best practices are in some ways being made pointless. However, the principles of both understanding what is going on, and doing good housekeeping still seem valid to me. Some may argue otherwise.

  19. #79
    VBAX Regular
    Joined
    Nov 2011
    Posts
    71
    Location
    Your explanation makes perfect sense, and I also think that by keeping everything tidy, you can't mess up your code! So I'd better go that way.
    At least, if I tend to follow those simple rules, you won't be mad at me next time I show you some code I produced! ;-)

    Thank you so much again! Let's close and solve officially this topic for now! :-)

  20. #80
    Microsoft Word MVP 2003-2009 VBAX Guru gmaxey's Avatar
    Joined
    Sep 2005
    Posts
    3,334
    Location
    Gerry,

    "Jason is probably more qualified to answer the technical questions."

    No slight intended. It is just that Jason seems to enjoy explaining the technical side and usually is spot on.

    A more appropriate statement would be: "Jason or Gerry are more qualified to answer the technical questions."
    Greg

    Visit my website: http://gregmaxey.com

Tags for this Thread

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •