Community
    • Login

    Autosave development request

    Scheduled Pinned Locked Moved Notepad++ & Plugin Development
    13 Posts 3 Posters 9.9k 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.
    • decodermanD
      decoderman
      last edited by

      I am still using the deprecated Autosave v 1.4 by Franco Stellari.
      Now, I very much like it when the Notepad++ window loses focus to save all open files.
      It allows for a much quicker workflow for me when testing code on the fly.
      This version has several bugs in combination with the latest Notepad version and will be deactivated when installing.

      Since this Plugin is no longer maintained by the original developer I was wondering if anyone else can pick up from where he left.
      I myself lack the skills to do it, but I hope someone else does.
      I would be eternally grateful for such a Plugin and am willing to donate.

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

        Hello @decoderman,

        I never used autosave, so I don’t know its functionality.
        Saving if npp looses focus, is this the only feature you need?
        If so, do you care if this feature is called by a python script (python script plugin needs to be installed)?
        Need to check a few things but I assume it is possible to realize.

        Cheers
        Claudia

        1 Reply Last reply Reply Quote 1
        • decodermanD
          decoderman
          last edited by

          Hello @Claudia-Frank

          Thanks for jumping in and maybe save me from a lot of headaches!
          I am using the Python Plugin anyway, so this could work.

          The basic functionality of saving all open files without a prompt would do.
          That means it has to overwrite existing files.

          There is the matter of new files, like new 1, new 2 and so forth. I use these a lot for temporary code and snippets for later use.
          I have set Autosave to save those to a configured temp directory, using the name given when opening a new file. This also would overwrite existing files with the same name in that directory. Which I have no problem with as these are only session critical files for me.

          Cheers
          Martin

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

            Hello Martin,

            this is not only a bit of a hack it is a serious hack you should know that it might be able that it will crash your
            npp if something unforeseen happens.
            The reason is because we subclass npps message queue which means npp doesn’t get the messages first,
            python script plugin gets the messages and after checking if the message in question has been received it
            passes it to npp. So be warned.

            I wouldn’t recommend that you put the whole code into startup.py as long as you didn’t test if everything works as expected.
            If you execute the script it registered the hook, it you run it another time it unregisters it and everything should be as it was before.

            You only need to modify two variables.

            NewFileDir = 'D:\\tests\\npp\\'
            NewFileExt = '.txt'
            

            NewFileDir should point to the directory where “New X” files should be stored and
            NewFileExt which extension should be used.

            import ctypes, os                                                                           # import needed modules
            from ctypes.wintypes import LONG, HWND, UINT, WPARAM, LPARAM                                # import needed types    
            
            SetWindowLong = ctypes.windll.user32.SetWindowLongA                                         # used to subclass npp window
            CallWindowProc = ctypes.windll.user32.CallWindowProcA                                       # used to send message to npp
            FindWindow = ctypes.windll.user32.FindWindowA                                               # used to find npp window handle
            
            WndProcType = ctypes.WINFUNCTYPE(LONG, HWND, UINT, WPARAM, LPARAM)                          # callback prototype
            
            GWL_WNDPROC = -4                                                                            # used to set a new address for the window procedure
            WM_ACTIVATEAPP = 0x001C                                                                     # message we are interested in
            
            NewFileDir = 'D:\\tests\\npp\\'
            NewFileExt = '.txt'
            
            class Hook():                                                                               # hook class
            
                def __init__(self):                                                                     # class constructor
                    self.nppHandle = FindWindow('Notepad++',None)                                       # find npp window handle and store it
                    
                def register(self):                                                                     # function to register the hook, first
                    self.newWndProc = WndProcType(self.MyWndProc)                                       # get the address of our wndproc
                    self.oldWndProc = SetWindowLong(self.nppHandle, GWL_WNDPROC, self.newWndProc)       # register it and receive old wndproc address
                    console.editor.setProperty('oldWndProc', self.oldWndProc)                           # store it to be able to unregister it needed
                    
                def unregister(self):                                                                   # function to unregister hook
                    self.prevWndProc = console.editor.getProperty('oldWndProc')                         # receive previously stored wndproc address
                    dummy = SetWindowLong(self.nppHandle, GWL_WNDPROC, int(self.prevWndProc))           # register it - return isn't interesting 
            
                def MyWndProc(self, hWnd, msg, wParam, lParam):                                         # our own wndproc function receives windows messages
                    if msg == WM_ACTIVATEAPP:                                                           # if it is WM_ACTIVATEAPP and 
                        if wParam == False:                                                             # if wparam is false then npp has lost focus and
                            self.saveFiles()                                                            # we can start saving the files
            
                    return CallWindowProc(self.oldWndProc, hWnd, msg, wParam, lParam)                   # IMPORTANT we need to pass received msg to npp, otherwise npp will be blocked.
            
                def saveFiles(self):
                    buf = notepad.getCurrentBufferID()                                                  # remember current document id
                    
                    for f in notepad.getFiles():                                                        # loop through all files and
                        if os.path.isfile(f[0]):                                                        # check if file is real (no new X file)
                            notepad.activateBufferID(f[1])                                              # if so, switch to the doc
                            if notepad.getCurrentBufferID() == f[1]:                                    # and check if we have the focus we want (and ignore hidden files)
                                notepad.save()                                                          # now save it.
                        else:                                                                           # here <- looks like this is a new x file
                            notepad.activateBufferID(f[1])                                              # switch to the doc
                            if notepad.getCurrentBufferID() == f[1]:                                    # and check if we have the focus we want (and ignore hidden files)
                                notepad.saveAs(NewFileDir + f[0] + NewFileExt)                          # now save it as ...
                    
                    notepad.activateBufferID(buf)                                                       # switch back to initial doc
                    
            _hook = Hook()                                                                              # get a instance of our class
            
            if console.editor.getProperty('Hookstatus') != '1':                                         # see if this call is used to register or unregister our wndproc
                console.editor.setProperty('Hookstatus', '1')                                           # it is the first call or call to register it again
                _hook.register()                                                                        # let's do it
            else:                                                                                       
                console.editor.setProperty('Hookstatus', '0')                                           # we should unregister our hook
                _hook.unregister()                                                                      # so we do it.
            

            If something is unclear, let me know.

            Cheers
            Claudia

            1 Reply Last reply Reply Quote 1
            • decodermanD
              decoderman
              last edited by

              Hello Claudia

              That appears to be working very well, thank you!
              I have done some testing with remote files opened with WinSCP and that has also worked so far.
              Very promising indeed.

              The next step is to use it later today, doing some productive work in the way I usually do.

              One thing I noticed is the new file creation.
              Say, I double click the tab bar for a new file and it opens as ‘new 1’.
              I enter some text and have it Autosave to ‘new 1.txt’. So far so good, works as intended.

              Now I double click again for another new file and I get another instance of ‘new 1’.
              Entering text and have it Autosave overwrites the existing and still open ‘new 1.txt’ file.
              Is there any way you could know that npp should open ‘new 2’ instead of ‘new 1’ when the ‘new 1.txt’ is already open in npp?

              I have tried this without the .txt extension by removing the " + NewFileExt" on line 48 of your code.
              It is the same outcome.

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

                Hello Martin,

                no, afaik I can’t force npp to open as new 2 or whatever needs to be open next but what we can do is to save it with the
                next number by checking if a new 2 already exists.
                Save function needs to be changed like

                def saveFiles(self):
                    buf = notepad.getCurrentBufferID()                                                  # remember current document id
                    
                    for f in notepad.getFiles():                                                        # loop through all files and
                        if os.path.isfile(f[0]):                                                        # check if file is real (no new X file)
                            notepad.activateBufferID(f[1])                                              # if so, switch to the doc
                            if notepad.getCurrentBufferID() == f[1]:                                    # and check if we have the focus we want (and ignore hidden files)
                                notepad.save()                                                          # now save it.
                        else:                                                                           # here <- looks like this is a new x file
                            notepad.activateBufferID(f[1])                                              # switch to the doc
                            if notepad.getCurrentBufferID() == f[1]:                                    # and check if we have the focus we want (and ignore hidden files)
                                counter = 0                                                             # initial counter
                                filename = f[0]                                                         # get filename from tuple
                                while os.path.isfile(NewFileDir + filename + NewFileExt):               # loop - check if file already exists
                                    counter += 1                                                        # yes, increase counter
                                    filename = filename[:-1] + str(counter)                             # create new filename based on counter
                                notepad.saveAs(NewFileDir + filename + NewFileExt)                      # filename is unique, now save it as ...
                    
                    notepad.activateBufferID(buf)                                                       # switch back to initial doc
                

                Cheers
                Claudia

                1 Reply Last reply Reply Quote 0
                • decodermanD
                  decoderman
                  last edited by decoderman

                  Hi Claudia

                  I have worked all weekend long with your first code and never once had a problem!
                  This is working extremely well. Thank you.

                  Now, with your additional saveFiles I get the following error:

                  Traceback (most recent call last):
                    File "_ctypes/callbacks.c", line 314, in 'calling callback function'
                    File "C:\Users\<user>\AppData\Roaming\Notepad++\plugins\Config\PythonScript\scripts\startup.py", line 33, in MyWndProc
                      self.saveFiles()                                                            # we can start saving the files
                  AttributeError: Hook instance has no attribute 'saveFiles'
                  

                  The complete startup.py looks like so now:

                  import ctypes, os                                                                           # import needed modules
                  from ctypes.wintypes import LONG, HWND, UINT, WPARAM, LPARAM                                # import needed types    
                  
                  SetWindowLong = ctypes.windll.user32.SetWindowLongA                                         # used to subclass npp window
                  CallWindowProc = ctypes.windll.user32.CallWindowProcA                                       # used to send message to npp
                  FindWindow = ctypes.windll.user32.FindWindowA                                               # used to find npp window handle
                  
                  WndProcType = ctypes.WINFUNCTYPE(LONG, HWND, UINT, WPARAM, LPARAM)                          # callback prototype
                  
                  GWL_WNDPROC = -4                                                                            # used to set a new address for the window procedure
                  WM_ACTIVATEAPP = 0x001C                                                                     # message we are interested in
                  
                  NewFileDir = 'F:\\npp\\'
                  NewFileExt = '.txt'
                  
                  class Hook():                                                                               # hook class
                  
                      def __init__(self):                                                                     # class constructor
                          self.nppHandle = FindWindow('Notepad++',None)                                       # find npp window handle and store it
                          
                      def register(self):                                                                     # function to register the hook, first
                          self.newWndProc = WndProcType(self.MyWndProc)                                       # get the address of our wndproc
                          self.oldWndProc = SetWindowLong(self.nppHandle, GWL_WNDPROC, self.newWndProc)       # register it and receive old wndproc address
                          console.editor.setProperty('oldWndProc', self.oldWndProc)                           # store it to be able to unregister it needed
                          
                      def unregister(self):                                                                   # function to unregister hook
                          self.prevWndProc = console.editor.getProperty('oldWndProc')                         # receive previously stored wndproc address
                          dummy = SetWindowLong(self.nppHandle, GWL_WNDPROC, int(self.prevWndProc))           # register it - return isn't interesting 
                  
                      def MyWndProc(self, hWnd, msg, wParam, lParam):                                         # our own wndproc function receives windows messages
                          if msg == WM_ACTIVATEAPP:                                                           # if it is WM_ACTIVATEAPP and 
                              if wParam == False:                                                             # if wparam is false then npp has lost focus and
                                  self.saveFiles()                                                            # we can start saving the files
                  
                          return CallWindowProc(self.oldWndProc, hWnd, msg, wParam, lParam)                   # IMPORTANT we need to pass received msg to npp, otherwise npp will be blocked.
                  
                  	def saveFiles(self):
                  		buf = notepad.getCurrentBufferID()                                                  # remember current document id
                  		
                  		for f in notepad.getFiles():                                                        # loop through all files and
                  			if os.path.isfile(f[0]):                                                        # check if file is real (no new X file)
                  				notepad.activateBufferID(f[1])                                              # if so, switch to the doc
                  				if notepad.getCurrentBufferID() == f[1]:                                    # and check if we have the focus we want (and ignore hidden files)
                  					notepad.save()                                                          # now save it.
                  			else:                                                                           # here <- looks like this is a new x file
                  				notepad.activateBufferID(f[1])                                              # switch to the doc
                  				if notepad.getCurrentBufferID() == f[1]:                                    # and check if we have the focus we want (and ignore hidden files)
                  					counter = 0                                                             # initial counter
                  					filename = f[0]                                                         # get filename from tuple
                  					while os.path.isfile(NewFileDir + filename + NewFileExt):               # loop - check if file already exists
                  						counter += 1                                                        # yes, increase counter
                  						filename = filename[:-1] + str(counter)                             # create new filename based on counter
                  					notepad.saveAs(NewFileDir + filename + NewFileExt)                      # filename is unique, now save it as ...
                  		
                  		notepad.activateBufferID(buf)                                                       # switch back to initial doc
                          
                  _hook = Hook()                                                                              # get a instance of our class
                  
                  if console.editor.getProperty('Hookstatus') != '1':                                         # see if this call is used to register or unregister our wndproc
                      console.editor.setProperty('Hookstatus', '1')                                           # it is the first call or call to register it again
                      _hook.register()                                                                        # let's do it
                  else:                                                                                       
                      console.editor.setProperty('Hookstatus', '0')                                           # we should unregister our hook
                      _hook.unregister()                                                                      # so we do it.
                  
                  Claudia FrankC 1 Reply Last reply Reply Quote 0
                  • Claudia FrankC
                    Claudia Frank @decoderman
                    last edited by

                    Hello Martin,

                    does this happen immediately and always when npp looses focus?
                    Or randomly?
                    Could you double check that saveFiles is part of the hook class? Indentation?
                    Do you use user startup or machine startup.py?
                    Just tested it on my windows 7 and npp 6.9.1 - it works.

                    Cheers
                    Claudia

                    1 Reply Last reply Reply Quote 1
                    • decodermanD
                      decoderman
                      last edited by

                      Hugs and kisses to you Claudia!

                      I copied my last posts code into the machine startup.py, removed the user startup.py and have no more errors.
                      It was likely an indentation error on my side.

                      This is on Windows 10 64bit, npp 6.9.1.
                      Your ‘hack’ works way better than I would have hoped for.
                      Goodbye Autosave 1.4 by Franco Stellari.
                      I warmly welcome Autosave 1.0 by Claudia Frank!

                      Cheers
                      Martin

                      1 Reply Last reply Reply Quote 0
                      • Franco StellariF
                        Franco Stellari
                        last edited by

                        Can you be more specific on what the issue may be with newer version?
                        I have not been able to see the problem.

                        decodermanD 1 Reply Last reply Reply Quote 0
                        • decodermanD
                          decoderman @Franco Stellari
                          last edited by

                          @Franco-Stellari Altough your plugin gets deactivated every time NPP is updated, I always copy the .dll back into the plugins folder.
                          For my workflow it works best when opened unsaved files are auto saved when loosing focus.
                          That still works to my full satisfaction with v1.4.
                          My settings:

                          • when losing focus
                          • All open files
                          • Overwrite existing file
                          • Save (overwrite) silently to tmp dir

                          Scenario:
                          Double click Files Tab, a new file opens called new1.

                          • Write something, NPP loses focus, file gets saved to tmp dir, overwriting existing one. Correct behavior.
                          • Double click files tab again, new file opens, also called new1
                          • type something, NPP loses focus and the new new1 overwrites the old new1 file.
                            The new file creation with the same filename in such a way only happens if your plugin is activated.
                            Normal behavior by NPP is to name the second new file new2.
                          Franco StellariF 1 Reply Last reply Reply Quote 0
                          • Franco StellariF
                            Franco Stellari @decoderman
                            last edited by

                            @decoderman what does it mean that “your plugin gets deactivated”?
                            I use a portable version and when I copy the new notepad++ files and start, the plugin does not get deactivated… why would it be?

                            Regarding your scenario, it does not have anything to do with Autosave per se. In fact you will obtain the same behavior in normal use without Autosave enabled:

                            1. Double click files tab new file opens, called “new 1”
                            2. Manually save the file as “new 1”
                            3. Double click files tab again and a new file opens, called also “new 1”
                            4. Manually save the file as “new 1”… obviously it get overwritten

                            In some older version the new file created by NPP kept increasing so you would not get a second “new 1”.

                            I think what you want to do is create unique empty files and that is achieved using TakeNotes plugin.

                            decodermanD 1 Reply Last reply Reply Quote 0
                            • decodermanD
                              decoderman @Franco Stellari
                              last edited by

                              @Franco-Stellari Last time I checked I only had the double new 1 file problem when Autosave is enabled.
                              I’ll try the TakeNotes plugin, sounds what I needed for a long time. Thanks

                              Regarding the deactivation: If you use the installer, NPP deems your plugin as not stable (and says so) and will move it to the disabled folder. Sad but true.

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