#pragma once #include #include #include #include #include #include #include #include #include #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& 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 float getEavesSize() const; /// 获取子图元坐标系下的边框(位置), 类似于Windows的Client Rect QRectF getBoundsChildCoord() const; /// 获取场景坐标系下的图元基础边框(位置) QRectF getBoundsSceneCoord() const; /// 获取场景坐标系下的图元扩展边框(位置) QRectF getExtendedBoundsSceneCoord() const; /// 获取图元x坐标, short for getX() float x() const; /// 获取图元y坐标, short for getY() float y() const; /// 获取图元左上角坐标, short for getPos() QPointF pos() const; /// 获取图元宽度, short for getWidth() float width() const; /// 获取图元高度, short for getHeight() float height() const; /// 获取图元x坐标 float getX() const; /// 获取图元y坐标 float getY() const; /// 获取图元左上角坐标 QPointF getPos() const; /// 获取图元宽度 float getWidth() const; /// 获取图元高度 float 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(float x, float y, float width, float height, bool bSendEvent = true); /// 移动图元 /// @param x X坐标 /// @param y Y坐标 /// @note 此方法会发出move事件, 如果不想发出事件, 请用 setBounds 方法 /// @see onMove void move(float x, float y); /// 移动图元 /// @param pos 坐标 /// @note 此方法会发出move事件, 如果不想发出事件, 请用 setBounds 方法 /// @see onMove void move(const QPointF& pos); /// 改变图元尺寸 /// @param width 宽度 /// @param height 高度 /// @note 此方法会发出resize事件, 如果不想发出事件, 请用 setBounds 方法. /// @see onResize void resize(float width, float height); /// 改变图元尺寸 /// @param sz 尺寸 /// @note 此方法会发出resize事件, 如果不想发出事件, 请用 setBounds 方法. /// @see onResize void resize(const QSizeF & sz); /// 移动图元 /// @param x X坐标 /// @note 此方法会发出move事件, 如果不想发出事件, 请用 setBounds 方法 /// @see onMove void setX(float x); /// 移动图元 /// @param y Y坐标 /// @note 此方法会发出move事件, 如果不想发出事件, 请用 setBounds 方法 /// @see onMove void setY(float y); /// 改变图元宽度 /// @param width 宽度 /// @note 此方法会发出resize事件, 如果不想发出事件, 请用 setBounds 方法. /// @see onResize void setWidth(float width); /// 改变图元高度 /// @param height 高度 /// @note 此方法会发出resize事件, 如果不想发出事件, 请用 setBounds 方法. /// @see onResize void setHeight(float height); // 位置改变 virtual void onMove(const QPointF& ptOld, const QPointF& ptNew); // 尺寸改变 virtual void onResize(const QSizeF & szOld, const QSizeF & szNew); /// 查找指定位置处的控制点 ZxHandleRef findHandle(const QPointF& pos, float fHandleSize, bool bSelectedOnly); /// 查找指定位置处的控制点 ZxHandleRef findHandle(float x, float y, float 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(float x, float y, ZxFilterFun filter = NULL, bool bRecursive = true); /// 查找指定位置处的多个图元 /// @param pos 查找的坐标 /// @param bRecursive true表示查找所有子孙节点, false表示只查找直接子节点. 默认为true. /// @param filter 查找条件, 这是一个过滤函数. 默认为NULL, 匹配所有图元. /// @return 所有匹配的图元 QList 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 findItems(float x, float y, ZxFilterFun filter = NULL, bool bRecursive = true); /// 查找指定类型的子(孙)图元 template 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(true); } if (p == 0) { p = dynamic_cast(c); } if (p) { return p; } } return 0; } /// 检测指定点处是否位于图元内部 virtual bool isItemBody(float x, float 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 static T load(const QString& filePath) { ZxRenderItem* p = load(filePath); T p1 = dynamic_cast(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 T clone() const { ZxRenderItem* p = this->clone(); T ret = dynamic_cast(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 & 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 & list); static QList 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 & list, float x, float 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 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);