RichEdit is an OLE container that uses RichEdit to display IM chat content, typically using an OLE object to implement an emoticon animation in RichEdit.
There are two ways to draw a trigger expression:
1. Refresh message from RichEdit.
2. Refresh message from the expression animation timer.
To refresh the display of an emoticon, you first need to know where the expression appears.
During the first refresh process, the drawing position is given in the draw message parameter, which can be drawn directly at the specified location.
But how to get the display position of the expression when the expression is actively refreshed is a problem.
There's a lot of code on the web that shows how to get emoticons by enumerating OLE objects in RichEdit.
Many of these codes are estimated to come from a common ancestor:
To get the display location of an OLE object, you need to enumerate the OLE objects individually from the current RichEdit, and find the OLE object that triggered the flush directly.
The display location is then computed from the character index of the OLE object.
This approach also satisfies the need when there are relatively few OLE objects inserted in the RichEdit, but if there are thousands of expressions, such a query will inevitably lead to a spike in CPU usage.
Although it is now possible to get the location of an OLE object in the RichEdit, it seems that there is no easier way to first determine whether an OLE object is within the visible range.
To solve this problem, you first need to get the range of visible characters from the RichEdit.
The first character index number of the first visible row is obtained by Em_getfirstvisibleline+em_lineindex.
The index number of the last visible character can be obtained by Em_getrect+em_charfrompos.
So we can get the range of visible characters.
We only know the pointer to this OLE object when the animation is triggered, but we don't know the character index of the OLE object.
To get the display position, you first need to determine whether the OLE object is in the visible range.
For this I can quickly find all the OLE objects within the visible range using a two-point method, and then compare the current OLE object with the OLE object in the visible range one by one to determine whether the object is visible and finally get the display position.
This method, when looking for the display location of an OLE object, is compared to an OLE object in the visible range, which is usually very small, so you don't have to worry about CPU usage at all.
Here is the source code, hoping to help those who are troubled by the problem.
LONG GETOLECP (IRichEditOle *pole,intIole) {Reobject Reobj={0}; Reobj.cbstruct=sizeof(Reobject); POle->getobject (iole,&reobj,reo_getobj_no_interfaces); returnREOBJ.CP;}//Find First Ole Object in char range of [Cpmin,cpmax]intFindfirstoleinrange (IRichEditOle *pole,intIbegin,intIend,intCPMin,intCpmax) { if(Ibegin==iend)return-1; intIMiD = (Ibegin + iend)/2; LONG CP=GETOLECP (Pole,imid); if(CP <cpmin) { returnFindfirstoleinrange (pole,imid+1, Iend,cpmin,cpmax); }Else if(CP >=Cpmax) { returnFindfirstoleinrange (Pole,ibegin,imid,cpmin,cpmax); }Else { intIRet =IMiD; while(iret>Ibegin) {CP= GETOLECP (pole,iret-1); if(cp<cpmin) Break; IRet--; } returnIRet; }}//find last Ole Object in char range of [Cpmin,cpmax]intFindlastoleinrange (IRichEditOle *pole,intIbegin,intIend,intCPMin,intCpmax) { if(Ibegin==iend)return-1; intIMiD = (Ibegin + iend)/2; LONG CP=GETOLECP (Pole,imid); if(CP <cpmin) { returnFindlastoleinrange (pole,imid+1, Iend,cpmin,cpmax); }Else if(CP >=Cpmax) { returnFindlastoleinrange (Pole,ibegin,imid,cpmin,cpmax); }Else { intIRet =IMiD; while(iret< (iend-1) ) {CP= GETOLECP (pole,iret+1); if(Cp>=cpmax) Break; IRet++; } returnIRet; }}intCgifsmileyctrl::getobjectpos (HWND hwnd) {if(!hwnd)return-1; IRichEditOle* ole=NULL; if(!::sendmessage (HWnd, Em_getoleinterface,0, (LPARAM) &ole))return-1; intIRet =-1; //get the visible character range intIfirstline = SendMessage (Hwnd,em_getfirstvisibleline,0,0); RECT Rcview; SendMessage (Hwnd,em_getrect,0, (LPARAM) &Rcview); Point PT={rcview.right+1, rcview.bottom-2}; LONG Cpfirst= SendMessage (Hwnd,em_lineindex,ifirstline,0); LONG Cplast= SendMessage (Hwnd,em_charfrompos,0, (LPARAM) &PT); //find OLE objects in visible range using two-point method intNcount=ole->Getobjectcount (); intIfirstvisibleole = Findfirstoleinrange (OLE,0, Ncount,cpfirst,cplast); if(ifirstvisibleole!=-1) { intIlastvisibleole =Findlastoleinrange (ole,ifirstvisibleole,ncount,cpfirst,cplast); Atlassert (Ilastvisibleole!=-1); for(inti=ifirstvisibleole;i<=ilastvisibleole;i++) {Reobject reobj={0}; Reobj.cbstruct=sizeof(Reobject); Ole->getobject (i,&reobj,reo_getobj_no_interfaces); if(Reobj.clsid==__uuidof (Cgifsmileyctrl) && ((cgifsmileyctrl*) reobj.dwuser) = = This) {IRet=i; Break; }}}} OLE-Release (); returnIRet;}
The above Cgifsmileyctrl represents an emoticon OLE object.
Implement a method to quickly find OLE objects in a visible area of RichEdit