目录
- 1 实现方法1——钩子函数
- 1.1 钩子函数的作用
- 1.2 利用钩子函数监控双击事件
- 2 实现方法2——反应器
- 2.1 反应器的作用
- 2.2 利用编辑器反应器监控双击事件
在ObjectARX开发中,常常要监控鼠标的双击事件,比如,往一个实体中写了扩展数据,然后你希望用户双击这个实体的时候进行特殊处理。怎么办呢?
ObjectARX至少提供了两种方法:钩子函数和反应器。
1 实现方法1——钩子函数
1.1 钩子函数的作用
在ObjectARX中,可以在CAD的窗口消息循环中注册一个钩子函数。注册钩子函数的函数原型为:
BOOL acedRegisterFilterWinMsg( const AcedFilterWinMsgFn pfn);
其中,AcedFilterWinMsgFn 的类型为:
BOOL (* AcedFilterWinMsgFn)(MSG*);
acedRegisterFilterWinMsg中形参pfn指向的函数就是钩子函数,它可以更改传递进入的消息值。如果pfn返回FALSE,这个消息将继续传递给其他钩子函数和AutoCAD(假设其他钩子函数没有终止该消息的处理);如果返回TRUE,则消息不再传递。
在CAD的窗口消息循环中,所有窗口消息都会进入到钩子函数,你可以在钩子函数中对特定消息进行处理。
1.2 利用钩子函数监控双击事件
在加载arx时会调用OnLoadApp,在卸载时会调用OnUnloadApp。因此,我们可以在OnLoadApp注册钩子函数,在OnUnloadApp删掉注册的钩子函数,注册与反注册的代码如下:
void OnLoadApp()
{
//..........省略
//注册一个钩子函数
//If the function returns TRUE, the message will not be passed to other hook functions or AutoCAD. The message is terminated.
acedRegisterFilterWinMsg(FilterEntDBClick);
}
void OnUnloadApp()
{
//..........省略
//remove the hook function.
acedRemoveFilterWinMsg(FilterEntDBClick);
}
上述钩子函数具体实现为
bool FilterEntDBClick(MSG *pMsg)
{
if (pMsg->message == WM_LBUTTONDBLCLK)
{
ads_name ss;
if (RTNORM != acedSSGet(_T("I"), NULL, NULL, NULL, ss))
return false;
ads_name ent;
int32_t lLen;
acedSSLength(ss, &lLen);
acedSSName(ss, lLen-1, ent);
acedSSFree(ss);
AcDbObjectId idObj;
if (Acad::eOk == acdbGetObjectId(idObj, ent))
{
AcDbEntity* pEnt = NULL;
if (Acad::eOk == acdbOpenObject(pEnt, idObj))
{
CString sDxfName = pEnt->isA()->dxfName();
acutPrintf(_T("\n\t当前双击实体的dxf名称:%s"), sDxfName);
CString sClassName = pEnt->isA()->name();
acutPrintf(_T("\n\t当前双击实体的类名称:%s"), sClassName);
pEnt->close();
}
}
}
//消息继续传递
return false;
}
当双击某个实体的时候,这个钩子函数会获取实体的对象类型、类名称等信息。关于这个函数还有几点说明:
1.对象的图元类型、类名称信息存在在AcRxClass类对象中,通过IsA()方法,获取对象的AcRxClass类对象。然后进一步获取对象类型、类名称等信息。
2.双击某个对象的时候,可能出现一种情况,就是已经选择了多个对象;所以,在钩子函数中,我们应当获取当前选择集的最后一个对象,这才是双击的对象,这是通过acedSSName方法来完成的。
最后效果如下:
2 实现方法2——反应器
2.1 反应器的作用
反应器实际上就是给ObjectARX提供了捕获AutoCAD特定事件的接口,例如,通过文档反应器捕获到创建新文档、打开图形文档、关闭图形文档的事件,通过数据库反应器捕获到添加新实体、修改实体、删除实体的事件,就可以针对这些事件做一些特定的处理。
AutoCAD提供了多种类型的反应器,供ObjectARX处理特定的事件,包括:
1. 编辑器反应器:AcEditorReactor
2. 文档反应器:AcApDocManagerReactor
3. 数据库反应器:AcDbDatabaseReactor
4. 对象反应器:AcDbObject
每个反应器的用法都有差异,监控实体双击事件,我们需要使用编辑器反应器。
2.2 利用编辑器反应器监控双击事件
以下反应器实例监控对象的双击事件,并且当对象是组中成员的时候,在命令行输出组的名称。
从AcEditorReactor派生出一个新类CDBClickReactor,头文件为:
#pragma once
class CDBClickReactor : public AcEditorReactor
{
public:
CDBClickReactor() {};
~CDBClickReactor() {};
virtual void beginDoubleClick(const AcGePoint3d& clickPoint);
};
该类的实现为:
#include "stdafx.h"
#include "CDBClickReactor.h"
#include <dbgroup.h>
void CDBClickReactor::beginDoubleClick(const AcGePoint3d& clickPoint)
{
//获取预选择集
ads_name ss;
if (RTNORM != acedSSGet(_T("I"), NULL, NULL, NULL, ss))
return;
ads_name ent;
acedSSName(ss, 0, ent);
acedSSFree(ss);
AcDbObjectId objId;
acdbGetObjectId(objId, ent);
AcDbEntity* pEnt = NULL;
if (Acad::eOk == acdbOpenObject(pEnt, objId))
{
const AcDbVoidPtrArray *pReactors;
void *pSomething;
AcDbObjectReactor *pObjReactor;
AcDbObjectId persObjId;
AcDbGroup *pGroup;
pReactors = pEnt->reactors();
if (pReactors != NULL && pReactors->length() > 0)
{
for (int i = 0; i < pReactors->length(); i++)
{
pSomething = pReactors->at(i);
// Is it a persistent reactor?
if (acdbIsPersistentReactor(pSomething))
{
persObjId = acdbPersistentReactorObjectId(pSomething);
// 如果是组,就打开,输出组名称
if ((Acad::eOk == acdbOpenObject(pGroup, persObjId, AcDb::kForRead)))
{
acutPrintf(_T("\n\t当前组的名称为:%s\n"), pGroup->name());
pGroup->close();
break;
}
}
}
}
}
}
关于类的实现,着重说明以下几点:
1.用户双击对象以后,会进入到CDBClickReactor::beginDoubleClick,我们为了演示方便,针对用户双击组对象的情况,做了特殊处理,输出组的名称;其他情况没有特殊处理。
2.用户双击组时,实际上双击的是组中的某个成员实体,但是这个实体添加了一个永久反应器,这个永久反应器关联的对象才是组对象。上述例程用到的acdbPersistentReactorObjectId,返回的就是永久反应器关联的实体ID。
除了上述实现文件以外,需要声明全局变量
CDBClickReactor* g_ReactorDBClick = NULL;
在加载arx时,往CAD编辑器加载这个反应器
void OnLoadApp()
{
//.........省略
g_ReactorDBClick = new CDBClickReactor();
acedEditor->addReactor(g_ReactorDBClick);
}
在卸载arx时,需要删掉这个反应器
void OnUnloadApp()
{
//.........省略
acedEditor->removeReactor(g_ReactorDBClick);
delete g_ReactorDBClick;
}
实现效果如下:
以上,就是利用编辑器反应器监控对象双击的简单示例。