qeditor is very good at what it does, and it has many good features. There
are times however, that the need arises for customized features that are just not
present in qeditor, or its selection of built-in plugins. Scripting is like
learning a new language. It would indeed handle such needs, but for folks like
myself, it just isn't their cup of tea.
Enter the qeditor plugin interface. It was I believe, designed to handle such
needs. However, to the newcomer, it's operation is not very straightforward. I
myself had a hard time creating my first few plugins. Of course, I thought that
they were brilliant, and unique. But one major flaw - they only worked part of the
time, and only with short texts. It took days to figure out why. I'm not big on
reading instructions - lol. But olly had helped me find the errors. That
is when I decided to embark on a mission to even further simplify the process.
Most of you have already seen my 'Automated Plugin Builder', and the
subsequent plugin version (that was actually used to create itself). The following
are the templates that I had used before I started the "Automated" builders.
Some of the old-timers may roll their eyes, but I present the templates that
served me well - for the newcomers that may wish to experiment creating their own
plugins without much hassle. They are an expansion by what is offered already in the
masm32 package.
(A version for the 'Legacy interface is attached as well) - I wanted to post both versions, but reached the character limit.
For the newer qeditor plugin interface:
include \masm32\include\masm32rt.inc
; built-in plugin interface - *slightly* modified as we will deal
; mainly with the richedit control here
PiEntryPoint proto :dword
; my own interface to further simplify
; the creation of plugins for qe.
CallPlugin proto :dword ; ( arg = ptrPlugin:dword )
; The Plugin ; ( args = src:lpRichEdSelectedText, dst:lpModifiedText )
plugin proto :dword, :dword
; some string handling functions, that
; preserve all registers except for eax
zstcmpiz proto :dword, :dword
zstrcatz proto :dword, :dword
zstrclrz proto :dword
zstrcpyz proto :dword, :dword
zstrlenz proto :dword
.data?
DLLinstance dd ? ; local DLL instance handle
hRichEd dd ? ; handle to rich edit control in qeditor
.code
PiEntryPoint proc QEinterface:DWORD
LOCAL Cr:CHARRANGE
push 2 ; The handle to the rich edit control
call QEinterface ; is the second (zero based) member
mov hRichEd, eax ; in the array 'QEinterface'
; =====================================================================
; The 'CallPlugin' procedure handles memory the allocation and releasing
; of memory, as well as getting the selected text prior to operation
; and replacing the selection with the text modified by the plugin
; =====================================================================
invoke CallPlugin, addr plugin ; doing it in this fashion allows
; for running multiple plugins
; from one interface at the same time
; invoke CallPlugin, addr plugin2 ; not used, for illustrative purposes
; invoke CallPlugin, addr plugin3 ; not used, for illustrative purposes
; invoke CallPlugin, addr plugin4 ; not used, for illustrative purposes
; =====================================================================
; That is all there is to it! (Setting up the plugin, that is)
; The 'CallPlugin' procedure does most of the work.
; The plugin itself is up to you to design.
; =====================================================================
; =====================================================================
; IMPORTANT NOTE!
; =====================================================================
; When dealing with the text from qeditor each line is terminated only
; by "0Dh"; expecting there to be an accompanying "0Ah" will lead to
; errors, and will eventually crash qeditor.
;=====================================================================
@@:
ret
PiEntryPoint endp
;--- actual plugin handler ---------------------------------------------
CallPlugin proc ptrPlugin:dword
local tmpbuf:dword, Cr:CHARRANGE, tmpsize:dword, selsize:dword
local tmpbuf2:dword, pluginx:dword
; put the address of the plugin into a variable
mov eax, ptrPlugin
mov pluginx, eax
; get the start and end position of the selected text
; put those vales into a 'CHARRANGE' structure
invoke SendMessage, hRichEd, EM_EXGETSEL, 0, ADDR Cr
mov eax, Cr.cpMin
mov edx, Cr.cpMax
sub edx, eax ; compare the end position to the start position
jz nosel ; which is checking if text is actually selected
mov selsize, edx
mov tmpsize, edx
invoke GlobalAlloc, GPTR, tmpsize ; allocate input buffer
mov tmpbuf, eax
; doubling the size of the output buffer, to ensure
; that there is ample memory.
; Often needed for plugins of this type, that manipulate
; the text in the editor.
mov edx, tmpsize
shl edx, 1
invoke GlobalAlloc, GPTR, edx ; allocate output buffer
mov tmpbuf2, eax
; put selection into input buffer
invoke SendMessage, hRichEd, EM_GETSELTEXT, 0, tmpbuf
push tmpbuf2 ; push offset to OUTPUT buffer
push tmpbuf ; push offset to INPUT buffer
call pluginx ; call the plugin
;---------------------------------------------------------
; replace the selected text in qeditor with the modified
; text from the output buffer
;---------------------------------------------------------
invoke SendMessage, hRichEd, EM_REPLACESEL, 1, tmpbuf2
invoke zstrlenz, tmpbuf2 ; obtain length of modified text
mov tmpsize, eax
mov eax, Cr.cpMin ; add text length to the start
add eax, tmpsize ; position to obtain end position
mov Cr.cpMax, eax ; adjust the end position of the selection
; by resetting the cpMax value in the structure 'Cr'
;---------------------------------------------------------
; Update the selection in the rich edit control
;---------------------------------------------------------
invoke SendMessage, hRichEd, EM_EXSETSEL, 0, ADDR Cr
;---------------------------------------------------------
; Free the input and output memory blocks
;---------------------------------------------------------
invoke GlobalFree, tmpbuf
invoke GlobalFree, tmpbuf2
jmp done
nosel:
fn MessageBoxA, 0, "Sorry, no text is selected", 0, 0
done:
ret
CallPlugin endp
; ===================================================================
;
; example plugin - it removes duplicate spaces from the selected text
;
; ====================================================================
plugin proc src:dword, dst:dword
push esi
push edi
mov esi, src
mov edi, dst
top:
@@:
mov eax, [esi]
cmp al, 0
jz done
cmp al, 20h
jnz @f
cmp ax, 2020h
jnz @f
inc esi
jmp @b
@@:
mov [edi], al
inc edi
inc esi
jmp top
done:
pop edi
pop esi
ret
plugin endp
zstrcatz proc dst:dword, subs:dword ; works like lstrcat
push esi
push edi
xor eax, eax
invoke zstrlenz, dst
mov edi, dst
add edi, eax
invoke zstrcpyz, edi, subs
mov eax, dst
pop edi
pop esi
ret
zstrcatz endp
zstrcpyz proc dst:dword, subs:dword ; works like lstrcpy
push esi
push edi
mov esi, subs
mov edi, dst
xor eax, eax
@@:
mov al, [esi]
cmp al, 0
jz @f
mov [edi], al
inc esi
inc edi
jmp @b
@@:
invoke zstrclrz, edi
mov eax, dst
pop edi
pop esi
ret
zstrcpyz endp
zstrclrz proc src:dword ; zeros a buffer containing a zero term. string
mov eax, src
push eax
@@:
cmp byte ptr [eax], 0
jz @f
mov byte ptr [eax], 0
inc eax
jmp @b
@@:
pop eax
ret
zstrclrz endp
zstrlenz proc src:dword ; works the same as lstrlen
push esi
push ecx
mov esi, src
xor eax, eax
@@:
mov cl, [esi]
cmp cl, 0
jz @f
inc eax
inc esi
jmp @b
@@:
pop ecx
pop esi
ret
zstrlenz endp
.data
ztbl \
db 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15
db 16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31
db 32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47
db 48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63
db 64,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111
db 112,113,114,115,116,117,118,119,120,121,122,91,92,93,94,95
db 96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111
db 112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127
db 128 dup (0FFh)
.code
;----------------- case insensitive string cmp routine -------------------
zstcmpiz proc src:DWORD, dst:DWORD
push esi
push edi
push ebx
push edx
push ecx
mov esi, src
mov edi, dst
xor eax, eax
@@:
movzx edx, byte ptr [esi+eax]
movzx ebx, byte ptr [edi+eax]
movzx ecx, byte ptr [edx+ztbl]
add eax, 1
cmp cl, [ebx+ztbl]
jne quit
cmp edx, 0
jz @f
cmp ebx, 0
jz @f
jmp @b
@@:
sub eax, eax
quit:
pop ecx
pop edx
pop ebx
pop edi
pop esi
ret
zstcmpiz endp
LibMain proc instance:dword,reason:dword,unused:dword
switch reason
case DLL_PROCESS_ATTACH
mrm DLLinstance,instance
mov eax, TRUE
endsw
ret
LibMain endp
end LibMain
edit = found an error in the 'legacy' source - fixed
*SNIP*