Page 1 of 2

Trap 'paste' in an editorgadget?

Posted: Thu Jul 17, 2008 6:45 am
by rsts
I need to trap the control/v paste operation for an editorgadget and substitute my own routine for the paste.

The only method I've been able to come up with to trap the paste is via an addkeyboardshortcut command. But I seem to get multiple 'hits' to the menu routine and see no easy, foolproof method to just do my routine once per control/v.

(In my experience, trapping vk_control and VK_V still lets the paste occur).

Is there some easier way to accomplish what would seem to be a fairly simple action?

Code: Select all

Procedure ctlV()
  If GetAsyncKeyState_(#VK_CONTROL) 
    If GetAsyncKeyState_(#VK_V) 
    Debug "Do My Thing ONCE"
    EndIf
  EndIf
  
EndProcedure
If OpenWindow(0, 0, 0, 300, 300, "EditorGadget", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)  And CreateGadgetList(WindowID(0)) 
  EditorGadget(0, 8, 8, 250, 250) 
  AddKeyboardShortcut(0, #PB_Shortcut_Control|#PB_Shortcut_V, 5);without this all pastes work
  Repeat 
    
    If EventMenu()=5
      ctlV()
    EndIf
  
  Until WaitWindowEvent() = #PB_Event_CloseWindow 
 
EndIf 
End 
cheers

Posted: Thu Jul 17, 2008 10:46 am
by Blue
Your solution effectively intercepts the Control-V keyboard command.
But a cascading events problem occurs the first time the user presses Control-V.

As you certainly did, i tried a zillion different things... well, maybe fewer :lol:
and found that the following modifications produce what you want (i think :shock: ):

Code: Select all

Procedure ctlV()
  Static count  ; <<< added only to follow closely how the thing behaves 

  If GetAsyncKeyState_(#VK_CONTROL) 
    kv = GetAsyncKeyState_(#VK_V)            ;;<<< modified code 
    If kv & $F = 1                           ;;<<< modified condition
      count + 1                              ;; counting every pass 
      Debug Str(count)+". " + + "Do My Thing ONCE"
;;      Debug Space(8)+"  kv = " + Hex(kv)    ;; <<< just curious
    EndIf
  EndIf

EndProcedure
The new condition is apparntly enough to eliminate the unexpected events.
You get the debug string displayed exactly once for each press of Control-V.

(tested under Vista SP1)

Posted: Thu Jul 17, 2008 11:15 am
by netmaestro
Try subclassing the editorgadget and look for the #WM_PASTE message. If you get it, insert code for your custom paste and then ProcedureReturn 0 to prevent the default processing.

Posted: Thu Jul 17, 2008 11:22 am
by srod
Afraid that #WM_PASTE is not sent in response to a ctrl-v etc.

The following seems to be pretty reliable here - although I haven't tested a great deal :

Code: Select all

Procedure callback(hWnd, uMsg, wParam, lParam) 
  Protected result = #PB_ProcessPureBasicEvents
  Protected *nmh.NMHDR, *pmsg.MSGFILTER
  Select uMsg
    Case #WM_NOTIFY
      *nmh = lParam
      If *nmh\code = #EN_MSGFILTER
        *pmsg = lParam
        If *pmsg\msg = #WM_KEYDOWN
          If *pmsg\wparam = #VK_V And GetAsyncKeyState_(#VK_CONTROL)>>15
            Debug "Attempted paste!"
            result = 1 ;Prevent the paste!
          EndIf
        EndIf
      EndIf
  EndSelect
  ProcedureReturn result
EndProcedure 


If OpenWindow(0, 0, 0, 300, 300, "EditorGadget", #PB_Window_SystemMenu | #PB_Window_ScreenCentered) And CreateGadgetList(WindowID(0))
  EditorGadget(0, 8, 8, 250, 250) 
  SendMessage_(GadgetID(0), #EM_SETEVENTMASK, 0, #ENM_KEYEVENTS)
  SetWindowCallback(@callback())
  Repeat 
  Until WaitWindowEvent() = #PB_Event_CloseWindow 
EndIf 
End 
The above is probably equivalent to simply subclassing the editor gadget and looking at #WM_KEYDOWN etc!

Posted: Thu Jul 17, 2008 11:26 am
by rsts
Nice Blue. I had tried it with GetAsyncKeyState_(#VK_V) & $8000, based on some code in the forum, but it didn't work.

Didn't know I could eliminate the 'duplicates' with &F.

I believe I'll try this and see if it will meet my needs before I dive into subclassing :wink:

cheers

@srod - yes, that's what I started out looking for - how to prevent the paste, so I could do my own. This appears it would work fine too.

Can anyone see any obvious 'downside' to either Blue's or srods solution?

Many thanks again, guys.

Posted: Thu Jul 17, 2008 6:53 pm
by Blue
rsts wrote:[...]Didn't know I could eliminate the 'duplicates' with &F.
In fact, you can simplify the condition to

Code: Select all

...
   if GetAsyncKeyState_(#VK_V) & 1
     ...
   endif
...
which is simply adding & 1 to your original code :lol:
Restricting the test to the first bit (the only one of interest here) is all you require.

subclassing: :shock: Like many other basic programmers, i just read the word and I start sweating and fighting panick attacks...
But there's the beginning of a therapy at this Wikipedia entry and in this msdn explanation ! :wink:

Posted: Thu Jul 17, 2008 8:42 pm
by netmaestro
Can anyone see any obvious 'downside' to either Blue's or srods solution?
Remember GetAsyncKeyState is a global thing, not specific to your program. You could trigger your custom paste code when the user is pasting into some other application doing it this way. Srod's solution traps the keypress when it's made in your editor gadget only, which is much cleaner imho.

Posted: Thu Jul 17, 2008 8:44 pm
by Blue
I just figured out why you had spurious events in your original code, and only for the first press of Control-V.

The logic error was in your events loop, where you looked for an EventMenu() before calling on the system to advise you of the presence of messages specific to your app.

In short, if you modify your original events loop as follows ...

Code: Select all

  Repeat 
     event=WaitWindowEvent()   ;; <<< (1) asking for messages at the top of the loop 

     If event = #PB_Event_Menu ;; <<< (2) filtering and trapping events pertaining only to YOUR app ! 
;       If EventMenu()=5       ;; <<< this even becomes unnecessary IF you have no other kbd shortcut
         ctlV() 
;       EndIf
     EndIf
  Until event = #PB_Event_CloseWindow 
then your original idea (and code) works as you thought it should.
If you discount the re-organized logic, the difference boils down to a single additional line !

You can even get rid of the GetAsyncKeyState_() checks in the ctlv() procedure. They become redundant, since PB, at that point, has already done the work for you !

All this is much better than using GetAsyncKeyState_() & 1 (only fixes a side-effect !) since it pinpoints the source of the trouble and eliminates it.

Posted: Thu Jul 17, 2008 10:36 pm
by PB
Just register a hotkey for CTRL+V so that you have 100% control over it.
Then when your app doesn't need to respond to it, just pass it on to the
next app when it's pressed. Easy. I do it all the time with no ill effects.

Posted: Fri Jul 18, 2008 12:16 am
by Sparkie
Can you make use of this...

Code: Select all

Procedure ProtectEditor(editorGadget)
  With editFormat.CHARFORMAT2
    \cbSize = SizeOf(CHARFORMAT2)
    \dwMask = #CFM_PROTECTED
    \dwEffects = #CFE_PROTECTED
  EndWith
  SendMessage_(GadgetID(editorGadget), #EM_SETCHARFORMAT, #SCF_ALL, @editFormat)
  SendMessage_(GadgetID(editorGadget), #EM_SETEVENTMASK, 0, #ENM_PROTECTED)
EndProcedure

Procedure WinCallback(hwnd, msg, wParam, lParam)
  result = #PB_ProcessPureBasicEvents
  Select msg
    Case #WM_NOTIFY
      *pNMHDR.NMHDR = lParam
      Select *pNMHDR\code
        Case #EN_PROTECTED
          *enp.ENPROTECTED = lParam
          If *enp\msg = #WM_PASTE
            ;...Do your thing here and return nonzero to prevent paste
            MessageRequester("Sorry", "Paste not allowed", #PB_MessageRequester_Ok | #MB_ICONERROR)
            result = 1
          Else
            ;...Allow everything else
            result = 0
          EndIf
      EndSelect
  EndSelect
  ProcedureReturn result
EndProcedure
        
If OpenWindow(0, 0, 0, 500, 300, "ProtectEditorGadtget", #PB_Window_SystemMenu | #PB_Window_ScreenCentered) And CreateGadgetList(WindowID(0)) 
  SetWindowCallback(@WinCallback())
  EditorGadget (0, 10, 10, 480, 280) 
  ProtectEditor(0)
  For i = 0 To 15 
    AddGadgetItem(0, i, "Line " + Str(i)) 
  Next 
  Repeat : Until WaitWindowEvent()=#PB_Event_CloseWindow 
EndIf 

Posted: Fri Jul 18, 2008 2:14 am
by rsts
Sparkie wrote:Can you make use of this...
There's very little you post that I can't make use of :)

Actually, just about any of the solutions would be workable.

If fact, once Blue pointed out the error in my original submission, I was surprised I hadn't 'stumbled' on a solution myself. My original code would have worked in my actual program, but in working up the 'demo' editor gadget program, I had copied code which had the event loop which wasn't what I had wanted.

I had almost deleted the post because I had 'solved' the problem using a timer in the submitted code, but wasn't happy with the method, so left the post to see what the responses would be. I'm GLAD I did.

Since I already had a callback in my 'real' program, I just stuck srod's code in and it worked fine. I'm sure several of the others would have too.

Right now, I'm studying your's just to compare it to srod's and see if I may want to switch.

My brother/partner taught a physics class at Georgia tech and is always riding me because I don't use a 'visual' solution for our projects. I even considered it before I started the current project. I'm glad I stuck with PureBasic. The language has made significant advances since then, and the assistance you receive via these forums is the best I've ever seen anywhere. Props to ALL of you.

cheers

Posted: Fri Jul 18, 2008 9:50 am
by srod
I'd advise using sparkie's code for sure - no use of GetAsyncKeyState_() etc. :)

Posted: Fri Jul 18, 2008 10:43 am
by PB
Nobody thinks my approach is any good? It's short and works. Oh well.

Posted: Fri Jul 18, 2008 10:59 am
by srod
Does such a hot-key (never used them myself) still prevent ctrl-v reaching the editor gadget? If so, then yes as good a method as any - except Sparkie's ( :wink: ) which is using nothing more than functionality built into the rich edit control and so really has to be the preferred method in this case! :)

Posted: Fri Jul 18, 2008 11:43 am
by PB
> Does such a hot-key (never used them myself) still prevent ctrl-v reaching
> the editor gadget?

Yep. It totally disables CTRL+V except for how you use it. So, an event is
triggered when it's pressed, and if the focus is on the EditorGadget, then
do your thang; otherwise just do a Paste message to the target window,
so it works as expected in all other situations and apps.

> If so, then yes as good a method as any - except Sparkie's ( :wink: )

It's smaller than Sparkie's... oh wait, size matters! :shock: