实现一种快速查找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 ...
随机推荐
- Redis提供的持久化机制(RDB和AOF)
Redis提供的持久化机制 Redis是一种面向"key-value"类型数据的分布式NoSQL数据库系统,具有高性能.持久存储.适应高并发应用场景等优势.它虽然起步较晚,但发展却 ...
- C#值数值类型转换
1.十进制转16进制 string result=number.ToString("X2"); >>0A //X2表示大写2位 2.字符串转数值类型 "); ...
- 常见概率组合题目总结quickstart
[本文链接] http://www.cnblogs.com/hellogiser/p/interview-questions-quickstart-for-combination-permutatio ...
- 给一个正在运行的Docker容器动态添加Volume
给一个正在运行的Docker容器动态添加Volume本文转自:http://dockone.io/article/149 [编者的话]之前有人问我Docker容器启动之后还能否再挂载卷,考虑到mnt命 ...
- PHP json_encode / json_decode
2015年3月26日 14:14:16 PHP的json函数对几个特殊值的处理笔记 <?php //----------编码 $a = array(); $b = json_encode($a) ...
- 表现层的设计(一)——常用的模式、Json与DTO
上几篇博文介绍了 业务逻辑层和数据访问层,我认为写博文的作用主要是向业界的读者交流一种思想,点到为止,至于学习架构设计,通过几篇博文是讲不清楚的,还需要[基础]扎实的情况下,[反复]研究[权威]的书籍 ...
- AIX系统的环境变量设置
AIX系统的环境变量设置 用户环境的定义是通过设置环境变量来实现的.AIX系统主要使用两大类profile文件来定义用户环境.一类是用来为所有用户定制环境,另一类是为个人定义自己的环境. 登录时,sh ...
- DB2用一张表更新其他表的数据
表结构: CREATE TABLE ATEST (ID INTEGER, NAME VARCHAR(256), CODE INTEGER, NAME2 VARCHAR(256) ...
- 【leetcode】Count Primes(easy)
Count the number of prime numbers less than a non-negative number, n 思路:数质数的个数 开始写了个蛮力的,存储已有质数,判断新数字 ...
- 用SSH登录局域网中使用网络设置为NAT的虚拟机中的linux
环境描述: A客户机,安装有putty软件使用SSH方式登录B主机中的虚拟机中的linux:A机的网络地址:192.168.1.2 B主机为Windows7平台,B主机的网络地址为:192.168.1 ...