instAdmin/Cpp/GisDes/SaRibbon/SARibbonBar/SARibbonActionsManager.cpp
2024-10-29 22:24:50 +08:00

639 lines
16 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "SARibbonActionsManager.h"
#include <QMap>
#include <QHash>
#include <QDebug>
#include <QWidgetAction>
#include "SARibbonBar.h"
class SARibbonActionsManager::PrivateData
{
SA_RIBBON_DECLARE_PUBLIC(SARibbonActionsManager)
public:
PrivateData(SARibbonActionsManager* p);
void clear();
QMap< int, QList< QAction* > > mTagToActions; ///< tag : QList<QAction*>
QMap< int, QString > mTagToName; ///< tag对应的名字
QHash< QString, QAction* > mKeyToAction; ///< key对应action
QMap< QAction*, QString > mActionToKey; ///< action对应key
QMap< int, SARibbonCategory* > mTagToCategory; ///< 仅仅在autoRegisteActions函数会有用
int mSale; ///< 盐用于生成固定的id在用户不主动设置key时id基于msale生成只要SARibbonActionsManager的调用registeAction顺序不变生成的id都不变因为它是基于自增实现的
};
SARibbonActionsManager::PrivateData::PrivateData(SARibbonActionsManager* p) : q_ptr(p), mSale(0)
{
}
void SARibbonActionsManager::PrivateData::clear()
{
mTagToActions.clear();
mTagToName.clear();
mKeyToAction.clear();
mActionToKey.clear();
mTagToCategory.clear();
mSale = 0;
}
SARibbonActionsManager::SARibbonActionsManager(SARibbonBar* bar)
: QObject(bar), d_ptr(new SARibbonActionsManager::PrivateData(this))
{
autoRegisteActions(bar);
}
SARibbonActionsManager::~SARibbonActionsManager()
{
}
/**
* @brief 设置tag对应的名字通过这个可以得到tag和文本的映射
* @param tag
* @param name
* @note 在支持多语言的环境下,在语言切换时需要重新设置,以更新名字
*/
void SARibbonActionsManager::setTagName(int tag, const QString& name)
{
d_ptr->mTagToName[ tag ] = name;
}
/**
* @brief 获取tag对应的中文名字
* @param tag
* @return
*/
QString SARibbonActionsManager::tagName(int tag) const
{
return (d_ptr->mTagToName.value(tag, ""));
}
/**
* @brief 移除tag
* @note 注意,这个函数非常耗时
* @param tag
*/
void SARibbonActionsManager::removeTag(int tag)
{
QList< QAction* > oldacts = actions(tag);
// 开始移除
d_ptr->mTagToActions.remove(tag);
d_ptr->mTagToName.remove(tag);
// 开始查找需要移出总表的action
QList< QAction* > needRemoveAct;
QList< QAction* > total;
for (auto i = d_ptr->mTagToActions.begin(); i != d_ptr->mTagToActions.end(); ++i) {
total += i.value();
}
for (QAction* a : qAsConst(oldacts)) {
if (!total.contains(a)) {
needRemoveAct.append(a);
}
}
// 从总表移除action
for (QAction* a : qAsConst(needRemoveAct)) {
auto i = d_ptr->mActionToKey.find(a);
if (i != d_ptr->mActionToKey.end()) {
d_ptr->mKeyToAction.remove(i.value());
d_ptr->mActionToKey.erase(i);
}
}
}
/**
* @brief 把action注册到管理器中实现action的管理
* @param act
* @param tag tag是可以按照位进行叠加见 @ref ActionTag 如果
* 要定义自己的标签,建议定义大于@ref ActionTag::UserDefineActionTag 的值,
* registeAction的tag是直接记录进去的如果要多个标签并存在registe之前先或好tag
* @param key key是action对应的key一个key只对应一个action是查找action的关键
* ,默认情况为一个QString(),这时key是QAction的objectName
* @param enableEmit 控制是否发射@ref actionTagChanged 信号
* @note 同一个action多次注册不同的tag可以通过tag索引到action但通过action只能索引到最后一个注册的tag
* @note tag的新增会触发actionTagChanged信号
*/
bool SARibbonActionsManager::registeAction(QAction* act, int tag, const QString& key, bool enableEmit)
{
if (nullptr == act) {
return (false);
}
QString k = key;
if (k.isEmpty()) {
k = QString("id_%1_%2").arg(d_ptr->mSale++).arg(act->objectName());
}
if (d_ptr->mKeyToAction.contains(k)) {
qWarning() << "key: "
<< k << " have been exist,you can set key in an unique value when use SARibbonActionsManager::registeAction";
return (false);
}
d_ptr->mKeyToAction[ k ] = act;
d_ptr->mActionToKey[ act ] = k;
// 记录tag 对 action
bool isneedemit = !(d_ptr->mTagToActions.contains(tag)); // 记录是否需要发射信号
d_ptr->mTagToActions[ tag ].append(act);
// 绑定槽
connect(act, &QObject::destroyed, this, &SARibbonActionsManager::onActionDestroyed);
if (isneedemit && enableEmit) {
// 说明新增tag
Q_EMIT actionTagChanged(tag, false);
}
return (true);
}
/**
* @brief 取消action的注册
*
* 如果tag对应的最后一个action被撤销tag也将一块删除
* @param act
* @note tag的删除会触发actionTagChanged信号
* @note 如果action关联了多个tag这些tag里的action都会被删除对应的key也同理
*/
void SARibbonActionsManager::unregisteAction(QAction* act, bool enableEmit)
{
if (nullptr == act) {
return;
}
// 绑定槽
disconnect(act, &QObject::destroyed, this, &SARibbonActionsManager::onActionDestroyed);
removeAction(act, enableEmit);
}
/**
* @brief 移除action
*
* 仅移除内存内容
* @param act
* @param enableEmit
*/
void SARibbonActionsManager::removeAction(QAction* act, bool enableEmit)
{
QList< int > deletedTags; // 记录删除的tag用于触发actionTagChanged
QMap< int, QList< QAction* > > tagToActions; ///< tag : QList<QAction*>
for (auto i = d_ptr->mTagToActions.begin(); i != d_ptr->mTagToActions.end(); ++i) {
// 把不是act的内容转移到tagToActions和tagToActionKeys中之后再和m_d里的替换
auto tmpi = tagToActions.insert(i.key(), QList< QAction* >());
int count = 0;
for (int j = 0; j < i.value().size(); ++j) {
if (i.value()[ j ] != act) {
tmpi.value().append(act);
++count;
}
}
if (0 == count) {
// 说明这个tag没有内容
tagToActions.erase(tmpi);
deletedTags.append(i.key());
}
}
// 删除mKeyToAction
QString key = d_ptr->mActionToKey.value(act);
d_ptr->mActionToKey.remove(act);
d_ptr->mKeyToAction.remove(key);
// 置换
d_ptr->mTagToActions.swap(tagToActions);
// 发射信号
if (enableEmit) {
for (int tagdelete : qAsConst(deletedTags)) {
Q_EMIT actionTagChanged(tagdelete, true);
}
}
}
/**
* @brief 等同actions
* @param tag
* @return
*/
QList< QAction* >& SARibbonActionsManager::filter(int tag)
{
return (actions(tag));
}
/**
* @brief 根据tag得到actions
* @param tag
* @return
*/
QList< QAction* >& SARibbonActionsManager::actions(int tag)
{
return (d_ptr->mTagToActions[ tag ]);
}
const QList< QAction* > SARibbonActionsManager::actions(int tag) const
{
return (d_ptr->mTagToActions[ tag ]);
}
/**
* @brief 获取所有的标签
* @return
*/
QList< int > SARibbonActionsManager::actionTags() const
{
return (d_ptr->mTagToActions.keys());
}
/**
* @brief 通过key获取action
* @param key
* @return 如果没有key返回nullptr
*/
QAction* SARibbonActionsManager::action(const QString& key) const
{
return (d_ptr->mKeyToAction.value(key, nullptr));
}
/**
* @brief 通过action找到key
* @param act
* @return 如果找不到返回QString()
*/
QString SARibbonActionsManager::key(QAction* act) const
{
return (d_ptr->mActionToKey.value(act, QString()));
}
/**
* @brief 返回所有管理的action数
* @return
*/
int SARibbonActionsManager::count() const
{
return (d_ptr->mKeyToAction.size());
}
/**
* @brief 返回所有管理的actions
* @return
*/
QList< QAction* > SARibbonActionsManager::allActions() const
{
return (d_ptr->mKeyToAction.values());
}
/**
* @brief 自动加载SARibbonBar的action
* 此函数会遍历@ref SARibbonBar的父窗口(一般是SARibbonMainWindow)下的所有子object找到action注册
* 并会遍历所有@ref SARibbonCategory,把SARibbonCategory下的action按SARibbonCategory的title name进行分类
*
* 此函数会把所有category下的action生成tag并注册返回的QMap<int, SARibbonCategory *>是记录了category对应的tag
*
* 此函数还会把SARibbonBar的父窗口(一般是SARibbonMainWindow)下面的action但不在任何一个category下的作为NotInRibbonCategoryTag标签注册默认名字会赋予not
* in ribbon 可以通过@ref setTagName 改变
*
* @param w
* @return
* @note 此函数的调用最好在category设置了标题后调用因为会以category的标题作为标签的命名
*/
QMap< int, SARibbonCategory* > SARibbonActionsManager::autoRegisteActions(SARibbonBar* bar)
{
QMap< int, SARibbonCategory* > res;
// 先遍历SARibbonBar的父窗口(一般是SARibbonMainWindow)下的所有子对象把所有action找到
QWidget* parWidget = bar->parentWidget();
QSet< QAction* > mainwindowActions;
if (parWidget) {
for (QObject* o : qAsConst(parWidget->children())) {
if (QAction* a = qobject_cast< QAction* >(o)) {
// 说明是action
if (!a->objectName().isEmpty()) {
mainwindowActions.insert(a);
}
}
}
}
// 开始遍历每个category加入action
if (nullptr == bar) {
// 非ribbon模式直接退出
return (res);
}
QSet< QAction* > categoryActions;
QList< SARibbonCategory* > categorys = bar->categoryPages();
int tag = AutoCategoryDistinguishBeginTag;
for (SARibbonCategory* c : qAsConst(categorys)) {
QList< SARibbonPannel* > pannels = c->pannelList();
for (SARibbonPannel* p : qAsConst(pannels)) {
categoryActions += autoRegisteWidgetActions(p, tag, false);
}
setTagName(tag, c->categoryName());
res[ tag ] = c;
++tag;
}
// 找到不在功能区的actions
QSet< QAction* > notincategory = mainwindowActions - categoryActions;
for (QAction* a : qAsConst(notincategory)) {
if (!a->objectName().isEmpty()) {
registeAction(a, NotInRibbonCategoryTag, a->objectName(), false);
}
}
if (notincategory.size() > 0) {
setTagName(NotInRibbonCategoryTag, tr("not in ribbon"));
}
for (auto i = res.begin(); i != res.end(); ++i) {
connect(i.value(), &SARibbonCategory::categoryNameChanged, this, &SARibbonActionsManager::onCategoryTitleChanged);
}
d_ptr->mTagToCategory = res;
return (res);
}
/**
* @brief 自动加载action
* @param w
* @param tag
* @param enableEmit
* @return 返回成功加入SARibbonActionsManager管理的action
*/
QSet< QAction* > SARibbonActionsManager::autoRegisteWidgetActions(QWidget* w, int tag, bool enableEmit)
{
QSet< QAction* > res;
QList< QAction* > was = w->actions();
for (QAction* a : qAsConst(was)) {
if (res.contains(a) || a->objectName().isEmpty()) {
// 重复内容不重复加入
// 没有object name不加入
continue;
}
if (registeAction(a, tag, a->objectName(), enableEmit)) {
res.insert(a);
}
}
return (res);
}
/**
* @brief 根据标题查找action
* @param text
* @return
*/
QList< QAction* > SARibbonActionsManager::search(const QString& text)
{
QList< QAction* > res;
if (text.isEmpty()) {
return (res);
}
QStringList kws = text.split(" ");
if (kws.isEmpty()) {
kws.append(text);
}
QList< QAction* > acts = d_ptr->mActionToKey.keys();
for (const QString& k : qAsConst(kws)) {
for (auto i = d_ptr->mActionToKey.begin(); i != d_ptr->mActionToKey.end(); ++i) {
if (i.key()->text().contains(k, Qt::CaseInsensitive)) {
res.append(i.key());
}
}
}
return (res);
}
void SARibbonActionsManager::clear()
{
d_ptr->clear();
}
/**
* @brief action 被delete时将触发此槽把管理的action删除
* @param o
* @note 这个函数不会触发actionTagChanged信号
*/
void SARibbonActionsManager::onActionDestroyed(QObject* o)
{
QAction* act = static_cast< QAction* >(o);
removeAction(act, false);
}
/**
* @brief autoRegisteActions函数会关联此槽在标签内容改变时改变tag 对应 文本
* @param title
*/
void SARibbonActionsManager::onCategoryTitleChanged(const QString& title)
{
SARibbonCategory* c = qobject_cast< SARibbonCategory* >(sender());
if (nullptr == c) {
return;
}
int tag = d_ptr->mTagToCategory.key(c, -1);
if (tag == -1) {
return;
}
setTagName(tag, title);
}
////////////////////////////////////////////////////////////////////////////////////////////////////////
/// SARibbonActionsModel
////////////////////////////////////////////////////////////////////////////////////////////////////////
class SARibbonActionsManagerModel::PrivateData
{
SA_RIBBON_DECLARE_PUBLIC(SARibbonActionsManagerModel)
public:
PrivateData(SARibbonActionsManagerModel* p);
void updateRef();
int count() const;
QAction* at(int index);
bool isNull() const;
public:
SARibbonActionsManager* mMgr { nullptr };
int mTag { SARibbonActionsManager::CommonlyUsedActionTag };
QString mSeatchText;
QList< QAction* > mActions;
};
SARibbonActionsManagerModel::PrivateData::PrivateData(SARibbonActionsManagerModel* p) : q_ptr(p)
{
}
void SARibbonActionsManagerModel::PrivateData::updateRef()
{
if (isNull()) {
return;
}
if (!mSeatchText.isEmpty()) {
mActions = mMgr->search(mSeatchText);
} else {
mActions = mMgr->actions(mTag);
}
}
int SARibbonActionsManagerModel::PrivateData::count() const
{
if (isNull()) {
return (0);
}
return (mActions.size());
}
QAction* SARibbonActionsManagerModel::PrivateData::at(int index)
{
if (isNull()) {
return (nullptr);
}
if (index >= count()) {
return (nullptr);
}
return (mActions.at(index));
}
bool SARibbonActionsManagerModel::PrivateData::isNull() const
{
return (mMgr == nullptr);
}
//===================================================
// SARibbonActionsManagerModel
//===================================================
SARibbonActionsManagerModel::SARibbonActionsManagerModel(QObject* p)
: QAbstractListModel(p), d_ptr(new SARibbonActionsManagerModel::PrivateData(this))
{
}
SARibbonActionsManagerModel::SARibbonActionsManagerModel(SARibbonActionsManager* m, QObject* p)
: QAbstractListModel(p), d_ptr(new SARibbonActionsManagerModel::PrivateData(this))
{
setupActionsManager(m);
}
SARibbonActionsManagerModel::~SARibbonActionsManagerModel()
{
}
int SARibbonActionsManagerModel::rowCount(const QModelIndex& parent) const
{
if (parent.isValid()) { // 非顶层
return (0);
}
// 顶层
return (d_ptr->count());
}
QVariant SARibbonActionsManagerModel::headerData(int section, Qt::Orientation orientation, int role) const
{
Q_UNUSED(section);
if (role != Qt::DisplayRole) {
return (QVariant());
}
if (Qt::Horizontal == orientation) {
return (tr("action name"));
}
return (QVariant());
}
Qt::ItemFlags SARibbonActionsManagerModel::flags(const QModelIndex& index) const
{
if (!index.isValid()) {
return (Qt::NoItemFlags);
}
return (Qt::ItemIsSelectable | Qt::ItemIsEnabled);
}
QVariant SARibbonActionsManagerModel::data(const QModelIndex& index, int role) const
{
QAction* act = indexToAction(index);
if (nullptr == act) {
return (QVariant());
}
switch (role) {
case Qt::DisplayRole: {
QString str = act->text();
if (!str.isEmpty()) {
return str;
}
// 如果文本为空一般是没有设置文本或者是窗口如果是窗口那么用windowtitle作为文本
if (QWidgetAction* wa = qobject_cast< QWidgetAction* >(act)) {
if (QWidget* w = wa->defaultWidget()) {
str = w->windowTitle();
}
}
return str;
}
case Qt::DecorationRole:
return (act->icon());
default:
break;
}
return (QVariant());
}
void SARibbonActionsManagerModel::setFilter(int tag)
{
d_ptr->mTag = tag;
update();
}
void SARibbonActionsManagerModel::update()
{
beginResetModel();
d_ptr->updateRef();
endResetModel();
}
void SARibbonActionsManagerModel::setupActionsManager(SARibbonActionsManager* m)
{
d_ptr->mMgr = m;
d_ptr->mTag = SARibbonActionsManager::CommonlyUsedActionTag;
d_ptr->mActions = m->filter(d_ptr->mTag);
connect(m, &SARibbonActionsManager::actionTagChanged, this, &SARibbonActionsManagerModel::onActionTagChanged);
update();
}
void SARibbonActionsManagerModel::uninstallActionsManager()
{
if (!d_ptr->isNull()) {
disconnect(
d_ptr->mMgr, &SARibbonActionsManager::actionTagChanged, this, &SARibbonActionsManagerModel::onActionTagChanged);
d_ptr->mMgr = nullptr;
d_ptr->mTag = SARibbonActionsManager::CommonlyUsedActionTag;
}
update();
}
QAction* SARibbonActionsManagerModel::indexToAction(QModelIndex index) const
{
if (!index.isValid()) {
return (nullptr);
}
if (index.row() >= d_ptr->count()) {
return (nullptr);
}
return (d_ptr->at(index.row()));
}
/**
* @brief 查找
* @param text
*/
void SARibbonActionsManagerModel::search(const QString& text)
{
d_ptr->mSeatchText = text;
update();
}
void SARibbonActionsManagerModel::onActionTagChanged(int tag, bool isdelete)
{
if (isdelete && (tag == d_ptr->mTag)) {
d_ptr->mTag = SARibbonActionsManager::UnknowActionTag;
update();
} else {
if (tag == d_ptr->mTag) {
update();
}
}
}