8#include "kacceleratormanager.h"
9#include "kacceleratormanager_p.h"
11#include <QApplication>
21#include <QMetaProperty>
24#include <QRadioButton>
25#include <QStackedWidget>
30#include "common_helpers_p.h"
31#include "loggingcategory.h"
42bool KAcceleratorManagerPrivate::programmers_mode =
false;
43QString KAcceleratorManagerPrivate::changed_string;
44QString KAcceleratorManagerPrivate::added_string;
45QString KAcceleratorManagerPrivate::removed_string;
47QStringList KAcceleratorManagerPrivate::standardNames;
49void KAcceleratorManagerPrivate::addStandardActionNames(
const QStringList &list)
51 standardNames.
append(list);
54bool KAcceleratorManagerPrivate::standardName(
const QString &str)
56 return standardNames.contains(str);
59KAcceleratorManagerPrivate::Item::~Item()
62 while (!m_children->isEmpty()) {
63 delete m_children->takeFirst();
69void KAcceleratorManagerPrivate::Item::addChild(
Item *item)
72 m_children =
new ItemList;
75 m_children->append(item);
78void KAcceleratorManagerPrivate::manage(
QWidget *widget)
81 qCDebug(KWidgetsAddonsLog) <<
"null pointer given to manage";
85 if (KAcceleratorManagerPrivate::ignored_widgets.contains(widget)) {
89 if (qobject_cast<QMenu *>(widget)) {
91 KPopupAccelManager::manage(
static_cast<QMenu *
>(widget));
98 manageWidget(widget, root, used);
99 calculateAccelerators(root, used);
103void KAcceleratorManagerPrivate::calculateAccelerators(
Item *item,
QString &used)
105 if (!item->m_children) {
110 KAccelStringList contents;
111 contents.reserve(item->m_children->
size());
112 for (
Item *it : std::as_const(*item->m_children)) {
113 contents << it->m_content;
117 KAccelManagerAlgorithm::findAccelerators(contents, used);
121 for (
Item *it : std::as_const(*item->m_children)) {
124 QDockWidget *dock = qobject_cast<QDockWidget *>(it->m_widget);
126 if (checkChange(contents[cnt])) {
131 QTabBar *tabBar = qobject_cast<QTabBar *>(it->m_widget);
133 if (checkChange(contents[cnt])) {
134 tabBar->
setTabText(it->m_index, contents[cnt].accelerated());
138 QMenuBar *menuBar = qobject_cast<QMenuBar *>(it->m_widget);
140 if (it->m_index >= 0) {
143 checkChange(contents[cnt]);
144 maction->
setText(contents[cnt].accelerated());
151 QGroupBox *groupBox = qobject_cast<QGroupBox *>(it->m_widget);
156 int tprop = it->m_widget->metaObject()->indexOfProperty(
"text");
158 if (checkChange(contents[cnt])) {
159 it->m_widget->setProperty(
"text", contents[cnt].accelerated());
162 tprop = it->m_widget->metaObject()->indexOfProperty(
"title");
163 if (tprop != -1 && checkChange(contents[cnt])) {
164 it->m_widget->setProperty(
"title", contents[cnt].accelerated());
170 for (
Item *it : std::as_const(*item->m_children)) {
171 if (it->m_widget && it->m_widget->isVisibleTo(item->m_widget)) {
172 calculateAccelerators(it, used);
177void KAcceleratorManagerPrivate::traverseChildren(
QWidget *widget,
Item *item,
QString &used)
182 if (!w->isVisibleTo(widget) || (w->isWindow() && qobject_cast<QMenu *>(w) ==
nullptr)) {
186 if (KAcceleratorManagerPrivate::ignored_widgets.contains(w)) {
190 manageWidget(w, item, used);
200 for (
QAction *action : widgetActions) {
204 const QStringList splitSequence = sequenceAsText.
split(QStringLiteral(
", "));
205 for (
const QString &shortcut : splitSequence) {
206 if (
shortcut.length() == 5 &&
shortcut.startsWith(QStringLiteral(
"Alt+"))) {
215 QTabBar *tabBar = qobject_cast<QTabBar *>(w);
217 manageTabBar(tabBar, item);
223 QWidgetStackAccelManager::manage(wds);
227 QDockWidget *dock = qobject_cast<QDockWidget *>(w);
230 manageDockWidget(dock, item);
233 QMenu *popupMenu = qobject_cast<QMenu *>(w);
236 KPopupAccelManager::manage(popupMenu);
242 QWidgetStackAccelManager::manage(wdst);
246 QMenuBar *menuBar = qobject_cast<QMenuBar *>(w);
248 manageMenuBar(menuBar, item);
252 if (qobject_cast<QComboBox *>(w) || qobject_cast<QLineEdit *>(w)
254 || qobject_cast<QTextEdit *>(w)
255 || qobject_cast<QAbstractSpinBox *>(w)
257 || w->
inherits(
"qdesigner_internal::TextPropertyEditor")) {
262 traverseChildren(w, item, used);
269 if (!
label->buddy()) {
279 if (w->
focusPolicy() !=
Qt::NoFocus || label || qobject_cast<QGroupBox *>(w) || qobject_cast<QRadioButton *>(w)) {
311 int weight = KAccelManagerAlgorithm::DEFAULT_WEIGHT;
312 if (qobject_cast<QPushButton *>(w) || qobject_cast<QCheckBox *>(w) || qobject_cast<QRadioButton *>(w) || qobject_cast<QLabel *>(w)) {
313 weight = KAccelManagerAlgorithm::ACTION_ELEMENT_WEIGHT;
318 QGroupBox *groupBox = qobject_cast<QGroupBox *>(w);
321 weight = KAccelManagerAlgorithm::CHECKABLE_GROUP_BOX_WEIGHT;
323 weight = KAccelManagerAlgorithm::GROUP_BOX_WEIGHT;
327 i->m_content = KAccelString(content, weight);
331 traverseChildren(w, item, used);
334void KAcceleratorManagerPrivate::manageTabBar(
QTabBar *bar,
Item *item)
341 QMainWindow *mainWindow = qobject_cast<QMainWindow *>(parentWidget);
348 for (
int i = 0; i < bar->
count(); i++) {
358 it->m_content = KAccelString(content);
362void KAcceleratorManagerPrivate::manageDockWidget(
QDockWidget *dock,
Item *item)
382 it->m_content = KAccelString(content, KAccelManagerAlgorithm::STANDARD_ACCEL);
385void KAcceleratorManagerPrivate::manageMenuBar(
QMenuBar *mbar,
Item *item)
390 for (
int i = 0; i < mbar->
actions().count(); ++i) {
405 it->m_content = KAccelString(s,
406 KAccelManagerAlgorithm::MENU_TITLE_WEIGHT);
413 if (maction->
menu()) {
414 KPopupAccelManager::manage(maction->
menu());
429 KAcceleratorManagerPrivate::changed_string.
clear();
430 KAcceleratorManagerPrivate::added_string.
clear();
431 KAcceleratorManagerPrivate::removed_string.
clear();
432 KAcceleratorManagerPrivate::programmers_mode = programmers_mode;
433 KAcceleratorManagerPrivate::manage(widget);
438 added = KAcceleratorManagerPrivate::added_string;
439 changed = KAcceleratorManagerPrivate::changed_string;
440 removed = KAcceleratorManagerPrivate::removed_string;
449KAccelString::KAccelString(
const QString &input,
int initialWeight)
454 if (m_orig_accel != -1) {
455 m_pureText.remove(m_orig_accel, 4);
459 if (m_orig_accel != -1) {
460 m_pureText.replace(m_orig_accel, 4, QStringLiteral(
"&"));
463 m_origText = m_pureText;
465 const int tabPos = m_pureText.indexOf(
QLatin1Char(
'\t'));
467 m_pureText.truncate(tabPos);
470 m_orig_accel = m_accel = stripAccelerator(m_pureText);
472 if (initialWeight == -1) {
473 initialWeight = KAccelManagerAlgorithm::DEFAULT_WEIGHT;
476 calculateWeights(initialWeight);
481QString KAccelString::accelerated()
const
488 if (KAcceleratorManagerPrivate::programmers_mode) {
489 if (m_accel != m_orig_accel) {
490 int oa = m_orig_accel;
494 if (m_accel < m_orig_accel) {
498 if (m_orig_accel >= 0) {
499 result.
replace(oa, 1, QStringLiteral(
"(&&)"));
503 if (m_accel >= 0 && m_orig_accel != m_accel) {
504 if (m_orig_accel != -1) {
505 result.
remove(m_orig_accel, 1);
513QChar KAccelString::accelerator()
const
515 if ((m_accel < 0) || (m_accel > m_pureText.length())) {
519 return m_pureText[m_accel].
toLower();
522void KAccelString::calculateWeights(
int initialWeight)
524 m_weight.resize(m_pureText.length());
527 bool start_character =
true;
529 while (pos < m_pureText.length()) {
530 QChar c = m_pureText[pos];
532 int weight = initialWeight + 1;
536 weight += KAccelManagerAlgorithm::FIRST_CHARACTER_EXTRA_WEIGHT;
540 if (start_character) {
541 weight += KAccelManagerAlgorithm::WORD_BEGINNING_EXTRA_WEIGHT;
542 start_character =
false;
547 weight += (50 - pos);
551 if (pos == accel()) {
552 weight += KAccelManagerAlgorithm::WANTED_ACCEL_EXTRA_WEIGHT;
554 if (KAcceleratorManagerPrivate::standardName(m_origText)) {
555 weight += KAccelManagerAlgorithm::STANDARD_ACCEL;
562 start_character =
true;
565 m_weight[pos] = weight;
571int KAccelString::stripAccelerator(
QString &text)
579 if (p <= 0 || p >= text.
length()) {
597int KAccelString::maxWeight(
int &index,
const QString &used)
const
602 for (
int pos = 0; pos < m_pureText.length(); ++pos) {
604 if (m_weight[pos] > max) {
614void KAccelString::dump()
617 for (
int i = 0; i < m_weight.count(); ++i) {
618 s += QStringLiteral(
"%1(%2) ").
arg(pure()[i]).
arg(m_weight[i]);
620 qCDebug(KWidgetsAddonsLog) <<
"s " << s;
656void KAccelManagerAlgorithm::findAccelerators(KAccelStringList &result,
QString &used)
658 KAccelStringList accel_strings = result;
661 for (KAccelStringList::Iterator it = result.begin(), total = result.end(); it != total; ++it) {
666 for (
int cnt = 0; cnt < accel_strings.count(); ++cnt) {
672 for (
int i = 0; i < accel_strings.count(); ++i) {
674 int m = accel_strings[i].maxWeight(a, used);
689 result[index].setAccel(accel);
690 used.
append(result[index].accelerator());
694 accel_strings[index] = KAccelString();
704KPopupAccelManager::KPopupAccelManager(
QMenu *popup)
713void KPopupAccelManager::aboutToShow()
720 if (m_count != m_popup->actions().count()) {
721 findMenuEntries(m_entries);
722 calculateAccelerators();
723 m_count = m_popup->actions().count();
725 KAccelStringList entries;
726 findMenuEntries(entries);
727 if (entries != m_entries) {
729 calculateAccelerators();
734void KPopupAccelManager::calculateAccelerators()
738 KAccelManagerAlgorithm::findAccelerators(m_entries, used);
741 setMenuEntries(m_entries);
744void KPopupAccelManager::findMenuEntries(KAccelStringList &list)
751 const auto menuActions = m_popup->actions();
752 for (
QAction *maction : menuActions) {
768 if (maction->
menu()) {
769 KPopupAccelManager::manage(maction->
menu());
778 for (
int i = 0; i < s.
size(); ++i) {
786void KPopupAccelManager::setMenuEntries(
const KAccelStringList &list)
789 const auto menuActions = m_popup->actions();
790 for (
QAction *maction : menuActions) {
799 if (iconText == copy_of_qt_strippedText(oldText)) {
800 iconText = removeAcceleratorMarker(oldText);
805 if (iconText != maction->
iconText()) {
810 if (KAcceleratorManagerPrivate::checkChange(list[cnt])) {
811 maction->
setText(list[cnt].accelerated());
817void KPopupAccelManager::manage(
QMenu *popup)
821 new KPopupAccelManager(popup);
828 new QWidgetStackAccelManager(stack);
832QWidgetStackAccelManager::QWidgetStackAccelManager(
QStackedWidget *stack)
840bool QWidgetStackAccelManager::eventFilter(
QObject *watched,
QEvent *e)
849void QWidgetStackAccelManager::currentChanged(
int child)
851 if (child < 0 || child >=
static_cast<QStackedWidget *
>(parent())->count()) {
861 KAcceleratorManagerPrivate::ignored_widgets[widget] = 1;
866 KAcceleratorManagerPrivate::addStandardActionNames(names);
869#include "moc_kacceleratormanager_p.cpp"
static void last_manage(QString &added, QString &changed, QString &removed)
static void setNoAccel(QWidget *widget)
Use this method for a widget (and its children) you want no accels to be set on.
static void manage(QWidget *widget, bool programmers_mode=false)
Manages the accelerators of a widget.
static void addStandardActionNames(const QStringList &names)
Append names to a list of standard action names.
KIOCORE_EXPORT QStringList list(const QString &fileClass)
QString label(StandardShortcut id)
const QList< QKeySequence > & shortcut(StandardShortcut id)
bool isSeparator() const const
void setText(const QString &text)
bool isLetterOrNumber(char32_t ucs4)
bool isPrint(char32_t ucs4)
char32_t toLower(char32_t ucs4)
bool isCheckable() const const
void append(QList< T > &&value)
T findChild(const QString &name, Qt::FindChildOptions options) const const
QList< T > findChildren(Qt::FindChildOptions options) const const
bool inherits(const char *className) const const
void installEventFilter(QObject *filterObj)
void removeEventFilter(QObject *obj)
QSizeF size() const const
QString & append(QChar ch)
QString arg(Args &&... args) const const
const QChar at(qsizetype position) const const
bool contains(QChar ch, Qt::CaseSensitivity cs) const const
qsizetype indexOf(QChar ch, qsizetype from, Qt::CaseSensitivity cs) const const
QString & insert(qsizetype position, QChar ch)
bool isEmpty() const const
qsizetype length() const const
QString & remove(QChar ch, Qt::CaseSensitivity cs)
QString & replace(QChar before, QChar after, Qt::CaseSensitivity cs)
qsizetype size() const const
QStringList split(QChar sep, Qt::SplitBehavior behavior, Qt::CaseSensitivity cs) const const
QString trimmed() const const
bool mightBeRichText(const QString &text)
void setTabText(int index, const QString &text)
QString tabText(int index) const const
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
bool isValid() const const
QString toString() const const