639 lines
16 KiB
C++
639 lines
16 KiB
C++
#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();
|
||
}
|
||
}
|
||
}
|