/** * RobloxQuickAccessConfig.cpp * Copyright (c) 2013 ROBLOX Corp. All Rights Reserved. */ #include "stdafx.h" #include "RobloxQuickAccessConfig.h" #include #include #include #include #include #include #include #include #include "RobloxMainWindow.h" #include "UpdateUIManager.h" #include "PluginAction.h" #include "QtUtilities.h" FASTFLAG(StudioSeparateActionByActivationMethod) static const QString QuickAccessConfigKey = "Roblox Quick Access Config"; static const QString QuickAccessActionNameKey = "name"; static const QString QuickAccessActionTextKey = "text"; static const QString QuickAccessActionVisibleKey = "visible"; static const QString QuickAccessActionShortcutKey = "shortcut"; static const QString QuickAccessProxyActionExt = "_proxyAct"; static const char* sProp_DisableForQuickAccess = "disableForQuickAccess"; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // ActionItemDelegate //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// class ActionItemDelegate: public QStyledItemDelegate { public: ActionItemDelegate(QWidget* parent) : QStyledItemDelegate(parent) {} QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const { return QStyledItemDelegate::sizeHint(option, index) + QSize(2, 2); } }; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // ActionItem //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ActionItem::ActionItem(QAction* pAction, Qt::ItemFlags flags) : m_pAction(pAction) { setFlags(flags); } QVariant ActionItem::data(int column, int role) const { if (m_pAction) { if (role == Qt::ToolTipRole) return m_pAction->statusTip(); if ((column == AC_NAME) && (role == Qt::DisplayRole)) return m_pAction->text(); if ((column == AC_ICON) && (role == Qt::DecorationRole)) return m_pAction->icon(); } return QVariant(); } bool ActionItem::hasAction(QAction* action) { if (m_pAction == action) return true; QuickAccessBarProxyAction* pProxyAction = qobject_cast(m_pAction); if (pProxyAction) return (pProxyAction->hasAction(action)); return false; } void ActionItem::applyFilter(const QString& filter, int column) { bool filterStringFound = filter.isEmpty() || text(column).contains(filter, Qt::CaseInsensitive); bool itemShown = filterStringFound && canBeShown(); setHidden(!itemShown); } bool ActionItem::canBeShown() { if (dynamic_cast(m_pAction.data()) && m_pAction->parentWidget()) return !m_pAction->parentWidget()->isHidden(); return true; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // QuickAccessActionItem //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// QuickAccessActionItem::QuickAccessActionItem(QAction* pAction, bool isVisible, Qt::ItemFlags flags) : ActionItem(pAction, flags) , m_isQuickAccessCheckState(isVisible ? Qt::Checked : Qt::Unchecked) { } QVariant QuickAccessActionItem::data(int column, int role) const { if (m_pAction) { if (role == Qt::ToolTipRole) return m_pAction->statusTip(); if ((column == QAC_NAME) && (role == Qt::DisplayRole)) return m_pAction->text(); if ((column == QAC_ICON) && (role == Qt::DecorationRole)) return m_pAction->icon(); if ((column == QAC_VISIBILITY) && (role == Qt::CheckStateRole)) return m_isQuickAccessCheckState; } return QVariant(); } void QuickAccessActionItem::setData(int column, int role, const QVariant & value) { if ((role == Qt::CheckStateRole) && (column == QAC_VISIBILITY)) { m_isQuickAccessCheckState = (value == Qt::Checked) ? Qt::Checked : Qt::Unchecked; emitDataChanged(); } } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // BaseListWidget //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// BaseListWidget::BaseListWidget(QWidget* parent) { setAlternatingRowColors(true); setRootIsDecorated(false); setUniformRowHeights(true); setAllColumnsShowFocus(true); setIconSize(QSize(16, 16)); setSelectionMode(QAbstractItemView::SingleSelection); header()->setVisible(false); header()->setResizeMode(QHeaderView::ResizeToContents); header()->setStretchLastSection(true); setItemDelegate(new ActionItemDelegate(this)); setStyleSheet(STRINGIFY( QTreeView::item:selected:!active { background: lightgray;} QTreeView::item { border: none; } )); } void BaseListWidget::addAction(QAction* action) { // check if action is valid and not added already if (isValid(action) && !getItem(action)) { ActionItem* pItem = createItem(action); if (pItem) addTopLevelItem(pItem); } } void BaseListWidget::removeAction(QAction* action) { ActionItem* pItem = getItem(action); if (pItem) delete pItem; } ActionItem* BaseListWidget::getItem(QAction* action) { int numChild = topLevelItemCount(), currentChild = 0; ActionItem *pCurrentItem = NULL; while (currentChild < numChild) { pCurrentItem = static_cast(topLevelItem(currentChild++)); if (pCurrentItem && pCurrentItem->hasAction(action)) return pCurrentItem; } return NULL; } void BaseListWidget::drawRow(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const { QStyleOptionViewItemV4 opt = option; QTreeWidgetItem *pItem = itemFromIndex(index); if (!pItem) { const QColor c = QColor(233, 233, 233); painter->fillRect(option.rect, c); opt.palette.setColor(QPalette::AlternateBase, c); } QTreeWidget::drawRow(painter, opt, index); } bool BaseListWidget::isValid(QAction* action) { return (action && !action->text().isEmpty() && !action->text().startsWith("&") && !action->objectName().isEmpty() && action->isVisible()); } bool BaseListWidget::isActionAvailable(QAction* pAction) { int numChild = topLevelItemCount(), currentChild = 0; ActionItem* pCurrentItem = NULL; while (currentChild < numChild) { pCurrentItem = static_cast(topLevelItem(currentChild++)); if (pCurrentItem && pCurrentItem->hasAction(pAction)) return true; } return false; } void BaseListWidget::selectItem(int row) { QTreeWidgetItem* itemToSelect = NULL; while (row > 0 && row < topLevelItemCount()) { itemToSelect = topLevelItem(row++); if (!itemToSelect->isHidden()) { setCurrentItem(itemToSelect); break; } } } void BaseListWidget::setFilter(const QString& filter, int column) { setUpdatesEnabled(false); QString text; int numChild = topLevelItemCount(), currentChild = 0; ActionItem *pCurrentItem = NULL; while (currentChild < numChild) { pCurrentItem = static_cast(topLevelItem(currentChild++)); if (pCurrentItem) pCurrentItem->applyFilter(filter, column); } setUpdatesEnabled(true); } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // BaseListWidget //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// AllActionsListWidget::AllActionsListWidget(QWidget* parent) : BaseListWidget(parent) { setColumnCount(2); QAction* pAction = NULL; QVariant propVal; QList mainWindowActions = UpdateUIManager::Instance().getMainWindow().actions(); for (QList::const_iterator iter = mainWindowActions.begin(); iter != mainWindowActions.end() ; ++iter) { pAction = *iter; if (!isValid(pAction)) continue; // check if property "disableForQuickAccess" is available or set to true propVal = pAction->property(sProp_DisableForQuickAccess); if (propVal.isValid() && propVal.toBool()) continue; // action is valid and can be added into quickaccess bar addAction(pAction); } sort(); } ActionItem* AllActionsListWidget::createItem(QAction* action) { return new ActionItem(action); } void AllActionsListWidget::sort() { sortByColumn(AC_NAME, Qt::AscendingOrder); } bool AllActionsListWidget::isValid(QAction* action) { return (BaseListWidget::isValid(action) && !qobject_cast(action)); } void AllActionsListWidget::setFilter(const QString& filter) { BaseListWidget::setFilter(filter, AC_NAME); } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // BaseListWidget //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// QuickAccessActionsListWidget::QuickAccessActionsListWidget(QWidget* parent) : BaseListWidget(parent) { setColumnCount(3); initialize(); } void QuickAccessActionsListWidget::initialize() { setUpdatesEnabled(false); clear(); RobloxQuickAccessConfig::QuickAccessActionCollection& quickAccessBarActions = RobloxQuickAccessConfig::singleton().getQuickAccessBarActions(); for (int ii = 0; ii < quickAccessBarActions.size(); ++ii) { if (isValid(quickAccessBarActions.at(ii).action) && quickAccessBarActions.at(ii).isInitialized) addTopLevelItem(new QuickAccessActionItem(quickAccessBarActions.at(ii).action, quickAccessBarActions.at(ii).isVisible)); } setUpdatesEnabled(true); } ActionItem* QuickAccessActionsListWidget::createItem(QAction* action) { // get visibility if it's already in quick access bar (for proxy actions) if (RobloxQuickAccessConfig::singleton().availableInQuickAccessBar(action)) return new QuickAccessActionItem(action, RobloxQuickAccessConfig::singleton().visibleInQuickAccessBar(action)); return new QuickAccessActionItem(action, true); } void QuickAccessActionsListWidget::moveItem(QTreeWidgetItem* itemToAdd, bool moveUp) { int row = indexOfTopLevelItem(itemToAdd); if (itemToAdd && ((moveUp && row > 0) || (!moveUp && row < topLevelItemCount() - 1))) { takeTopLevelItem(row); insertTopLevelItem(moveUp ? row - 1 : row + 1, itemToAdd); setCurrentItem(itemToAdd); } } void QuickAccessActionsListWidget::removeItem(QTreeWidgetItem* itemToRemove) { int row = indexOfTopLevelItem(itemToRemove); if (row >= 0) { QuickAccessActionItem* item = static_cast(takeTopLevelItem(row)); if (item) { delete item; // select item above the deleted item if (row > topLevelItemCount() - 1) row = row - 1; selectItem(row); } } } void QuickAccessActionsListWidget::accept() { RobloxQuickAccessConfig::QuickAccessActionCollection quickAccessCollection; int numChild = topLevelItemCount(), currentChild = 0; ActionItem* pCurrentItem = NULL; // get list of current actions added in quick access list while (currentChild < numChild) { pCurrentItem = static_cast(topLevelItem(currentChild++)); if (pCurrentItem && !pCurrentItem->isHidden()) { quickAccessCollection.append(RobloxQuickAccessConfig::ActionData(pCurrentItem->getAction(), pCurrentItem->data(QAC_VISIBILITY, Qt::CheckStateRole).toBool(), true)); } } // check if we've any uninitialized actions, if not then add them RobloxQuickAccessConfig::QuickAccessActionCollection& quickAccessBarActions = RobloxQuickAccessConfig::singleton().getQuickAccessBarActions(); for (int ii = 0; ii < quickAccessBarActions.size(); ++ii) { if (!quickAccessBarActions.at(ii).isInitialized) quickAccessCollection.append(quickAccessBarActions.at(ii)); } // re-create quick access bar with the latest list of actions RobloxQuickAccessConfig::singleton().recreateQuickAccessBar(quickAccessCollection); } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // QuickAccessConfigDialog //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// QuickAccessConfigDialog::QuickAccessConfigDialog(QWidget* parent) : RobloxSavingStateDialog(parent, "QuickAccessConfigDialog/Geometry") , m_dataChanged(false) { setWindowTitle(tr("Customize Quick Access Bar")); setAttribute(Qt::WA_DeleteOnClose,false); setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); m_pQuickAccessActionsListWidget = new QuickAccessActionsListWidget(this); m_pAllActionListWidget = new AllActionsListWidget(this); m_pFilterEdit = new QLineEdit(this); m_pFilterEdit->setPlaceholderText("Search Actions"); m_pFilterEdit->setFocusPolicy(Qt::ClickFocus); m_pAddButton = new QPushButton(tr("Add >>"), this); m_pRemoveButton = new QPushButton(tr("Remove"), this); m_pMoveUpButton = new QPushButton(this); m_pMoveUpButton->setIcon(QIcon(":/RibbonBar/images/Studio Ribbon Icons/ribbonMinimize.png")); m_pMoveDownButton = new QPushButton(this); m_pMoveDownButton->setIcon(QIcon(":/RibbonBar/images/Studio Ribbon Icons/ribbonMaximize.png")); m_dialogButtonBox = new QDialogButtonBox(QDialogButtonBox::RestoreDefaults | QDialogButtonBox::Ok | QDialogButtonBox::Cancel); connect(m_pAddButton, SIGNAL(clicked()), this, SLOT(onAddButtonClicked())); connect(m_pRemoveButton, SIGNAL(clicked()), this, SLOT(onRemoveButtonClicked())); connect(m_pMoveUpButton, SIGNAL(clicked()), this, SLOT(onMoveButtonClicked())); connect(m_pMoveDownButton, SIGNAL(clicked()), this, SLOT(onMoveButtonClicked())); connect(m_dialogButtonBox, SIGNAL(accepted()), this, SLOT(accept())); connect(m_dialogButtonBox, SIGNAL(rejected()), this, SLOT(cancel())); connect(m_dialogButtonBox, SIGNAL(clicked(QAbstractButton*)), this, SLOT(restoreDefaults(QAbstractButton*))); connect(m_pFilterEdit, SIGNAL(textChanged(QString)), this, SLOT(setFilter(QString))); connect(m_pQuickAccessActionsListWidget, SIGNAL(itemChanged(QTreeWidgetItem *, int)), this, SLOT(onQuickAccessListDataChanged())); connect(&RobloxQuickAccessConfig::singleton(), SIGNAL(defaultsRestored()), this, SLOT(onDefaultsRestored())); UpdateUIManager::Instance().getMainWindow().installEventFilter(this); doLayout(); setWindowModality(Qt::ApplicationModal); } void QuickAccessConfigDialog::doLayout() { QVBoxLayout* allActionsLayout = new QVBoxLayout(); allActionsLayout->addWidget(new QLabel(tr("All Actions"), this)); allActionsLayout->addWidget(m_pFilterEdit); allActionsLayout->addWidget(m_pAllActionListWidget); QVBoxLayout* buttonLayout = new QVBoxLayout(); buttonLayout->setObjectName(QString::fromUtf8("verticalLayout")); buttonLayout->addItem(new QSpacerItem(20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding)); buttonLayout->addWidget(m_pAddButton); buttonLayout->addWidget(m_pRemoveButton); buttonLayout->addItem(new QSpacerItem(20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding)); QVBoxLayout* quickAccessConfigLayout = new QVBoxLayout(); quickAccessConfigLayout->addWidget(new QLabel(tr("Quick Access Bar Actions"), this)); quickAccessConfigLayout->addWidget(m_pQuickAccessActionsListWidget); QVBoxLayout* moveUpDownLayout = new QVBoxLayout(); moveUpDownLayout->addItem(new QSpacerItem(20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding)); moveUpDownLayout->addWidget(m_pMoveUpButton); moveUpDownLayout->addWidget(m_pMoveDownButton); moveUpDownLayout->addItem(new QSpacerItem(20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding)); QGridLayout* mainLayout = new QGridLayout(this); mainLayout->addLayout(allActionsLayout, 0, 0, 1, 1); mainLayout->addLayout(buttonLayout, 0, 1, 1, 1); mainLayout->addLayout(quickAccessConfigLayout, 0, 2, 1, 1); mainLayout->addLayout(moveUpDownLayout, 0, 3, 1, 1); mainLayout->addWidget(m_dialogButtonBox, 1, 0, 1, 4); } bool QuickAccessConfigDialog::eventFilter(QObject* watched, QEvent* evt) { if (evt->type() == QEvent::ActionAdded) { QAction* action = static_cast(evt)->action(); m_pAllActionListWidget->addAction(action); m_pAllActionListWidget->setFilter(m_pFilterEdit->text()); m_pAllActionListWidget->sort(); if (RobloxQuickAccessConfig::singleton().availableInQuickAccessBar(action)) { QAction* actionToAdd = RobloxQuickAccessConfig::singleton().getProxyAction(action); if (!actionToAdd) actionToAdd = action; m_pQuickAccessActionsListWidget->addAction(actionToAdd); } } else if (evt->type() == QEvent::ActionRemoved) { QAction* action = static_cast(evt)->action(); m_pAllActionListWidget->removeAction(action); if (RobloxQuickAccessConfig::singleton().availableInQuickAccessBar(action)) { QAction* isProxyAction = RobloxQuickAccessConfig::singleton().getProxyAction(action); if (!isProxyAction) { m_pQuickAccessActionsListWidget->removeAction(action); } else { RobloxQuickAccessConfig::singleton().resetProxyAction(action); } } } return QDialog::eventFilter(watched, evt); } void QuickAccessConfigDialog::showEvent(QShowEvent* evt) { // reinitialize quickaccess list m_pQuickAccessActionsListWidget->initialize(); // update all actions list m_pAllActionListWidget->setFilter(m_pFilterEdit->text()); m_pAllActionListWidget->sort(); // set default focus on cancel button if (m_dialogButtonBox->button(QDialogButtonBox::Cancel)) m_dialogButtonBox->button(QDialogButtonBox::Cancel)->setFocus(); //now call base class function QDialog::showEvent(evt); } void QuickAccessConfigDialog::onAddButtonClicked() { ActionItem* pActionItem = static_cast(m_pAllActionListWidget->currentItem()); if (!pActionItem) return; // check if selection action is already available in quick access bar if (m_pQuickAccessActionsListWidget->isActionAvailable(pActionItem->getAction())) { QtUtilities::RBXMessageBox msgBox; msgBox.setText("The selected action is already available in Quick Access Bar.\nPlease try to add a different action."); msgBox.exec(); return; } // map action if it's PluginAction QAction* actionToAdd = RobloxQuickAccessConfig::singleton().mapProxyAction(pActionItem->getAction(), true); if (!actionToAdd) actionToAdd = pActionItem->getAction(); // add updated action in quick access list, also select the next item in all actions list m_pQuickAccessActionsListWidget->addAction(actionToAdd); m_pQuickAccessActionsListWidget->selectItem(m_pQuickAccessActionsListWidget->topLevelItemCount() - 1); m_pAllActionListWidget->selectItem(m_pAllActionListWidget->indexOfTopLevelItem(pActionItem) + 1); m_dataChanged = true; } void QuickAccessConfigDialog::onRemoveButtonClicked() { // remove current selected item m_pQuickAccessActionsListWidget->removeItem(m_pQuickAccessActionsListWidget->currentItem()); m_dataChanged = true; } void QuickAccessConfigDialog::onMoveButtonClicked() { // move up/down current selected item m_pQuickAccessActionsListWidget->moveItem(m_pQuickAccessActionsListWidget->currentItem(), sender() == m_pMoveUpButton); m_dataChanged = true; } void QuickAccessConfigDialog::restoreDefaults(QAbstractButton* button) { if (m_dialogButtonBox->button(QDialogButtonBox::RestoreDefaults) == button) RobloxQuickAccessConfig::singleton().restoreDefaults(); } void QuickAccessConfigDialog::cancel() { if (m_dataChanged) { QtUtilities::RBXMessageBox msgBox; msgBox.setText("You have made changes to your settings.\nDo you want to keep your changes?"); msgBox.setStandardButtons(QMessageBox::Ok | QMessageBox::Discard); msgBox.setDefaultButton(QMessageBox::Ok); int ret = msgBox.exec(); // recreate quick access bar on OK, cancel will be taken care in showEvent if (ret == QMessageBox::Ok) m_pQuickAccessActionsListWidget->accept(); } m_dataChanged = false; hide(); } void QuickAccessConfigDialog::accept() { if (m_dataChanged) { m_pQuickAccessActionsListWidget->accept(); m_dataChanged = false; } hide(); } void QuickAccessConfigDialog::onQuickAccessListDataChanged() { m_dataChanged = true; } void QuickAccessConfigDialog::onDefaultsRestored() { m_pQuickAccessActionsListWidget->initialize(); m_dataChanged = false; } void QuickAccessConfigDialog::setFilter(QString filter) { m_pAllActionListWidget->setFilter(filter); } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // RobloxQuickAccessConfig //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// RobloxQuickAccessConfig& RobloxQuickAccessConfig::singleton() { static RobloxQuickAccessConfig* robloxQuickAccessConfig = new RobloxQuickAccessConfig(); return *robloxQuickAccessConfig; } RobloxQuickAccessConfig::RobloxQuickAccessConfig() : m_pMainWindow(&UpdateUIManager::Instance().getMainWindow()) , m_pluginHostInitialized(false) { m_pQuickAccessBar = m_pMainWindow->ribbonBar() ? m_pMainWindow->ribbonBar()->getQuickAccessBar() : NULL; connect(m_pQuickAccessBar, SIGNAL(showCustomizeMenu(QMenu*)), this, SLOT(updateQuickAccessMenu(QMenu*))); QActionGroup* pActionGroup = m_pQuickAccessBar->findChild(); if (pActionGroup) connect(pActionGroup, SIGNAL(triggered(QAction*)), this, SLOT(updateActionVisiblity(QAction*))); // HACK to show "Customize..." action in extension menu! QList toolButtons = m_pQuickAccessBar->findChildren(); for (int jj = 0; jj < toolButtons.size(); ++jj) { QToolButton* button = toolButtons.at(jj); if (button->objectName() == "qt_toolbar_ext_button" && button->menu()) { connect(button->menu(), SIGNAL(aboutToShow()), this, SLOT(onExtensionMenuShow())); connect(button->menu(), SIGNAL(aboutToHide()), this, SLOT(onExtensionMenuHide())); break; } } } void RobloxQuickAccessConfig::updateQuickAccessMenu(QMenu* pMenu) { pMenu->addSeparator()->setText(tr("Advanced Customization")); pMenu->addAction(m_pMainWindow->customizeQuickAccessAction); } void RobloxQuickAccessConfig::updateActionVisiblity(QAction* action) { QuickAccessActionCollection::iterator iter = m_currentQuickAccessBarActions.begin(); while (iter != m_currentQuickAccessBarActions.end()) { iter->isVisible = m_pQuickAccessBar->isActionVisible(iter->action); ++iter; } } void RobloxQuickAccessConfig::onExtensionMenuShow() { QMenu* menu = qobject_cast(sender()); if (menu) { m_pSeparationAction = menu->addSeparator(); menu->addAction(m_pMainWindow->customizeQuickAccessAction); } } void RobloxQuickAccessConfig::onExtensionMenuHide() { QMenu* menu = qobject_cast(sender()); if (menu) { menu->removeAction(m_pSeparationAction); menu->removeAction(m_pMainWindow->customizeQuickAccessAction); } } void RobloxQuickAccessConfig::storeDefaults(const QuickAccessActionCollection& defaultQuickAccessBarActions) { m_defaultQuickAccessBarActions = defaultQuickAccessBarActions; } void RobloxQuickAccessConfig::restoreDefaults() { recreateQuickAccessBar(m_defaultQuickAccessBarActions); Q_EMIT defaultsRestored(); } void RobloxQuickAccessConfig::recreateQuickAccessBar(const QuickAccessActionCollection& quickAccessBarActions) { m_pQuickAccessBar->setUpdatesEnabled(false); // first remove all existing actions from Quick Access bar QList proxyActions; QuickAccessActionCollection::iterator quickAccessActionIter = m_currentQuickAccessBarActions.begin(); while (quickAccessActionIter != m_currentQuickAccessBarActions.end()) { if (qobject_cast(quickAccessActionIter->action)) proxyActions.append(quickAccessActionIter->action); removeFromQuickAccessBar(quickAccessActionIter->action); ++quickAccessActionIter; } m_currentQuickAccessBarActions.clear(); // now add actions QuickAccessActionCollection::const_iterator iter = quickAccessBarActions.constBegin(); while (iter != quickAccessBarActions.constEnd()) { // if the action is initialized then directly add to quickaccess bar if (iter->isInitialized) { addToQuickAccessBar(iter->action, iter->isVisible); } // or else wait till it get initialized else { m_currentQuickAccessBarActions.append(*iter); } if (qobject_cast(iter->action)) proxyActions.removeAll(iter->action); ++iter; } // delete unutilized proxy actions QList::iterator proxyActionsIter = proxyActions.begin(); while (proxyActionsIter != proxyActions.end()) { delete *proxyActionsIter; ++proxyActionsIter; } m_pQuickAccessBar->setUpdatesEnabled(true); } bool RobloxQuickAccessConfig::loadQuickAccessConfig() { QSettings settings; int size = settings.beginReadArray(QuickAccessConfigKey); if (size < 1) { settings.endArray(); return false; } QString objectName; for (int i = 0; i < size; ++i) { settings.setArrayIndex(i); objectName = settings.value(QuickAccessActionNameKey).toString(); bool visibility = settings.value(QuickAccessActionVisibleKey, true).toBool(); QAction* pAction = m_pMainWindow->findChild(objectName); PluginAction* pPluginAction = dynamic_cast(pAction); if (pPluginAction || !pAction) { QuickAccessBarProxyAction* pProxyAction = new QuickAccessBarProxyAction(m_pMainWindow); pProxyAction->setObjectName(objectName); pProxyAction->setText(settings.value(QuickAccessActionTextKey).toString()); pProxyAction->setShortcut(settings.value(QuickAccessActionShortcutKey).toString()); // do not add to quick access bar instead just have it in local data m_currentQuickAccessBarActions.append(ActionData(pProxyAction, visibility)); } else if (FFlag::StudioSeparateActionByActivationMethod) { QuickAccessBarProxyAction* pProxyAction = new QuickAccessBarProxyAction(m_pMainWindow); pProxyAction->setObjectName(objectName); pProxyAction->setText(settings.value(QuickAccessActionTextKey).toString()); pProxyAction->setShortcut(pAction->shortcut().toString()); pProxyAction->setSourceAction(pAction); addToQuickAccessBar(pProxyAction, visibility); } else { addToQuickAccessBar(pAction, visibility); } } settings.endArray(); return true; } void RobloxQuickAccessConfig::saveQuickAccessConfig() { // remove all settings data for quick access QSettings settings; settings.beginWriteArray(QuickAccessConfigKey); settings.remove(""); settings.endArray(); // recreate new data settings.beginWriteArray(QuickAccessConfigKey); int index = 0; QAction* action = NULL; QuickAccessActionCollection::const_iterator iter = m_currentQuickAccessBarActions.constBegin(); while (iter != m_currentQuickAccessBarActions.constEnd()) { action = iter->action.data(); if (action && (iter->isInitialized || !m_pluginHostInitialized)) { settings.setArrayIndex(index++); settings.setValue(QuickAccessActionNameKey, action->objectName()); settings.setValue(QuickAccessActionVisibleKey, (bool)iter->isVisible); if (iter->isProxyAction()) { settings.setValue(QuickAccessActionTextKey, action->text()); settings.setValue(QuickAccessActionShortcutKey, action->shortcut().toString()); } } ++iter; } settings.endArray(); } void RobloxQuickAccessConfig::addToQuickAccessBar(QAction* actionToAdd, bool visibility) { if (!actionToAdd || !m_pQuickAccessBar) return; QAction* pActionBefore = NULL; // check if we can update already existing proxy action or else add data if (!updateProxyAction(actionToAdd)) { m_currentQuickAccessBarActions.append(ActionData(actionToAdd, visibility, true)); } else { bool actionFound = false; QuickAccessActionCollection::iterator iter = m_currentQuickAccessBarActions.begin(); while (iter != m_currentQuickAccessBarActions.end()) { if (actionFound && iter->isInitialized && iter->action) { pActionBefore = iter->action; break; } if (iter->action == actionToAdd) actionFound = true; ++iter; } } pActionBefore ? m_pQuickAccessBar->insertAction(pActionBefore, actionToAdd) : m_pQuickAccessBar->addAction(actionToAdd); if (m_pQuickAccessBar->isActionVisible(actionToAdd) != visibility) m_pQuickAccessBar->setActionVisible(actionToAdd, visibility); Q_EMIT actionAdded(actionToAdd); } void RobloxQuickAccessConfig::removeFromQuickAccessBar(QAction* actionToRemove) { if (!actionToRemove || !m_pQuickAccessBar) return; QuickAccessActionCollection::iterator iter = m_currentQuickAccessBarActions.begin(); while (iter != m_currentQuickAccessBarActions.end()) { if (iter->action == actionToRemove) { m_currentQuickAccessBarActions.erase(iter); break; } ++iter; } // remove all instances of action i.e. remove from both quick access bar and menu Q_EMIT actionRemoved(actionToRemove); m_pQuickAccessBar->removeAllInstances(actionToRemove); } QAction* RobloxQuickAccessConfig::mapProxyAction(QAction* action, bool force) { // proxy actions are used by PluginAction only PluginAction* pluginAction = dynamic_cast(action); if (!pluginAction && (!FFlag::StudioSeparateActionByActivationMethod || !force)) return NULL; QuickAccessBarProxyAction* pProxyAction = dynamic_cast(getProxyAction(action)); if (!pProxyAction && !force) return NULL; if (!pProxyAction) { // if no proxy action is available then create new pProxyAction = new QuickAccessBarProxyAction(m_pMainWindow); pProxyAction->setObjectName(action->objectName() + QuickAccessProxyActionExt); } // update source action pProxyAction->setSourceAction(action); return pProxyAction; } QAction* RobloxQuickAccessConfig::resetProxyAction(QAction* action) { QuickAccessBarProxyAction* pProxyAction = dynamic_cast(getProxyAction(action)); if (!pProxyAction) return NULL; pProxyAction->setSourceAction(NULL); return pProxyAction; } bool RobloxQuickAccessConfig::updateProxyAction(QAction* action) { QuickAccessActionCollection::iterator iter = m_currentQuickAccessBarActions.begin(); while (iter != m_currentQuickAccessBarActions.end()) { if (iter->action == action) { iter->isInitialized = true; return true; } ++iter; } return false; } bool RobloxQuickAccessConfig::visibleInQuickAccessBar(QAction* action) { QAction* actionToLookFor = getProxyAction(action); if (!actionToLookFor) actionToLookFor = action; QuickAccessActionCollection::const_iterator iter = m_currentQuickAccessBarActions.constBegin(); while (iter != m_currentQuickAccessBarActions.constEnd()) { if (iter->action == actionToLookFor) return iter->isVisible; ++iter; } return false; } bool RobloxQuickAccessConfig::availableInQuickAccessBar(QAction* action) { QAction* actionToLookFor = getProxyAction(action); if (!actionToLookFor) actionToLookFor = action; QuickAccessActionCollection::const_iterator iter = m_currentQuickAccessBarActions.constBegin(); while (iter != m_currentQuickAccessBarActions.constEnd()) { if (iter->action == actionToLookFor) return true; ++iter; } return false; } QAction* RobloxQuickAccessConfig::getProxyAction(QAction* action) { QString proxyActObjName = action->objectName() + QuickAccessProxyActionExt; QString actionText = action->text(); QList proxyActions = m_pMainWindow->findChildren(); for (int ii = 0; ii < proxyActions.size(); ++ii) { if ((proxyActions[ii]->text() == actionText) && (proxyActions[ii]->objectName() == proxyActObjName)) return proxyActions[ii]; } return NULL; } bool RobloxQuickAccessConfig::ActionData::isProxyAction() const { return dynamic_cast(action.data()); } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // QuickAccessBarProxyAction //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// QuickAccessBarProxyAction::QuickAccessBarProxyAction(QObject* parent) : QAction(parent) , m_pCurrentSourceAction(NULL) { onChanged(); } void QuickAccessBarProxyAction::setSourceAction(QAction* pSourceAction) { if ((m_pCurrentSourceAction != pSourceAction) || !pSourceAction) { if (m_pCurrentSourceAction) { disconnect(this, SIGNAL(triggered()), m_pCurrentSourceAction, SLOT(trigger())); disconnect(m_pCurrentSourceAction, SIGNAL(changed()), this, SLOT(onChanged())); } m_pCurrentSourceAction = pSourceAction; if (pSourceAction) { m_pSourceActions.push(pSourceAction); setCheckable(pSourceAction->isCheckable()); pSourceAction->setShortcut(shortcut()); setText(pSourceAction->text()); setIcon(pSourceAction->icon()); if (FFlag::StudioSeparateActionByActivationMethod) { setShortcut(QKeySequence()); connect(this, SIGNAL(triggered()), this, SLOT(onActionTriggered())); } else { connect(this, SIGNAL(triggered()), pSourceAction, SLOT(trigger())); } connect(pSourceAction, SIGNAL(changed()), this, SLOT(onChanged())); connect(pSourceAction, SIGNAL(destroyed(QObject*)), this, SLOT(onSourceActionDestroyed(QObject*))); } } onChanged(); } void QuickAccessBarProxyAction::onActionTriggered() { if (!UpdateUIManager::Instance().getMainWindow().commonSlotQuickAccess(m_pCurrentSourceAction)) { UpdateUIManager::Instance().getMainWindow().setActionCounterSendingEnabled(false); m_pCurrentSourceAction->trigger(); UpdateUIManager::Instance().getMainWindow().setActionCounterSendingEnabled(true); } } bool QuickAccessBarProxyAction::hasAction(QAction* action) { for (QStack::iterator i = m_pSourceActions.begin(); i != m_pSourceActions.end(); ++i) { if (*i == action) return true; } return false; } void QuickAccessBarProxyAction::onChanged() { setEnabled(m_pCurrentSourceAction ? m_pCurrentSourceAction->isEnabled() : false); setChecked(m_pCurrentSourceAction ? m_pCurrentSourceAction->isChecked() : false); setVisible(m_pCurrentSourceAction ? m_pCurrentSourceAction->isVisible() : true); if (m_pCurrentSourceAction) setIcon(m_pCurrentSourceAction->icon()); } void QuickAccessBarProxyAction::onSourceActionDestroyed(QObject* object) { bool currentDestroyed = (object == m_pSourceActions.top()); for (QStack::iterator i = m_pSourceActions.begin(); i != m_pSourceActions.end(); ++i) { if (*i == object) { m_pSourceActions.erase(i); break; } } if (currentDestroyed) { m_pCurrentSourceAction = NULL; QAction* sourceAction = NULL; if (!m_pSourceActions.isEmpty()) { sourceAction = m_pSourceActions.pop(); disconnect(sourceAction, SIGNAL(destroyed(QObject*)), this, SLOT(onSourceActionDestroyed(QObject*))); } setSourceAction(sourceAction); } }