Just a note to say that if you are streaming text into an editor gadget using CatchRTF() then, in the case of the text being Unicode encoded, you must add the #SF_UNICODE flag to the format parameter (use #SF_TEXT | #SF_UNICODE). In particular, if streaming in text from a string variable, then use these flags if the Unicode compiler switch is set.
Update : 29th Nov 2007.
Have adjusted the CatchRTF() and LoadRTF() functions which now add a second optional parameter in which you specify the format of the stream/file being loaded; #SF_TEXT (default) or #SF_RTF.
See topic : http://www.purebasic.fr/english/viewtop ... 733#220733
Updated code is below.
Bug fix: 30th August 2006.
Code updated 2nd July 2006.
Code updated: 27 March.
2 bugs fixed: 25 March.
Code updated: 24 March.
I've been taking a delve into COM and interfaces and thought that a good start would be to try and load and paste images into an editor gadget.
It turns out to be quite straight forward; for more so than I imagined!

The following include file is based on some Powerbasic code I found at
http://www.hellobasic.com/ by Edwin Knoppert which translated very painlessly to Purebasic.
I take no credit for this, but simply used Edwin's code to learn from.
I intend to add to this code somewhat when I get the time. A save as .rtf format will be first.
***Update***
Have added functions:
- CatchRTF() - Now with optional parameter to replace selection or entire contents.
- LoadRTF() - Now with optional parameter to replace selection or entire contents.
- SaveRTF()
Include file
Code: Select all
;Rich edit functions.
;Include basic formatting procedures by 'Freak',
;IRichEditOleCallback - place images into an editor gadget.
;Based on some Powerbasic code found at http://www.hellobasic.com/ by Edwin Knoppert
;and translated to Purebasic by Stephen Rodriguez.
;Coded in Purebasic 4.
;Enhanced by Nico - July 01 2007.
#STGM_SHARE_EXCLUSIVE=$00000010
#STGM_READWRITE = $00000002
#STGM_CREATE = $00001000
;Future proof!
CompilerIf Defined(ENM_LINK, #PB_Constant)
CompilerElse
#ENM_LINK = $04000000
CompilerEndIf
CompilerIf Defined(CFM_LINK, #PB_Constant)
CompilerElse
#CFM_LINK = $00000020
CompilerEndIf
CompilerIf Defined(CFE_LINK, #PB_Constant)
CompilerElse
#CFE_LINK = $0020
CompilerEndIf
CompilerIf Defined(CFE_SUBSCRIPT, #PB_Constant)
CompilerElse
#CFE_SUBSCRIPT = $00010000
CompilerEndIf
CompilerIf Defined(CFE_SUPERSCRIPT, #PB_Constant)
CompilerElse
#CFE_SUPERSCRIPT = $00020000
CompilerEndIf
CompilerIf Defined(CFM_SUBSCRIPT, #PB_Constant)
CompilerElse
#CFM_SUBSCRIPT = #CFE_SUBSCRIPT | #CFE_SUPERSCRIPT
#CFM_SUPERSCRIPT=#CFM_SUBSCRIPT
CompilerEndIf
CompilerIf Defined(CFM_BACKCOLOR, #PB_Constant)
CompilerElse
#CFM_BACKCOLOR =$4000000
CompilerEndIf
;-Declares.
Declare Editor_BackColor(Gadget, Color.l)
Declare Editor_Color(Gadget, Color.l)
Declare Editor_Font(Gadget, FontName.s)
Declare Editor_FontSize(Gadget, Fontsize.l)
Declare Editor_Format(Gadget, flags, alternate=0)
Declare Editor_Select(Gadget, LineStart.l, CharStart.l, LineEnd.l, CharEnd.l)
Declare Editor_Bulleted(Gadget)
Declare Editor_JustifyParagraph(Gadget, justify)
Declare Editor_CopyText(gadget)
Declare Editor_CutText(gadget)
Declare Editor_InsertText(gadget,Text$)
Declare Editor_PasteText(gadget)
Declare.l StreamDataCallback(dwCookie, pbBuff, cb, pcb)
Declare.l StreamFileInCallback(dwCookie, pbBuff, cb, pcb)
Declare.l StreamFileOutCallback(dwCookie, pbBuff, cb, pcb)
Structure RichEditOle
*pIntf.IRicheditOle
Refcount.l
hwnd.l
EndStructure
;The following variable forms the IRichEditOleCallback interface for a rich edit control.
Global NewList RichComObject.RichEditOle()
;The following variable points to the rtf stream when including rtf files.
Global prtf
;-*****USER FUNCTIONS***************************************************************************
;***********************************************************************************************
;The following procedure includes a text or an rtf file from a memory stream.
;Include the file using Include Binary etc.
;Returns zero if no error encountered.
;***********************************************************************************************
Procedure.l CatchRTF(gadget, datastart, dataend, format=#SF_TEXT, replaceall=0)
Protected edstr.EDITSTREAM
prtf = datastart
edstr\dwCookie = dataend
edstr\dwError = 0
edstr\pfnCallback = @StreamDataCallback()
SendMessage_(GadgetID(gadget), #EM_STREAMIN, format|replaceall, edstr)
ProcedureReturn edstr\dwError
EndProcedure
;The following is called repeatedly by Windows to stream data into an editor gadget.
Procedure.l StreamDataCallback(dwCookie, pbBuff, cb, pcb)
Protected result
result = 0
If prtf>=dwCookie
cb = 0
result = 1
ElseIf prtf+cb>=dwCookie
cb = dwCookie-prtf
EndIf
CopyMemory(prtf, pbBuff, cb)
prtf+cb
PokeL(pcb, cb)
ProcedureReturn result
EndProcedure
;***********************************************************************************************
;The following procedure loads text or an rtf file into an editor gadget.
;Returns zero if no error encountered.
;***********************************************************************************************
Procedure.l LoadRTF(gadget, filename.s, format=#SF_TEXT, replaceall=0)
Protected edstr.EDITSTREAM
edstr\dwCookie = ReadFile(#PB_Any, filename)
If edstr\dwCookie
edstr\dwError = 0
edstr\pfnCallback = @StreamFileInCallback()
SendMessage_(GadgetID(gadget), #EM_STREAMIN, format|replaceall, edstr)
CloseFile(edstr\dwCookie)
ProcedureReturn edstr\dwError
Else
ProcedureReturn 1
EndIf
EndProcedure
;The following is called repeatedly by Windows to stream data into an editor gadget from an external file.
Procedure.l StreamFileInCallback(dwCookie, pbBuff, cb, pcb)
Protected result, length
result=0
length=ReadData(dwCookie, pbBuff, cb)
PokeL(pcb, length)
If length = 0
result = 1
EndIf
ProcedureReturn result
EndProcedure
;***********************************************************************************************
;The following procedure saves the rtf content of an editor gadget to an external file.
;Returns zero if no error encountered.
;***********************************************************************************************
Procedure.l SaveRTF(gadget, filename.s)
Protected edstr.EDITSTREAM
edstr\dwCookie = CreateFile(#PB_Any, filename)
If edstr\dwCookie
edstr\dwError = 0
edstr\pfnCallback = @StreamFileOutCallback()
SendMessage_(GadgetID(gadget), #EM_STREAMOUT, #SF_RTF, edstr)
CloseFile(edstr\dwCookie)
ProcedureReturn edstr\dwError
Else
ProcedureReturn 1
EndIf
EndProcedure
;The following is called repeatedly by Windows to stream data from an editor gadget to an external file.
Procedure.l StreamFileOutCallback(dwCookie, pbBuff, cb, pcb)
Protected result, length
result=0
WriteData(dwCookie, pbBuff, cb)
PokeL(pcb, cb)
If cb = 0
result = 1
EndIf
ProcedureReturn result
EndProcedure
;-----------------------------------------------Character formatting.
Procedure Editor_BackColor(Gadget, Color.l)
format.CHARFORMAT2
format\cbSize = SizeOf(CHARFORMAT2)
format\dwMask = #CFM_BACKCOLOR
format\crBackColor = Color
SendMessage_(GadgetID(Gadget), #EM_SETCHARFORMAT, #SCF_SELECTION, @format)
EndProcedure
; Set the Text color for the Selection
; in RGB format
Procedure Editor_Color(Gadget, Color.l)
format.CHARFORMAT2
format\cbSize = SizeOf(CHARFORMAT2)
format\dwMask = #CFM_COLOR
format\crTextColor = Color
SendMessage_(GadgetID(Gadget), #EM_SETCHARFORMAT, #SCF_SELECTION, @format)
EndProcedure
; Set Font for the Selection
; You must specify a font name, the font doesn't need
; to be loaded
Procedure Editor_Font(Gadget, FontName.s)
format.CHARFORMAT2
format\cbSize = SizeOf(CHARFORMAT2)
format\dwMask = #CFM_FACE
PokeS(@format\szFaceName, FontName)
SendMessage_(GadgetID(Gadget), #EM_SETCHARFORMAT, #SCF_SELECTION, @format)
EndProcedure
; Set Font Size for the Selection
; in pt
Procedure Editor_FontSize(Gadget, Fontsize.l)
format.CHARFORMAT2
format\cbSize = SizeOf(CHARFORMAT2)
format\dwMask = #CFM_SIZE
format\yHeight = FontSize*20
SendMessage_(GadgetID(Gadget), #EM_SETCHARFORMAT, #SCF_SELECTION, @format)
EndProcedure
; Set Format of the Selection. This can be a combination of
; the following values:
; #CFE_BOLD
; #CFE_ITALIC
; #CFE_UNDERLINE
; #CFE_STRIKEOUT
; #CFE_LINK
; #CFE_SUBSCRIPT
; #CFE_SUPERSCRIPT
;If the optional parameter 'alternate' is non-zero then the formatting attributes specified in
;'flags' will be xored with those already present within the first character of the selection.
;This has the effect of removing individual attributes if already present.
;E.g. specifying #CFE_BOLD on an already bold selection, will remove the bold formatting etc.
Procedure Editor_Format(Gadget, flags, alternate=0)
format.CHARFORMAT2
format\cbSize = SizeOf(CHARFORMAT2)
If alternate
SendMessage_(GadgetID(Gadget), #EM_GETCHARFORMAT, 1, @format)
flags=format\dwEffects!flags
EndIf
format\dwMask = #CFM_ITALIC|#CFM_BOLD|#CFM_STRIKEOUT|#CFM_UNDERLINE|#CFM_LINK|#CFM_SUBSCRIPT|#CFM_SUPERSCRIPT
format\dwEffects = flags
SendMessage_(GadgetID(Gadget), #EM_SETCHARFORMAT, #SCF_SELECTION, @format)
EndProcedure
; Selects Text inside an EditorGadget
; Line numbers range from 0 to CountGadgetItems(#Gadget)-1
; Char numbers range from 0 to the length of a line
; Set Line numbers to -1 to indicate the last line, and Char
; numbers to -1 to indicate the end of a line
; selecting from 0,1 to -1, -1 selects all.
Procedure Editor_Select(Gadget, LineStart.l, CharStart.l, LineEnd.l, CharEnd.l)
sel.CHARRANGE
sel\cpMin = SendMessage_(GadgetID(Gadget), #EM_LINEINDEX, LineStart, 0) + CharStart
If LineEnd = -1
LineEnd = SendMessage_(GadgetID(Gadget), #EM_GETLINECOUNT, 0, 0)-1
EndIf
sel\cpMax = SendMessage_(GadgetID(Gadget), #EM_LINEINDEX, LineEnd, 0)
If CharEnd = -1
sel\cpMax + SendMessage_(GadgetID(Gadget), #EM_LINELENGTH, sel\cpMax, 0)
Else
sel\cpMax + CharEnd
EndIf
SendMessage_(GadgetID(Gadget), #EM_EXSETSEL, 0, @sel)
EndProcedure
;-----------------------------------------------Paragraph formatting.
Procedure Editor_Bulleted(Gadget)
format.PARAFORMAT
format\cbSize = SizeOf(PARAFORMAT)
format\dwMask = #PFM_NUMBERING
format\wnumbering = #PFN_BULLET
SendMessage_(GadgetID(Gadget), #EM_SETPARAFORMAT, 0, @format)
EndProcedure
;Set paragraph justification.
;Can be one of the following values:
; #PFA_LEFT
; #PFA_RIGHT
; #PFA_CENTER
Procedure Editor_JustifyParagraph(Gadget, justify)
format.PARAFORMAT
format\cbSize = SizeOf(PARAFORMAT)
format\dwMask = #PFM_ALIGNMENT
format\wAlignment = justify
SendMessage_(GadgetID(Gadget), #EM_SETPARAFORMAT, 0, @format)
EndProcedure
;-----------------------------------------------Clipboard.
Procedure Editor_CopyText(gadget)
SendMessage_(GadgetID(gadget), #WM_COPY,0,0)
EndProcedure
Procedure Editor_CutText(gadget)
SendMessage_(GadgetID(gadget), #WM_CUT,0,0)
EndProcedure
Procedure Editor_InsertText(gadget,Text$)
ProcedureReturn SendMessage_(GadgetID(gadget),#EM_REPLACESEL,0,Text$)
EndProcedure
Procedure Editor_PasteText(gadget)
SendMessage_(GadgetID(gadget), #WM_PASTE,0,0)
EndProcedure
;-*****END USER FUNCTIONS***********************************************************************
;***********************************************************************************************
;Implementation procedures for OLE. Most are not actually used but are still needed.
;***********************************************************************************************
;***********************************************************************************************
;Set up the com interface for our rich edit control.
;***********************************************************************************************
Procedure.l RichEdit_SetInterface(hWnd)
Protected No_Com.l
ForEach RichComObject()
If RichComObject()\hwnd=hwnd
No_Com=1
Break
EndIf
Next
If No_Com=0
AddElement(RichComObject())
RichComObject()\pIntf = ?VTable
RichComObject()\hwnd=hwnd
SendMessage_(hWnd, #EM_SETOLECALLBACK, 0, RichComObject())
ProcedureReturn RichComObject()
EndIf
EndProcedure
Procedure.l RichEdit_QueryInterface(*pObject.RichEditOle, REFIID, *ppvObj.LONG)
Protected *pointeur.IRicheditOle
*pointeur=*pObject
If CompareMemory(REFIID, ?IID_IUnknown, 16)=1 Or CompareMemory(REFIID, ?IID_IRichEditOleCallback, 16)=1
Debug "QueryInterface"
*ppvObj\l = *pObject
*pointeur\AddRef()
ProcedureReturn #S_OK
Else
*ppvObject=0
ProcedureReturn #E_NOINTERFACE
EndIf
EndProcedure
Procedure.l RichEdit_AddRef(*pObject.RichEditOle)
*pObject\Refcount+1
ProcedureReturn *pObject\Refcount
EndProcedure
Procedure.l RichEdit_Release(*pObject.RichEditOle)
*pObject\Refcount-1
If *pObject\Refcount > 0
ProcedureReturn *pObject\Refcount
Else
;Remove entry in the linked list.
ForEach RichComObject()
If RichComObject()=*pObject
DeleteElement(RichComObject()) : Break
EndIf
Next
*pObject=0
EndIf
EndProcedure
Procedure.l RichEdit_GetInPlaceContext(*pObject.RichEditOle, lplpFrame, lplpDoc, lpFrameInfo)
Debug 1
ProcedureReturn #E_NOTIMPL
EndProcedure
Procedure.l RichEdit_ShowContainerUI(*pObject.RichEditOle, fShow)
ProcedureReturn #E_NOTIMPL
EndProcedure
Procedure.l RichEdit_QueryInsertObject(*pObject.RichEditOle, lpclsid, lpstg, cp)
ProcedureReturn #S_OK
EndProcedure
Procedure.l RichEdit_DeleteObject(*pObject.RichEditOle, lpoleobj)
ProcedureReturn #E_NOTIMPL
EndProcedure
Procedure.l RichEdit_QueryAcceptData(*pObject.RichEditOle, lpdataobj, lpcfFormat, reco, fReally, hMetaPict)
ProcedureReturn #S_OK
EndProcedure
Procedure.l RichEdit_ContextSensitiveHelp(*pObject.RichEditOle, fEnterMode)
ProcedureReturn #E_NOTIMPL
EndProcedure
Procedure.l RichEdit_GetClipboardData(*pObject.RichEditOle, lpchrg, reco, lplpdataobj)
ProcedureReturn #E_NOTIMPL
EndProcedure
Procedure.l RichEdit_GetDragDropEffect(*pObject.RichEditOle, fDrag, grfKeyState, pdwEffect)
;PokeL(pdwEffect,0) ;Uncomment this to prevent dropping to the editor gadget.
ProcedureReturn #E_NOTIMPL
EndProcedure
Procedure.l RichEdit_GetContextMenu(*pObject.RichEditOle, seltype.w, lpoleobj, lpchrg, lphmenu)
ProcedureReturn #E_NOTIMPL
EndProcedure
;The following function does the main work!
Procedure.l RichEdit_GetNewStorage(*pObject.RichEditOle, lplpstg)
Protected sc, lpLockBytes, t.ILockBytes
;Attempt to create a byte array object which acts as the 'foundation' for the upcoming compound file.
sc=CreateILockBytesOnHGlobal_(#Null, #True, @lpLockBytes)
If sc ;This means that the allocation failed.
ProcedureReturn sc
EndIf
;Allocation succeeded so we now attempt to create a compound file storage object.
sc=StgCreateDocfileOnILockBytes_(lpLockBytes, #STGM_SHARE_EXCLUSIVE|#STGM_READWRITE|#STGM_CREATE, 0, lplpstg)
t = lpLockBytes
t\Release()
EndProcedure
;***********************************************************************************************
DataSection
VTable:
Data.l @RichEdit_QueryInterface(), @RichEdit_AddRef(), @RichEdit_Release(), @RichEdit_GetNewStorage()
Data.l @RichEdit_GetInPlaceContext(), @RichEdit_ShowContainerUI(), @RichEdit_QueryInsertObject()
Data.l @RichEdit_DeleteObject(), @RichEdit_QueryAcceptData(), @RichEdit_ContextSensitiveHelp(), @RichEdit_GetClipboardData()
Data.l @RichEdit_GetDragDropEffect(), @RichEdit_GetContextMenu()
IID_IRichEditOleCallback: ;" 0x00020D03, 0, 0, 0xC0,0,0,0,0,0,0,0x46"
Data.l $00020D03
Data.w $0000,$0000
Data.b $C0,$00,$00,$00,$00,$00,$00,$46
IID_IUnknown: ;"{00000000-0000-0000-C000-000000000046}"
Data.l $00000000
Data.w $0000,$0000
Data.b $C0,$00,$00,$00,$00,$00,$00,$46
EndDataSection