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对象的方法的更多相关文章

  1. 一种快速刷新richedit中内嵌动画的方法的实现

    在IM中使用动画表情是一种非常有趣的方式,然而选择一种合适的方式来实现却并不容易. 一般来说,除了自己去实现一个富文本控件,目前主要的解决方案有3种: 1.使用浏览器做容器. 2.使用QT提供的Ric ...

  2. 在IDEA中使用JSP中的out内置对象,out.println()——println红色解决方法

    今天在学习JSP的时候,在jsp中使用out内置对象,开发工具用的是IDEA,结果如下图所示 郁闷了半天找度娘,可能关键字输的不准确,乱七八糟的方法一大堆,什么加依赖啊啥的,反正都不管用,最后找到一篇 ...

  3. javaScript中Math内置对象基本方法入门

    概念 Math 是javaScript的内置对象,包含了部分数学常数属性和数学函数方法. Math 不是一个函数对象,用户Number类型进行使用,不支持BigInt. Math 的所有属性与方法都是 ...

  4. Eclipse添加快速查找Dao中方法所对应的Mybatis XML映射SQL的插件

    Dao关联Mybatis快速查找的插件安装地址:http://dl.bintray.com/harawata/eclipse 安装步骤: ①Eclipse ==> Help ==> Ins ...

  5. 在jsp中常用的内置对象(5个)小总结和两种页面跳转方式(服务器端调转、客户端跳转)的区别

    jsp中常用的几个内置对象: 一.request对象 主要作用:  (1)获取请求页面的信息   比如:request.getParameter("参数名");  (2)获取客户端 ...

  6. Servlet中的jsp内置对象

    Servlet和jsp本质相同,那么为什么还要使用jsp呢,原来的servlet又有什么不好的呢. Servlet和jsp可以做完全相同的事情,就要借助jsp的内置对象们,比如request,resp ...

  7. js中常用的内置对象

    Arguments 函数参数集合 arguments[ ] 函数参数的数组 Arguments 一个函数的参数和其他属性 Arguments.callee 当前正在运行的函数     Argument ...

  8. SpringMvc4中获取request、response对象的方法

    springMVC4中获取request和response对象有以下两种简单易用的方法: 1.在control层获取 在control层中获取HttpServletRequest和HttpServle ...

  9. jsp中9个内置对象与servlet对应关系及四个作用域

    参考:  <jsp&servlet学习笔记.第2版.林信良><JSR-245 JavaServer Pages 2.2 Maintenance Release Specifi ...

随机推荐

  1. Nagios+zabbix+ganglia的相关参数分析和优缺点介绍

    转自: http://blog.csdn.net/messiaDemo/article/details/52046822?utm_source=itdadao&utm_medium=refer ...

  2. CLR via C# 随记

    使用C# 编译器的方法: 1.csc.exe位于C:\Windows\Microsoft.NET\Framework\vxxxxx下面,将对应版本的路径配置到环境变量path中,如将";C: ...

  3. poll函数

    poll函数与select函数的功能基本一样,其定义如下: #include <poll.h> int poll(struct pollfd fds[], nfds_t nfds, int ...

  4. javascript 解析dom字符串

    知识要求:1:熟悉dom结构层次(如childNodes,nodeType,parent,children)等. 2:熟悉jq对象转换js 对象,反之 毕竟不是专业js人.借助第3方框架.其实jq也是 ...

  5. NoSQL之【MongoDB】学习(三):配置文件说明

    摘要: 继上一篇NoSQL之[MongoDB]学习(一):安装说明 之后,知道了如何安装和启动MongoDB,现在对启动时指定的配置文件(mongodb.conf)进行说明,详情请见官方. 启动Mon ...

  6. MinGW平台 openjpeg-2.1.0 静态编译后未定义引用的解决方法

    undefined reference to __imp_opj_xxx keyword: ffmpeg,openjpeg,OPJ_EXPORTS,OPJ_STATIC,opj_version,__i ...

  7. EMIS系统运行时提示【无法验证发行者,您确实要运行此软件吗? 】

    无法验证发行者,您确实要运行此软件吗? 遇到这个提示你怎么办? 运行 gpedit.msc 进入组策略用户配置 ==>管理模板==> winows组件 ==> 附件管理器在 &quo ...

  8. MST:Out of Hay(POJ 2395)

    缺乏粮草 题目大意:一群牛要修建一些通道,到各个农场距离总和要最小,求这些通道的最大值 水题了,一个Kruskal搞定 #include <iostream> #include <f ...

  9. HTML认识

    1.1 认识什么是纯文本文件 txt window自带有一个软件,叫做记事本,记事本保存的格式TXT,就是英文text的缩写,术语上称呼为"纯文本文件" TXT文件,只能保存文本内 ...

  10. [Android Pro] AOSP download

    Ubuntu14.04系统下载Android源码,直接上步骤: 清华大学 TUNA 镜像源 https://mirrors.tuna.tsinghua.edu.cn/help/AOSP/ https: ...