实现一种快速查找Richedit中可见区域内OLE对象的方法
Richedit是一个OLE容器,使用Richedit来显示IM聊天内容时,通常使用OLE对象来实现在Richedit中播放表情动画。
触发表情的绘制有两种途径:
1、来自Richedit的刷新消息。
2、来自表情动画定时器的刷新消息。
要刷新表情的显示首先需要知道表情的显示位置。
第一种刷新过程中,绘制消息参数里已经给出绘制位置,直接在指定的位置绘制即可。
但是表情主动刷新时如何获取表情的显示位置确是一个问题。
网上有不少代码演示了如何获通过枚举Richedit中的OLE对象获取表情的代码。
这些代码估计很多都来自一个共同的祖先:
为了获得一个OLE对象的显示位置,都需要从当前Richedit中逐个枚举OLE对象,直接找到这个触发刷新的OLE对象为止。
再通过该OLE对象的字符索引计算出显示位置。
当Richedit中插入的OLE对象相对较少时,这种方法还可以满足需要,但是如果表情成千上万,这样的查询方式必然导致CPU占用飙升。
虽然要获得一个OLE对象的显示位置,目前看来只有先获得该对象在Richedit中的插入位置,但是有没有更简单的方法先判断一个OLE对象是否在可见范围内呢?
要解决这个问题,首先需要从Richedit中获取可见字符的范围。
首先通过EM_GETFIRSTVISIBLELINE+EM_LINEINDEX获得第一个可见行的第一个字符的索引号。
通过EM_GETRECT+EM_CHARFROMPOS可以获得最后一个可见字符的索引号。
如此我们就可以获得可见字符的范围。
表情动画触发时我们只知道这个OLE对象的指针,但并不知道这个OLE对象的字符索引。
要获得显示位置,首先需要判断这个OLE对象是不是在可见范围。
为此我可以采用两分法快速找出所有在可见范围内的OLE对象,再将当前的OLE对象和可见范围内的OLE对象逐个比较,进而判断该对象是否可见,并最终获得显示位置。
相比网上流传的方法,这种方法在查找一个OLE对象的显示位置时只需要和可见范围内的OLE对象逐个比较,比较次数通常是非常小的,因此完全不用担心CPU占用问题。
下面是用源代码,希望对那些被这个问题困扰的人有些帮助。
LONG GetOleCP(IRichEditOle *pOle, int iOle)
{
REOBJECT reobj={};
reobj.cbStruct=sizeof(REOBJECT);
pOle->GetObject(iOle,&reobj,REO_GETOBJ_NO_INTERFACES);
return reobj.cp;
} //find first Ole Object in char range of [cpMin,cpMax)
int FindFirstOleInrange(IRichEditOle *pOle, int iBegin,int iEnd,int cpMin,int cpMax)
{
if(iBegin==iEnd) return -; int iMid = (iBegin + iEnd)/; LONG cp = GetOleCP(pOle,iMid); if(cp < cpMin)
{
return FindFirstOleInrange(pOle,iMid+,iEnd,cpMin,cpMax);
}else if(cp >= cpMax)
{
return FindFirstOleInrange(pOle,iBegin,iMid,cpMin,cpMax);
}else
{
int iRet = iMid;
while(iRet>iBegin)
{
cp = GetOleCP(pOle,iRet-);
if(cp<cpMin) break;
iRet --;
}
return iRet;
}
} //find Last Ole Object in char range of [cpMin,cpMax)
int FindLastOleInrange(IRichEditOle *pOle, int iBegin,int iEnd,int cpMin,int cpMax)
{
if(iBegin==iEnd) return -; int iMid = (iBegin + iEnd)/; LONG cp = GetOleCP(pOle,iMid); if(cp < cpMin)
{
return FindLastOleInrange(pOle,iMid+,iEnd,cpMin,cpMax);
}else if(cp >= cpMax)
{
return FindLastOleInrange(pOle,iBegin,iMid,cpMin,cpMax);
}else
{
int iRet = iMid;
while(iRet<(iEnd-))
{
cp = GetOleCP(pOle,iRet+);
if(cp>=cpMax) break;
iRet ++;
}
return iRet;
}
} int CGifSmileyCtrl::GetObjectPos( HWND hWnd)
{
if ( !hWnd ) return -;
IRichEditOle * ole=NULL;
if (!::SendMessage(hWnd, EM_GETOLEINTERFACE, , (LPARAM)&ole)) return -; int iRet = -; //获得可见字符范围
int iFirstLine = SendMessage(hWnd,EM_GETFIRSTVISIBLELINE,,);
RECT rcView;
SendMessage(hWnd,EM_GETRECT,,(LPARAM)&rcView);
POINT pt={rcView.right+,rcView.bottom-}; LONG cpFirst = SendMessage(hWnd,EM_LINEINDEX,iFirstLine,);
LONG cpLast = SendMessage(hWnd,EM_CHARFROMPOS,,(LPARAM)&pt); //采用两分法查找在可见范围中的OLE对象
int nCount=ole->GetObjectCount(); int iFirstVisibleOle = FindFirstOleInrange(ole,,nCount,cpFirst,cpLast);
if(iFirstVisibleOle!=-)
{
int iLastVisibleOle = FindLastOleInrange(ole,iFirstVisibleOle,nCount,cpFirst,cpLast);
ATLASSERT(iLastVisibleOle!=-); for(int i=iFirstVisibleOle;i<=iLastVisibleOle;i++)
{
REOBJECT reobj={};
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();
return iRet;
}
上面CGifSmileyCtrl代表一个表情OLE对象.
实现一种快速查找Richedit中可见区域内OLE对象的方法的更多相关文章
- 一种快速刷新richedit中内嵌动画的方法的实现
在IM中使用动画表情是一种非常有趣的方式,然而选择一种合适的方式来实现却并不容易. 一般来说,除了自己去实现一个富文本控件,目前主要的解决方案有3种: 1.使用浏览器做容器. 2.使用QT提供的Ric ...
- 在IDEA中使用JSP中的out内置对象,out.println()——println红色解决方法
今天在学习JSP的时候,在jsp中使用out内置对象,开发工具用的是IDEA,结果如下图所示 郁闷了半天找度娘,可能关键字输的不准确,乱七八糟的方法一大堆,什么加依赖啊啥的,反正都不管用,最后找到一篇 ...
- javaScript中Math内置对象基本方法入门
概念 Math 是javaScript的内置对象,包含了部分数学常数属性和数学函数方法. Math 不是一个函数对象,用户Number类型进行使用,不支持BigInt. Math 的所有属性与方法都是 ...
- Eclipse添加快速查找Dao中方法所对应的Mybatis XML映射SQL的插件
Dao关联Mybatis快速查找的插件安装地址:http://dl.bintray.com/harawata/eclipse 安装步骤: ①Eclipse ==> Help ==> Ins ...
- 在jsp中常用的内置对象(5个)小总结和两种页面跳转方式(服务器端调转、客户端跳转)的区别
jsp中常用的几个内置对象: 一.request对象 主要作用: (1)获取请求页面的信息 比如:request.getParameter("参数名"); (2)获取客户端 ...
- Servlet中的jsp内置对象
Servlet和jsp本质相同,那么为什么还要使用jsp呢,原来的servlet又有什么不好的呢. Servlet和jsp可以做完全相同的事情,就要借助jsp的内置对象们,比如request,resp ...
- js中常用的内置对象
Arguments 函数参数集合 arguments[ ] 函数参数的数组 Arguments 一个函数的参数和其他属性 Arguments.callee 当前正在运行的函数 Argument ...
- SpringMvc4中获取request、response对象的方法
springMVC4中获取request和response对象有以下两种简单易用的方法: 1.在control层获取 在control层中获取HttpServletRequest和HttpServle ...
- jsp中9个内置对象与servlet对应关系及四个作用域
参考: <jsp&servlet学习笔记.第2版.林信良><JSR-245 JavaServer Pages 2.2 Maintenance Release Specifi ...
随机推荐
- DOS与批处理
cmd命令不区分大小写 d: cd .. cd 文件夹 dir dir 文件或文件夹 可执行文件(.exe, .bat., .com),只需进入文件当前目录并输入文件名(不需要后缀)即可执行, 如果将 ...
- Peeking Iterator
Given an Iterator class interface with methods: next() and hasNext(), design and implement a Peeking ...
- 【leetcode】Partition List
Partition List Given a linked list and a value x, partition it such that all nodes less than x come ...
- 【leetcode】Unique Paths II
Unique Paths II Total Accepted: 22828 Total Submissions: 81414My Submissions Follow up for "Uni ...
- centos7安装docker并设置开机启动
版本要求:查看内核版本,需大于3.10 [root@localhost ~]# uname -r -.el7.x86_64 更新内核:如果是生产机器务必慎重更新内核,避免出现不必要的问题. sudo ...
- DataStage
parallel job shell调用:dsjob ./dsjob -run -mode NORMAL -paramfile xxx.param <PROJECT> <JOB> ...
- [第三方]AFNetWorking3.0网络框架使用方法
官网地址https://github.com/AFNetworking/AFNetworking #import <AFNetworking.h> - (void)viewDidLoad ...
- Mathematics:DNA Sorting(POJ 1007)
DNA排序 题目大意:给定多个ACGT序列,按照字母顺序算出逆序数,按逆序数从小到大排列 这题其实很简单,我们只要用一个归并排序算逆序数,然后快排就可以了(插入排序也可以,数据量不大),但是要注意的是 ...
- NHibernate实战详解(二)映射配置与应用
关于NHibernate的资料本身就不多,中文的就更少了,好在有一些翻译文章含金量很高,另外NHibernate与Hibernate的使用方式可谓神似,所以也有不少经验可以去参考Hibernate. ...
- October 5th 2016 Week 41st Wednesday
Don't follow the crowd, let the crowd follow you. 不要随波逐流,要引领潮流. But to be a good follower is already ...