Announcement

Collapse
No announcement yet.

Capture Selected Text

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • Capture Selected Text

    You can insert your app into the clipboard chain of viewers, so that your app is notified when the clipboard content has changed and your app can then access the content.

    I wonder if there is a similar method for detecting "selected text" as it occurs - where an app can detect when a selection is made in other apps and then extract that text into a buffer - where there was just a selection, NOT a copy, performed by a user. Perhaps the "when it occurs" is not possible, but the identification of any current selection is?






  • #2
    If a user selects something, it should mean the window in question has focus right then, so a simple timer event where repeated combination GetFocus, GetClassName (to make sure it's an Edit control), %EM_GETSEL message, GetText and use MID$ to grab SelText should do it. At least grabbing text from any edit control is possible (have done it from both NotePad and WordPad).

    Comment


    • #3
      Come to think of it - GetFocus only works "f the window is associated with the calling thread's message queue". But should be other ways, like GetForegroundWindow?

      Comment


      • #4
        Howdy Borje!

        Continuous monitoring does have possibilities.

        As with the clipboard, I was hoping there might be a system notification available, which I could tap into, that gives the handle of the window in which the selection was made. Something that requires less hunting (monitoring) from the code.

        The idea is that when a selection is made, I want to be alerted so that my app can go into play, getting the selection and then do something with it - such as reading it out loud automatically to my low vision folks. Doing it for selections outside my own app complicates the task.

        There are various nuances in the solution - knowing if an existing selection is new or old, the possibility that a selection includes text and images, multiple selections within the same document, a residual selection in a window that does not have focus, ... perhaps others .

        I'll play with the monitoring approach. The selection can only be made in the foreground window, so the monitoring may not be that complicated.

        Comment


        • #5
          No, not so complicated. I played with this some years ago to be able to implement spell check and some string handling routines (UCASE, LCASE, etc.) to NotePad and WordPad. Used timer + window enumeration to get a list of started editors then, but a cleaner way could be like code below. Compile, run, type or paste in for example NotePad - select something there and see the selection being copied to the program's TextBox. As a starter..
          '
          Code:
          #COMPILE EXE
          #DIM ALL
          #INCLUDE "WIN32API.INC"
          %IDC_TEXTBOX1 = 131
          
          '====================================================================
          FUNCTION PBMAIN () AS LONG
            LOCAL hDlg AS DWORD
          
            DIALOG NEW 0, "Get any Edit seltext",,, 220, 145, %WS_CAPTION OR _
                                                    %WS_SYSMENU, 0 TO hDlg
            '------------------------------------------------------------------
            CONTROL ADD TEXTBOX, hDlg, %IDC_TEXTBOX1, "", 0, 0, 220, 145, _
                      %WS_CHILD OR %WS_VISIBLE OR %WS_VSCROLL OR %ES_MULTILINE OR _
                      %ES_WANTRETURN, %WS_EX_DLGMODALFRAME
          
            '------------------------------------------------------------------
            DIALOG SHOW MODAL hDlg CALL DlgProc
          
          END FUNCTION
          
          '====================================================================
          CALLBACK FUNCTION DlgProc() AS LONG
            LOCAL hWnd AS DWORD, pt AS POINTAPI
            STATIC hTimer AS DWORD
          
            SELECT CASE CB.MSG
            CASE %WM_INITDIALOG
                SetWindowPos CB.HNDL, %HWND_TOPMOST, 0, 0, 0, 0, %SWP_NOMOVE OR %SWP_NOSIZE
                hTimer = SetTimer(CB.HNDL, 1, 100, 0)
          
            CASE %WM_TIMER
               GetCursorPos pt               ' grab cursor pos on screen
               hWnd = WindowFromPoint(pt)    ' see what window is under cursor
               IF hWnd AND hWnd <> (GetDlgItem(CB.HNDL, %IDC_TEXTBOX1)) THEN
                   CONTROL SET TEXT CB.HNDL, %IDC_TEXTBOX1, GetFarText(hWnd)
               END IF
          
            CASE %WM_DESTROY
                IF hTimer THEN KillTimer(CB.HNDL, hTimer)
          
            CASE %WM_COMMAND
                SELECT CASE CB.CTL
                CASE %IDCANCEL
                   IF CB.CTLMSG = %BN_CLICKED OR CB.CTLMSG = 1 THEN 'end prog
                      DIALOG END CB.HNDL
                   END IF
                END SELECT
          
            END SELECT
          END FUNCTION
          
          '====================================================================
          FUNCTION GetFarText(BYVAL hEdit AS DWORD) AS STRING
          '--------------------------------------------------------------------
            LOCAL lRes, lSelStart, lSelEnd AS LONG
            LOCAL sTemp AS STRING, szClass AS ASCIIZ * 32
          
            IF hEdit THEN
                GetClassName(hEdit, szClass, SIZEOF(szClass))
                IF UCASE$(szClass) = "EDIT" THEN
                    SendMessage hEdit, %EM_GETSEL, VARPTR(lSelStart), VARPTR(lSelEnd)
                    IF lSelStart <> lSelEnd THEN
                        SendMessage hEdit, %WM_GETTEXTLENGTH, 0, 0 TO lRes
                        sTemp = SPACE$(lRes + 1)
                        SendMessage hEdit, %WM_GETTEXT, LEN(sTemp), STRPTR(sTemp)
                        '------------------------------------------------------------------
                        IF lSelEnd < lSelStart THEN SWAP lSelStart, lSelEnd
                        FUNCTION = MID$(sTemp, lSelStart + 1, lSelEnd - lSelStart)
                    END IF
                END IF
            END IF
          
          END FUNCTION
          '

          Comment


          • #6
            Howdy, Borje!

            Thanks for the example. Wouldn't you know that the first test I ran was on a Scintilla-based window and the "EDIT" class didn't work. But on NotePad it works great!

            I suspect there are a large number of custom controls, whose class is not "EDIT"?

            I am generally more interested in ensuring the code works on browsers, so that might limit the class count somewhat. I'll nose around in the Chrome/Edge/FireFox and see what classes I find.

            Comment


            • #7
              Hello Borje, I tried using your prog in #5, in the below steps but was not successful in saving the captured text in the textbox

              1. open a notepad and type something
              2. run the program in #5
              3. Select a group of text in the notepad and these text were captured by the program
              4. but when you move the cursor back to the notepad the captured text in the textbox disappear ?

              Is there a way to keep these texts there captured text in the textbox when you move your cursor back to the notepad
              something like an auto save into a temporary string for the textbox, to prevent them disappearing

              Comment


              • #8
                Yeah, better to store result from GetFarText into a string variable and check for length, but also need to return a zero byte from GetFarText if nothing is selected. Quick fix - see changes in GetFarText and DlgProc:
                '
                Code:
                #COMPILE EXE
                #DIM ALL
                #INCLUDE "WIN32API.INC"
                %IDC_TEXTBOX1 = 131
                
                '====================================================================
                FUNCTION PBMAIN () AS LONG
                  LOCAL hDlg AS DWORD
                
                  DIALOG NEW 0, "Get any Edit seltext",,, 220, 145, %WS_CAPTION OR _
                                                          %WS_SYSMENU, 0 TO hDlg
                  '------------------------------------------------------------------
                  CONTROL ADD TEXTBOX, hDlg, %IDC_TEXTBOX1, "", 0, 0, 220, 145, _
                            %WS_CHILD OR %WS_VISIBLE OR %WS_VSCROLL OR %ES_MULTILINE OR _
                            %ES_WANTRETURN, %WS_EX_DLGMODALFRAME
                
                  '------------------------------------------------------------------
                  DIALOG SHOW MODAL hDlg CALL DlgProc
                
                END FUNCTION
                
                '====================================================================
                CALLBACK FUNCTION DlgProc() AS LONG
                  LOCAL hWnd AS DWORD, pt AS POINTAPI, sText AS STRING
                  STATIC hTimer AS DWORD
                
                  SELECT CASE CB.MSG
                  CASE %WM_INITDIALOG
                      SetWindowPos CB.HNDL, %HWND_TOPMOST, 0, 0, 0, 0, %SWP_NOMOVE OR %SWP_NOSIZE
                      hTimer = SetTimer(CB.HNDL, 1, 100, 0)
                
                  CASE %WM_TIMER
                     GetCursorPos pt               ' grab cursor pos on screen
                     hWnd = WindowFromPoint(pt)    ' see what window is under cursor
                     IF hWnd AND hWnd <> (GetDlgItem(CB.HNDL, %IDC_TEXTBOX1)) THEN
                         sText = GetFarText(hWnd)
                         IF LEN(sText) THEN CONTROL SET TEXT CB.HNDL, %IDC_TEXTBOX1, sText
                     END IF
                
                  CASE %WM_DESTROY
                      IF hTimer THEN KillTimer(CB.HNDL, hTimer)
                
                  CASE %WM_COMMAND
                      SELECT CASE CB.CTL
                      CASE %IDCANCEL
                         IF CB.CTLMSG = %BN_CLICKED OR CB.CTLMSG = 1 THEN 'end prog
                            DIALOG END CB.HNDL
                         END IF
                      END SELECT
                
                  END SELECT
                END FUNCTION
                
                '====================================================================
                FUNCTION GetFarText(BYVAL hEdit AS DWORD) AS STRING
                '--------------------------------------------------------------------
                  LOCAL lRes, lSelStart, lSelEnd AS LONG
                  LOCAL sTemp AS STRING, szClass AS ASCIIZ * 32
                
                  IF hEdit THEN
                      GetClassName(hEdit, szClass, SIZEOF(szClass))
                      IF UCASE$(szClass) = "EDIT" THEN
                          SendMessage hEdit, %EM_GETSEL, VARPTR(lSelStart), VARPTR(lSelEnd)
                          IF lSelStart <> lSelEnd THEN
                              SendMessage hEdit, %WM_GETTEXTLENGTH, 0, 0 TO lRes
                              sTemp = SPACE$(lRes + 1)
                              SendMessage hEdit, %WM_GETTEXT, LEN(sTemp), STRPTR(sTemp)
                              '------------------------------------------------------------------
                              IF lSelEnd < lSelStart THEN SWAP lSelStart, lSelEnd
                              FUNCTION = MID$(sTemp, lSelStart + 1, lSelEnd - lSelStart)
                          ELSE
                              FUNCTION = CHR$(0)  ' return a zero byte if nothing is selected
                          END IF
                      END IF
                  END IF
                
                END FUNCTION
                '

                Comment


                • #9
                  BTW, in case you are interested in tapping into other programs, I once posted a small "AnyPad" proggie that enables you to start and manipulate text direcvtly in both NotePad and WordPad. Main reason for that one was that I sometimes happen to press CapsLock and type lots of text before I discover the result with toggled U/Lcase letters. See link to code and downloadable zip file with icons and all at https://forum.powerbasic.com/forum/u...ordpad-utility

                  Comment


                  • #10
                    Thank you Sir Borje

                    I intend to retain the capture text in the textbox1 rather than it keeps clearing off the textbox1
                    each time a new highlighted text is selected.
                    But I found that the capture text kept on repeating itself so I modified your program to partially stop repeats

                    Code:
                    ' Capture Selected Text.bas
                    ' https://forum.powerbasic.com/forum/user-to-user-discussions/powerbasic-for-windows/806439-capture-selected-text?p=806586#post806586
                    
                      ' steps to use
                     ' 1. open a notepad and type something
                    '  2. run this program 
                     ' 3. Select a group of text in the notepad and then these text are automatically
                     '    captured by the program 
                     ' 4.  Text in the textbox is retained
                     ' Great thanks to Sir Borje
                    
                    #COMPILE EXE
                    #DIM ALL
                    #INCLUDE "WIN32API.INC"
                    %IDC_TEXTBOX1 = 131
                    
                    GLOBAL  CapTxt ,PrevCapTxt AS STRING
                    
                    '====================================================================
                    FUNCTION PBMAIN () AS LONG
                      LOCAL hDlg AS DWORD
                    
                      DIALOG NEW 0, "Get any Edit seltext",50,150, 220, 145, %WS_CAPTION OR _
                                                              %WS_SYSMENU, 0 TO hDlg
                      '------------------------------------------------------------------
                      CONTROL ADD TEXTBOX, hDlg, %IDC_TEXTBOX1, "", 0, 0, 220, 145, _
                                %WS_CHILD OR %WS_VISIBLE OR %WS_VSCROLL OR %ES_MULTILINE OR _
                                %ES_WANTRETURN, %WS_EX_DLGMODALFRAME
                    
                      '------------------------------------------------------------------
                      DIALOG SHOW MODAL hDlg CALL DlgProc
                    
                    END FUNCTION
                    
                    '====================================================================
                    CALLBACK FUNCTION DlgProc() AS LONG
                      LOCAL hWnd AS DWORD, pt AS POINTAPI
                      LOCAL  OrigTxt   AS STRING
                      STATIC hTimer AS DWORD
                    
                      SELECT CASE CB.MSG
                      CASE %WM_INITDIALOG
                          SetWindowPos CB.HNDL, %HWND_TOPMOST, 0, 0, 0, 0, %SWP_NOMOVE OR %SWP_NOSIZE
                       '  modify this to slow down the capture to prevent duplicate captures
                          hTimer = SetTimer(CB.HNDL, 1, 1800, 0)
                    
                      CASE %WM_TIMER
                         ' save the previous capture text
                         PrevCapTxt = TRIM$(CapTxt)
                         GetCursorPos pt               ' grab cursor pos on screen
                         hWnd = WindowFromPoint(pt)    ' see what window is under cursor
                         IF hWnd AND hWnd <> (GetDlgItem(CB.HNDL, %IDC_TEXTBOX1)) THEN
                             CapTxt = GetFarText(hWnd)
                    
                             IF LEN(CapTxt) THEN
                             '   gets the original text from the textbox
                                 CONTROL GET TEXT CB.HNDL, %IDC_TEXTBOX1 TO OrigTxt
                                 IF TRIM$(CapTxt) <> PrevCapTxt THEN
                             '      when the previous capture text is NOT the same as
                             '      newly captured text , we can accumulate
                             '      this newly captured text into the textbox
                                    OrigTxt = OrigTxt + "  " + CapTxt
                                    CONTROL SET TEXT CB.HNDL, %IDC_TEXTBOX1, OrigTxt
                                 END IF
                             END IF
                         END IF
                    
                      CASE %WM_DESTROY
                          IF hTimer THEN
                              KillTimer(CB.HNDL, hTimer)
                          END IF
                    
                      CASE %WM_COMMAND
                          SELECT CASE CB.CTL
                          CASE %IDCANCEL
                             IF CB.CTLMSG = %BN_CLICKED OR CB.CTLMSG = 1 THEN
                                 'end prog
                                DIALOG END CB.HNDL
                             END IF
                          END SELECT
                    
                      END SELECT
                    END FUNCTION
                    
                    '====================================================================
                    FUNCTION GetFarText(BYVAL hEdit AS DWORD) AS STRING
                    '--------------------------------------------------------------------
                      LOCAL lRes, lSelStart, lSelEnd AS LONG
                      LOCAL sTemp AS STRING, szClass AS ASCIIZ * 32
                    
                      IF hEdit THEN
                          GetClassName(hEdit, szClass, SIZEOF(szClass))
                          IF UCASE$(szClass) = "EDIT" THEN
                              SendMessage hEdit, %EM_GETSEL, VARPTR(lSelStart), VARPTR(lSelEnd)
                              IF lSelStart <> lSelEnd THEN
                                  SendMessage hEdit, %WM_GETTEXTLENGTH, 0, 0 TO lRes
                                  sTemp = SPACE$(lRes + 1)
                                  SendMessage hEdit, %WM_GETTEXT, LEN(sTemp), STRPTR(sTemp)
                                  '------------------------------------------------------------------
                                  IF lSelEnd < lSelStart THEN
                                      SWAP lSelStart, lSelEnd
                                  END IF
                                  FUNCTION = MID$(sTemp, lSelStart + 1, lSelEnd - lSelStart)
                              ELSE
                                  FUNCTION = CHR$(0)  ' return a zero byte if nothing is selected
                              END IF
                          END IF
                      END IF
                    
                    END FUNCTION

                    But it is not perfect as it depends on the duration of the timer, maybe someone can
                    modify this program for a better capturing and retaining the highlighted text

                    Comment


                    • #11
                      Over-automation.
                      Dale

                      Comment


                      • #12
                        That's how computer works -- automate with AI ?

                        Comment


                        • #13
                          Hm, looks like you add each selection to previous results, but you have changed timer to 1800 (ms = 1.8 second / tick) instead of 100 (ms), which of course will make it miss some actions. Why?
                          Also, I do not understand why or for what purpose you add up selections autonatically like this. Maybe you can explain the final goal better?

                          Comment


                          • #14
                            Ok, so you changed timer to prevent repeated actions - a better way would be to wrap all code under WM_TIMER with
                            '
                            Code:
                            CASE %WM_TIMER
                            IF GetAsyncKeyState(%VK_LBUTTON) = 0 THEN  ' only act if left mouse button is up? But there will still be problems, like if user uses keyboard...
                              ' other code goes here..
                            END IF
                            '


                            Comment


                            • #15
                              Thanks Borje that solve the problem even if I revert the timer back to 100 ms it works perfectly

                              Comment

                              Working...
                              X