实现一种快速查找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 ...
随机推荐
- 【Nginx】nginx 代理 Haproxy 怎么设置?
由于Haproxy是通过 url 正则匹配 识别 的,nginx代理到 haproxy需要设置 proxy_set_header Host 为 haproxy的目标 url 直接上配置 upstrea ...
- 用extern定义全局变量
1.extern的作用 extern有两个作用,第一个,当它与"C"一起连用时,如: extern "C" void fun(int a, int b); 则告 ...
- uniq 重复行统计
uniq 命令 文字 uniq 是LINUX命令 用途 报告或删除文件中重复的行. 语法 uniq [ -c | -d | -u ] [ -f Fields ] [ -s Characters ] ...
- cxGrid 增加序号 (非数据库绑定模式) (测试通过)
cxGrid 增加序号 (非数据库绑定模式) ----------------------------------- 1. 选在 adoQuery 控件 , 鼠标右键菜单中 选择 Fields Edi ...
- Shell编程—定时任务
应用:定时催收,定时下发,对新增内容入库同步, 时间:凌晨 服务器Reahat Linux: Bash HPUX:kbash? #!/bin/sh #!/bin/bash echo awk #输入重定 ...
- nyoj19_排列
擅长排列的小明 时间限制:1000 ms | 内存限制:65535 KB 难度:4 描述 小明十分聪明,而且十分擅长排列计算.比如给小明一个数字5,他能立刻给出1-5按字典序的全排列,如果你想 ...
- [C#]Datatable和json互相转换操作
#region DataTable 转换为Json字符串实例方法 /// <summary> /// GetClassTypeJosn 的摘要说明 /// </summary> ...
- 根据OSG中的ref_ptr和Reference简化的智能指针
main.cpp测试代码 #include "TestSmartPointer" void fun() { SP<TestSmartPointer> sp1=new T ...
- August 7th 2016, Week 33rd Sunday
Knowing yourself is the height of wisdom. 了解自己就是大智慧. Two-day holiday, even I didn't have enought tim ...
- Innodb之拷贝InnoDB表从一服务器到另一台服务器
将Innodb类型的表从一台服务器拷贝到另一台服务器,或从一个库拷贝到另一个库. 前提是:innodb_file_per_table =ON. 1 先在目标服务器(库)上创建一个相同的表结构. 如: ...