Community
    • Login

    Replace 1 + lines?

    Scheduled Pinned Locked Moved General Discussion
    43 Posts 7 Posters 34.1k Views
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • Alan KilbornA
      Alan Kilborn @AZJIO AZJIO
      last edited by

      @AZJIO-AZJIO

      I couldn’t get your copy_multiline.au3 script to run reliably. The Find dialog box would be shown but the if Not $hWnd Then Exit line exits the program most of the time for me. :(

      1 Reply Last reply Reply Quote 0
      • AZJIO AZJIOA
        AZJIO AZJIO
        last edited by

        Simplified version


        Send(‘^{INS}’)
        ControlSetText(“[CLASS:#32770]”, “”, “Edit2”, ClipGet())


        But the search dialog should be open

        1 Reply Last reply Reply Quote 0
        • Alan KilbornA
          Alan Kilborn
          last edited by

          @AZJIO-AZJIO said:

          But the search dialog should be open

          I was most interested in the part that does the opening of the search dialog and switching its active tab. I had tried to do something like that in the past and didn’t have good luck. I can’t remember if it was for the same reason I am now having trouble with your original script, or not, but it very well could be. Do you know if there is an alternate way of doing it that might be more reliable?

          AZJIO AZJIOA 1 Reply Last reply Reply Quote 0
          • AZJIO AZJIOA
            AZJIO AZJIO
            last edited by

            Add before each command Sleep (1000) to see which command has failed

            1 Reply Last reply Reply Quote 0
            • Claudia FrankC
              Claudia Frank
              last edited by

              If it is still about the status message, then be aware that it is ownerdrawed text,
              which means that you need to resolve the pointer returned by the statusbar message used.
              Well, I assume ControlGetText returns the message from SB_GETTEXT (never really used autoit).

              A python script to get the text would look like this

              #!/usr/bin/python
              # -*- coding: utf-8 -*-
              
              ################################################################################# 
              ## automate find dialog
              import ctypes
              from ctypes.wintypes import BOOL, HWND, LPARAM
              
              WNDENUMPROC = ctypes.WINFUNCTYPE(BOOL, HWND, LPARAM)                         
              
              FindWindow = ctypes.windll.user32.FindWindowW
              FindWindowEx = ctypes.windll.user32.FindWindowExW
              SendMessage = ctypes.windll.user32.SendMessageW
              EnumChildWindows = ctypes.windll.user32.EnumChildWindows
              GetClassName = ctypes.windll.user32.GetClassNameW
              create_unicode_buffer = ctypes.create_unicode_buffer
              
              
              BM_CLICK = 0x00F5
              SBT_OWNERDRAW = 0x1000
              WM_USER = 0x400
              
              SB_GETTEXTLENGTHW = WM_USER + 12
              SB_GETTEXTW = WM_USER + 13
              
              STATUSBAR_HANDLE = None
              
              def on_press(window_handle, button_text):
                  button_handle = FindWindowEx(window_handle, 0, u"Button", button_text)
                  SendMessage(button_handle, BM_CLICK, 0, 0)
              
              def get_result_from_statusbar(statusbar_item):
                  retcode = SendMessage(STATUSBAR_HANDLE, SB_GETTEXTLENGTHW, statusbar_item, 0)
                  length = retcode & 0xFFFF
                  type = (retcode >> 16) & 0xFFFF
                  _text = ''
                  
                  if type == SBT_OWNERDRAW:
                      retcode = SendMessage(STATUSBAR_HANDLE, SB_GETTEXTW, statusbar_item, 0)
                      _text =  'SBT_OWNERDRAW:{}'.format(ctypes.wstring_at(retcode))
                  else: # not sure if this gets called at all
                      text_buffer = create_unicode_buffer(length)        
                      retcode = SendMessage(STATUSBAR_HANDLE, SB_GETTEXTW, statusbar_item, text_buffer)
                      _text = 'text_buffer:{}'.format(text_buffer[:length]) 
                          
                  return _text
              
                  
              def EnumCallback(hwnd, lparam):
                  global STATUSBAR_HANDLE
                  curr_class = create_unicode_buffer(256)
                  GetClassName(hwnd, curr_class, 256)
                  if curr_class.value.lower() == "msctls_statusbar32":
                      STATUSBAR_HANDLE = hwnd
                      return False
                  return True
              
              notepad.menuCommand(MENUCOMMAND.SEARCH_FIND)
              
              find_hwnd = FindWindow(None, u'Find')
              EnumChildWindows(find_hwnd, WNDENUMPROC(EnumCallback), 0)
              
              if STATUSBAR_HANDLE:
                  on_press(find_hwnd, u'Coun&t')
                  console.write(get_result_from_statusbar(0) + '\n')
              

              The script opens the find dialog, then gets the window handle from that dialog,
              enumerates all its child windows (aka controls) and if a statusbar control has been found,
              simulates an button press on Count button and writes the statusbar message to the console.

              Cheers
              Claudia

              Alan KilbornA 2 Replies Last reply Reply Quote 4
              • AZJIO AZJIOA
                AZJIO AZJIO @Alan Kilborn
                last edited by

                @Alan-Kilborn said:

                Do you know if there is an alternate way of doing it that might be more reliable?

                Using Au3Info.exe, check CLASS: # 32770

                1 Reply Last reply Reply Quote 0
                • Alan KilbornA
                  Alan Kilborn @Claudia Frank
                  last edited by

                  @Claudia-Frank

                  Once again you are amazing! I haven’t tried what you propose yet but I have no doubt it will be awesome.

                  1 Reply Last reply Reply Quote 0
                  • Alan KilbornA
                    Alan Kilborn @Claudia Frank
                    last edited by

                    @Claudia-Frank

                    I started working with and extending your script to do some other things on the Find dialog. I can get and set the state of radio buttons and checkboxes–yay! Some final things that I need to do are to get and/or set the text of the Find-what and Replace-with edit boxes (or comboboxes?). Is there a best way to do this? It isn’t as easy as with the other controls because these don’t have a caption. Can you post a short example of how to get text and set text to these edit controls on the Find dialog?

                    Claudia FrankC 1 Reply Last reply Reply Quote 0
                    • Claudia FrankC
                      Claudia Frank @Alan Kilborn
                      last edited by

                      @Alan-Kilborn

                      Hi Alan,

                      you right, edit box aren’t easy to identify but, as far as I remember, you can rely on the order (z-order) how the controls get identified/returned.

                      So in this case you could check when the static box (aka label) returns with &Find what and set a
                      flag and when the edit control appears, check if flag is set and treat it. To retrieve or set the text
                      you can use the api functions GetWindowText and SetWindowText.

                      A simple example like this (only the callback part and the definitions of the code)

                      GetWindowText = ctypes.windll.user32.GetWindowTextW
                      GetWindowTextLength = ctypes.windll.user32.GetWindowTextLengthW
                      SetWindowText = ctypes.windll.user32.SetWindowTextW
                      
                      STATUSBAR_HANDLE = None
                      FIND_WHAT = None
                      REPLACE_WITH = None
                      
                      
                      def EnumCallback(hwnd, lparam):
                          global STATUSBAR_HANDLE
                          global FIND_WHAT
                          global REPLACE_WITH
                          
                          curr_class = ctypes.create_unicode_buffer(256)
                          GetClassName(hwnd, curr_class, 256)
                          
                          length = GetWindowTextLength(hwnd)
                          buff = ctypes.create_unicode_buffer(length + 1)
                          GetWindowText(hwnd, buff, length + 1)
                          
                          if curr_class.value.lower() == 'msctls_statusbar32':
                              STATUSBAR_HANDLE = hwnd
                              # return False
                          elif curr_class.value.lower() == 'static':
                              if buff.value == '&Find what :':
                                  FIND_WHAT = True
                              elif buff.value == 'Rep&lace with :':
                                  REPLACE_WITH = True
                          elif curr_class.value.lower() == 'edit':
                              if FIND_WHAT:
                                  SetWindowText(hwnd, u'xyz789')
                                  FIND_WHAT = False
                              elif REPLACE_WITH:
                                  REPLACE_WITH = False
                                  SetWindowText(hwnd, u'123abc')
                          else:
                              console.write('{}({:^7}) : {}'.format(curr_class.value,hwnd, buff.value) + '\n' )
                          return True
                      

                      Cheers
                      Claudia

                      Scott SumnerS 1 Reply Last reply Reply Quote 2
                      • Scott SumnerS
                        Scott Sumner @Claudia Frank
                        last edited by

                        So the ideas presented here by @Claudia-Frank I think will allow me to stop using AutoIt in favor of Pythonscript for controlling the Find dialog functions. It seems very nice. To bring this thread back to where it began, here’s a P.S. based upon Claudia’s work that puts the current selection (which can include multiple lines) into the Find dialog’s Replace-with box. The part of it I question for general usage is the .decode('utf-8') – it seems to work for me, but I’m not an encoding expert by any means.

                        Here’s ReplaceWithBoxFromSelection.py :

                        import ctypes
                        from ctypes.wintypes import BOOL, HWND, LPARAM
                        
                        WNDENUMPROC = ctypes.WINFUNCTYPE(BOOL, HWND, LPARAM)
                        
                        FindWindow = ctypes.windll.user32.FindWindowW
                        FindWindowEx = ctypes.windll.user32.FindWindowExW
                        SendMessage = ctypes.windll.user32.SendMessageW
                        EnumChildWindows = ctypes.windll.user32.EnumChildWindows
                        GetClassName = ctypes.windll.user32.GetClassNameW
                        GetWindowText = ctypes.windll.user32.GetWindowTextW
                        GetWindowTextLength = ctypes.windll.user32.GetWindowTextLengthW
                        SetWindowText = ctypes.windll.user32.SetWindowTextW
                        create_unicode_buffer = ctypes.create_unicode_buffer
                        
                        replace_tab_caption  = u'Replace'
                        replacewith_caption = u'Rep&lace with :'
                        replacewith_is_next_ctrl = False
                        replacewith_handle = None
                        
                        def EnumCallback(hwnd, lparam):
                            global replacewith_is_next_ctrl
                            global replacewith_handle
                            curr_class = create_unicode_buffer(256)
                            GetClassName(hwnd, curr_class, 256)
                            length = GetWindowTextLength(hwnd)
                            buff = ctypes.create_unicode_buffer(length + 1)
                            GetWindowText(hwnd, buff, length + 1)
                            if curr_class.value.lower() == 'static':
                                if buff.value == replacewith_caption:
                                    replacewith_is_next_ctrl = True
                            elif curr_class.value.lower() == 'edit':
                                if replacewith_is_next_ctrl:
                                    replacewith_handle = hwnd
                                    return False  # stop enumeration
                            return True  # let enumeration continue
                        
                        def main():
                            msg = ''
                            if editor.getSelections() == 1 and not editor.getSelectionEmpty():
                                sel = editor.getSelText()
                                if len(sel) > 2046:
                                    sel = sel[:2046]
                                    msg += 'Warning:  Selected text too long for replace-with box; truncating to 2046 characters.  '
                                notepad.menuCommand(MENUCOMMAND.SEARCH_REPLACE)
                                find_dialog_hwnd = FindWindow(None, replace_tab_caption)
                                if find_dialog_hwnd:
                                    EnumChildWindows(find_dialog_hwnd, WNDENUMPROC(EnumCallback), 0)
                                    if replacewith_handle:
                                        if SetWindowText(replacewith_handle, sel.decode('utf-8')) == 0:
                                            msg += 'Error:  Problem setting window text'
                                    else:
                                        msg += 'Error:  Bad value for replacewith_handle'
                                else:
                                    msg += 'Error:  Bad value for find_dialog_hwnd'
                            else:
                                msg += 'Warning:  Nothing to copy into replace-with field'
                            if len(msg) > 0: notepad.messageBox(msg)
                        
                        main()
                        
                        Claudia FrankC 1 Reply Last reply Reply Quote 2
                        • Claudia FrankC
                          Claudia Frank @Scott Sumner
                          last edited by

                          @Scott-Sumner

                          The part of it I question for general usage is the .decode(‘utf-8’)

                          I assume your way should work, regardless of the encoding used by the user,
                          if I understand scintilla and pythonscript plugin correctly.
                          editor.getSelText() always returns a byte string (python str) but, because we defined
                          unicode version of SetWindowText, this string needs to be converted as you did.
                          Maybe I’m wrong.

                          Cheers
                          Claudia

                          Scott SumnerS 1 Reply Last reply Reply Quote 0
                          • Scott SumnerS
                            Scott Sumner @Claudia Frank
                            last edited by Scott Sumner

                            @Claudia-Frank

                            I did notice something else that was a bit odd (and drove me a little nuts until I figured out what was going on) when I was working on that script. Let’s see if I can describe it. Usually I work with English_customizable.xml loaded, and everything is consistent and as-expected. When working on that script, I switched back to English.xml, thinking that if I share the script here, it should not use my specific and custom Find dialog captions, but rather the generic English ones.

                            With English configured, I noticed that when Notepad++ starts up, it doesn’t exactly have English.xml set up, it has something close to it, but not exactly! I found that if I go back into the Preferences, and again select English from the dropdown (even though it is already selected), and have the Find dialog up at the same time, I can watch subtle changes occur in the Find dialog’s label/caption fields. For example, and I can watch a space be inserted (or removed) between “Find what” and the colon that follows (same for “Replace with”. I really have no idea why this is happening. Again, this only happens with English.xml, not English_customizable.xml.

                            The reason it was making me crazy was that I’d work on the script, have it working fine, then later I would notice it was breaking because it couldn’t find the controls. I kept changing the text strings to make it work and then later it would break again.

                            1 Reply Last reply Reply Quote 0
                            • guy038G
                              guy038
                              last edited by

                              Hello, @scott-sumner and @claudia-frank,

                              Claudia, your automatic counting of any normal selection, multi-lines or not, works fine !

                              Scott, your ReplaceWithBoxFromSelection.py script is working nice, too !. I’ve just noticed that your script, automatically, copies the current normal and unique selection, BOTH, in the Find what : and in the Replace with: boxes. Is it the normal behaviour or am I wrong about it ?


                              This topic makes me think about an other possibility ! Let’s suppose that the option Settings > Preferences…> Editing > Multi-Editing Settings is enabled

                              Now, the goal would be :

                              • To define TWO selected blocks of text, which could, independently, include multiple lines

                              • To Write a Python script which :

                                • Opens the Find/Replace dialog

                                • Writes the first user selection, in the Find what: box

                                • Writes the second user selection, in the Replace with: box

                              Scott and Claudia, what is your feeling about it ?

                              Best Regards,

                              guy038

                              Claudia FrankC Scott SumnerS 3 Replies Last reply Reply Quote 0
                              • Claudia FrankC
                                Claudia Frank @guy038
                                last edited by Claudia Frank

                                @Scott-Sumner and @guy038

                                Scott, I discovered something similar but thought it was related to using npp with wine.
                                If I open the find dialog and then switch from english to english_customizable and back
                                to english I loose the >> from “find next” button and the underscores (ampersands in text)
                                for the “count” and “find all in all opened documents” buttons.
                                I don’t see it as an issue as you normally set this once and done, but I see why it bothered you. ;-)
                                I have a shortcut to “Run Previous Script” which I use quite often.
                                What happens from time to time is that I execute another script while editing
                                the “current” script and then when I use the shortcut I assume that the current script
                                should be executed, which isn’t the case as another script was executed in between.
                                First thought I have in such a case is always, damn - what did I miss why isn’t it working anymore? What did I broke?
                                Checking the source again and again just to find out that haven’t executed the script. :-)
                                You see - seems to be “normal” programmer behavior ;-)

                                Guy, haven’t check it yet but I guess it should be easy to implement, yes.
                                If Scott, or anyone else hasn’t proposed a solution, I will give it a try in 10-12 hours.
                                Need to leave now.

                                Cheers
                                Claudia

                                1 Reply Last reply Reply Quote 1
                                • Scott SumnerS
                                  Scott Sumner @guy038
                                  last edited by

                                  @guy038 said:

                                  copies the current normal and unique selection, BOTH, in the Find what : and in the Replace with: boxes

                                  Hmmm. Yes, that is what it does but that was actually UNINTENTIONAL! I guess I didn’t notice because for years I have had an AutoIt script which does just that behavior. A lot of the time when doing replacements, I’m just doing a minor adjustment to the text I am finding, so it saves time to have both the Find-what and Replace-with fields initally set to the same data. I just make my minor tweak to the replace-with part and I am off to the races.

                                  In this thread’s script (ReplaceWithBoxFromSelection.py), however, I believe this happens due to the normal invocation of the Replace command (with notepad.menuCommand(MENUCOMMAND.SEARCH_REPLACE))–when a selection is active, Notepad++ on its own puts the selected text in the Find-what box. Perhaps that is less than desirable here in this script. :(

                                  1 Reply Last reply Reply Quote 1
                                  • Scott SumnerS
                                    Scott Sumner @guy038
                                    last edited by

                                    @guy038 said:

                                    This topic makes me think about an other possibility

                                    I like your idea and it should be easy, but I will defer to Claudia as I am extremely busy at the moment. :-D

                                    1 Reply Last reply Reply Quote 0
                                    • guy038G
                                      guy038
                                      last edited by

                                      Hi, @scott-sumner and @claudia-frank

                                      Many thanks, in advance, for this future script ! This short post give me the opportunity to express, once again, all my gratitude towards you, Claudia and Scott, for your numerous scripts, on various topics, which helped us, significantly, so many times :-D

                                      Cheers,

                                      guy038

                                      Claudia FrankC 1 Reply Last reply Reply Quote 0
                                      • Claudia FrankC
                                        Claudia Frank @guy038
                                        last edited by

                                        @guy038, @scott-sumner

                                        Hi Guy,

                                        sorry, family celebration was a bit longer than expected.
                                        Just did the script and some basic tests and discovered that encoding
                                        is an issue like Scott already mentioned.
                                        Will try to find a way to make it work regardless which encoding is used.

                                        Cheers
                                        Claudia

                                        1 Reply Last reply Reply Quote 0
                                        • guy038G
                                          guy038
                                          last edited by

                                          Hi, @claudia-frank,

                                          Don’t worry, Claudia and just enjoy these family’s moments ! Luckily, we, all, own more valuable things than Notepad++ and some Python scripts :-D

                                          …I’m just thinking about actualities and the death’s celebration of Helmut Kohl. Thanks to him and some important European women and men, we all enjoy, in Europe, a peace period, of near 75 years ! and THIS, it’s a basic point !!

                                          Best Regards,

                                          guy038

                                          1 Reply Last reply Reply Quote 1
                                          • Claudia FrankC
                                            Claudia Frank
                                            last edited by Claudia Frank

                                            From my basic tests it looks like it should do the trick.
                                            Tested utf-8, ansi, winows-1250, winows-1251 and winows-1252.
                                            It works only with normal selection - not with column mode selection.

                                            import ctypes
                                            from ctypes.wintypes import BOOL, HWND, LPARAM
                                            
                                            WNDENUMPROC = ctypes.WINFUNCTYPE(BOOL, HWND, LPARAM)
                                            
                                            FindWindow = ctypes.windll.user32.FindWindowW
                                            FindWindowEx = ctypes.windll.user32.FindWindowExW
                                            SendMessage = ctypes.windll.user32.SendMessageW
                                            EnumChildWindows = ctypes.windll.user32.EnumChildWindows
                                            GetClassName = ctypes.windll.user32.GetClassNameW
                                            GetWindowText = ctypes.windll.user32.GetWindowTextW
                                            GetWindowTextLength = ctypes.windll.user32.GetWindowTextLengthW
                                            SetWindowText = ctypes.windll.user32.SetWindowTextW
                                            GetACP = ctypes.windll.kernel32.GetACP
                                            create_unicode_buffer = ctypes.create_unicode_buffer
                                            
                                            replace_tab_caption  = u'Replace'
                                            replacewith_caption = u'Rep&lace with :'
                                            replacewith_is_next_ctrl = False
                                            replacewith_handle = None
                                            replacewith_text = None
                                            findwhat_caption = u'&Find what :'
                                            findwhat_is_next_ctrl = False
                                            findwhat_handle = None
                                            findwhat_text = None
                                            
                                            
                                            def EnumCallback(hwnd, lparam):
                                                global replacewith_is_next_ctrl
                                                global findwhat_is_next_ctrl
                                                global replacewith_handle
                                                global findwhat_handle
                                                curr_class = create_unicode_buffer(256)
                                                GetClassName(hwnd, curr_class, 256)
                                                length = GetWindowTextLength(hwnd)
                                                buff = ctypes.create_unicode_buffer(length + 1)
                                                GetWindowText(hwnd, buff, length + 1)
                                                if curr_class.value.lower() == 'static':
                                                    if buff.value == replacewith_caption:
                                                        replacewith_is_next_ctrl = True
                                                    elif buff.value == findwhat_caption:
                                                        findwhat_is_next_ctrl = True
                                                elif curr_class.value.lower() == 'edit':
                                                    if findwhat_is_next_ctrl:
                                                        findwhat_handle = hwnd
                                                        findwhat_is_next_ctrl = False
                                                    elif replacewith_is_next_ctrl:
                                                        replacewith_handle = hwnd
                                                        replacewith_is_next_ctrl = False
                                                        return False  # stop enumeration
                                                return True  # let enumeration continue
                                            
                                            def return_proper_string(selected_text):
                                                if notepad.getEncoding() == BUFFERENCODING.ENC8BIT:
                                                    _selected_text = unicode(selected_text, '{}'.format(GetACP()))
                                                else:
                                                    _selected_text = unicode(selected_text, 'utf-8')
                                                return _selected_text
                                            
                                            def main():
                                                msg = ''
                                                if editor.getSelections() == 2:
                                                    if editor.getSelectionMode() == 0 and not editor.getSelectionEmpty():
                                                        _find_what = editor.getTextRange(editor.getSelectionNStart(0), editor.getSelectionNEnd(0))
                                                        _replace_with = editor.getTextRange(editor.getSelectionNStart(1), editor.getSelectionNEnd(1))
                                            
                                                        if len(_find_what) > 2046:
                                                            _find_what = _find_what[:2046]
                                                            msg += 'Warning:  Selected text too long for find-what box; truncating to 2046 characters.  '
                                                                
                                                        if len(_replace_with) > 2046:
                                                            _replace_with = _replace_with[:2046]
                                                            msg += 'Warning:  Selected text too long for replace-with box; truncating to 2046 characters.  '
                                                                                    
                                                        notepad.menuCommand(MENUCOMMAND.SEARCH_REPLACE)
                                                        find_dialog_hwnd = FindWindow(None, replace_tab_caption)
                                                        
                                                        if find_dialog_hwnd:
                                                            EnumChildWindows(find_dialog_hwnd, WNDENUMPROC(EnumCallback), 0)
                                                            if findwhat_handle and replacewith_handle:
                                                                if SetWindowText(findwhat_handle, return_proper_string(_find_what)) == 0:
                                                                    msg += 'Error:  Problem setting find-what text'
                                                                if SetWindowText(replacewith_handle, return_proper_string(_replace_with)) == 0:
                                                                    msg += 'Error:  Problem setting replace-with text'
                                                            else:
                                                                msg += 'Error:  Bad value for replacewith_handle'
                                                        else:
                                                            msg += 'Error:  Bad value for find_dialog_hwnd'
                                                    else:
                                                        msg += 'Error:  Either empty selection or unsupported selection mode detected'
                                                else:
                                                    msg += 'Warning:  Only two selections are possible. Received:{}'.format(editor.getSelections())
                                                if len(msg) > 0: notepad.messageBox(msg)
                                            
                                            main()
                                            

                                            Cheers
                                            Claudia

                                            1 Reply Last reply Reply Quote 0
                                            • First post
                                              Last post
                                            The Community of users of the Notepad++ text editor.
                                            Powered by NodeBB | Contributors