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. HTML中属性ID和属性NAME有何区别?

    今天出美工面试题的时候,David让我加上一道题:HTML中id和name的区别.一听对呀,HTML中id和name有什么区别,只是平时在用,倒没怎么想过,只是那么用了罢了,呵呵,其实在做网页的时候有 ...

  2. shiro学习中报错解决方法

    [1] 最近在学习shiro,在学习过程中出现了一个问题,报错如下: org.apache.shiro.UnavailableSecurityManagerException: No Security ...

  3. H-Index I & II

    H-Index I Given an array of citations (each citation is a non-negative integer) of a researcher, wri ...

  4. (转载)使用 udev 高效、动态地管理 Linux 设备文件

    概述: Linux 用户常常会很难鉴别同一类型的设备名,比如 eth0, eth1, sda, sdb 等等.通过观察这些设备的内核设备名称,用户通常能知道这些是什么类型的设备,但是不知道哪一个设备是 ...

  5. mysql备份与还原

    一.直接拷贝数据库文件 直接拷贝数据库文件一般是使用文件系统备份工具cp,适合小型数据库,是最可靠的. 当你拷贝数据库文件时,必须保证表没有正在使用.如果服务器在你拷贝一个表的时候改变这个表,拷贝就失 ...

  6. 【leetcode】Min Stack(easy)

    Design a stack that supports push, pop, top, and retrieving the minimum element in constant time. pu ...

  7. CSS颜色代码 颜色值 颜色名字大全(转载)

    CSS颜色代码 颜色值 颜色名字大全 转载处http://flyjj.com/css-colour-code.html 颜色值 CSS 颜色使用组合了红绿蓝颜色值 (RGB) 的十六进制 (hex) ...

  8. JDBC题库

    一.    填空题 JDBC    ,是一种用于执行SQL语句的Java API,为多种关系数据库提供统一访问.它由一组用Java语言编写的类和接口组成. JDBC API:供程序员调用的接口与类,集 ...

  9. stdafx.h的作用

    // stdafx.h : include file for standard system include files,// or project specific include files th ...

  10. 素数环(dfs+回溯)

    题目描述: 输入正整数n,把整数1,2...n组成一个环,使得相邻两个数和为素数.输出时从整数1开始逆时针排列并且不能重复: 例样输入: 6 例样输出: 1 4 3 2 5 6 1 6 5 2 3 4 ...