You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
nmWATI/Include/iPlot/iPlotBase/ZxRenderItem.h

750 lines
23 KiB
C++

#pragma once
#include <QVector>
#include <QList>
#include <QCursor>
#include <QObject>
#include <QRectF>
#include <QPen>
#include <QBrush>
#include <IxPtySource.h>
#include <IxRenderItem.h>
#include "ZxPaintParam.h"
#include "ZxRenderItemIterator.h"
#include "ZxHandleRef.h"
#include "iPlotBase_global.h"
class ZxRenderView;
class ZxSceneItem;
class ZxPaintParam;
//class QPainter;
class _ZxRenderChildIndex;
class ZxHandlePolicy;
class ZxEdit;
class ZxRenderItem;
class QSizeF;
class QPointF;
class ZxLayout;
class QFile;
class QDataStream;
class QAction;
class IxPtyItem;
class QMimeData;
class ZxSceneItem;
class QMenu;
class ZxTool;
/// 图元的根类
class I_PLOTBASE_EXPORT ZxRenderItem
: public QObject
, virtual public IxRenderItem
, virtual public IxPtySource
{
Q_OBJECT
ZX_DECLARE_DYNAMIC
public:
ZxRenderItem();
virtual ~ZxRenderItem();
void init();
virtual void onSerialize(ZxSerializer* ser);
virtual void onDeserialize(ZxSerializer* ser);
virtual void onLoadTempl(ZxSerializer* ser);
virtual void onSaveTempl(ZxSerializer* ser);
// 这是从外界调用的统一封装,模板
// bLoad 加载 or 保存
// bSelf 是否仅仅某类名称的,比如 TheorySource
void dealwithTempl(bool bLoad, bool bSelf);
virtual QString getTemplPath(bool bSelf);
bool loadTempl(QString s = ""); //模板的处理
bool saveTempl(QString s);
/// 指定父节点, 并加自动入父节点的子图元列表中
virtual void setParent(IxRenderItem* pParent);
/// 重新实现 QObject::setParent()
virtual void setParentObject(QObject* pParent);
/// 获取父节点的指针. 对于根结点, 这个函数返回NULL.
ZxRenderItem* getParent() const;
/// 获取根结点
ZxRenderItem* getRootItem() const;
/// 获取所在的场景
ZxSceneItem* getScene() const;
/// 获取当前工具
ZxTool* getCurrentTool() const;
/// 获取当前视图
ZxRenderView* getCurrentToolView() const;
/// 获取图元的显示名
virtual QString getTitle() const;
/// 获取图元的图标
virtual QIcon getIcon() const;
/// 递归绘图, 含自身和子图元
virtual void paint(QPainter* painter, const ZxPaintParam& param);
/// 绘制背景(位于子图元下方), 如无特殊情况, 普通内容应在此绘制
virtual void paintBack(QPainter* painter, const ZxPaintParam& param);
/// 绘制前景(位于子图元上方)
virtual void paintFront(QPainter* painter, const ZxPaintParam& param);
/// 画子图元(一般不需要重写)
virtual void paintChildren(QPainter* painter, const ZxPaintParam& param);
/// 绘制Edit和Policy
/// @note 此方法是独立的, 和绘制图元完全是不同的系统
virtual void paintEdit(QPainter* painter, const ZxPaintParam& param);
/// 获取子图元列表
const QVector<ZxRenderItem*>& getChildren() const;
/// 子图元数量, 等同于 size()
int count() const;
/// 子图元数量, 等同于 count()
int size() const;
/// 子图元数量是否为0, 等同于 empty()
bool isEmpty() const;
/// 子图元数量是否为0, 等同于 isEmpty()
bool empty() const;
/// 在指定位置插入子图元, 并自动把子图元的parent指向当前图元.
/// index 为 0 时插到前面, index为-1或等于size()时追加到最后.
void insert(int index, ZxRenderItem* pItem);
/// 替换子图元.
/// @param index 替换的位置
/// @param pItem 用来替换旧图元的新图元
/// @param bDelete 是否删除旧图元
/// @return bDelete为FALSE时返回旧图元, 否则返回NULL
ZxRenderItem* replace(int index, ZxRenderItem* pItem, bool bDelete);
/// 追加子图元
void append(ZxRenderItem* p);
/// 移除指定的子图元(不销毁)
void remove(ZxRenderItem* p);
/// 返回指定位置的子图元
ZxRenderItem* at(int i) const;
/// 返回指定子图元的位置索引, 如果没找到则返回-1. 对于空指针, 此方法总是返回-1.
int indexOf(const ZxRenderItem* p) const;
/// 从图元树上移除
ZxRenderItem* detach();
/// 删除所有子节点
virtual void clear();
// 在手动操作时, 判断此图元是否能容纳指定的子图元.
// 例如在曲线道里, 就可以对曲线以外的类型返回false.
//virtual bool canManualAdd(const ZxRenderItem* p) const;
/// 获取图元基础边框(位置)
virtual QRectF getBounds() const;
/// 获取图元的扩展边框. 默认是基础边框+外檐
QRectF getExtendedBounds() const;
/// 获取图元的外檐尺寸
virtual double getEavesSize() const;
/// 获取子图元坐标系下的边框(位置), 类似于Windows的Client Rect
QRectF getBoundsChildCoord() const;
/// 获取场景坐标系下的图元基础边框(位置)
QRectF getBoundsSceneCoord() const;
/// 获取场景坐标系下的图元扩展边框(位置)
QRectF getExtendedBoundsSceneCoord() const;
/// 获取图元x坐标, short for getX()
double x() const;
/// 获取图元y坐标, short for getY()
double y() const;
/// 获取图元左上角坐标, short for getPos()
QPointF pos() const;
/// 获取图元宽度, short for getWidth()
double width() const;
/// 获取图元高度, short for getHeight()
double height() const;
/// 获取图元x坐标
double getX() const;
/// 获取图元y坐标
double getY() const;
/// 获取图元左上角坐标
QPointF getPos() const;
/// 获取图元宽度
double getWidth() const;
/// 获取图元高度
double getHeight() const;
/// 设置图元边框
/// @param rect 边框
/// @param bSendEvent 是否发出 resize 和 move 事件, 默认为true.
/// @see onMove onResize
/// @note 普通情况都应该发事件, 但有一类逻辑问题可以用不发事件来解决, 见代码:
/// @code
/// void ZxPolyItem::setPoints(const QPolygonF & val)
/// {
/// m_points = val;
/// setBounds(m_points.boundingRect(), false);
/// }
///
/// void ZxPolyItem::onMove(const QPointF& ptOld, const QPointF& ptNew)
/// {
/// m_points.translate(ptNew - ptOld);
/// }
/// @endcode
virtual void setBounds(const QRectF& rect, bool bSendEvent = true);
/// 设置图元边框
/// @see setBounds(const QRectF &, bool)
virtual void setBounds(double x, double y, double width, double height, bool bSendEvent = true);
/// 移动图元
/// @param x X坐标
/// @param y Y坐标
/// @note 此方法会发出move事件, 如果不想发出事件, 请用 setBounds 方法
/// @see onMove
void move(double x, double y);
/// 移动图元
/// @param pos 坐标
/// @note 此方法会发出move事件, 如果不想发出事件, 请用 setBounds 方法
/// @see onMove
void move(const QPointF& pos);
/// 改变图元尺寸
/// @param width 宽度
/// @param height 高度
/// @note 此方法会发出resize事件, 如果不想发出事件, 请用 setBounds 方法.
/// @see onResize
void resize(double width, double height);
/// 改变图元尺寸
/// @param sz 尺寸
/// @note 此方法会发出resize事件, 如果不想发出事件, 请用 setBounds 方法.
/// @see onResize
void resize(const QSizeF & sz);
/// 移动图元
/// @param x X坐标
/// @note 此方法会发出move事件, 如果不想发出事件, 请用 setBounds 方法
/// @see onMove
void setX(double x);
/// 移动图元
/// @param y Y坐标
/// @note 此方法会发出move事件, 如果不想发出事件, 请用 setBounds 方法
/// @see onMove
void setY(double y);
/// 改变图元宽度
/// @param width 宽度
/// @note 此方法会发出resize事件, 如果不想发出事件, 请用 setBounds 方法.
/// @see onResize
void setWidth(double width);
/// 改变图元高度
/// @param height 高度
/// @note 此方法会发出resize事件, 如果不想发出事件, 请用 setBounds 方法.
/// @see onResize
void setHeight(double height);
// 位置改变
virtual void onMove(const QPointF& ptOld, const QPointF& ptNew);
// 尺寸改变
virtual void onResize(const QSizeF & szOld, const QSizeF & szNew);
/// 查找指定位置处的控制点
ZxHandleRef findHandle(const QPointF& pos, double fHandleSize, bool bSelectedOnly);
/// 查找指定位置处的控制点
ZxHandleRef findHandle(double x, double y, double fHandleSize, bool bSelectedOnly);
/// 查找指定位置处的单个图元
/// @param pos 查找的坐标
/// @param bRecursive true表示查找所有子孙节点, false表示只查找直接子节点. 默认为true.
/// @param filter 查找条件, 这是一个过滤函数. 默认为NULL, 匹配所有图元.
/// @return 第一个匹配的图元
ZxRenderItem* findItem(const QPointF& pos, ZxFilterFun filter = NULL, bool bRecursive = true);
/// 查找指定位置处的单个图元
/// @param x 查找的X坐标
/// @param y 查找的Y坐标
/// @param bRecursive true表示查找所有子孙节点, false表示只查找直接子节点. 默认为true.
/// @param filter 查找条件, 这是一个过滤函数. 默认为NULL, 匹配所有图元.
/// @return 第一个匹配的图元
virtual ZxRenderItem* findItem(double x, double y, ZxFilterFun filter = NULL, bool bRecursive = true);
/// 查找指定位置处的多个图元
/// @param pos 查找的坐标
/// @param bRecursive true表示查找所有子孙节点, false表示只查找直接子节点. 默认为true.
/// @param filter 查找条件, 这是一个过滤函数. 默认为NULL, 匹配所有图元.
/// @return 所有匹配的图元
QList<ZxRenderItem*> findItems(const QPointF& pos, ZxFilterFun filter = NULL, bool bRecursive = true);
/// 查找指定位置处的多个图元
/// @param x 查找的X坐标
/// @param y 查找的Y坐标
/// @param bRecursive true表示查找所有子孙节点, false表示只查找直接子节点. 默认为true.
/// @param filter 查找条件, 这是一个过滤函数. 默认为NULL, 匹配所有图元.
/// @return 所有匹配的图元
virtual QList<ZxRenderItem*> findItems(double x, double y, ZxFilterFun filter = NULL, bool bRecursive = true);
/// 查找指定类型的子(孙)图元
template<typename T>
T findChildItem(bool recursive = true)
{
int n = this->count();
for (int i = n - 1; i >= 0; i--)
{
ZxRenderItem* c = at(i);
T p = 0;
if (recursive)
{
p = c->findChildItem<T>(true);
}
if (p == 0)
{
p = dynamic_cast<T>(c);
}
if (p)
{
return p;
}
}
return 0;
}
/// 检测指定点处是否位于图元内部
virtual bool isItemBody(double x, double y);
/// 图元标志集
Flags getFlags() const;
void setFlags(Flags flags);
/// 是否选中
bool isSelected() const;
virtual bool isEqual(const IxRenderItem* pAnother) const;
/// 设置选中标志
void setSelected(bool b);
public:
/// 选中状态改变
virtual void onSelectedChanged();
/// 选择本图元
/// @note 此方法不但设置图元选中状态, 而且把图元加入场景的选中列表中.
/// @see setSelected(bool)
virtual void select(bool bClearOldSelect = true);
/// 取消选择本图元.
/// @note 此方法不但取消图元选中状态, 而且从场景的选中列表中删除图元.
/// @see setSelected(bool)
virtual void deselect(bool bRecursive = false);
/// 是否可见
bool isVisible() const;
void setVisible(bool b);
/// 可见状态改变
virtual void onVisibleChanged();
/// 显示, 相当于setVisible(true)
void show();
/// 隐藏, 相当于setVisible(false)
void hide();
/// 是否隐藏, 等于 !isVisible()
bool isHidden() const;
/// 是否可选
bool isSelectable() const;
void setSelectable(bool b);
/// 可选状态改变
virtual void onSelectableChanged();
/// 是否锁定位置
bool isLockPos() const;
void setLockPos(bool b);
/// 锁定位置状态改变
virtual void onLockPosChanged();
/// 是否锁定尺寸
bool isLockSize() const;
void setLockSize(bool b);
/// 锁定尺寸状态改变
virtual void onLockSizeChanged();
/// 是否只读
bool isReadOnly() const;
void setReadOnly(bool b);
/// 只读状态改变
virtual void onReadOnlyChanged();
/// 是否剪裁
bool isClipping() const;
void setClipping(bool b);
/// 剪裁状态改变
virtual void onClippingChanged();
/// 是否偏移坐标
bool hasLocalCoord() const;
void setLocalCoord(bool b);
/// 偏移坐标状态改变
virtual void onLocalCoordChanged();
/// 获取坐标系所属的图元.
/// @return 往祖先结点找, 返回最近一个hasLocalCoord()的图元. 没找到则返回图元树的根结点.
virtual ZxRenderItem* getCoordItem() const;
/// 是否自动尺寸
bool isAutoSize() const;
void setAutoSize(bool b);
/// 自动尺寸标志改变
virtual void onAutoSizeChanged();
/// 是否自动布局
bool isAutoLayout() const;
void setAutoLayout(bool b);
/// 自动布局标志改变
virtual void onAutoLayoutChanged();
/// 绘图时是否反走样
bool isAntiAlias() const;
void setAntiAlias(bool b);
/// 反走样标志改变
virtual void onAntiAliasChanged();
/// 是否紧缩
bool isShrink() const;
void setShrink(bool b);
/// 紧缩标志改变
virtual void onShrinkChanged();
/// 是否需要重新布局
bool needLayout() const;
/// 是否强制重新布局
bool isForceLayout() const;
/// 刷新布局
void updateLayout(bool bForce = false);
/// 可否手动删除
bool canManualDelete() const;
void setManualDelete(bool b = true);
/// 是否静态Policy
bool isStaticPolicy() const;
void setStaticPolicy(bool b);
/// 是否平凡Policy
bool isOrdinaryPolicy() const;
void setOrdinaryPolicy(bool b);
/// 实际上是否锁定位置
bool isActuallyLockPos() const;
bool isActuallyLockSize() const;
/// 刷新
/// @note 刷新图元所占的区域, 包括附着在图元上的Policy等所占的区域
virtual void update();
/// 刷新指定区域(图元的当前坐标系)
virtual void update(const QRectF& rect);
/// 准备右键菜单
virtual void onPrepareContextMenu(QMenu * pMenu, const QPointF& pos);
/// 从本图元坐标系转到子图元坐标系
QPointF mapToChild(const QPointF& pos) const;
/// 从子图元坐标系转到本图元坐标系
QPointF mapFromChild(const QPointF& pos) const;
/// 从本图元坐标系转到子图元坐标系
QRectF mapToChild(const QRectF& rect) const;
/// 从子图元坐标系转到本图元坐标系
QRectF mapFromChild(const QRectF& rect) const;
/// 从其他图元坐标系转到本图元坐标系
QPointF mapFrom(ZxRenderItem* item, const QPointF& pt) const;
/// 从本图元坐标系转到其他图元坐标系
QPointF mapTo(ZxRenderItem* item, const QPointF& pt) const;
/// 从本图元坐标系转到文档坐标系
QPointF mapToScene(const QPointF& pos) const;
/// 从文档图元坐标系转到本坐标系
QPointF mapFromScene(const QPointF& pos) const;
/// 从本图元坐标系转到文档坐标系
QRectF mapToScene(const QRectF& rect) const;
/// 从文档图元坐标系转到本坐标系
QRectF mapFromScene(const QRectF& rect) const;
/// 填属性表
virtual void fillPtyPano(IxPtyPano* sheet);
/// 获取标志集的字符串, 用来显示和调试
virtual QString getFlagsString() const;
/// 是否有祖先图元被选中
/// @note 在某些操作中, 选中某个图元时相当于把整棵子树全选中了. 这个函数提了供一种快速检测这种状态的方法.
virtual bool isAncestorSelected() const;
/// 判断本图元是否指定图元的祖先
/// @note 这的祖先指图元树上的关系, 不是类的继承关系. 任何图元都不是它自己的祖先
bool isAncestorOf(const ZxRenderItem* p) const;
/// 判断本图元是否指定图元的子孙
/// @note 这的子孙指图元树的上的关系, 不是类的继承关系. 任何图元都不是它自己的子孙
bool isDescendantOf(const ZxRenderItem* p) const;
// 左键双击
virtual void onLeftDoubleClick(const QPointF& pt);
// 左键单击
virtual void onLeftClick(const QPointF& pt);
// parent指针被改变
/// @note 不是parent的内容被改变
virtual void onParentChanged();
/// 不变性(完整性)验证. 这个函数里有一堆ASSERT, 验证图元未损坏.
/// @note ZxSceneItem 要求子图元必须是几个特定图层, 在ZxSceneItem::invariant() 中就可以加上这些验证.
/// @note 此为契约编程的概念, C++无法自动调用此方法, 但底层会在paint等使用频繁使用的函数里调用. 必要时模块层也可以显示调用.
virtual void invariant() const;
/// 加载文件
template <typename T>
static T load(const QString& filePath)
{
ZxRenderItem* p = load(filePath);
T p1 = dynamic_cast<T>(p);
if (p1 == 0 && p != 0)
{
Q_ASSERT(false);
delete p;
}
return p1;
}
/// 加载
static ZxRenderItem* load(const QString& filePath);
static ZxRenderItem* load(QFile& file);
static ZxRenderItem* load(QDataStream& stream);
/// 保存
virtual bool save(const QString& filePath) const;
virtual bool save(QFile& file) const;
virtual bool save(QDataStream& stream) const;
/// 克隆图元
/// @note 此函数自动使用序列化来克隆图元, 一般情况下无需重写.
virtual IxRenderItem* clone() const;
/// 克隆图元
/// @note 此函数自动使用序列化来克隆图元, 一般情况下无需重写.
template <typename T>
T clone() const
{
ZxRenderItem* p = this->clone();
T ret = dynamic_cast<T>(p);
if (ret == 0)
{
delete p;
}
return ret;
}
/// 图元导出为图片文件
/// @param fileName 文件路径.
/// @param format 图片格式, 支持Qt所支持的格式和"SVG".
/// @param quality 暂未使用, 请设为-1
/// @return 导出成功返回true, 失败返回false.
virtual bool exportImage(const QString& fileName, const char * format = 0, int quality = -1) const;
/// 获取图元树迭代器
virtual ZxRenderItemIterator getRenderTree();
/// 设置布局器
virtual void setLayout(ZxLayout* p);
/// 查找多个图元的最近公共祖先节点
static ZxRenderItem* findCommonAncestor(const QList<ZxRenderItem*> & items);
/// 获取当前活动的属性表条目.
/// @note 用户在属性表里修改属性后, 通常会有一个回调, 例如调用对象的setXXXX()函数.
/// 在这些回调函数里, 我们就可以用getPropertyItem(bool)来获取当前所修改的属性表条目.
/// @param bOnlyWhenIsFirst 是否仅在当前图元是第一个图元时才返回.
/// @return 当前活动的(用户正在修改的)属性条目. 如果当前没有活动条目, 此函数返回NULL.
/// @see findPropertyItem(const QString &, bool)
static IxPtyItem * getPropertyItem(bool bOnlyWhenIsFirst = true);
/// 从当前活动的属性表里查找指定条目.
/// @note 用户在属性表里修改属性后, 通常会有一个回调, 例如调用对象的setXXXX()函数.
/// 在这些回调函数里, findPropertyItem()来查找当前所修改的属性表中的指定条目.
/// @param key 指定条目的名称(路径), 如 "井头.井名.字体"
/// @param bOnlyWhenIsFirst 是否仅在当前图元是第一个图元时才返回.
/// @return 找到的条目, 当前没有活动条目或者查找失败时返回NULL.
/// @see getPropertyItem(bool)
static IxPtyItem * findPropertyItem(const QString& key, bool bOnlyWhenIsFirst = true);
protected:
/// 布局操作, 可重写, 默认操作是使用布局器布局.
/// \li 无论needLayout()的值是什么, 都要做布局.
/// \li 布局完了以后一定要setNeedLaout(false)
/// \li 此方法内要递归调用子图元的 doLayout()
virtual void onLayout();
public:
/// 在需要布局的时候调用 onLayout() 重新布局
void doLayout();
/// 获取属性窗口
virtual IxPtyPano* getPtyPano(bool tryOuter = true) const;
/// 获取图元的编辑器
/// @param bCreate 是否创建
/// @return 图元的编辑器, bCreate为true时保证返回可用的编辑器, 否则可能为NULL.
/// @note 每个图元都关联着一个编辑器
/// @see emitSigInstallPolicies() sigInstallPolicies() onInstallPolicies()
virtual ZxEdit* getEdit(bool bCreate = true) const;
// short for getEdit()->getHandlePolicy()
ZxHandlePolicy* getHandlePolicy() const;
/// The recommended size for the widget.
/// If the value is an invalid size, no size is recommended.
/// The default implementation of sizeHint() returns an invalid size if there is no layout for this item, and returns the layout's preferred size otherwise.
virtual QSizeF getSizeHint() const;
/// 调用 onInstallPolicies(), 然后发出信号 sigInstallPolicies()
void emitSigInstallPolicies();
/// (内部使用)
/// \internal
ZxRenderItem* getClippedBy();
static void saveMulti(QDataStream& stream, const QList<ZxRenderItem*> & list);
static QList<ZxRenderItem*> loadMulti(QDataStream& stream);
public slots:
protected:
/// 在此函数内安装编辑策略
virtual void onInstallPolicies();
virtual bool needPaintSelf(QPainter* painter, const ZxPaintParam& param) const;
virtual bool needPaintDescendants(QPainter* painter, const ZxPaintParam& param) const;
virtual void onApplyAutoSize();
virtual void onChildItemAdded(ZxRenderItem* p);
virtual void onChildItemRemoved(ZxRenderItem* p);
private:
void checkNeedLayoutFlagConsistency();
bool _findItems(QList<ZxRenderItem*> & list, double x, double y, \
ZxFilterFun filter, bool bRecursive);
void _insert(int index, ZxRenderItem* p);
void _setNeedLayout(bool bNeed);
void _setForceLayout(bool bForce);
_ZxRenderChildIndex* get_item_index_map();
private:
// QList内部用的是指针数组, 我们直接用指针数据效率可能更高
QVector<ZxRenderItem*> m_vecChildren;
// 父节点
ZxRenderItem* m_pParent;
// 外接矩形
QRectF m_rectBounds;
// 标志集
Flags m_nFlags;
// 当前的编辑器, 如内嵌文本输入框
mutable ZxEdit* m_pEdit;
// 布局器
ZxLayout* m_pLayout;
// 子图元位置索引, 加快indexOf等操作
_ZxRenderChildIndex * m_pChildIndex;
bool m_bInDoLayout : 1; // for debug
bool m_bHasOldOnLayout : 1; // for debug
bool m_bInPainting : 1; // for debug
friend class ZxRenderView;
friend class ZxSceneItem;
friend class ZxSelectionPrivate;
friend class ZxLayout;
};
I_PLOTBASE_EXPORT QString zxSplitTagName(const QString& s);