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
and found that the following modifications produce what you want (i think

):
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
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
Restricting the test to the first bit (the only one of interest here) is all you require.
subclassing: 
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 !

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 (

) 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 (

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