diff --git a/autotests/kacceleratormanagertest.cpp b/autotests/kacceleratormanagertest.cpp index 91fec5a..cb4431d 100644 --- a/autotests/kacceleratormanagertest.cpp +++ b/autotests/kacceleratormanagertest.cpp @@ -1,117 +1,118 @@ /* This file is part of the KDE libraries * * Copyright (c) 2017 David Faure * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA */ #include #include #include #include #define QSL QStringLiteral static QStringList extractActionTexts(QMenu &menu, std::function func) { menu.aboutToShow(); // signals are now public in Qt5, how convenient :-) QStringList ret; bool lastIsSeparator = false; - foreach (const QAction *action, menu.actions()) { + const auto menuActions = menu.actions(); + for (const QAction *action : menuActions) { if (action->isSeparator()) { if (!lastIsSeparator) { // Qt gets rid of duplicate separators, so we should too ret.append(QStringLiteral("separator")); } lastIsSeparator = true; } else { lastIsSeparator = false; if (action->menu()) { ret.append(QStringLiteral("submenu")); } else { const QString text = func(action); ret.append(text); } } } return ret; } class KAcceleratorManagerTest : public QObject { Q_OBJECT private Q_SLOTS: void initTestCase() { } void testActionTexts_data() { QTest::addColumn("initialTexts"); QTest::addColumn("expectedTexts"); QTest::newRow("basic") << QStringList{QSL("Open"), QSL("Close"), QSL("Quit")} << QStringList{QSL("&Open"), QSL("&Close"), QSL("&Quit")}; QTest::newRow("same_first") << QStringList{QSL("Open"), QSL("Close"), QSL("Clone"), QSL("Quit")} << QStringList{QSL("&Open"), QSL("&Close"), QSL("C&lone"), QSL("&Quit")}; QTest::newRow("cjk") << QStringList{QSL("Open (&O)"), QSL("Close (&C)"), QSL("Clone (&C)"), QSL("Quit (&Q)")} << QStringList{QSL("Open (&O)"), QSL("Close (&C)"), QSL("C&lone (C)"), QSL("Quit (&Q)")}; } void testActionTexts() { // GIVEN QFETCH(QStringList, initialTexts); QFETCH(QStringList, expectedTexts); QMenu menu; for (const QString &text : qAsConst(initialTexts)) { menu.addAction(text); } // WHEN KAcceleratorManager::manage(&menu); // THEN const QStringList texts = extractActionTexts(menu, &QAction::text); QCOMPARE(texts, expectedTexts); } void testActionIconTexts_data() { QTest::addColumn("initialTexts"); QTest::addColumn("expectedTexts"); QTest::newRow("basic") << QStringList{QSL("Open"), QSL("Close"), QSL("Quit")} << QStringList{QSL("Open"), QSL("Close"), QSL("Quit")}; QTest::newRow("same_first") << QStringList{QSL("Open"), QSL("Close"), QSL("Clone"), QSL("Quit")} << QStringList{QSL("Open"), QSL("Close"), QSL("Clone"), QSL("Quit")}; QTest::newRow("cjk") << QStringList{QSL("Open (&O)"), QSL("Close (&C)"), QSL("Clone (&C)"), QSL("Quit (&Q)")} << QStringList{QSL("Open"), QSL("Close"), QSL("Clone"), QSL("Quit")}; } void testActionIconTexts() { // GIVEN QFETCH(QStringList, initialTexts); QFETCH(QStringList, expectedTexts); QMenu menu; for (const QString &text : qAsConst(initialTexts)) { menu.addAction(text); } // WHEN KAcceleratorManager::manage(&menu); // THEN const QStringList iconTexts = extractActionTexts(menu, &QAction::iconText); QCOMPARE(iconTexts, expectedTexts); } }; QTEST_MAIN(KAcceleratorManagerTest) #include "kacceleratormanagertest.moc" diff --git a/autotests/kdatetimeedittest.cpp b/autotests/kdatetimeedittest.cpp index f14a4fe..f2bfc24 100644 --- a/autotests/kdatetimeedittest.cpp +++ b/autotests/kdatetimeedittest.cpp @@ -1,257 +1,259 @@ /* Copyright 2011 John Layt This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kdatetimeedittest.h" #include #include #include #include #include #include "kdatetimeedit.h" #include "kdatecombobox.h" QTEST_MAIN(KDateTimeEditTest) void KDateTimeEditTest::testDefaults() { m_edit = new KDateTimeEdit(nullptr); QCOMPARE(m_edit->dateTime(), QDateTime(QDate::currentDate(), QTime(0, 0, 0))); QCOMPARE(m_edit->date(), QDate::currentDate()); QCOMPARE(m_edit->time(), QTime(0, 0, 0)); // Missing support in QLocale //QCOMPARE(m_edit->minimumDateTime(), KDateTime(KLocale::global()->calendar()->earliestValidDate(), QTime(0, 0, 0))); //QCOMPARE(m_edit->maximumDateTime(), KDateTime(KLocale::global()->calendar()->latestValidDate(), QTime(23, 59, 59, 999))); QCOMPARE(m_edit->isValid(), true); QCOMPARE(m_edit->isNull(), false); QCOMPARE(m_edit->options(), KDateTimeEdit::ShowDate | KDateTimeEdit::EditDate | KDateTimeEdit::SelectDate | KDateTimeEdit::DatePicker | KDateTimeEdit::DateKeywords | KDateTimeEdit::ShowTime | KDateTimeEdit::EditTime | KDateTimeEdit::SelectTime); QCOMPARE(m_edit->dateDisplayFormat(), QLocale::ShortFormat); QCOMPARE(m_edit->timeListInterval(), 15); QCOMPARE(m_edit->timeDisplayFormat(), QLocale::ShortFormat); delete m_edit; } void KDateTimeEditTest::testValidNull() { m_edit = new KDateTimeEdit(nullptr); QCOMPARE(m_edit->isValid(), true); QCOMPARE(m_edit->isNull(), false); m_edit->setDateTime(QDateTime()); QCOMPARE(m_edit->isValid(), false); QCOMPARE(m_edit->isNull(), true); delete m_edit; } void KDateTimeEditTest::testDateTimeRange() { m_edit = new KDateTimeEdit(nullptr); m_edit->setDateTime(QDateTime(QDate(2000, 1, 1), QTime(12, 0, 0))); // Missing support in QLocale //QCOMPARE(m_edit->minimumDateTime(), KDateTime(KLocale::global()->calendar()->earliestValidDate(), QTime(0, 0, 0))); //QCOMPARE(m_edit->maximumDateTime(), KDateTime(KLocale::global()->calendar()->latestValidDate(), QTime(23, 59, 59, 999))); QCOMPARE(m_edit->isValid(), true); m_edit->setDateTimeRange(QDateTime(QDate(2001, 1, 1), QTime(10, 0, 0)), QDateTime(QDate(2002, 1, 1), QTime(20, 0, 0))); QCOMPARE(m_edit->minimumDateTime(), QDateTime(QDate(2001, 1, 1), QTime(10, 0, 0))); QCOMPARE(m_edit->maximumDateTime(), QDateTime(QDate(2002, 1, 1), QTime(20, 0, 0))); QCOMPARE(m_edit->isValid(), false); m_edit->setDateTime(QDateTime(QDate(2001, 1, 1), QTime(9, 59, 59, 999))); QCOMPARE(m_edit->isValid(), false); m_edit->setDateTime(QDateTime(QDate(2001, 1, 1), QTime(10, 0, 0))); QCOMPARE(m_edit->isValid(), true); m_edit->setDateTime(QDateTime(QDate(2002, 1, 1), QTime(20, 0, 0, 1))); QCOMPARE(m_edit->isValid(), false); m_edit->setDateTime(QDateTime(QDate(2002, 1, 1), QTime(20, 0, 0, 0))); QCOMPARE(m_edit->isValid(), true); m_edit->setDateTimeRange(QDateTime(QDate(1995, 1, 1), QTime(10, 0, 0)), QDateTime(QDate(1990, 1, 1), QTime(20, 0, 0))); QCOMPARE(m_edit->minimumDateTime(), QDateTime(QDate(2001, 1, 1), QTime(10, 0, 0))); QCOMPARE(m_edit->maximumDateTime(), QDateTime(QDate(2002, 1, 1), QTime(20, 0, 0))); m_edit->setMinimumDateTime(QDateTime(QDate(2000, 1, 1), QTime(0, 0, 0))); QCOMPARE(m_edit->minimumDateTime(), QDateTime(QDate(2000, 1, 1), QTime(0, 0, 0))); QCOMPARE(m_edit->maximumDateTime(), QDateTime(QDate(2002, 1, 1), QTime(20, 0, 0))); m_edit->setMaximumDateTime(QDateTime(QDate(2003, 1, 1), QTime(0, 0, 0))); QCOMPARE(m_edit->minimumDateTime(), QDateTime(QDate(2000, 1, 1), QTime(0, 0, 0))); QCOMPARE(m_edit->maximumDateTime(), QDateTime(QDate(2003, 1, 1), QTime(0, 0, 0))); delete m_edit; } void KDateTimeEditTest::testDateList() { m_edit = new KDateTimeEdit(nullptr); QMap map; // KDateTimeEditTest default map QCOMPARE(m_edit->dateMap(), map); // KDateTimeEditTest basic map map.clear(); map.insert(QDate(2000, 1, 1), QStringLiteral("New Years Day")); map.insert(QDate(2000, 1, 2), QString()); map.insert(QDate(2000, 1, 3), QStringLiteral("separator")); map.insert(QDate(), QStringLiteral("No Date")); m_edit->setDateMap(map); QCOMPARE(m_edit->dateMap(), map); delete m_edit; } void KDateTimeEditTest::testOptions() { m_edit = new KDateTimeEdit(nullptr); KDateTimeEdit::Options options = KDateTimeEdit::ShowDate | KDateTimeEdit::EditDate | KDateTimeEdit::SelectDate | KDateTimeEdit::DatePicker | KDateTimeEdit::DateKeywords | KDateTimeEdit::ShowTime | KDateTimeEdit::EditTime | KDateTimeEdit::SelectTime; QCOMPARE(m_edit->options(), options); options = KDateTimeEdit::EditDate | KDateTimeEdit::WarnOnInvalid; m_edit->setOptions(options); QCOMPARE(m_edit->options(), options); delete m_edit; } void KDateTimeEditTest::testDateDisplayFormat() { m_edit = new KDateTimeEdit(nullptr); QLocale::FormatType format = QLocale::ShortFormat; QCOMPARE(m_edit->dateDisplayFormat(), format); format = QLocale::NarrowFormat; m_edit->setDateDisplayFormat(format); QCOMPARE(m_edit->dateDisplayFormat(), format); delete m_edit; } void KDateTimeEditTest::testTimeListInterval() { m_edit = new KDateTimeEdit(); QCOMPARE(m_edit->timeListInterval(), 15); m_edit-> setTimeListInterval(60); QCOMPARE(m_edit->timeListInterval(), 60); delete m_edit; } void KDateTimeEditTest::testTimeList() { m_edit = new KDateTimeEdit(); QList list; // KDateTimeEditTest default list QTime thisTime = QTime(0, 0, 0); for (int i = 0; i < 1440; i = i + 15) { list << thisTime.addSecs(i * 60); } list << QTime(23, 59, 59, 999); QCOMPARE(m_edit->timeList(), list); // KDateTimeEditTest basic list list.clear(); list << QTime(3, 0, 0) << QTime(15, 16, 17); m_edit->setTimeList(list); QCOMPARE(m_edit->timeList(), list); delete m_edit; } void KDateTimeEditTest::testTimeDisplayFormat() { m_edit = new KDateTimeEdit(); QLocale::FormatType format = QLocale::ShortFormat; QCOMPARE(m_edit->timeDisplayFormat(), format); format = QLocale::NarrowFormat; m_edit->setTimeDisplayFormat(format); QCOMPARE(m_edit->timeDisplayFormat(), format); delete m_edit; } void KDateTimeEditTest::testCalendarSystem() { m_edit = new KDateTimeEdit(); QList calendars; calendars << QLocale(); QCOMPARE(m_edit->locale(), QLocale()); QCOMPARE(m_edit->calendarLocalesList(), calendars); m_edit->setLocale(QLocale(QLocale::Hebrew)); QCOMPARE(m_edit->locale(), QLocale(QLocale::Hebrew)); calendars.clear(); calendars.append(QLocale(QLocale::Hebrew)); calendars.append(QLocale(QLocale::Chinese)); m_edit->setCalendarLocalesList(calendars); QCOMPARE(m_edit->calendarLocalesList(), calendars); delete m_edit; } void KDateTimeEditTest::testTimeSpec() { m_edit = new KDateTimeEdit(); QCOMPARE(m_edit->timeZone(), QDateTime::currentDateTime().timeZone()); QList zones; - foreach (const QByteArray &zoneId, QTimeZone::availableTimeZoneIds()) { + const auto zoneIds = QTimeZone::availableTimeZoneIds(); + for (const QByteArray &zoneId : zoneIds) { zones << QTimeZone(zoneId); } QCOMPARE(m_edit->timeZones(), zones); QTimeZone zone(3600); m_edit->setTimeZone(zone); QCOMPARE(m_edit->timeZone(), zone); zones << zone; m_edit->setTimeZones(zones); QCOMPARE(m_edit->timeZones(), zones); delete m_edit; } template static T findVisibleChild(QWidget *parent) { - foreach (T child, parent->findChildren()) { + const auto children = parent->findChildren(); + for (T child : children) { if (child->isVisible()) { return child; } } return nullptr; } void KDateTimeEditTest::testDateMenu() { m_edit = new KDateTimeEdit(); KDateTimeEdit::Options options = KDateTimeEdit::ShowDate | KDateTimeEdit::EditDate | KDateTimeEdit::SelectDate | KDateTimeEdit::DatePicker | KDateTimeEdit::DateKeywords; m_edit->setOptions(options); m_edit->setDate(QDate(2002, 1, 1)); m_edit->show(); QComboBox *combo = findVisibleChild(m_edit); QVERIFY(combo); combo->showPopup(); QMenu *menu = findVisibleChild(combo); QVERIFY(menu); QAction *nextMonthAction = menu->actions().at(3); QCOMPARE(nextMonthAction->text(), KDateComboBox::tr("Next Month", "@option next month")); nextMonthAction->trigger(); QCOMPARE(m_edit->date(), QDate::currentDate().addMonths(1)); } diff --git a/src/common_helpers.cpp b/src/common_helpers.cpp index af2e108..df7fcf5 100644 --- a/src/common_helpers.cpp +++ b/src/common_helpers.cpp @@ -1,105 +1,105 @@ /* This file is part of the KDE libraries Copyright (C) 2008 Chusslove Illich This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include // If pos points to alphanumeric X in "...(X)...", which is preceded or // followed only by non-alphanumerics, then "(X)" gets removed. static QString removeReducedCJKAccMark(const QString &label, int pos) { if (pos > 0 && pos + 1 < label.length() && label[pos - 1] == QLatin1Char('(') && label[pos + 1] == QLatin1Char(')') && label[pos].isLetterOrNumber()) { // Check if at start or end, ignoring non-alphanumerics. int len = label.length(); int p1 = pos - 2; while (p1 >= 0 && !label[p1].isLetterOrNumber()) { --p1; } ++p1; int p2 = pos + 2; while (p2 < len && !label[p2].isLetterOrNumber()) { ++p2; } --p2; if (p1 == 0) { return label.leftRef(pos - 1) + label.midRef(p2 + 1); } else if (p2 + 1 == len) { return label.leftRef(p1) + label.midRef(pos + 2); } } return label; } QString removeAcceleratorMarker(const QString &label_) { QString label = label_; int p = 0; bool accmarkRemoved = false; while (true) { p = label.indexOf(QLatin1Char('&'), p); if (p < 0 || p + 1 == label.length()) { break; } if (label[p + 1].isLetterOrNumber()) { // Valid accelerator. label = QString(label.leftRef(p) + label.midRef(p + 1)); // May have been an accelerator in CJK-style "(&X)" // at the start or end of text. label = removeReducedCJKAccMark(label, p); accmarkRemoved = true; } else if (label[p + 1] == QLatin1Char('&')) { // Escaped accelerator marker. label = QString(label.leftRef(p) + label.midRef(p + 1)); } ++p; } // If no marker was removed, and there are CJK characters in the label, // also try to remove reduced CJK marker -- something may have removed // ampersand beforehand. if (!accmarkRemoved) { bool hasCJK = false; - foreach (const QChar c, label) { + for (const QChar c : qAsConst(label)) { if (c.unicode() >= 0x2e00) { // rough, but should be sufficient hasCJK = true; break; } } if (hasCJK) { p = 0; while (true) { p = label.indexOf(QLatin1Char('('), p); if (p < 0) { break; } label = removeReducedCJKAccMark(label, p + 1); ++p; } } } return label; } diff --git a/src/fonthelpers.cpp b/src/fonthelpers.cpp index 9be604e..207891c 100644 --- a/src/fonthelpers.cpp +++ b/src/fonthelpers.cpp @@ -1,128 +1,129 @@ /* Copyright (C) 2008 Chusslove Illich This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "fonthelpers_p.h" #include #ifdef NEVERDEFINE // never true // Font names up for translation, listed for extraction. //: Generic sans serif font presented in font choosers. When selected, //: the system will choose a real font, mandated by distro settings. QT_TRANSLATE_NOOP3("FontHelpers", "Sans Serif", "@item Font name"); //: Generic serif font presented in font choosers. When selected, //: the system will choose a real font, mandated by distro settings. QT_TRANSLATE_NOOP3("FontHelpers", "Serif", "@item Font name"); //: Generic monospace font presented in font choosers. When selected, //: the system will choose a real font, mandated by distro settings. QT_TRANSLATE_NOOP3("FontHelpers", "Monospace", "@item Font name"); #endif void splitFontString(const QString &name, QString *family, QString *foundry) { int p1 = name.indexOf(QLatin1Char('[')); if (p1 < 0) { if (family) { *family = name.trimmed(); } if (foundry) { foundry->clear(); } } else { int p2 = name.indexOf(QLatin1Char(']'), p1); p2 = p2 > p1 ? p2 : name.length(); if (family) { *family = name.leftRef(p1).trimmed().toString(); } if (foundry) { *foundry = name.midRef(p1 + 1, p2 - p1 - 1).trimmed().toString(); } } } QString translateFontName(const QString &name) { QString family, foundry; splitFontString(name, &family, &foundry); // Obtain any regular translations for the family and foundry. QString trFamily = QCoreApplication::translate("FontHelpers", family.toUtf8().constData(), "@item Font name"); QString trFoundry = foundry; if (!foundry.isEmpty()) { trFoundry = QCoreApplication::translate("FontHelpers", foundry.toUtf8().constData(), "@item Font foundry"); } // Assemble full translation. QString trfont; if (foundry.isEmpty()) { // i18n: Filter by which the translators can translate, or otherwise // operate on the font names not put up for regular translation. trfont = QCoreApplication::translate("FontHelpers", "%1", "@item Font name").arg(trFamily); } else { // i18n: Filter by which the translators can translate, or otherwise // operate on the font names not put up for regular translation. trfont = QCoreApplication::translate("FontHelpers", "%1 [%2]", "@item Font name [foundry]") .arg(trFamily, trFoundry); } return trfont; } static bool localeLessThan(const QString &a, const QString &b) { return QString::localeAwareCompare(a, b) < 0; } QStringList translateFontNameList(const QStringList &names, QHash *trToRawNames) { // Generic fonts, in the inverse of desired order. - QStringList genericNames; - genericNames.append(QStringLiteral("Monospace")); - genericNames.append(QStringLiteral("Serif")); - genericNames.append(QStringLiteral("Sans Serif")); + const QStringList genericNames { + QStringLiteral("Monospace"), + QStringLiteral("Serif"), + QStringLiteral("Sans Serif"), + }; // Translate fonts, but do not add generics to the list right away. QStringList trNames; QHash trMap; for (const QString &name : names) { const QString trName = translateFontName(name); if (!genericNames.contains(name)) { trNames.append(trName); } trMap.insert(trName, name); } // Sort real fonts alphabetically. std::sort(trNames.begin(), trNames.end(), localeLessThan); // Prepend generic fonts, in the predefined order. - Q_FOREACH (const QString &genericName, genericNames) { + for (const QString &genericName : genericNames) { const QString trGenericName = translateFontName(genericName); if (trMap.contains(trGenericName)) { trNames.prepend(trGenericName); } } if (trToRawNames) { *trToRawNames = trMap; } return trNames; } diff --git a/src/kacceleratormanager.cpp b/src/kacceleratormanager.cpp index 7af008a..4a0bed3 100644 --- a/src/kacceleratormanager.cpp +++ b/src/kacceleratormanager.cpp @@ -1,855 +1,858 @@ /* This file is part of the KDE project Copyright (C) 2002 Matthias Hölzer-Klüpfel This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kacceleratormanager.h" #include "kacceleratormanager_p.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "loggingcategory.h" #include "common_helpers_p.h" /********************************************************************* class Item - helper class containing widget information This class stores information about the widgets the need accelerators, as well as about their relationship. *********************************************************************/ bool KAcceleratorManagerPrivate::programmers_mode = false; QString KAcceleratorManagerPrivate::changed_string; QString KAcceleratorManagerPrivate::added_string; QString KAcceleratorManagerPrivate::removed_string; QMap KAcceleratorManagerPrivate::ignored_widgets; QStringList KAcceleratorManagerPrivate::standardNames; void KAcceleratorManagerPrivate::addStandardActionNames(const QStringList &list) { standardNames.append(list); } bool KAcceleratorManagerPrivate::standardName(const QString &str) { return standardNames.contains(str); } KAcceleratorManagerPrivate::Item::~Item() { if (m_children) { while (!m_children->isEmpty()) { delete m_children->takeFirst(); } delete m_children; } } void KAcceleratorManagerPrivate::Item::addChild(Item *item) { if (!m_children) { m_children = new ItemList; } m_children->append(item); } void KAcceleratorManagerPrivate::manage(QWidget *widget) { if (!widget) { qCDebug(KWidgetsAddonsLog) << "null pointer given to manage"; return; } if (KAcceleratorManagerPrivate::ignored_widgets.contains(widget)) { return; } if (qobject_cast(widget)) { // create a popup accel manager that can deal with dynamic menus KPopupAccelManager::manage(static_cast(widget)); return; } Item *root = new Item; manageWidget(widget, root); QString used; calculateAccelerators(root, used); delete root; } void KAcceleratorManagerPrivate::calculateAccelerators(Item *item, QString &used) { if (!item->m_children) { return; } // collect the contents KAccelStringList contents; - Q_FOREACH (Item *it, *item->m_children) { + contents.reserve(item->m_children->size()); + for (Item *it : qAsConst(*item->m_children)) { contents << it->m_content; } // find the right accelerators KAccelManagerAlgorithm::findAccelerators(contents, used); // write them back into the widgets int cnt = -1; - Q_FOREACH (Item *it, *item->m_children) { + for (Item *it : qAsConst(*item->m_children)) { cnt++; QDockWidget *dock = qobject_cast(it->m_widget); if (dock) { if (checkChange(contents[cnt])) { dock->setWindowTitle(contents[cnt].accelerated()); } continue; } QTabBar *tabBar = qobject_cast(it->m_widget); if (tabBar) { if (checkChange(contents[cnt])) { tabBar->setTabText(it->m_index, contents[cnt].accelerated()); } continue; } QMenuBar *menuBar = qobject_cast(it->m_widget); if (menuBar) { if (it->m_index >= 0) { QAction *maction = menuBar->actions()[it->m_index]; if (maction) { checkChange(contents[cnt]); maction->setText(contents[cnt].accelerated()); } continue; } } // we possibly reserved an accel, but we won't set it as it looks silly QGroupBox *groupBox = qobject_cast(it->m_widget); if (groupBox && !groupBox->isCheckable()) { continue; } int tprop = it->m_widget->metaObject()->indexOfProperty("text"); if (tprop != -1) { if (checkChange(contents[cnt])) { it->m_widget->setProperty("text", contents[cnt].accelerated()); } } else { tprop = it->m_widget->metaObject()->indexOfProperty("title"); if (tprop != -1 && checkChange(contents[cnt])) { it->m_widget->setProperty("title", contents[cnt].accelerated()); } } } // calculate the accelerators for the children - Q_FOREACH (Item *it, *item->m_children) { + for (Item *it : qAsConst(*item->m_children)) { if (it->m_widget && it->m_widget->isVisibleTo(item->m_widget)) { calculateAccelerators(it, used); } } } void KAcceleratorManagerPrivate::traverseChildren(QWidget *widget, Item *item) { - QList childList = widget->findChildren(); - Q_FOREACH (QWidget *w, childList) { + const QList childList = widget->findChildren(); + for (QWidget *w : childList) { // Ignore unless we have the direct parent if (qobject_cast(w->parent()) != widget) { continue; } if (!w->isVisibleTo(widget) || (w->isTopLevel() && qobject_cast(w) == nullptr)) { continue; } if (KAcceleratorManagerPrivate::ignored_widgets.contains(w)) { continue; } manageWidget(w, item); } } void KAcceleratorManagerPrivate::manageWidget(QWidget *w, Item *item) { // first treat the special cases QTabBar *tabBar = qobject_cast(w); if (tabBar) { manageTabBar(tabBar, item); return; } QStackedWidget *wds = qobject_cast(w); if (wds) { QWidgetStackAccelManager::manage(wds); // return; } QDockWidget *dock = qobject_cast(w); if (dock) { //QWidgetStackAccelManager::manage( wds ); manageDockWidget(dock, item); } QMenu *popupMenu = qobject_cast(w); if (popupMenu) { // create a popup accel manager that can deal with dynamic menus KPopupAccelManager::manage(popupMenu); return; } QStackedWidget *wdst = qobject_cast(w); if (wdst) { QWidgetStackAccelManager::manage(wdst); // return; } QMenuBar *menuBar = qobject_cast(w); if (menuBar) { manageMenuBar(menuBar, item); return; } if (qobject_cast(w) || qobject_cast(w) || w->inherits("Q3TextEdit") || qobject_cast(w) || qobject_cast(w) || w->inherits("KMultiTabBar") || w->inherits("qdesigner_internal::TextPropertyEditor")) { return; } if (w->inherits("KUrlRequester")) { traverseChildren(w, item); return; } // now treat 'ordinary' widgets QLabel *label = qobject_cast(w); if (label) { if (!label->buddy()) { return; } else { if (label->textFormat() == Qt::RichText || (label->textFormat() == Qt::AutoText && Qt::mightBeRichText(label->text()))) { return; } } } if (w->focusPolicy() != Qt::NoFocus || label || qobject_cast(w) || qobject_cast(w)) { QString content; QVariant variant; int tprop = w->metaObject()->indexOfProperty("text"); if (tprop != -1) { QMetaProperty p = w->metaObject()->property(tprop); if (p.isValid() && p.isWritable()) { variant = p.read(w); } else { tprop = -1; } } if (tprop == -1) { tprop = w->metaObject()->indexOfProperty("title"); if (tprop != -1) { QMetaProperty p = w->metaObject()->property(tprop); if (p.isValid() && p.isWritable()) { variant = p.read(w); } } } if (variant.isValid()) { content = variant.toString(); } if (!content.isEmpty()) { Item *i = new Item; i->m_widget = w; // put some more weight on the usual action elements int weight = KAccelManagerAlgorithm::DEFAULT_WEIGHT; if (qobject_cast(w) || qobject_cast(w) || qobject_cast(w) || qobject_cast(w)) { weight = KAccelManagerAlgorithm::ACTION_ELEMENT_WEIGHT; } // don't put weight on non-checkable group boxes, // as usually the contents are more important QGroupBox *groupBox = qobject_cast(w); if (groupBox) { if (groupBox->isCheckable()) { weight = KAccelManagerAlgorithm::CHECKABLE_GROUP_BOX_WEIGHT; } else { weight = KAccelManagerAlgorithm::GROUP_BOX_WEIGHT; } } i->m_content = KAccelString(content, weight); item->addChild(i); } } traverseChildren(w, item); } void KAcceleratorManagerPrivate::manageTabBar(QTabBar *bar, Item *item) { // ignore QTabBar for QDockWidgets, because QDockWidget on its title change // also updates its tabbar entry, so on the next run of KCheckAccelerators // this looks like a conflict and triggers a new reset of the shortcuts -> endless loop QWidget *parentWidget = bar->parentWidget(); if (parentWidget) { QMainWindow *mainWindow = qobject_cast(parentWidget); // TODO: find better hints that this is a QTabBar for QDockWidgets if (mainWindow) { // && (mainWindow->layout()->indexOf(bar) != -1)) QMainWindowLayout lacks proper support return; } } for (int i = 0; i < bar->count(); i++) { QString content = bar->tabText(i); if (content.isEmpty()) { continue; } Item *it = new Item; item->addChild(it); it->m_widget = bar; it->m_index = i; it->m_content = KAccelString(content); } } void KAcceleratorManagerPrivate::manageDockWidget(QDockWidget *dock, Item *item) { // As of Qt 4.4.3 setting a shortcut to a QDockWidget has no effect, // because a QDockWidget does not grab it, even while displaying an underscore // in the title for the given shortcut letter. // Still it is useful to set the shortcut, because if QDockWidgets are tabbed, // the tab automatically gets the same text as the QDockWidget title, including the shortcut. // And for the QTabBar the shortcut does work, it gets grabbed as usual. // Having the QDockWidget without a shortcut and resetting the tab text with a title including // the shortcut does not work, the tab text is instantly reverted to the QDockWidget title // (see also manageTabBar()). // All in all QDockWidgets and shortcuts are a little broken for now. QString content = dock->windowTitle(); if (content.isEmpty()) { return; } Item *it = new Item; item->addChild(it); it->m_widget = dock; it->m_content = KAccelString(content, KAccelManagerAlgorithm::STANDARD_ACCEL); } void KAcceleratorManagerPrivate::manageMenuBar(QMenuBar *mbar, Item *item) { QAction *maction; QString s; for (int i = 0; i < mbar->actions().count(); ++i) { maction = mbar->actions()[i]; if (!maction) { continue; } // nothing to do for separators if (maction->isSeparator()) { continue; } s = maction->text(); if (!s.isEmpty()) { Item *it = new Item; item->addChild(it); it->m_content = KAccelString(s, // menu titles are important, so raise the weight KAccelManagerAlgorithm::MENU_TITLE_WEIGHT); it->m_widget = mbar; it->m_index = i; } // have a look at the popup as well, if present if (maction->menu()) { KPopupAccelManager::manage(maction->menu()); } } } /********************************************************************* class KAcceleratorManager - main entry point This class is just here to provide a clean public API... *********************************************************************/ void KAcceleratorManager::manage(QWidget *widget, bool programmers_mode) { KAcceleratorManagerPrivate::changed_string.clear(); KAcceleratorManagerPrivate::added_string.clear(); KAcceleratorManagerPrivate::removed_string.clear(); KAcceleratorManagerPrivate::programmers_mode = programmers_mode; KAcceleratorManagerPrivate::manage(widget); } void KAcceleratorManager::last_manage(QString &added, QString &changed, QString &removed) { added = KAcceleratorManagerPrivate::added_string; changed = KAcceleratorManagerPrivate::changed_string; removed = KAcceleratorManagerPrivate::removed_string; } /********************************************************************* class KAccelString - a string with weighted characters *********************************************************************/ KAccelString::KAccelString(const QString &input, int initialWeight) : m_pureText(input), m_weight() { m_orig_accel = m_pureText.indexOf(QLatin1String("(!)&")); if (m_orig_accel != -1) { m_pureText.remove(m_orig_accel, 4); } m_orig_accel = m_pureText.indexOf(QLatin1String("(&&)")); if (m_orig_accel != -1) { m_pureText.replace(m_orig_accel, 4, QStringLiteral("&")); } m_origText = m_pureText; const int tabPos = m_pureText.indexOf(QLatin1Char('\t')); if (tabPos != -1) { m_pureText.truncate(tabPos); } m_orig_accel = m_accel = stripAccelerator(m_pureText); if (initialWeight == -1) { initialWeight = KAccelManagerAlgorithm::DEFAULT_WEIGHT; } calculateWeights(initialWeight); // dump(); } QString KAccelString::accelerated() const { QString result = m_origText; if (result.isEmpty()) { return result; } if (KAcceleratorManagerPrivate::programmers_mode) { if (m_accel != m_orig_accel) { int oa = m_orig_accel; if (m_accel >= 0) { result.insert(m_accel, QLatin1String("(!)&")); if (m_accel < m_orig_accel) { oa += 4; } } if (m_orig_accel >= 0) { result.replace(oa, 1, QStringLiteral("(&&)")); } } } else { if (m_accel >= 0 && m_orig_accel != m_accel) { if (m_orig_accel != -1) { result.remove(m_orig_accel, 1); } result.insert(m_accel, QLatin1Char('&')); } } return result; } QChar KAccelString::accelerator() const { if ((m_accel < 0) || (m_accel > m_pureText.length())) { return QChar(); } return m_pureText[m_accel].toLower(); } void KAccelString::calculateWeights(int initialWeight) { m_weight.resize(m_pureText.length()); int pos = 0; bool start_character = true; while (pos < m_pureText.length()) { QChar c = m_pureText[pos]; int weight = initialWeight + 1; // add special weight to first character if (pos == 0) { weight += KAccelManagerAlgorithm::FIRST_CHARACTER_EXTRA_WEIGHT; } // add weight to word beginnings if (start_character) { weight += KAccelManagerAlgorithm::WORD_BEGINNING_EXTRA_WEIGHT; start_character = false; } // add decreasing weight to left characters if (pos < 50) { weight += (50 - pos); } // try to preserve the wanted accelerators if (pos == accel()) { weight += KAccelManagerAlgorithm::WANTED_ACCEL_EXTRA_WEIGHT; // qCDebug(KWidgetsAddonsLog) << "wanted " << m_pureText << " " << KAcceleratorManagerPrivate::standardName(m_origText); if (KAcceleratorManagerPrivate::standardName(m_origText)) { weight += KAccelManagerAlgorithm::STANDARD_ACCEL; } } // skip non typeable characters if (!c.isLetterOrNumber()) { weight = 0; start_character = true; } m_weight[pos] = weight; ++pos; } } int KAccelString::stripAccelerator(QString &text) { // Note: this code is derived from QAccel::shortcutKey int p = 0; while (p >= 0) { p = text.indexOf(QLatin1Char('&'), p) + 1; if (p <= 0 || p >= text.length()) { break; } if (text[p] != QLatin1Char('&')) { QChar c = text[p]; if (c.isPrint()) { text.remove(p - 1, 1); return p - 1; } } p++; } return -1; } int KAccelString::maxWeight(int &index, const QString &used) const { int max = 0; index = -1; for (int pos = 0; pos < m_pureText.length(); ++pos) if (used.indexOf(m_pureText[pos], 0, Qt::CaseInsensitive) == -1 && m_pureText[pos].toLatin1() != 0) if (m_weight[pos] > max) { max = m_weight[pos]; index = pos; } return max; } void KAccelString::dump() { QString s; for (int i = 0; i < m_weight.count(); ++i) { s += QStringLiteral("%1(%2) ").arg(pure()[i]).arg(m_weight[i]); } qCDebug(KWidgetsAddonsLog) << "s " << s; } /********************************************************************* findAccelerators - the algorithm determining the new accelerators The algorithm is very crude: * each character in each widget text is assigned a weight * the character with the highest weight over all is picked * that widget is removed from the list * the weights are recalculated * the process is repeated until no more accelerators can be found The algorithm has some advantages: * it favors 'nice' accelerators (first characters in a word, etc.) * it is quite fast, O(N²) * it is easy to understand :-) The disadvantages: * it does not try to find as many accelerators as possible TODO: * The result is always correct, but not necessarily optimal. Perhaps it would be a good idea to add another algorithm with higher complexity that gets used when this one fails, i.e. leaves widgets without accelerators. * The weights probably need some tweaking so they make more sense. *********************************************************************/ void KAccelManagerAlgorithm::findAccelerators(KAccelStringList &result, QString &used) { KAccelStringList accel_strings = result; // initially remove all accelerators for (KAccelStringList::Iterator it = result.begin(), total = result.end(); it != total; ++it) { (*it).setAccel(-1); } // pick the highest bids for (int cnt = 0; cnt < accel_strings.count(); ++cnt) { int max = 0, index = -1, accel = -1; // find maximum weight for (int i = 0; i < accel_strings.count(); ++i) { int a; int m = accel_strings[i].maxWeight(a, used); if (m > max) { max = m; index = i; accel = a; } } // stop if no more accelerators can be found if (index < 0) { return; } // insert the accelerator if (accel >= 0) { result[index].setAccel(accel); used.append(result[index].accelerator()); } // make sure we don't visit this one again accel_strings[index] = KAccelString(); } } /********************************************************************* class KPopupAccelManager - managing QMenu widgets dynamically *********************************************************************/ KPopupAccelManager::KPopupAccelManager(QMenu *popup) : QObject(popup), m_popup(popup), m_count(-1) { aboutToShow(); // do one check and then connect to show connect(popup, &QMenu::aboutToShow, this, &KPopupAccelManager::aboutToShow); } void KPopupAccelManager::aboutToShow() { // Note: we try to be smart and avoid recalculating the accelerators // whenever possible. Unfortunately, there is no way to know if an // item has been added or removed, so we can not do much more than // to compare the items each time the menu is shown :-( if (m_count != m_popup->actions().count()) { findMenuEntries(m_entries); calculateAccelerators(); m_count = m_popup->actions().count(); } else { KAccelStringList entries; findMenuEntries(entries); if (entries != m_entries) { m_entries = entries; calculateAccelerators(); } } } void KPopupAccelManager::calculateAccelerators() { // find the new accelerators QString used; KAccelManagerAlgorithm::findAccelerators(m_entries, used); // change the menu entries setMenuEntries(m_entries); } void KPopupAccelManager::findMenuEntries(KAccelStringList &list) { QString s; list.clear(); // read out the menu entries - Q_FOREACH (QAction *maction, m_popup->actions()) { + const auto menuActions = m_popup->actions(); + for (QAction *maction : menuActions) { if (maction->isSeparator()) { continue; } s = maction->text(); // in full menus, look at entries with global accelerators last int weight = 50; if (s.contains(QLatin1Char('\t'))) { weight = 0; } list.append(KAccelString(s, weight)); // have a look at the popup as well, if present if (maction->menu()) { KPopupAccelManager::manage(maction->menu()); } } } // Duplicated from qaction.cpp static QString copy_of_qt_strippedText(QString s) { s.remove(QLatin1String("...")); for (int i = 0; i < s.size(); ++i) { if (s.at(i) == QLatin1Char('&')) { s.remove(i, 1); } } return s.trimmed(); } void KPopupAccelManager::setMenuEntries(const KAccelStringList &list) { uint cnt = 0; - Q_FOREACH (QAction *maction, m_popup->actions()) { + const auto menuActions = m_popup->actions(); + for (QAction *maction : menuActions) { if (maction->isSeparator()) { continue; } QString iconText = maction->iconText(); const QString oldText = maction->text(); // Check if iconText was generated by Qt. In that case ignore it (no support for CJK accelerators) and set it from the text. if (iconText == copy_of_qt_strippedText(oldText)) { iconText = removeAcceleratorMarker(oldText); if (iconText != maction->iconText()) { maction->setIconText(iconText); } } if (KAcceleratorManagerPrivate::checkChange(list[cnt])) { maction->setText(list[cnt].accelerated()); } cnt++; } } void KPopupAccelManager::manage(QMenu *popup) { // don't add more than one manager to a popup if (popup->findChild(QString()) == nullptr) { new KPopupAccelManager(popup); } } void QWidgetStackAccelManager::manage(QStackedWidget *stack) { if (stack->findChild(QString()) == nullptr) { new QWidgetStackAccelManager(stack); } } QWidgetStackAccelManager::QWidgetStackAccelManager(QStackedWidget *stack) : QObject(stack), m_stack(stack) { currentChanged(stack->currentIndex()); // do one check and then connect to show connect(stack, &QStackedWidget::currentChanged, this, &QWidgetStackAccelManager::currentChanged); } bool QWidgetStackAccelManager::eventFilter(QObject *watched, QEvent *e) { if (e->type() == QEvent::Show && qApp->activeWindow()) { KAcceleratorManager::manage(qApp->activeWindow()); watched->removeEventFilter(this); } return false; } void QWidgetStackAccelManager::currentChanged(int child) { if (child < 0 || child >= static_cast(parent())->count()) { // NOTE: QStackedWidget emits currentChanged(-1) when it is emptied return; } static_cast(parent())->widget(child)->installEventFilter(this); } void KAcceleratorManager::setNoAccel(QWidget *widget) { KAcceleratorManagerPrivate::ignored_widgets[widget] = 1; } void KAcceleratorManager::addStandardActionNames(const QStringList &names) { KAcceleratorManagerPrivate::addStandardActionNames(names); } #include "moc_kacceleratormanager_p.cpp" diff --git a/src/kactionselector.cpp b/src/kactionselector.cpp index d26ac91..a485823 100644 --- a/src/kactionselector.cpp +++ b/src/kactionselector.cpp @@ -1,572 +1,572 @@ /* This file is part of the KDE project Copyright (C) 2002 Anders Lund This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kactionselector.h" #include #include #include #include #include #include #include class KActionSelectorPrivate { public: KActionSelectorPrivate(KActionSelector *q): q(q) {} KActionSelector *q = nullptr; QListWidget *availableListWidget = nullptr; QListWidget *selectedListWidget = nullptr; QToolButton *btnAdd = nullptr; QToolButton *btnRemove = nullptr; QToolButton *btnUp = nullptr; QToolButton *btnDown = nullptr; QLabel *lAvailable = nullptr; QLabel *lSelected = nullptr; bool moveOnDoubleClick : 1; bool keyboardEnabled : 1; bool showUpDownButtons : 1; QString addIcon, removeIcon, upIcon, downIcon; KActionSelector::InsertionPolicy availableInsertionPolicy, selectedInsertionPolicy; /** Move item @p item to the other listbox */ void moveItem(QListWidgetItem *item); /** loads the icons for the move buttons. */ void loadIcons(); /** @return the index to insert an item into listbox @p lb, given InsertionPolicy @p policy. Note that if policy is Sorted, this will return -1. Sort the listbox after inserting the item in that case. */ int insertionIndex(QListWidget *lb, KActionSelector::InsertionPolicy policy); /** @return the index of the first selected item in listbox @p lb. If no item is selected, it will return -1. */ int selectedRowIndex(QListWidget *lb); void buttonAddClicked(); void buttonRemoveClicked(); void buttonUpClicked(); void buttonDownClicked(); void itemDoubleClicked(QListWidgetItem *item); void slotCurrentChanged(QListWidgetItem *) { q->setButtonsEnabled(); } }; //BEGIN Constructor/destructor KActionSelector::KActionSelector(QWidget *parent) : QWidget(parent) , d(new KActionSelectorPrivate(this)) { d->moveOnDoubleClick = true; d->keyboardEnabled = true; d->addIcon = QLatin1String(QApplication::isRightToLeft() ? "go-previous" : "go-next"); d->removeIcon = QLatin1String(QApplication::isRightToLeft() ? "go-next" : "go-previous"); d->upIcon = QStringLiteral("go-up"); d->downIcon = QStringLiteral("go-down"); d->availableInsertionPolicy = Sorted; d->selectedInsertionPolicy = BelowCurrent; d->showUpDownButtons = true; QHBoxLayout *lo = new QHBoxLayout(this); lo->setContentsMargins(0, 0, 0, 0); QVBoxLayout *loAv = new QVBoxLayout(); lo->addLayout(loAv); d->lAvailable = new QLabel(tr("&Available:"), this); loAv->addWidget(d->lAvailable); d->availableListWidget = new QListWidget(this); loAv->addWidget(d->availableListWidget); d->lAvailable->setBuddy(d->availableListWidget); QVBoxLayout *loHBtns = new QVBoxLayout(); lo->addLayout(loHBtns); loHBtns->addStretch(1); d->btnAdd = new QToolButton(this); loHBtns->addWidget(d->btnAdd); d->btnRemove = new QToolButton(this); loHBtns->addWidget(d->btnRemove); loHBtns->addStretch(1); QVBoxLayout *loS = new QVBoxLayout(); lo->addLayout(loS); d->lSelected = new QLabel(tr("&Selected:"), this); loS->addWidget(d->lSelected); d->selectedListWidget = new QListWidget(this); loS->addWidget(d->selectedListWidget); d->lSelected->setBuddy(d->selectedListWidget); QVBoxLayout *loVBtns = new QVBoxLayout(); lo->addLayout(loVBtns); loVBtns->addStretch(1); d->btnUp = new QToolButton(this); d->btnUp->setAutoRepeat(true); loVBtns->addWidget(d->btnUp); d->btnDown = new QToolButton(this); d->btnDown->setAutoRepeat(true); loVBtns->addWidget(d->btnDown); loVBtns->addStretch(1); d->loadIcons(); connect(d->btnAdd, &QToolButton::clicked, this, [this]() { d->buttonAddClicked(); }); connect(d->btnRemove, &QToolButton::clicked, this, [this]() { d->buttonRemoveClicked(); }); connect(d->btnUp, &QToolButton::clicked, this, [this]() { d->buttonUpClicked(); }); connect(d->btnDown, &QToolButton::clicked, this, [this]() { d->buttonDownClicked(); }); connect(d->availableListWidget, SIGNAL(itemDoubleClicked(QListWidgetItem*)), this, SLOT(itemDoubleClicked(QListWidgetItem*))); connect(d->selectedListWidget, SIGNAL(itemDoubleClicked(QListWidgetItem*)), this, SLOT(itemDoubleClicked(QListWidgetItem*))); connect(d->availableListWidget, &QListWidget::itemSelectionChanged, this, &KActionSelector::setButtonsEnabled); connect(d->selectedListWidget, &QListWidget::itemSelectionChanged, this, &KActionSelector::setButtonsEnabled); d->availableListWidget->installEventFilter(this); d->selectedListWidget->installEventFilter(this); setButtonsEnabled(); } KActionSelector::~KActionSelector() { delete d; } //END Constructor/destroctor //BEGIN Public Methods QListWidget *KActionSelector::availableListWidget() const { return d->availableListWidget; } QListWidget *KActionSelector::selectedListWidget() const { return d->selectedListWidget; } void KActionSelector::setButtonIcon(const QString &icon, MoveButton button) { switch (button) { case ButtonAdd: d->addIcon = icon; d->btnAdd->setIcon(QIcon::fromTheme(icon)); break; case ButtonRemove: d->removeIcon = icon; d->btnRemove->setIcon(QIcon::fromTheme(icon)); break; case ButtonUp: d->upIcon = icon; d->btnUp->setIcon(QIcon::fromTheme(icon)); break; case ButtonDown: d->downIcon = icon; d->btnDown->setIcon(QIcon::fromTheme(icon)); break; default: // qCDebug(KWidgetsAddonsLog)<<"KActionSelector::setButtonIcon: DAINBREAD!"; break; } } void KActionSelector::setButtonIconSet(const QIcon &iconset, MoveButton button) { switch (button) { case ButtonAdd: d->btnAdd->setIcon(iconset); break; case ButtonRemove: d->btnRemove->setIcon(iconset); break; case ButtonUp: d->btnUp->setIcon(iconset); break; case ButtonDown: d->btnDown->setIcon(iconset); break; default: // qCDebug(KWidgetsAddonsLog)<<"KActionSelector::setButtonIconSet: DAINBREAD!"; break; } } void KActionSelector::setButtonTooltip(const QString &tip, MoveButton button) { switch (button) { case ButtonAdd: d->btnAdd->setText(tip); d->btnAdd->setToolTip(tip); break; case ButtonRemove: d->btnRemove->setText(tip); d->btnRemove->setToolTip(tip); break; case ButtonUp: d->btnUp->setText(tip); d->btnUp->setToolTip(tip); break; case ButtonDown: d->btnDown->setText(tip); d->btnDown->setToolTip(tip); break; default: // qCDebug(KWidgetsAddonsLog)<<"KActionSelector::setButtonToolTip: DAINBREAD!"; break; } } void KActionSelector::setButtonWhatsThis(const QString &text, MoveButton button) { switch (button) { case ButtonAdd: d->btnAdd->setWhatsThis(text); break; case ButtonRemove: d->btnRemove->setWhatsThis(text); break; case ButtonUp: d->btnUp->setWhatsThis(text); break; case ButtonDown: d->btnDown->setWhatsThis(text); break; default: // qCDebug(KWidgetsAddonsLog)<<"KActionSelector::setButtonWhatsThis: DAINBREAD!"; break; } } //END Public Methods //BEGIN Properties bool KActionSelector::moveOnDoubleClick() const { return d->moveOnDoubleClick; } void KActionSelector::setMoveOnDoubleClick(bool b) { d->moveOnDoubleClick = b; } bool KActionSelector::keyboardEnabled() const { return d->keyboardEnabled; } void KActionSelector::setKeyboardEnabled(bool b) { d->keyboardEnabled = b; } QString KActionSelector::availableLabel() const { return d->lAvailable->text(); } void KActionSelector::setAvailableLabel(const QString &text) { d->lAvailable->setText(text); } QString KActionSelector::selectedLabel() const { return d->lSelected->text(); } void KActionSelector::setSelectedLabel(const QString &text) { d->lSelected->setText(text); } KActionSelector::InsertionPolicy KActionSelector::availableInsertionPolicy() const { return d->availableInsertionPolicy; } void KActionSelector::setAvailableInsertionPolicy(InsertionPolicy p) { d->availableInsertionPolicy = p; } KActionSelector::InsertionPolicy KActionSelector::selectedInsertionPolicy() const { return d->selectedInsertionPolicy; } void KActionSelector::setSelectedInsertionPolicy(InsertionPolicy p) { d->selectedInsertionPolicy = p; } bool KActionSelector::showUpDownButtons() const { return d->showUpDownButtons; } void KActionSelector::setShowUpDownButtons(bool show) { d->showUpDownButtons = show; if (show) { d->btnUp->show(); d->btnDown->show(); } else { d->btnUp->hide(); d->btnDown->hide(); } } //END Properties //BEGIN Public Slots void KActionSelector::setButtonsEnabled() { d->btnAdd->setEnabled(d->selectedRowIndex(d->availableListWidget) > -1); d->btnRemove->setEnabled(d->selectedRowIndex(d->selectedListWidget) > -1); d->btnUp->setEnabled(d->selectedRowIndex(d->selectedListWidget) > 0); d->btnDown->setEnabled(d->selectedRowIndex(d->selectedListWidget) > -1 && d->selectedRowIndex(d->selectedListWidget) < d->selectedListWidget->count() - 1); } //END Public Slots //BEGIN Protected void KActionSelector::keyPressEvent(QKeyEvent *e) { if (! d->keyboardEnabled) { return; } if ((e->modifiers() & Qt::ControlModifier)) { switch (e->key()) { case Qt::Key_Right: d->buttonAddClicked(); break; case Qt::Key_Left: d->buttonRemoveClicked(); break; case Qt::Key_Up: d->buttonUpClicked(); break; case Qt::Key_Down: d->buttonDownClicked(); break; default: e->ignore(); return; } } } bool KActionSelector::eventFilter(QObject *o, QEvent *e) { if (d->keyboardEnabled && e->type() == QEvent::KeyPress) { if ((((QKeyEvent *)e)->modifiers() & Qt::ControlModifier)) { switch (((QKeyEvent *)e)->key()) { case Qt::Key_Right: d->buttonAddClicked(); break; case Qt::Key_Left: d->buttonRemoveClicked(); break; case Qt::Key_Up: d->buttonUpClicked(); break; case Qt::Key_Down: d->buttonDownClicked(); break; default: return QWidget::eventFilter(o, e); } return true; } else if (QListWidget *lb = qobject_cast(o)) { switch (((QKeyEvent *)e)->key()) { case Qt::Key_Return: case Qt::Key_Enter: int index = lb->currentRow(); if (index < 0) { break; } d->moveItem(lb->item(index)); return true; } } } return QWidget::eventFilter(o, e); } //END Protected //BEGIN Private Slots void KActionSelectorPrivate::buttonAddClicked() { // move all selected items from available to selected listbox - QList list = availableListWidget->selectedItems(); - Q_FOREACH (QListWidgetItem *item, list) { + const QList list = availableListWidget->selectedItems(); + for (QListWidgetItem *item : list) { availableListWidget->takeItem(availableListWidget->row(item)); selectedListWidget->insertItem(insertionIndex(selectedListWidget, selectedInsertionPolicy), item); selectedListWidget->setCurrentItem(item); emit q->added(item); } if (selectedInsertionPolicy == KActionSelector::Sorted) { selectedListWidget->sortItems(); } selectedListWidget->setFocus(); } void KActionSelectorPrivate::buttonRemoveClicked() { // move all selected items from selected to available listbox - QList list = selectedListWidget->selectedItems(); - Q_FOREACH (QListWidgetItem *item, list) { + const QList list = selectedListWidget->selectedItems(); + for (QListWidgetItem *item : list) { selectedListWidget->takeItem(selectedListWidget->row(item)); availableListWidget->insertItem(insertionIndex(availableListWidget, availableInsertionPolicy), item); availableListWidget->setCurrentItem(item); emit q->removed(item); } if (availableInsertionPolicy == KActionSelector::Sorted) { availableListWidget->sortItems(); } availableListWidget->setFocus(); } void KActionSelectorPrivate::buttonUpClicked() { int c = selectedRowIndex(selectedListWidget); if (c < 1) { return; } QListWidgetItem *item = selectedListWidget->item(c); selectedListWidget->takeItem(c); selectedListWidget->insertItem(c - 1, item); selectedListWidget->setCurrentItem(item); emit q->movedUp(item); } void KActionSelectorPrivate::buttonDownClicked() { int c = selectedRowIndex(selectedListWidget); if (c < 0 || c == selectedListWidget->count() - 1) { return; } QListWidgetItem *item = selectedListWidget->item(c); selectedListWidget->takeItem(c); selectedListWidget->insertItem(c + 1, item); selectedListWidget->setCurrentItem(item); emit q->movedDown(item); } void KActionSelectorPrivate::itemDoubleClicked(QListWidgetItem *item) { if (moveOnDoubleClick) { moveItem(item); } } //END Private Slots //BEGIN Private Methods void KActionSelectorPrivate::loadIcons() { btnAdd->setIcon(QIcon::fromTheme(addIcon)); btnRemove->setIcon(QIcon::fromTheme(removeIcon)); btnUp->setIcon(QIcon::fromTheme(upIcon)); btnDown->setIcon(QIcon::fromTheme(downIcon)); } void KActionSelectorPrivate::moveItem(QListWidgetItem *item) { QListWidget *lbFrom = item->listWidget(); QListWidget *lbTo; if (lbFrom == availableListWidget) { lbTo = selectedListWidget; } else if (lbFrom == selectedListWidget) { lbTo = availableListWidget; } else { //?! somewhat unlikely... return; } KActionSelector::InsertionPolicy p = (lbTo == availableListWidget) ? availableInsertionPolicy : selectedInsertionPolicy; lbFrom->takeItem(lbFrom->row(item)); lbTo->insertItem(insertionIndex(lbTo, p), item); lbTo->setFocus(); lbTo->setCurrentItem(item); if (p == KActionSelector::Sorted) { lbTo->sortItems(); } if (lbTo == selectedListWidget) { emit q->added(item); } else { emit q->removed(item); } } int KActionSelectorPrivate::insertionIndex(QListWidget *lb, KActionSelector::InsertionPolicy policy) { int index; switch (policy) { case KActionSelector::BelowCurrent: index = lb->currentRow(); if (index > -1) { index += 1; } break; case KActionSelector::AtTop: index = 0; break; case KActionSelector::AtBottom: index = lb->count(); break; default: index = -1; } return index; } int KActionSelectorPrivate::selectedRowIndex(QListWidget *lb) { QList list = lb->selectedItems(); if (list.isEmpty()) { return -1; } return lb->row(list.at(0)); } //END Private Methods #include "moc_kactionselector.cpp" diff --git a/src/kcharselect.cpp b/src/kcharselect.cpp index 2e3680d..7f889b7 100644 --- a/src/kcharselect.cpp +++ b/src/kcharselect.cpp @@ -1,1137 +1,1139 @@ /* This file is part of the KDE libraries Copyright (C) 1999 Reginald Stadlbauer Copyright (C) 2017 Harald Sitter This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kcharselect.h" #include "kcharselect_p.h" #include "loggingcategory.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include Q_GLOBAL_STATIC(KCharSelectData, s_data) class KCharSelectTablePrivate { public: KCharSelectTablePrivate(KCharSelectTable *q): q(q), model(nullptr) {} KCharSelectTable *q; QFont font; KCharSelectItemModel *model; QVector chars; uint chr; void _k_resizeCells(); void _k_doubleClicked(const QModelIndex &index); void _k_slotSelectionChanged(const QItemSelection &selected, const QItemSelection &deselected); }; class Q_DECL_HIDDEN KCharSelect::KCharSelectPrivate { public: struct HistoryItem { uint c; bool fromSearch; QString searchString; }; enum { MaxHistoryItems = 100 }; KCharSelectPrivate(KCharSelect *q) : q(q) , searchLine(nullptr) , searchMode(false) , historyEnabled(false) , allPlanesEnabled(false) , inHistory(0) , actionParent(nullptr) { } QString tr(const char *str) { return KCharSelect::tr(str); } KCharSelect *q; QToolButton *backButton = nullptr; QToolButton *forwardButton = nullptr; QLineEdit *searchLine = nullptr; QFontComboBox *fontCombo = nullptr; QSpinBox *fontSizeSpinBox = nullptr; QComboBox *sectionCombo = nullptr; QComboBox *blockCombo = nullptr; KCharSelectTable *charTable = nullptr; QTextBrowser *detailBrowser = nullptr; bool searchMode; //a search is active bool historyEnabled; bool allPlanesEnabled; int inHistory; //index of current char in history QList history; QObject *actionParent; QString createLinks(QString s); void historyAdd(uint c, bool fromSearch, const QString &searchString); void showFromHistory(int index); void updateBackForwardButtons(); void _k_activateSearchLine(); void _k_back(); void _k_forward(); void _k_fontSelected(); void _k_charSelected(uint c); void _k_updateCurrentChar(uint c); void _k_slotUpdateUnicode(uint c); void _k_sectionSelected(int index); void _k_blockSelected(int index); void _k_searchEditChanged(); void _k_search(); void _k_linkClicked(QUrl url); }; /******************************************************************/ /* Class: KCharSelectTable */ /******************************************************************/ KCharSelectTable::KCharSelectTable(QWidget *parent, const QFont &_font) : QTableView(parent), d(new KCharSelectTablePrivate(this)) { d->font = _font; setTabKeyNavigation(false); setSelectionBehavior(QAbstractItemView::SelectItems); setSelectionMode(QAbstractItemView::SingleSelection); QPalette _palette; _palette.setColor(backgroundRole(), palette().color(QPalette::Base)); setPalette(_palette); verticalHeader()->setVisible(false); verticalHeader()->setSectionResizeMode(QHeaderView::Custom); horizontalHeader()->setVisible(false); horizontalHeader()->setSectionResizeMode(QHeaderView::Custom); setFocusPolicy(Qt::StrongFocus); setDragEnabled(true); setAcceptDrops(true); setDropIndicatorShown(false); setDragDropMode(QAbstractItemView::DragDrop); setTextElideMode(Qt::ElideNone); connect(this, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(_k_doubleClicked(QModelIndex))); d->_k_resizeCells(); } KCharSelectTable::~KCharSelectTable() { delete d; } void KCharSelectTable::setFont(const QFont &_font) { QTableView::setFont(_font); d->font = _font; if (d->model) { d->model->setFont(_font); } d->_k_resizeCells(); } uint KCharSelectTable::chr() { return d->chr; } QFont KCharSelectTable::font() const { return d->font; } QVector KCharSelectTable::displayedChars() const { return d->chars; } void KCharSelectTable::setChar(uint c) { int pos = d->chars.indexOf(c); if (pos != -1) { setCurrentIndex(model()->index(pos / model()->columnCount(), pos % model()->columnCount())); } } void KCharSelectTable::setContents(const QVector &chars) { d->chars = chars; auto oldModel = d->model; d->model = new KCharSelectItemModel(chars, d->font, this); setModel(d->model); d->_k_resizeCells(); // Setting a model changes the selectionModel. Make sure to always reconnect. connect(selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), this, SLOT(_k_slotSelectionChanged(QItemSelection,QItemSelection))); connect(d->model, &KCharSelectItemModel::showCharRequested, this, &KCharSelectTable::showCharRequested); delete oldModel; // The selection model is thrown away when the model gets destroyed(). } void KCharSelectTable::scrollTo(const QModelIndex &index, ScrollHint hint) { // this prevents horizontal scrolling when selecting a character in the last column if (index.isValid() && index.column() != 0) { QTableView::scrollTo(d->model->index(index.row(), 0), hint); } else { QTableView::scrollTo(index, hint); } } void KCharSelectTablePrivate::_k_slotSelectionChanged(const QItemSelection &selected, const QItemSelection &deselected) { Q_UNUSED(deselected); if (!model || selected.indexes().isEmpty()) { return; } QVariant temp = model->data(selected.indexes().at(0), KCharSelectItemModel::CharacterRole); if (temp.type() != QVariant::UInt) { return; } uint c = temp.toUInt(); chr = c; emit q->focusItemChanged(c); } void KCharSelectTable::resizeEvent(QResizeEvent *e) { QTableView::resizeEvent(e); if (e->size().width() != e->oldSize().width()) { // Resize our cells. But do so asynchronously through the event loop. // Otherwise we can end up with an infinite loop as resizing the cells in turn results in // a layout change which results in a resize event. More importantly doing this blockingly // crashes QAccessible as the resize we potentially cause will discard objects which are // still being used in the call chain leading to this event. // https://bugs.kde.org/show_bug.cgi?id=374933 // https://bugreports.qt.io/browse/QTBUG-58153 // This can be removed once a fixed Qt version is the lowest requirement for Frameworks. auto timer = new QTimer(this); timer->setSingleShot(true); connect(timer, &QTimer::timeout, [&,timer]() { d->_k_resizeCells(); timer->deleteLater(); }); timer->start(0); } } void KCharSelectTablePrivate::_k_resizeCells() { KCharSelectItemModel *model = static_cast(q->model()); if (!model) return; const int viewportWidth = q->viewport()->size().width(); QFontMetrics fontMetrics(font); // Determine the max width of the displayed characters // fontMetrics.maxWidth() doesn't help because of font fallbacks // (testcase: Malayalam characters) int maxCharWidth = 0; const QVector chars = model->chars(); for (int i = 0; i < chars.size(); ++i) { uint thisChar = chars.at(i); if(s_data()->isPrint(thisChar)) { maxCharWidth = qMax(maxCharWidth, fontMetrics.boundingRect(QString::fromUcs4(&thisChar, 1)).width()); } } // Avoid too narrow cells maxCharWidth = qMax(maxCharWidth, 2 * fontMetrics.xHeight()); maxCharWidth = qMax(maxCharWidth, fontMetrics.height()); // Add the necessary padding, trying to match the delegate const int textMargin = q->style()->pixelMetric(QStyle::PM_FocusFrameHMargin, nullptr, q) + 1; maxCharWidth += 2 * textMargin; const int columns = qMax(1, viewportWidth / maxCharWidth); model->setColumnCount(columns); const uint oldChar = q->chr(); const int new_w = viewportWidth / columns; const int rows = model->rowCount(); q->setUpdatesEnabled(false); QHeaderView *hHeader = q->horizontalHeader(); hHeader->setMinimumSectionSize(new_w); const int spaceLeft = viewportWidth - new_w * columns; for (int i = 0; i <= columns; ++i) { if (i < spaceLeft) { hHeader->resizeSection(i, new_w + 1); } else { hHeader->resizeSection(i, new_w); } } QHeaderView *vHeader = q->verticalHeader(); #ifdef Q_OS_WIN int new_h = fontMetrics.lineSpacing() + 1; #else int new_h = fontMetrics.xHeight() * 3; #endif const int fontHeight = fontMetrics.height(); if (new_h < 5 || new_h < 4 + fontHeight) { new_h = qMax(5, 4 + fontHeight); } vHeader->setMinimumSectionSize(new_h); for (int i = 0; i < rows; ++i) { vHeader->resizeSection(i, new_h); } q->setUpdatesEnabled(true); q->setChar(oldChar); } void KCharSelectTablePrivate::_k_doubleClicked(const QModelIndex &index) { uint c = model->data(index, KCharSelectItemModel::CharacterRole).toUInt(); if (s_data()->isPrint(c)) { emit q->activated(c); } } void KCharSelectTable::keyPressEvent(QKeyEvent *e) { if (d->model) { switch (e->key()) { case Qt::Key_Space: emit activated(QChar::Space); return; case Qt::Key_Enter: case Qt::Key_Return: { if (!currentIndex().isValid()) { return; } uint c = d->model->data(currentIndex(), KCharSelectItemModel::CharacterRole).toUInt(); if (s_data()->isPrint(c)) { emit activated(c); } return; } default: break; } } QTableView::keyPressEvent(e); } /******************************************************************/ /* Class: KCharSelect */ /******************************************************************/ #ifndef KWIDGETSADDONS_NO_DEPRECATED KCharSelect::KCharSelect(QWidget *parent, const Controls controls) : QWidget(parent), d(new KCharSelectPrivate(this)) { initWidget(controls, nullptr); } #endif KCharSelect::KCharSelect( QWidget *parent , QObject *actionParent , const Controls controls) : QWidget(parent), d(new KCharSelectPrivate(this)) { initWidget(controls, actionParent); } void attachToActionParent(QAction *action, QObject *actionParent, const QList &shortcuts) { if (!action || !actionParent) { return; } action->setParent(actionParent); if (actionParent->inherits("KActionCollection")) { QMetaObject::invokeMethod(actionParent, "addAction", Q_ARG(QString, action->objectName()), Q_ARG(QAction*, action)); QMetaObject::invokeMethod(actionParent, "setDefaultShortcuts", Q_ARG(QAction*, action), Q_ARG(QList, shortcuts)); } else { action->setShortcuts(shortcuts); } } void KCharSelect::initWidget(const Controls controls, QObject *actionParent) { d->actionParent = actionParent; QVBoxLayout *mainLayout = new QVBoxLayout(this); mainLayout->setContentsMargins(0, 0, 0, 0); if (SearchLine & controls) { QHBoxLayout *searchLayout = new QHBoxLayout(); mainLayout->addLayout(searchLayout); d->searchLine = new QLineEdit(this); searchLayout->addWidget(d->searchLine); d->searchLine->setPlaceholderText(tr("Enter a search term or character here")); d->searchLine->setClearButtonEnabled(true); d->searchLine->setToolTip(tr("Enter a search term or character here")); QAction *findAction = new QAction(this); connect(findAction, &QAction::triggered, this, [this]() { d->_k_activateSearchLine(); }); findAction->setObjectName(QStringLiteral("edit_find")); findAction->setText(tr("&Find...")); findAction->setIcon(QIcon::fromTheme(QStringLiteral("edit-find"))); attachToActionParent(findAction, actionParent, QKeySequence::keyBindings(QKeySequence::Find)); connect(d->searchLine, SIGNAL(textChanged(QString)), this, SLOT(_k_searchEditChanged())); connect(d->searchLine, SIGNAL(returnPressed()), this, SLOT(_k_search())); } if ((SearchLine & controls) && ((FontCombo & controls) || (FontSize & controls) || (BlockCombos & controls))) { QFrame *line = new QFrame(this); line->setFrameShape(QFrame::HLine); line->setFrameShadow(QFrame::Sunken); mainLayout->addWidget(line); } QHBoxLayout *comboLayout = new QHBoxLayout(); d->backButton = new QToolButton(this); comboLayout->addWidget(d->backButton); d->backButton->setEnabled(false); d->backButton->setText(tr("Previous in History", "Goes to previous character")); d->backButton->setIcon(QIcon::fromTheme(QStringLiteral("go-previous"))); d->backButton->setToolTip(tr("Previous Character in History")); d->forwardButton = new QToolButton(this); comboLayout->addWidget(d->forwardButton); d->forwardButton->setEnabled(false); d->forwardButton->setText(tr("Next in History", "Goes to next character")); d->forwardButton->setIcon(QIcon::fromTheme(QStringLiteral("go-next"))); d->forwardButton->setToolTip(tr("Next Character in History")); QAction *backAction = new QAction(this); connect(backAction, &QAction::triggered, d->backButton, &QAbstractButton::animateClick); backAction->setObjectName(QStringLiteral("go_back")); backAction->setText(tr("&Back", "go back")); backAction->setIcon(QIcon::fromTheme(QStringLiteral("go-previous"))); attachToActionParent(backAction, actionParent, QKeySequence::keyBindings(QKeySequence::Back)); QAction *forwardAction = new QAction(this); connect(forwardAction, &QAction::triggered, d->forwardButton, &QAbstractButton::animateClick); forwardAction->setObjectName(QStringLiteral("go_forward")); forwardAction->setText(tr("&Forward", "go forward")); forwardAction->setIcon(QIcon::fromTheme(QStringLiteral("go-next"))); attachToActionParent(forwardAction, actionParent, QKeySequence::keyBindings(QKeySequence::Forward)); if (QApplication::isRightToLeft()) { // swap the back/forward icons QIcon tmp = backAction->icon(); backAction->setIcon(forwardAction->icon()); forwardAction->setIcon(tmp); } connect(d->backButton, &QToolButton::clicked, this, [this]() { d->_k_back(); }); connect(d->forwardButton, &QToolButton::clicked, this, [this]() { d->_k_forward(); }); d->sectionCombo = new QComboBox(this); d->sectionCombo->setObjectName(QStringLiteral("sectionCombo")); d->sectionCombo->setToolTip(tr("Select a category")); comboLayout->addWidget(d->sectionCombo); d->blockCombo = new QComboBox(this); d->blockCombo->setObjectName(QStringLiteral("blockCombo")); d->blockCombo->setToolTip(tr("Select a block to be displayed")); d->blockCombo->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed); comboLayout->addWidget(d->blockCombo, 1); d->sectionCombo->addItems(s_data()->sectionList()); d->blockCombo->setMinimumWidth(QFontMetrics(QWidget::font()).averageCharWidth() * 25); connect(d->sectionCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(_k_sectionSelected(int))); connect(d->blockCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(_k_blockSelected(int))); d->fontCombo = new QFontComboBox(this); comboLayout->addWidget(d->fontCombo); d->fontCombo->setEditable(true); d->fontCombo->resize(d->fontCombo->sizeHint()); d->fontCombo->setToolTip(tr("Set font")); d->fontSizeSpinBox = new QSpinBox(this); comboLayout->addWidget(d->fontSizeSpinBox); d->fontSizeSpinBox->setValue(QWidget::font().pointSize()); d->fontSizeSpinBox->setRange(1, 400); d->fontSizeSpinBox->setSingleStep(1); d->fontSizeSpinBox->setToolTip(tr("Set font size")); connect(d->fontCombo, SIGNAL(currentIndexChanged(QString)), this, SLOT(_k_fontSelected())); connect(d->fontSizeSpinBox, SIGNAL(valueChanged(int)), this, SLOT(_k_fontSelected())); if ((HistoryButtons & controls) || (FontCombo & controls) || (FontSize & controls) || (BlockCombos & controls)) { mainLayout->addLayout(comboLayout); } if (!(HistoryButtons & controls)) { d->backButton->hide(); d->forwardButton->hide(); } if (!(FontCombo & controls)) { d->fontCombo->hide(); } if (!(FontSize & controls)) { d->fontSizeSpinBox->hide(); } if (!(BlockCombos & controls)) { d->sectionCombo->hide(); d->blockCombo->hide(); } QSplitter *splitter = new QSplitter(this); if ((CharacterTable & controls) || (DetailBrowser & controls)) { mainLayout->addWidget(splitter); } else { splitter->hide(); } d->charTable = new KCharSelectTable(this, QFont()); if (CharacterTable & controls) { splitter->addWidget(d->charTable); d->charTable->setFocus(Qt::OtherFocusReason); } else { d->charTable->hide(); } const QSize sz(200, 200); d->charTable->resize(sz); d->charTable->setMinimumSize(sz); d->charTable->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); setCurrentFont(QFont()); connect(d->charTable, SIGNAL(focusItemChanged(uint)), this, SLOT(_k_updateCurrentChar(uint))); connect(d->charTable, SIGNAL(activated(uint)), this, SLOT(_k_charSelected(uint))); connect(d->charTable, SIGNAL(showCharRequested(uint)), this, SLOT(setCurrentCodePoint(uint))); d->detailBrowser = new QTextBrowser(this); if (DetailBrowser & controls) { splitter->addWidget(d->detailBrowser); } else { d->detailBrowser->hide(); } d->detailBrowser->setOpenLinks(false); connect(d->detailBrowser, SIGNAL(anchorClicked(QUrl)), this, SLOT(_k_linkClicked(QUrl))); setFocusPolicy(Qt::StrongFocus); setFocusProxy(d->charTable); d->_k_sectionSelected(0); // this will also call _k_blockSelected(0) setCurrentCodePoint(QChar::Null); d->historyEnabled = true; } KCharSelect::~KCharSelect() { delete d; } QSize KCharSelect::sizeHint() const { return QWidget::sizeHint(); } void KCharSelect::setCurrentFont(const QFont &_font) { d->fontCombo->setCurrentFont(_font); d->fontSizeSpinBox->setValue(_font.pointSize()); d->_k_fontSelected(); } void KCharSelect::setAllPlanesEnabled(bool all) { d->allPlanesEnabled = all; } bool KCharSelect::allPlanesEnabled() const { return d->allPlanesEnabled; } QChar KCharSelect::currentChar() const { if (d->allPlanesEnabled) { qFatal("You must use KCharSelect::currentCodePoint instead of KCharSelect::currentChar"); } return QChar(d->charTable->chr()); } uint KCharSelect::currentCodePoint() const { return d->charTable->chr(); } QFont KCharSelect::currentFont() const { return d->charTable->font(); } QList KCharSelect::displayedChars() const { if (d->allPlanesEnabled) { qFatal("You must use KCharSelect::displayedCodePoints instead of KCharSelect::displayedChars"); } QList result; - foreach (uint c, d->charTable->displayedChars()) { + const auto displayedChars = d->charTable->displayedChars(); + result.reserve(displayedChars.size()); + for (uint c : displayedChars) { result.append(QChar(c)); } return result; } QVector KCharSelect::displayedCodePoints() const { return d->charTable->displayedChars(); } void KCharSelect::setCurrentChar(const QChar &c) { if (d->allPlanesEnabled) { qCritical("You should use KCharSelect::setCurrentCodePoint instead of KCharSelect::setCurrentChar"); } setCurrentCodePoint(c.unicode()); } void KCharSelect::setCurrentCodePoint(uint c) { if (!d->allPlanesEnabled && QChar::requiresSurrogates(c)) { qCritical("You must setAllPlanesEnabled(true) to use non-BMP characters"); c = QChar::ReplacementCharacter; } if (c > QChar::LastValidCodePoint) { qCWarning(KWidgetsAddonsLog, "Code point outside Unicode range"); c = QChar::LastValidCodePoint; } bool oldHistoryEnabled = d->historyEnabled; d->historyEnabled = false; int block = s_data()->blockIndex(c); int section = s_data()->sectionIndex(block); d->sectionCombo->setCurrentIndex(section); int index = d->blockCombo->findData(block); if (index != -1) { d->blockCombo->setCurrentIndex(index); } d->historyEnabled = oldHistoryEnabled; d->charTable->setChar(c); } void KCharSelect::KCharSelectPrivate::historyAdd(uint c, bool fromSearch, const QString &searchString) { //qCDebug(KWidgetsAddonsLog) << "about to add char" << c << "fromSearch" << fromSearch << "searchString" << searchString; if (!historyEnabled) { return; } if (!history.isEmpty() && c == history.last().c) { //avoid duplicates return; } //behave like a web browser, i.e. if user goes back from B to A then clicks C, B is forgotten while (!history.isEmpty() && inHistory != history.count() - 1) { history.removeLast(); } while (history.size() >= MaxHistoryItems) { history.removeFirst(); } HistoryItem item; item.c = c; item.fromSearch = fromSearch; item.searchString = searchString; history.append(item); inHistory = history.count() - 1; updateBackForwardButtons(); } void KCharSelect::KCharSelectPrivate::showFromHistory(int index) { Q_ASSERT(index >= 0 && index < history.count()); Q_ASSERT(index != inHistory); inHistory = index; updateBackForwardButtons(); const HistoryItem &item = history[index]; //qCDebug(KWidgetsAddonsLog) << "index" << index << "char" << item.c << "fromSearch" << item.fromSearch // << "searchString" << item.searchString; //avoid adding an item from history into history again bool oldHistoryEnabled = historyEnabled; historyEnabled = false; if (item.fromSearch) { if (searchLine->text() != item.searchString) { searchLine->setText(item.searchString); _k_search(); } charTable->setChar(item.c); } else { searchLine->clear(); q->setCurrentCodePoint(item.c); } historyEnabled = oldHistoryEnabled; } void KCharSelect::KCharSelectPrivate::updateBackForwardButtons() { backButton->setEnabled(inHistory > 0); forwardButton->setEnabled(inHistory < history.count() - 1); } void KCharSelect::KCharSelectPrivate::_k_activateSearchLine() { searchLine->setFocus(); searchLine->selectAll(); } void KCharSelect::KCharSelectPrivate::_k_back() { Q_ASSERT(inHistory > 0); showFromHistory(inHistory - 1); } void KCharSelect::KCharSelectPrivate::_k_forward() { Q_ASSERT(inHistory + 1 < history.count()); showFromHistory(inHistory + 1); } void KCharSelect::KCharSelectPrivate::_k_fontSelected() { QFont font = fontCombo->currentFont(); font.setPointSize(fontSizeSpinBox->value()); charTable->setFont(font); emit q->currentFontChanged(font); } void KCharSelect::KCharSelectPrivate::_k_charSelected(uint c) { if (!allPlanesEnabled) { emit q->charSelected(QChar(c)); } emit q->codePointSelected(c); } void KCharSelect::KCharSelectPrivate::_k_updateCurrentChar(uint c) { if (!allPlanesEnabled) { emit q->currentCharChanged(QChar(c)); } emit q->currentCodePointChanged(c); if (searchMode) { //we are in search mode. make the two comboboxes show the section & block for this character. //(when we are not in search mode the current character always belongs to the current section & block.) int block = s_data()->blockIndex(c); int section = s_data()->sectionIndex(block); sectionCombo->setCurrentIndex(section); int index = blockCombo->findData(block); if (index != -1) { blockCombo->setCurrentIndex(index); } } if (searchLine) { historyAdd(c, searchMode, searchLine->text()); } _k_slotUpdateUnicode(c); } void KCharSelect::KCharSelectPrivate::_k_slotUpdateUnicode(uint c) { QString html = QLatin1String("

") + tr("Character:") + QLatin1Char(' ') + s_data()->display(c, charTable->font()) + QLatin1Char(' ') + s_data()->formatCode(c) + QLatin1String("
"); QString name = s_data()->name(c); if (!name.isEmpty()) { //is name ever empty?

should always be there... html += tr("Name: ") + name.toHtmlEscaped() + QLatin1String("

"); } - QStringList aliases = s_data()->aliases(c); - QStringList notes = s_data()->notes(c); - QVector seeAlso = s_data()->seeAlso(c); - QStringList equivalents = s_data()->equivalents(c); - QStringList approxEquivalents = s_data()->approximateEquivalents(c); - QVector decomposition = s_data()->decomposition(c); + const QStringList aliases = s_data()->aliases(c); + const QStringList notes = s_data()->notes(c); + const QVector seeAlso = s_data()->seeAlso(c); + const QStringList equivalents = s_data()->equivalents(c); + const QStringList approxEquivalents = s_data()->approximateEquivalents(c); + const QVector decomposition = s_data()->decomposition(c); if (!(aliases.isEmpty() && notes.isEmpty() && seeAlso.isEmpty() && equivalents.isEmpty() && approxEquivalents.isEmpty() && decomposition.isEmpty())) { html += QLatin1String("

") + tr("Annotations and Cross References") + QLatin1String("

"); } if (!aliases.isEmpty()) { html += QLatin1String("

") + tr("Alias names:") + QLatin1String("

    "); - foreach (const QString &alias, aliases) { + for (const QString &alias : aliases) { html += QLatin1String("
  • ") + alias.toHtmlEscaped() + QLatin1String("
  • "); } html += QLatin1String("
"); } if (!notes.isEmpty()) { html += QLatin1String("

") + tr("Notes:") + QLatin1String("

    "); - foreach (const QString ¬e, notes) { + for (const QString ¬e : notes) { html += QLatin1String("
  • ") + createLinks(note.toHtmlEscaped()) + QLatin1String("
  • "); } html += QLatin1String("
"); } if (!seeAlso.isEmpty()) { html += QLatin1String("

") + tr("See also:") + QLatin1String("

"); } if (!equivalents.isEmpty()) { html += QLatin1String("

") + tr("Equivalents:") + QLatin1String("

    "); - foreach (const QString &equivalent, equivalents) { + for (const QString &equivalent : equivalents) { html += QLatin1String("
  • ") + createLinks(equivalent.toHtmlEscaped()) + QLatin1String("
  • "); } html += QLatin1String("
"); } if (!approxEquivalents.isEmpty()) { html += QLatin1String("

") + tr("Approximate equivalents:") + QLatin1String("

    "); - foreach (const QString &approxEquivalent, approxEquivalents) { + for (const QString &approxEquivalent : approxEquivalents) { html += QLatin1String("
  • ") + createLinks(approxEquivalent.toHtmlEscaped()) + QLatin1String("
  • "); } html += QLatin1String("
"); } if (!decomposition.isEmpty()) { html += QLatin1String("

") + tr("Decomposition:") + QLatin1String("

    "); - foreach (uint c2, decomposition) { + for (uint c2 : decomposition) { if (!allPlanesEnabled && QChar::requiresSurrogates(c2)) { continue; } html += QLatin1String("
  • ") + createLinks(s_data()->formatCode(c2, 4, QString())) + QLatin1String("
  • "); } html += QLatin1String("
"); } QStringList unihan = s_data()->unihanInfo(c); if (unihan.count() == 7) { html += QLatin1String("

") + tr("CJK Ideograph Information") + QLatin1String("

"); bool newline = true; if (!unihan[0].isEmpty()) { html += tr("Definition in English: ") + unihan[0]; newline = false; } if (!unihan[2].isEmpty()) { if (!newline) { html += QLatin1String("
"); } html += tr("Mandarin Pronunciation: ") + unihan[2]; newline = false; } if (!unihan[1].isEmpty()) { if (!newline) { html += QLatin1String("
"); } html += tr("Cantonese Pronunciation: ") + unihan[1]; newline = false; } if (!unihan[6].isEmpty()) { if (!newline) { html += QLatin1String("
"); } html += tr("Japanese On Pronunciation: ") + unihan[6]; newline = false; } if (!unihan[5].isEmpty()) { if (!newline) { html += QLatin1String("
"); } html += tr("Japanese Kun Pronunciation: ") + unihan[5]; newline = false; } if (!unihan[3].isEmpty()) { if (!newline) { html += QLatin1String("
"); } html += tr("Tang Pronunciation: ") + unihan[3]; newline = false; } if (!unihan[4].isEmpty()) { if (!newline) { html += QLatin1String("
"); } html += tr("Korean Pronunciation: ") + unihan[4]; newline = false; } html += QLatin1String("

"); } html += QLatin1String("

") + tr("General Character Properties") + QLatin1String("
"); html += tr("Block: ") + s_data()->block(c) + QLatin1String("
"); html += tr("Unicode category: ") + s_data()->categoryText(s_data()->category(c)) + QLatin1String("

"); - QByteArray utf8 = QString::fromUcs4(&c, 1).toUtf8(); + const QByteArray utf8 = QString::fromUcs4(&c, 1).toUtf8(); html += QLatin1String("

") + tr("Various Useful Representations") + QLatin1String("
"); html += tr("UTF-8:"); - foreach (unsigned char c, utf8) { + for (unsigned char c : utf8) { html += QLatin1Char(' ') + s_data()->formatCode(c, 2, QStringLiteral("0x")); } html += QLatin1String("
") + tr("UTF-16: "); if (QChar::requiresSurrogates(c)) { html += s_data()->formatCode(QChar::highSurrogate(c), 4, QStringLiteral("0x")); html += QLatin1Char(' ') + s_data->formatCode(QChar::lowSurrogate(c), 4, QStringLiteral("0x")); } else { html += s_data()->formatCode(c, 4, QStringLiteral("0x")); } html += QLatin1String("
") + tr("C octal escaped UTF-8: "); - foreach (unsigned char c, utf8) { + for (unsigned char c : utf8) { html += s_data()->formatCode(c, 3, QStringLiteral("\\"), 8); } html += QLatin1String("
") + tr("XML decimal entity:") + QLatin1String(" &#") + QString::number(c) + QLatin1String(";

"); detailBrowser->setHtml(html); } QString KCharSelect::KCharSelectPrivate::createLinks(QString s) { QRegExp rx(QStringLiteral("\\b([\\dABCDEF]{4,5})\\b")); QStringList chars; int pos = 0; while ((pos = rx.indexIn(s, pos)) != -1) { chars << rx.cap(1); pos += rx.matchedLength(); } - QSet chars2 = QSet::fromList(chars); - foreach (const QString &c, chars2) { + const QSet chars2 = QSet::fromList(chars); + for (const QString &c : chars2) { int unicode = c.toInt(nullptr, 16); if (!allPlanesEnabled && QChar::requiresSurrogates(unicode)) { continue; } QString link = QLatin1String(""); if (s_data()->isPrint(unicode)) { link += QLatin1String("‎&#") + QString::number(unicode) + QLatin1String("; "); } link += QLatin1String("U+") + c + QLatin1Char(' '); link += s_data()->name(unicode).toHtmlEscaped() + QLatin1String(""); s.replace(c, link); } return s; } void KCharSelect::KCharSelectPrivate::_k_sectionSelected(int index) { blockCombo->clear(); - QVector blocks = s_data()->sectionContents(index); - foreach (int block, blocks) { + const QVector blocks = s_data()->sectionContents(index); + for (int block : blocks) { if (!allPlanesEnabled) { const QVector contents = s_data()->blockContents(block); if (!contents.isEmpty() && QChar::requiresSurrogates(contents.at(0))) { continue; } } blockCombo->addItem(s_data()->blockName(block), QVariant(block)); } blockCombo->setCurrentIndex(0); } void KCharSelect::KCharSelectPrivate::_k_blockSelected(int index) { if (index == -1) { //the combo box has been cleared and is about to be filled again (because the section has changed) return; } if (searchMode) { //we are in search mode, so don't fill the table with this block. return; } int block = blockCombo->itemData(index).toInt(); const QVector contents = s_data()->blockContents(block); charTable->setContents(contents); emit q->displayedCharsChanged(); charTable->setChar(contents[0]); } void KCharSelect::KCharSelectPrivate::_k_searchEditChanged() { if (searchLine->text().isEmpty()) { sectionCombo->setEnabled(true); blockCombo->setEnabled(true); //upon leaving search mode, keep the same character selected searchMode = false; uint c = charTable->chr(); bool oldHistoryEnabled = historyEnabled; historyEnabled = false; _k_blockSelected(blockCombo->currentIndex()); historyEnabled = oldHistoryEnabled; q->setCurrentCodePoint(c); } else { sectionCombo->setEnabled(false); blockCombo->setEnabled(false); int length = searchLine->text().length(); if (length >= 3) { _k_search(); } } } void KCharSelect::KCharSelectPrivate::_k_search() { if (searchLine->text().isEmpty()) { return; } searchMode = true; QVector contents = s_data()->find(searchLine->text()); if (!allPlanesEnabled) { QVector::iterator it = contents.begin(); while (it != contents.end()) { if (QChar::requiresSurrogates(*it)) { it = contents.erase(it); } else { ++it; } } } charTable->setContents(contents); emit q->displayedCharsChanged(); if (!contents.isEmpty()) { charTable->setChar(contents[0]); } } void KCharSelect::KCharSelectPrivate::_k_linkClicked(QUrl url) { QString hex = url.toString(); if (hex.size() > 6) { return; } int unicode = hex.toInt(nullptr, 16); if (unicode > QChar::LastValidCodePoint) { return; } searchLine->clear(); q->setCurrentCodePoint(unicode); } //// QVariant KCharSelectItemModel::data(const QModelIndex &index, int role) const { int pos = m_columns * (index.row()) + index.column(); if (!index.isValid() || pos < 0 || pos >= m_chars.size() || index.row() < 0 || index.column() < 0) { if (role == Qt::BackgroundRole) { return QVariant(qApp->palette().color(QPalette::Button)); } return QVariant(); } uint c = m_chars[pos]; if (role == Qt::ToolTipRole) { QString result = s_data()->display(c, m_font) + QLatin1String("
") + s_data()->name(c).toHtmlEscaped() + QLatin1String("
") + tr("Unicode code point:") + QLatin1Char(' ') + s_data()->formatCode(c) + QLatin1String("
") + tr("In decimal", "Character") + QLatin1Char(' ') + QString::number(c); return QVariant(result); } else if (role == Qt::TextAlignmentRole) { return QVariant(Qt::AlignHCenter | Qt::AlignVCenter); } else if (role == Qt::DisplayRole) { if (s_data()->isPrint(c)) { return QVariant(QString::fromUcs4(&c, 1)); } return QVariant(); } else if (role == Qt::BackgroundRole) { QFontMetrics fm = QFontMetrics(m_font); if (fm.inFontUcs4(c) && s_data()->isPrint(c)) { return QVariant(qApp->palette().color(QPalette::Base)); } else { return QVariant(qApp->palette().color(QPalette::Button)); } } else if (role == Qt::FontRole) { return QVariant(m_font); } else if (role == CharacterRole) { return QVariant(c); } return QVariant(); } bool KCharSelectItemModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) { Q_UNUSED(row) Q_UNUSED(parent) if (action == Qt::IgnoreAction) { return true; } if (!data->hasText()) { return false; } if (column > 0) { return false; } QString text = data->text(); if (text.isEmpty()) { return false; } emit showCharRequested(text.toUcs4().at(0)); return true; } void KCharSelectItemModel::setColumnCount(int columns) { if (columns == m_columns) { return; } emit layoutAboutToBeChanged(); m_columns = columns; emit layoutChanged(); } #include "moc_kcharselect.cpp" #include "moc_kcharselect_p.cpp" diff --git a/src/kcharselectdata.cpp b/src/kcharselectdata.cpp index 3b01931..818c19b 100644 --- a/src/kcharselectdata.cpp +++ b/src/kcharselectdata.cpp @@ -1,1055 +1,1055 @@ /* This file is part of the KDE libraries Copyright (C) 2007 Daniel Laidig This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kcharselectdata_p.h" #include #include #include #include #include #include #include #include #include #include #include <../test-config.h> /* constants for hangul (de)composition, see UAX #15 */ #define SBase 0xAC00 #define LBase 0x1100 #define VBase 0x1161 #define TBase 0x11A7 #define LCount 19 #define VCount 21 #define TCount 28 #define NCount (VCount * TCount) #define SCount (LCount * NCount) class RunIndexCreation : public QFutureInterface, public QRunnable { public: RunIndexCreation(KCharSelectData *data, const QByteArray &dataFile) : m_data(data), m_dataFile(dataFile) { } QFuture start() { setRunnable(this); reportStarted(); QFuture f = this->future(); QThreadPool::globalInstance()->start(this); return f; } void run() override { Index index = m_data->createIndex(m_dataFile); reportResult(index); reportFinished(nullptr); } private: KCharSelectData *m_data; QByteArray m_dataFile; }; static const char JAMO_L_TABLE[][4] = { "G", "GG", "N", "D", "DD", "R", "M", "B", "BB", "S", "SS", "", "J", "JJ", "C", "K", "T", "P", "H" }; static const char JAMO_V_TABLE[][4] = { "A", "AE", "YA", "YAE", "EO", "E", "YEO", "YE", "O", "WA", "WAE", "OE", "YO", "U", "WEO", "WE", "WI", "YU", "EU", "YI", "I" }; static const char JAMO_T_TABLE[][4] = { "", "G", "GG", "GS", "N", "NJ", "NH", "D", "L", "LG", "LM", "LB", "LS", "LT", "LP", "LH", "M", "B", "BS", "S", "SS", "NG", "J", "C", "K", "T", "P", "H" }; bool KCharSelectData::openDataFile() { if (!dataFile.isEmpty()) { return true; } else { QString fileName = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("kf5/kcharselect/kcharselect-data")); if (fileName.isEmpty()) { fileName = QStringLiteral(TOP_SRCDIR "/src/kcharselect-data"); // for autotests before installation } QFile file(fileName); if (!file.open(QIODevice::ReadOnly)) { qWarning() << "Couldn't find kf5/kcharselect/kcharselect-data in the install prefix (under GenericDataLocation) nor in the builtin path" << TOP_SRCDIR; return false; } dataFile = file.readAll(); file.close(); if (dataFile.size() < 40) { dataFile.clear(); return false; } const uchar *data = reinterpret_cast(dataFile.constData()); const quint32 offsetBegin = qFromLittleEndian(data + 20); const quint32 offsetEnd = qFromLittleEndian(data + 24); uint blocks = (offsetEnd - offsetBegin) / 4; if (blocks <= 167) { // maximum possible number of blocks in BMP // no remapping remapType = -1; } else if (blocks >= 174 && blocks <= 178) { // remapping introduced in 5.25 remapType = 0; } else { // unknown remapping, abort dataFile.clear(); return false; } futureIndex = (new RunIndexCreation(this, dataFile))->start(); return true; } } // Temporary remapping code points <-> 16 bit database codes // See kcharselect-generate-datafile.py for details quint16 KCharSelectData::mapCodePointToDataBase(uint code) const { if (remapType == 0) { if (code >= 0xE000 && code <= 0xEFFF) { return 0xFFFF; } if (code >= 0xF000 && code <= 0xFFFF) { return code - 0x1000; } if (code >= 0x1F000 && code <= 0x1FFFF) { return code - 0x10000; } } if (code >= 0x10000) { return 0xFFFF; } return code; } uint KCharSelectData::mapDataBaseToCodePoint(quint16 code) const { if (remapType == 0) { if (code >= 0xE000 && code <= 0xEFFF) { return code + 0x1000; } if (code >= 0xF000) { return code + 0x10000; } } return code; } quint32 KCharSelectData::getDetailIndex(uint c) const { const uchar *data = reinterpret_cast(dataFile.constData()); // Convert from little-endian, so that this code works on PPC too. // http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=482286 const quint32 offsetBegin = qFromLittleEndian(data + 12); const quint32 offsetEnd = qFromLittleEndian(data + 16); int min = 0; int mid; int max = ((offsetEnd - offsetBegin) / 27) - 1; quint16 unicode = mapCodePointToDataBase(c); if (unicode == 0xFFFF) { return 0; } static quint16 most_recent_searched; static quint32 most_recent_result; if (unicode == most_recent_searched) { return most_recent_result; } most_recent_searched = unicode; while (max >= min) { mid = (min + max) / 2; const quint16 midUnicode = qFromLittleEndian(data + offsetBegin + mid * 27); if (unicode > midUnicode) { min = mid + 1; } else if (unicode < midUnicode) { max = mid - 1; } else { most_recent_result = offsetBegin + mid * 27; return most_recent_result; } } most_recent_result = 0; return 0; } QString KCharSelectData::formatCode(uint code, int length, const QString &prefix, int base) { QString s = QString::number(code, base).toUpper(); while (s.size() < length) { s.prepend(QLatin1Char('0')); } s.prepend(prefix); return s; } QVector KCharSelectData::blockContents(int block) { if (!openDataFile()) { return QVector(); } const uchar *data = reinterpret_cast(dataFile.constData()); const quint32 offsetBegin = qFromLittleEndian(data + 20); const quint32 offsetEnd = qFromLittleEndian(data + 24); int max = ((offsetEnd - offsetBegin) / 4) - 1; QVector res; if (block > max) { return res; } quint16 unicodeBegin = qFromLittleEndian(data + offsetBegin + block * 4); quint16 unicodeEnd = qFromLittleEndian(data + offsetBegin + block * 4 + 2); while (unicodeBegin < unicodeEnd) { res.append(mapDataBaseToCodePoint(unicodeBegin)); unicodeBegin++; } res.append(mapDataBaseToCodePoint(unicodeBegin)); // Be carefull when unicodeEnd==0xffff return res; } QVector KCharSelectData::sectionContents(int section) { if (!openDataFile()) { return QVector(); } const uchar *data = reinterpret_cast(dataFile.constData()); const quint32 offsetBegin = qFromLittleEndian(data + 28); const quint32 offsetEnd = qFromLittleEndian(data + 32); int max = ((offsetEnd - offsetBegin) / 4) - 1; QVector res; if (section > max) { return res; } for (int i = 0; i <= max; i++) { const quint16 currSection = qFromLittleEndian(data + offsetBegin + i * 4); if (currSection == section) { res.append(qFromLittleEndian(data + offsetBegin + i * 4 + 2)); } } return res; } QStringList KCharSelectData::sectionList() { if (!openDataFile()) { return QStringList(); } const uchar *udata = reinterpret_cast(dataFile.constData()); const quint32 stringBegin = qFromLittleEndian(udata + 24); const quint32 stringEnd = qFromLittleEndian(udata + 28); const char *data = dataFile.constData(); QStringList list; quint32 i = stringBegin; while (i < stringEnd) { list.append(QCoreApplication::translate("KCharSelectData", data + i, "KCharSelect section name")); i += qstrlen(data + i) + 1; } return list; } QString KCharSelectData::block(uint c) { return blockName(blockIndex(c)); } QString KCharSelectData::section(uint c) { return sectionName(sectionIndex(blockIndex(c))); } QString KCharSelectData::name(uint c) { if (!openDataFile()) { return QString(); } if ((c & 0xFFFE) == 0xFFFE || (c >= 0xFDD0 && c <= 0xFDEF)) { return QCoreApplication::translate("KCharSelectData", ""); } else if ((c >= 0x3400 && c <= 0x4DBF) || (c >= 0x4E00 && c <= 0x9FFF) || (c >= 0x20000 && c <= 0x2F7FF)) { return QLatin1String("CJK UNIFIED IDEOGRAPH-") + formatCode(c, 4, QString()); } else if (c >= 0xAC00 && c <= 0xD7AF) { /* compute hangul syllable name as per UAX #15 */ int SIndex = c - SBase; int LIndex, VIndex, TIndex; if (SIndex < 0 || SIndex >= SCount) { return QString(); } LIndex = SIndex / NCount; VIndex = (SIndex % NCount) / TCount; TIndex = SIndex % TCount; return QLatin1String("HANGUL SYLLABLE ") + QLatin1String(JAMO_L_TABLE[LIndex]) + QLatin1String(JAMO_V_TABLE[VIndex]) + QLatin1String(JAMO_T_TABLE[TIndex]); } else if (c >= 0xD800 && c <= 0xDB7F) { return QCoreApplication::translate("KCharSelectData", ""); } else if (c >= 0xDB80 && c <= 0xDBFF) { return QCoreApplication::translate("KCharSelectData", ""); } else if (c >= 0xDC00 && c <= 0xDFFF) { return QCoreApplication::translate("KCharSelectData", ""); } else if ((c >= 0xE000 && c <= 0xF8FF) || c >= 0xF0000) { return QCoreApplication::translate("KCharSelectData", ""); } else if ((c >= 0xF900 && c <= 0xFAFF) || (c >= 0x2F800 && c <= 0x2FFFF)) { return QLatin1String("CJK COMPATIBILITY IDEOGRAPH-") + formatCode(c, 4, QString()); } quint16 unicode = mapCodePointToDataBase(c); if (unicode == 0xFFFF) { return QLatin1String("NON-BMP-CHARACTER-") + formatCode(c, 4, QString()); } else { const uchar *data = reinterpret_cast(dataFile.constData()); const quint32 offsetBegin = qFromLittleEndian(data + 4); const quint32 offsetEnd = qFromLittleEndian(data + 8); int min = 0; int mid; int max = ((offsetEnd - offsetBegin) / 6) - 1; QString s; while (max >= min) { mid = (min + max) / 2; const quint16 midUnicode = qFromLittleEndian(data + offsetBegin + mid * 6); if (unicode > midUnicode) { min = mid + 1; } else if (unicode < midUnicode) { max = mid - 1; } else { quint32 offset = qFromLittleEndian(data + offsetBegin + mid * 6 + 2); s = QString::fromUtf8(dataFile.constData() + offset + 1); break; } } if (s.isNull()) { return QCoreApplication::translate("KCharSelectData", ""); } else { return s; } } } int KCharSelectData::blockIndex(uint c) { if (!openDataFile()) { return 0; } const uchar *data = reinterpret_cast(dataFile.constData()); const quint32 offsetBegin = qFromLittleEndian(data + 20); const quint32 offsetEnd = qFromLittleEndian(data + 24); const quint16 unicode = mapCodePointToDataBase(c); if (unicode == 0xFFFF) { return 0; } int max = ((offsetEnd - offsetBegin) / 4) - 1; int i = 0; while (unicode > qFromLittleEndian(data + offsetBegin + i * 4 + 2) && i < max) { i++; } return i; } int KCharSelectData::sectionIndex(int block) { if (!openDataFile()) { return 0; } const uchar *data = reinterpret_cast(dataFile.constData()); const quint32 offsetBegin = qFromLittleEndian(data + 28); const quint32 offsetEnd = qFromLittleEndian(data + 32); int max = ((offsetEnd - offsetBegin) / 4) - 1; for (int i = 0; i <= max; i++) { if (qFromLittleEndian(data + offsetBegin + i * 4 + 2) == block) { return qFromLittleEndian(data + offsetBegin + i * 4); } } return 0; } QString KCharSelectData::blockName(int index) { if (!openDataFile()) { return QString(); } const uchar *udata = reinterpret_cast(dataFile.constData()); const quint32 stringBegin = qFromLittleEndian(udata + 16); const quint32 stringEnd = qFromLittleEndian(udata + 20); quint32 i = stringBegin; int currIndex = 0; const char *data = dataFile.constData(); while (i < stringEnd && currIndex < index) { i += qstrlen(data + i) + 1; currIndex++; } return QCoreApplication::translate("KCharSelectData", data + i, "KCharselect unicode block name"); } QString KCharSelectData::sectionName(int index) { if (!openDataFile()) { return QString(); } const uchar *udata = reinterpret_cast(dataFile.constData()); const quint32 stringBegin = qFromLittleEndian(udata + 24); const quint32 stringEnd = qFromLittleEndian(udata + 28); quint32 i = stringBegin; int currIndex = 0; const char *data = dataFile.constData(); while (i < stringEnd && currIndex < index) { i += qstrlen(data + i) + 1; currIndex++; } return QCoreApplication::translate("KCharSelectData", data + i, "KCharselect unicode section name"); } QStringList KCharSelectData::aliases(uint c) { if (!openDataFile()) { return QStringList(); } const uchar *udata = reinterpret_cast(dataFile.constData()); const int detailIndex = getDetailIndex(c); if (detailIndex == 0) { return QStringList(); } const quint8 count = * (quint8 *)(udata + detailIndex + 6); quint32 offset = qFromLittleEndian(udata + detailIndex + 2); QStringList aliases; aliases.reserve(count); const char *data = dataFile.constData(); for (int i = 0; i < count; i++) { aliases.append(QString::fromUtf8(data + offset)); offset += qstrlen(data + offset) + 1; } return aliases; } QStringList KCharSelectData::notes(uint c) { if (!openDataFile()) { return QStringList(); } const int detailIndex = getDetailIndex(c); if (detailIndex == 0) { return QStringList(); } const uchar *udata = reinterpret_cast(dataFile.constData()); const quint8 count = * (quint8 *)(udata + detailIndex + 11); quint32 offset = qFromLittleEndian(udata + detailIndex + 7); QStringList notes; notes.reserve(count); const char *data = dataFile.constData(); for (int i = 0; i < count; i++) { notes.append(QString::fromUtf8(data + offset)); offset += qstrlen(data + offset) + 1; } return notes; } QVector KCharSelectData::seeAlso(uint c) { if (!openDataFile()) { return QVector(); } const int detailIndex = getDetailIndex(c); if (detailIndex == 0) { return QVector(); } const uchar *udata = reinterpret_cast(dataFile.constData()); const quint8 count = * (quint8 *)(udata + detailIndex + 26); quint32 offset = qFromLittleEndian(udata + detailIndex + 22); QVector seeAlso; seeAlso.reserve(count); for (int i = 0; i < count; i++) { seeAlso.append(mapDataBaseToCodePoint(qFromLittleEndian (udata + offset))); offset += 2; } return seeAlso; } QStringList KCharSelectData::equivalents(uint c) { if (!openDataFile()) { return QStringList(); } const int detailIndex = getDetailIndex(c); if (detailIndex == 0) { return QStringList(); } const uchar *udata = reinterpret_cast(dataFile.constData()); const quint8 count = * (quint8 *)(udata + detailIndex + 21); quint32 offset = qFromLittleEndian(udata + detailIndex + 17); QStringList equivalents; equivalents.reserve(count); const char *data = dataFile.constData(); for (int i = 0; i < count; i++) { equivalents.append(QString::fromUtf8(data + offset)); offset += qstrlen(data + offset) + 1; } return equivalents; } QStringList KCharSelectData::approximateEquivalents(uint c) { if (!openDataFile()) { return QStringList(); } const int detailIndex = getDetailIndex(c); if (detailIndex == 0) { return QStringList(); } const uchar *udata = reinterpret_cast(dataFile.constData()); const quint8 count = * (quint8 *)(udata + detailIndex + 16); quint32 offset = qFromLittleEndian(udata + detailIndex + 12); QStringList approxEquivalents; approxEquivalents.reserve(count); const char *data = dataFile.constData(); for (int i = 0; i < count; i++) { approxEquivalents.append(QString::fromUtf8(data + offset)); offset += qstrlen(data + offset) + 1; } return approxEquivalents; } QVector KCharSelectData::decomposition(uint c) { // for now, only decompose Hangul Syllable into Hangul Jamo uint SIndex = c - SBase; if (SIndex >= SCount) { return QVector(); } uint L = LBase + SIndex / NCount; // Choseong uint V = VBase + (SIndex % NCount) / TCount; // Jungseong uint T = TBase + SIndex % TCount; // Jongsung QVector jamoList; jamoList.append(L); jamoList.append(V); if (T != TBase) { jamoList.append(T); } return jamoList; } QStringList KCharSelectData::unihanInfo(uint c) { if (!openDataFile()) { return QStringList(); } quint16 unicode = mapCodePointToDataBase(c); if (unicode == 0xFFFF) { return QStringList(); } const char *data = dataFile.constData(); const uchar *udata = reinterpret_cast(data); const quint32 offsetBegin = qFromLittleEndian(udata + 36); const quint32 offsetEnd = dataFile.size(); int min = 0; int mid; int max = ((offsetEnd - offsetBegin) / 30) - 1; while (max >= min) { mid = (min + max) / 2; const quint16 midUnicode = qFromLittleEndian(udata + offsetBegin + mid * 30); if (unicode > midUnicode) { min = mid + 1; } else if (unicode < midUnicode) { max = mid - 1; } else { QStringList res; res.reserve(7); for (int i = 0; i < 7; i++) { quint32 offset = qFromLittleEndian(udata + offsetBegin + mid * 30 + 2 + i * 4); if (offset != 0) { res.append(QString::fromUtf8(data + offset)); } else { res.append(QString()); } } return res; } } return QStringList(); } QChar::Category KCharSelectData::category(uint c) { if (!openDataFile()) { return QChar::category(c); } ushort unicode = mapCodePointToDataBase(c); if (unicode == 0xFFFF) { return QChar::category(c); } const uchar *data = reinterpret_cast(dataFile.constData()); const quint32 offsetBegin = qFromLittleEndian(data + 4); const quint32 offsetEnd = qFromLittleEndian(data + 8); int min = 0; int mid; int max = ((offsetEnd - offsetBegin) / 6) - 1; while (max >= min) { mid = (min + max) / 2; const quint16 midUnicode = qFromLittleEndian(data + offsetBegin + mid * 6); if (unicode > midUnicode) { min = mid + 1; } else if (unicode < midUnicode) { max = mid - 1; } else { quint32 offset = qFromLittleEndian(data + offsetBegin + mid * 6 + 2); uchar categoryCode = *(data + offset); Q_ASSERT(categoryCode > 0); categoryCode--; /* Qt5 changed QChar::Category enum to start from 0 instead of 1 See QtBase commit d17c76feee9eece4 */ return QChar::Category(categoryCode); } } return QChar::category(c); } bool KCharSelectData::isPrint(uint c) { QChar::Category cat = category(c); return !(cat == QChar::Other_Control || cat == QChar::Other_NotAssigned); } bool KCharSelectData::isDisplayable(uint c) { // Qt internally uses U+FDD0 and U+FDD1 to mark the beginning and the end of frames. // They should be seen as non-printable characters, as trying to display them leads // to a crash caused by a Qt "noBlockInString" assertion. if (c == 0xFDD0 || c == 0xFDD1) { return false; } return !isIgnorable(c) && isPrint(c); } bool KCharSelectData::isIgnorable(uint c) { /* * According to the Unicode standard, Default Ignorable Code Points * should be ignored unless explicitly supported. For example, U+202E * RIGHT-TO-LEFT-OVERRIDE ir printable according to Qt, but displaying * it gives the undesired effect of all text being turned RTL. We do not * have a way to "explicitly" support it, so we will treat it as * non-printable. * * There is a list of these on * http://unicode.org/Public/UNIDATA/DerivedCoreProperties.txt under the * property Default_Ignorable_Code_Point. */ //NOTE: not very nice to hardcode these here; is it worth it to modify // the binary data file to hold them? return c == 0x00AD || c == 0x034F || c == 0x115F || c == 0x1160 || c == 0x17B4 || c == 0x17B5 || (c >= 0x180B && c <= 0x180D) || (c >= 0x200B && c <= 0x200F) || (c >= 0x202A && c <= 0x202E) || (c >= 0x2060 && c <= 0x206F) || c == 0x3164 || (c >= 0xFE00 && c <= 0xFE0F) || c == 0xFEFF || c == 0xFFA0 || (c >= 0xFFF0 && c <= 0xFFF8); } bool KCharSelectData::isCombining(uint c) { return section(c) == QCoreApplication::translate("KCharSelectData", "Combining Diacritics", "KCharSelect section name"); //FIXME: this is an imperfect test. There are many combining characters // that are outside of this section. See Grapheme_Extend in // http://www.unicode.org/Public/UNIDATA/DerivedCoreProperties.txt } QString KCharSelectData::display(uint c, const QFont &font) { if (!isDisplayable(c)) { return QLatin1String("") + QCoreApplication::translate("KCharSelectData", "Non-printable") + QLatin1String(""); } else { QString s = QLatin1String(""); if (isCombining(c)) { s += displayCombining(c); } else { s += QLatin1String("&#") + QString::number(c) + QLatin1Char(';'); } s += QLatin1String(""); return s; } } QString KCharSelectData::displayCombining(uint c) { /* * The purpose of this is to make it easier to see how a combining * character affects the text around it. * The initial plan was to use U+25CC DOTTED CIRCLE for this purpose, * as seen in pdfs from Unicode, but there seem to be a lot of alignment * problems with that. * * Eventually, it would be nice to determine whether the character * combines to the left or to the right, etc. */ QString s = QLatin1String(" &#") + QString::number(c) + QLatin1String("; ") + QLatin1String(" (ab&#") + QString::number(c) + QLatin1String(";c)"); return s; } QString KCharSelectData::categoryText(QChar::Category category) { switch (category) { case QChar::Other_Control: return QCoreApplication::translate("KCharSelectData", "Other, Control"); case QChar::Other_Format: return QCoreApplication::translate("KCharSelectData", "Other, Format"); case QChar::Other_NotAssigned: return QCoreApplication::translate("KCharSelectData", "Other, Not Assigned"); case QChar::Other_PrivateUse: return QCoreApplication::translate("KCharSelectData", "Other, Private Use"); case QChar::Other_Surrogate: return QCoreApplication::translate("KCharSelectData", "Other, Surrogate"); case QChar::Letter_Lowercase: return QCoreApplication::translate("KCharSelectData", "Letter, Lowercase"); case QChar::Letter_Modifier: return QCoreApplication::translate("KCharSelectData", "Letter, Modifier"); case QChar::Letter_Other: return QCoreApplication::translate("KCharSelectData", "Letter, Other"); case QChar::Letter_Titlecase: return QCoreApplication::translate("KCharSelectData", "Letter, Titlecase"); case QChar::Letter_Uppercase: return QCoreApplication::translate("KCharSelectData", "Letter, Uppercase"); case QChar::Mark_SpacingCombining: return QCoreApplication::translate("KCharSelectData", "Mark, Spacing Combining"); case QChar::Mark_Enclosing: return QCoreApplication::translate("KCharSelectData", "Mark, Enclosing"); case QChar::Mark_NonSpacing: return QCoreApplication::translate("KCharSelectData", "Mark, Non-Spacing"); case QChar::Number_DecimalDigit: return QCoreApplication::translate("KCharSelectData", "Number, Decimal Digit"); case QChar::Number_Letter: return QCoreApplication::translate("KCharSelectData", "Number, Letter"); case QChar::Number_Other: return QCoreApplication::translate("KCharSelectData", "Number, Other"); case QChar::Punctuation_Connector: return QCoreApplication::translate("KCharSelectData", "Punctuation, Connector"); case QChar::Punctuation_Dash: return QCoreApplication::translate("KCharSelectData", "Punctuation, Dash"); case QChar::Punctuation_Close: return QCoreApplication::translate("KCharSelectData", "Punctuation, Close"); case QChar::Punctuation_FinalQuote: return QCoreApplication::translate("KCharSelectData", "Punctuation, Final Quote"); case QChar::Punctuation_InitialQuote: return QCoreApplication::translate("KCharSelectData", "Punctuation, Initial Quote"); case QChar::Punctuation_Other: return QCoreApplication::translate("KCharSelectData", "Punctuation, Other"); case QChar::Punctuation_Open: return QCoreApplication::translate("KCharSelectData", "Punctuation, Open"); case QChar::Symbol_Currency: return QCoreApplication::translate("KCharSelectData", "Symbol, Currency"); case QChar::Symbol_Modifier: return QCoreApplication::translate("KCharSelectData", "Symbol, Modifier"); case QChar::Symbol_Math: return QCoreApplication::translate("KCharSelectData", "Symbol, Math"); case QChar::Symbol_Other: return QCoreApplication::translate("KCharSelectData", "Symbol, Other"); case QChar::Separator_Line: return QCoreApplication::translate("KCharSelectData", "Separator, Line"); case QChar::Separator_Paragraph: return QCoreApplication::translate("KCharSelectData", "Separator, Paragraph"); case QChar::Separator_Space: return QCoreApplication::translate("KCharSelectData", "Separator, Space"); default: return QCoreApplication::translate("KCharSelectData", "Unknown"); } } QVector KCharSelectData::find(const QString &needle) { QSet result; QVector returnRes; QString simplified = needle.simplified(); QStringList searchStrings; QRegularExpression octalExp(QStringLiteral("^\\\\[0-7][0-7\\\\]*$")); QRegularExpressionMatch match = octalExp.match(simplified); if (match.hasMatch()) { // search for C octal escaped UTF-8 QByteArray utf8; int byte = -1; for (int i = 0; i <= simplified.length(); ++i) { int c = simplified.at(i).unicode(); if (c >= '0' && c <= '7') { byte = 8 * byte + c - '0'; } else if (byte == -1) { byte = 0; } else if (byte >= 0x00 && byte <= 0xFF) { utf8.append((char) byte); byte = 0; } } simplified = QString::fromUtf8(utf8); } if (simplified.length() <= 2) { QVector ucs4 = simplified.toUcs4(); if (ucs4.size() == 1) { // search for hex representation of the character searchStrings = QStringList(formatCode(ucs4.at(0))); } } else { searchStrings = splitString(simplified); } if (searchStrings.isEmpty()) { return returnRes; } QRegularExpression hexExp(QStringLiteral("^(|u\\+|U\\+|0x|0X)([A-Fa-f0-9]{4,5})$")); foreach (const QString &s, searchStrings) { QRegularExpressionMatch match = hexExp.match(s); if (match.hasMatch()) { returnRes.append(match.captured(2).toInt(nullptr, 16)); // search for "1234" instead of "0x1234" if (s.length() == 6 || s.length() == 7) { searchStrings[searchStrings.indexOf(s)] = match.captured(2); } } // try to parse string as decimal number bool ok; int unicode = s.toInt(&ok); if (ok && unicode >= 0 && unicode <= QChar::LastValidCodePoint) { returnRes.append(unicode); } } bool firstSubString = true; - foreach (const QString &s, searchStrings) { + for (const QString &s : qAsConst(searchStrings)) { QSet partResult = getMatchingChars(s.toLower()); if (firstSubString) { result = partResult; firstSubString = false; } else { result = result.intersect(partResult); } } // remove results found by matching the code point to prevent duplicate results // while letting these characters stay at the beginning - foreach (uint c, returnRes) { + for (uint c : qAsConst(returnRes)) { result.remove(c); } QVector sortedResult; sortedResult.reserve(result.count()); for (auto c : qAsConst(result)) { sortedResult.append(c); } std::sort(sortedResult.begin(), sortedResult.end()); returnRes += sortedResult; return returnRes; } QSet KCharSelectData::getMatchingChars(const QString &s) { if (dataFile.isEmpty()) { return QSet(); } futureIndex.waitForFinished(); const Index index = futureIndex; Index::const_iterator pos = index.lowerBound(s); QSet result; while (pos != index.constEnd() && pos.key().startsWith(s)) { - foreach (quint16 c, pos.value()) { + for (quint16 c : pos.value()) { result.insert(mapDataBaseToCodePoint(c)); } ++pos; } return result; } QStringList KCharSelectData::splitString(const QString &s) { QStringList result; int start = 0; int end = 0; int length = s.length(); while (end < length) { while (end < length && (s[end].isLetterOrNumber() || s[end] == QLatin1Char('+'))) { end++; } if (start != end) { result.append(s.mid(start, end - start)); } start = end; while (end < length && !(s[end].isLetterOrNumber() || s[end] == QLatin1Char('+'))) { end++; start++; } } return result; } void KCharSelectData::appendToIndex(Index *index, quint16 unicode, const QString &s) { const QStringList strings = splitString(s); - foreach (const QString &s, strings) { + for (const QString &s : strings) { (*index)[s.toLower()].append(unicode); } } Index KCharSelectData::createIndex(const QByteArray &dataFile) { Index i; // character names const uchar *udata = reinterpret_cast(dataFile.constData()); const char *data = dataFile.constData(); const quint32 nameOffsetBegin = qFromLittleEndian(udata + 4); const quint32 nameOffsetEnd = qFromLittleEndian(udata + 8); int max = ((nameOffsetEnd - nameOffsetBegin) / 6) - 1; for (int pos = 0; pos <= max; pos++) { const quint16 unicode = qFromLittleEndian(udata + nameOffsetBegin + pos * 6); quint32 offset = qFromLittleEndian(udata + nameOffsetBegin + pos * 6 + 2); appendToIndex(&i, unicode, QString::fromUtf8(data + offset + 1)); } // details const quint32 detailsOffsetBegin = qFromLittleEndian(udata + 12); const quint32 detailsOffsetEnd = qFromLittleEndian(udata + 16); max = ((detailsOffsetEnd - detailsOffsetBegin) / 27) - 1; for (int pos = 0; pos <= max; pos++) { const quint16 unicode = qFromLittleEndian(udata + detailsOffsetBegin + pos * 27); // aliases const quint8 aliasCount = * (quint8 *)(udata + detailsOffsetBegin + pos * 27 + 6); quint32 aliasOffset = qFromLittleEndian(udata + detailsOffsetBegin + pos * 27 + 2); for (int j = 0; j < aliasCount; j++) { appendToIndex(&i, unicode, QString::fromUtf8(data + aliasOffset)); aliasOffset += qstrlen(data + aliasOffset) + 1; } // notes const quint8 notesCount = * (quint8 *)(udata + detailsOffsetBegin + pos * 27 + 11); quint32 notesOffset = qFromLittleEndian(udata + detailsOffsetBegin + pos * 27 + 7); for (int j = 0; j < notesCount; j++) { appendToIndex(&i, unicode, QString::fromUtf8(data + notesOffset)); notesOffset += qstrlen(data + notesOffset) + 1; } // approximate equivalents const quint8 apprCount = * (quint8 *)(udata + detailsOffsetBegin + pos * 27 + 16); quint32 apprOffset = qFromLittleEndian(udata + detailsOffsetBegin + pos * 27 + 12); for (int j = 0; j < apprCount; j++) { appendToIndex(&i, unicode, QString::fromUtf8(data + apprOffset)); apprOffset += qstrlen(data + apprOffset) + 1; } // equivalents const quint8 equivCount = * (quint8 *)(udata + detailsOffsetBegin + pos * 27 + 21); quint32 equivOffset = qFromLittleEndian(udata + detailsOffsetBegin + pos * 27 + 17); for (int j = 0; j < equivCount; j++) { appendToIndex(&i, unicode, QString::fromUtf8(data + equivOffset)); equivOffset += qstrlen(data + equivOffset) + 1; } // see also - convert to string (hex) const quint8 seeAlsoCount = * (quint8 *)(udata + detailsOffsetBegin + pos * 27 + 26); quint32 seeAlsoOffset = qFromLittleEndian(udata + detailsOffsetBegin + pos * 27 + 22); for (int j = 0; j < seeAlsoCount; j++) { quint16 seeAlso = qFromLittleEndian (udata + seeAlsoOffset); appendToIndex(&i, unicode, formatCode(seeAlso, 4, QString())); equivOffset += qstrlen(data + equivOffset) + 1; } } // unihan data // temporary disabled due to the huge amount of data // const quint32 unihanOffsetBegin = qFromLittleEndian(udata+36); // const quint32 unihanOffsetEnd = dataFile.size(); // max = ((unihanOffsetEnd - unihanOffsetBegin) / 30) - 1; // // for (int pos = 0; pos <= max; pos++) { // const quint16 unicode = qFromLittleEndian(udata + unihanOffsetBegin + pos*30); // for(int j = 0; j < 7; j++) { // quint32 offset = qFromLittleEndian(udata + unihanOffsetBegin + pos*30 + 2 + j*4); // if(offset != 0) { // appendToIndex(&i, unicode, QString::fromUtf8(data + offset)); // } // } // } return i; } diff --git a/src/kcollapsiblegroupbox.cpp b/src/kcollapsiblegroupbox.cpp index 5ee57a8..f99c6cf 100644 --- a/src/kcollapsiblegroupbox.cpp +++ b/src/kcollapsiblegroupbox.cpp @@ -1,367 +1,368 @@ /* * This file is part of the KDE project * Copyright (C) 2015 David Edmundson * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "kcollapsiblegroupbox.h" #include #include #include #include #include #include #include class KCollapsibleGroupBoxPrivate { public: KCollapsibleGroupBoxPrivate(KCollapsibleGroupBox *q); void updateChildrenFocus(bool expanded); void recalculateHeaderSize(); QSize contentSize() const; QSize contentMinimumSize() const; KCollapsibleGroupBox *q; QTimeLine *animation; QString title; bool isExpanded; bool headerContainsMouse; QSize headerSize; int shortcutId; QMap focusMap; // Used to restore focus policy of widgets. }; KCollapsibleGroupBoxPrivate::KCollapsibleGroupBoxPrivate(KCollapsibleGroupBox* q): q(q), isExpanded(false), headerContainsMouse(false), shortcutId(0) {} KCollapsibleGroupBox::KCollapsibleGroupBox(QWidget* parent): QWidget(parent), d(new KCollapsibleGroupBoxPrivate(this)) { d->recalculateHeaderSize(); d->animation = new QTimeLine(500, this); //duration matches kmessagewidget connect(d->animation, &QTimeLine::valueChanged, this, [this](qreal value) { setFixedHeight((d->contentSize().height() * value) + d->headerSize.height()); }); connect(d->animation, &QTimeLine::stateChanged, this, [this](QTimeLine::State state) { if (state == QTimeLine::NotRunning) { d->updateChildrenFocus(d->isExpanded); } }); setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed); setFocusPolicy(Qt::TabFocus); setMouseTracking(true); } KCollapsibleGroupBox::~KCollapsibleGroupBox() { if (d->animation->state() == QTimeLine::Running) { d->animation->stop(); } delete d; } void KCollapsibleGroupBox::setTitle(const QString& title) { d->title = title; d->recalculateHeaderSize(); update(); updateGeometry(); if (d->shortcutId) { releaseShortcut(d->shortcutId); } d->shortcutId = grabShortcut(QKeySequence::mnemonic(title)); #ifndef QT_NO_ACCESSIBILITY setAccessibleName(title); #endif emit titleChanged(); } QString KCollapsibleGroupBox::title() const { return d->title; } void KCollapsibleGroupBox::setExpanded(bool expanded) { if (expanded == d->isExpanded) { return; } d->isExpanded = expanded; emit expandedChanged(); d->updateChildrenFocus(expanded); d->animation->setDirection(expanded ? QTimeLine::Forward : QTimeLine::Backward); // QTimeLine::duration() must be > 0 const int duration = qMax(1, style()->styleHint(QStyle::SH_Widget_Animation_Duration)); d->animation->setDuration(duration); d->animation->start(); //when going from collapsed to expanded changing the child visibility calls an updateGeometry //which calls sizeHint with expanded true before the first frame of the animation kicks in //trigger an effective frame 0 if (expanded) { setFixedHeight(d->headerSize.height()); } } bool KCollapsibleGroupBox::isExpanded() const { return d->isExpanded; } void KCollapsibleGroupBox::collapse() { setExpanded(false); } void KCollapsibleGroupBox::expand() { setExpanded(true); } void KCollapsibleGroupBox::toggle() { setExpanded(!d->isExpanded); } void KCollapsibleGroupBox::paintEvent(QPaintEvent *event) { QPainter p(this); QStyleOptionButton baseOption; baseOption.initFrom(this); baseOption.rect = QRect(0, 0, width(), d->headerSize.height()); baseOption.text = d->title; if (d->headerContainsMouse) { baseOption.state |= QStyle::State_MouseOver; } QStyle::PrimitiveElement element; if (d->isExpanded) { element = QStyle::PE_IndicatorArrowDown; } else { element = isLeftToRight() ? QStyle::PE_IndicatorArrowRight : QStyle::PE_IndicatorArrowLeft; } QStyleOptionButton indicatorOption = baseOption; indicatorOption.rect = style()->subElementRect(QStyle::SE_CheckBoxIndicator, &indicatorOption, this); style()->drawPrimitive(element, &indicatorOption, &p, this); QStyleOptionButton labelOption = baseOption; labelOption.rect = style()->subElementRect(QStyle::SE_CheckBoxContents, &labelOption, this); style()->drawControl(QStyle::CE_CheckBoxLabel, &labelOption, &p, this); Q_UNUSED(event) } QSize KCollapsibleGroupBox::sizeHint() const { if (d->isExpanded) { return d->contentSize() + QSize(0, d->headerSize.height()); } else { return QSize(d->contentSize().width(), d->headerSize.height()); } } QSize KCollapsibleGroupBox::minimumSizeHint() const { int minimumWidth = qMax(d->contentSize().width(), d->headerSize.width()); return QSize(minimumWidth, d->headerSize.height()); } bool KCollapsibleGroupBox::event(QEvent *event) { switch (event->type()) { case QEvent::StyleChange: /*fall through*/ case QEvent::FontChange: d->recalculateHeaderSize(); break; case QEvent::Shortcut: { QShortcutEvent *se = static_cast(event); if(d->shortcutId == se->shortcutId()) { toggle(); return true; } break; } case QEvent::ChildAdded: { QChildEvent *ce = static_cast(event); if (ce->child()->isWidgetType()) { auto widget = static_cast(ce->child()); // Needs to be called asynchronously because at this point the widget is likely a "real" QWidget, // i.e. the QWidget base class whose constructor sets the focus policy to NoPolicy. // But the constructor of the child class (not yet called) could set a different focus policy later. QMetaObject::invokeMethod(this, "overrideFocusPolicyOf", Qt::QueuedConnection, Q_ARG(QWidget*, widget)); } break; } case QEvent::LayoutRequest: if (d->animation->state() == QTimeLine::NotRunning) { setFixedHeight(sizeHint().height()); } break; default: break; } return QWidget::event(event); } void KCollapsibleGroupBox::mousePressEvent(QMouseEvent *event) { const QRect headerRect(0, 0, width(), d->headerSize.height()); if (headerRect.contains(event->pos())) { toggle(); } event->setAccepted(true); } //if mouse has changed whether it is in the top bar or not refresh to change arrow icon void KCollapsibleGroupBox::mouseMoveEvent(QMouseEvent *event) { const QRect headerRect(0, 0, width(), d->headerSize.height()); bool headerContainsMouse = headerRect.contains(event->pos()); if (headerContainsMouse != d->headerContainsMouse) { d->headerContainsMouse = headerContainsMouse; update(); } QWidget::mouseMoveEvent(event); } void KCollapsibleGroupBox::leaveEvent(QEvent *event) { d->headerContainsMouse = false; update(); QWidget::leaveEvent(event); } void KCollapsibleGroupBox::keyPressEvent(QKeyEvent *event) { //event might have just propagated up from a child, if so we don't want to react to it if (!hasFocus()) { return; } const int key = event->key(); if (key == Qt::Key_Space || key == Qt::Key_Enter || key == Qt::Key_Return) { toggle(); event->setAccepted(true); } } void KCollapsibleGroupBox::resizeEvent(QResizeEvent *event) { int top, left, right, bottom; getContentsMargins(&left, &top, &right, &bottom); if (layout()) { //we don't want the layout trying to fit the current frame of the animation so always set it to the target height layout()->setGeometry(QRect(left, top, width() - left - right, layout()->sizeHint().height())); } Q_UNUSED(bottom) QWidget::resizeEvent(event); } void KCollapsibleGroupBox::overrideFocusPolicyOf(QWidget *widget) { // https://bugs.kde.org/show_bug.cgi?id=396450 // A label with word-wrapping enabled will break positioning of the groupbox in the layout. // The cause seems to be the setFocusPolicy() call below, but it's not clear why. // Until a proper fix is found, as workaround we toggle twice the groupbox which fixes the issue. if (auto label = qobject_cast(widget)) { if (label->wordWrap()) { toggle(); toggle(); } } d->focusMap.insert(widget, widget->focusPolicy()); if (!isExpanded()) { // Prevent tab focus if not expanded. widget->setFocusPolicy(Qt::NoFocus); } } void KCollapsibleGroupBoxPrivate::recalculateHeaderSize() { QStyleOption option; option.initFrom(q); QSize textSize = q->style()->itemTextRect(option.fontMetrics, QRect(), Qt::TextShowMnemonic, false, title).size(); headerSize = q->style()->sizeFromContents(QStyle::CT_CheckBox, &option, textSize, q); q->setContentsMargins(q->style()->pixelMetric(QStyle::PM_IndicatorWidth), headerSize.height(), 0, 0); } void KCollapsibleGroupBoxPrivate::updateChildrenFocus(bool expanded) { - foreach (QObject *child, q->children()) { + const auto children = q->children(); + for (QObject *child : children) { QWidget *widget = qobject_cast(child); if (!widget) { continue; } // Restore old focus policy if expanded, remove from focus chain otherwise. if (expanded) { widget->setFocusPolicy(focusMap.value(widget)); } else { widget->setFocusPolicy(Qt::NoFocus); } } } QSize KCollapsibleGroupBoxPrivate::contentSize() const { if (q->layout()) { const QMargins margins = q->contentsMargins(); const QSize marginSize(margins.left() + margins.right(), margins.top() + margins.bottom()); return q->layout()->sizeHint() + marginSize; } return QSize(0,0); } QSize KCollapsibleGroupBoxPrivate::contentMinimumSize() const { if (q->layout()) { const QMargins margins = q->contentsMargins(); const QSize marginSize(margins.left() + margins.right(), margins.top() + margins.bottom()); return q->layout()->minimumSize() + marginSize; } return QSize(0,0); } diff --git a/src/kcolumnresizer.cpp b/src/kcolumnresizer.cpp index 9ba0ec7..b39d8f4 100644 --- a/src/kcolumnresizer.cpp +++ b/src/kcolumnresizer.cpp @@ -1,249 +1,249 @@ /* This file is part of the KDE frameworks * * Copyright (c) 2014 Aurélien Gâteau * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA */ #include #include "loggingcategory.h" #include #include #include #include #include #include class FormLayoutWidgetItem : public QWidgetItem { public: FormLayoutWidgetItem(QWidget *widget, QFormLayout *formLayout, QFormLayout::ItemRole itemRole) : QWidgetItem(widget) , m_formLayout(formLayout) , m_width(-1) , m_itemRole(itemRole) {} void setWidth(int width) { if (width != m_width) { m_width = width; invalidate(); } } QFormLayout *formLayout() const { return m_formLayout; } QSize sizeHint() const override { QSize size = QWidgetItem::sizeHint(); if (m_width != -1) { size.setWidth(m_width); } return size; } QSize minimumSize() const override { QSize size = QWidgetItem::minimumSize(); if (m_width != -1) { size.setWidth(m_width); } return size; } QSize maximumSize() const override { QSize size = QWidgetItem::maximumSize(); if (m_width != -1) { size.setWidth(m_width); } return size; } void setGeometry(const QRect &_rect) override { QRect rect = _rect; int width = widget()->sizeHint().width(); if (m_itemRole == QFormLayout::LabelRole && m_formLayout->labelAlignment() & Qt::AlignRight) { rect.setLeft(rect.right() - width); } QWidgetItem::setGeometry(rect); } private: QFormLayout *m_formLayout; int m_width; QFormLayout::ItemRole m_itemRole; }; struct GridColumnInfo { GridColumnInfo(QGridLayout *layout_, int column_) : layout(layout_) , column(column_) {} QGridLayout *layout; int column; }; class KColumnResizerPrivate { public: KColumnResizerPrivate(KColumnResizer *q_ptr) : q(q_ptr) , m_updateTimer(new QTimer(q)) { m_updateTimer->setSingleShot(true); m_updateTimer->setInterval(0); QObject::connect(m_updateTimer, SIGNAL(timeout()), q, SLOT(updateWidth())); } void scheduleWidthUpdate() { m_updateTimer->start(); } void updateWidth() { int width = 0; - Q_FOREACH(QWidget *widget, m_widgets) { + for (QWidget *widget : qAsConst(m_widgets)) { width = qMax(widget->sizeHint().width(), width); } - Q_FOREACH(FormLayoutWidgetItem *item, m_formWidgetItemList) { + for (FormLayoutWidgetItem *item : qAsConst(m_formWidgetItemList)) { item->setWidth(width); item->formLayout()->update(); } - Q_FOREACH(const GridColumnInfo &info, m_gridColumnInfoList) { + for (const GridColumnInfo &info : qAsConst(m_gridColumnInfoList)) { info.layout->setColumnMinimumWidth(info.column, width); } } void addWidgetsFromGridLayout(QGridLayout *layout, int column) { for (int row = 0; row < layout->rowCount(); ++row) { QLayoutItem *item = layout->itemAtPosition(row, column); if (!item) { continue; } QWidget *widget = item->widget(); if (!widget) { continue; } q->addWidget(widget); } m_gridColumnInfoList << GridColumnInfo(layout, column); } void addWidgetsFromFormLayout(QFormLayout *layout, QFormLayout::ItemRole role) { for (int row = 0; row < layout->rowCount(); ++row) { QLayoutItem *item = layout->itemAt(row, role); if (!item) { continue; } QWidget *widget = item->widget(); if (!widget) { continue; } // Replace the QWidgetItem with our own layout->removeItem(item); delete item; FormLayoutWidgetItem *newItem = new FormLayoutWidgetItem(widget, layout, role); layout->setItem(row, role, newItem); m_formWidgetItemList << newItem; q->addWidget(widget); } } KColumnResizer *q; QTimer *m_updateTimer; QSet m_widgets; QList m_formWidgetItemList; QList m_gridColumnInfoList; }; KColumnResizer::KColumnResizer(QObject *parent) : QObject(parent) , d(new KColumnResizerPrivate(this)) {} KColumnResizer::~KColumnResizer() { delete d; } void KColumnResizer::addWidget(QWidget *widget) { if (d->m_widgets.contains(widget)) { return; } d->m_widgets.insert(widget); widget->installEventFilter(this); d->scheduleWidthUpdate(); } void KColumnResizer::removeWidget(QWidget *widget) { if (!d->m_widgets.remove(widget)) { return; } widget->removeEventFilter(this); d->scheduleWidthUpdate(); } bool KColumnResizer::eventFilter(QObject *, QEvent *event) { if (event->type() == QEvent::Resize) { d->scheduleWidthUpdate(); } return false; } void KColumnResizer::addWidgetsFromLayout(QLayout *layout, int column) { Q_ASSERT(column >= 0); if (column < 0) { qCWarning(KWidgetsAddonsLog) << "column must be >= 0"; return; } QGridLayout *gridLayout = qobject_cast(layout); if (gridLayout) { d->addWidgetsFromGridLayout(gridLayout, column); return; } QFormLayout *formLayout = qobject_cast(layout); if (formLayout) { Q_ASSERT(column <= QFormLayout::SpanningRole); if (column > QFormLayout::SpanningRole) { qCWarning(KWidgetsAddonsLog) << "column should not be more than" << QFormLayout::SpanningRole << "for QFormLayout"; return; } QFormLayout::ItemRole role = static_cast(column); d->addWidgetsFromFormLayout(formLayout, role); } else { qCWarning(KWidgetsAddonsLog) << "Don't know how to handle layout" << layout; Q_ASSERT(0); } } #include // vi: ts=4 sw=4 et diff --git a/src/kdatetimeedit.cpp b/src/kdatetimeedit.cpp index fd36c25..f011a98 100644 --- a/src/kdatetimeedit.cpp +++ b/src/kdatetimeedit.cpp @@ -1,555 +1,557 @@ /* Copyright 2011 John Layt This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kdatetimeedit.h" #include "kdatepicker.h" #include "kdatecombobox.h" #include "kmessagebox.h" #include "ui_kdatetimeedit.h" class KDateTimeEditPrivate { public: KDateTimeEditPrivate(KDateTimeEdit *q); virtual ~KDateTimeEditPrivate(); QDateTime defaultMinDateTime(); QDateTime defaultMaxDateTime(); void initWidgets(); void initDateWidget(); void initTimeWidget(); void initCalendarWidget(); void updateCalendarWidget(); void initTimeZoneWidget(); void updateTimeZoneWidget(); void warnDateTime(); //private Q_SLOTS: void selectCalendar(int index); void enterCalendar(const QLocale &calendarLocale); void selectTimeZone(int index); void enterTimeZone(const QByteArray &zoneId); KDateTimeEdit *const q; KDateTimeEdit::Options m_options; QDateTime m_dateTime; QDateTime m_minDateTime; QDateTime m_maxDateTime; QString m_minWarnMsg; QString m_maxWarnMsg; QList m_calendarLocales; QList m_zones; Ui::KDateTimeEdit ui; }; KDateTimeEditPrivate::KDateTimeEditPrivate(KDateTimeEdit *q) : q(q) { m_options = KDateTimeEdit::ShowDate | KDateTimeEdit::EditDate | KDateTimeEdit::SelectDate | KDateTimeEdit::ShowTime | KDateTimeEdit::EditTime | KDateTimeEdit::SelectTime | KDateTimeEdit::DatePicker | KDateTimeEdit::DateKeywords; m_dateTime = QDateTime::currentDateTime(); m_dateTime.setTime(QTime(0, 0, 0)); m_calendarLocales << q->locale(); - foreach (const QByteArray &zoneId, QTimeZone::availableTimeZoneIds()) { + const auto zoneIds = QTimeZone::availableTimeZoneIds(); + m_zones.reserve(zoneIds.size()); + for (const QByteArray &zoneId : zoneIds) { m_zones << QTimeZone(zoneId); } } KDateTimeEditPrivate::~KDateTimeEditPrivate() { } QDateTime KDateTimeEditPrivate::defaultMinDateTime() { // TODO: Find a way to get it from QLocale //return KDateTime(calendar()->earliestValidDate(), QTime(0, 0, 0, 0)); return QDateTime(); } QDateTime KDateTimeEditPrivate::defaultMaxDateTime() { // TODO: Find a way to get it from QLocale //return KDateTime(calendar()->latestValidDate(), QTime(23, 59, 59, 999)); return QDateTime(); } void KDateTimeEditPrivate::initWidgets() { initDateWidget(); initTimeWidget(); initCalendarWidget(); initTimeZoneWidget(); } void KDateTimeEditPrivate::initDateWidget() { ui.m_dateCombo->blockSignals(true); ui.m_dateCombo->setVisible((m_options & KDateTimeEdit::ShowDate) == KDateTimeEdit::ShowDate); KDateComboBox::Options options; if ((m_options & KDateTimeEdit::EditDate) == KDateTimeEdit::EditDate) { options = options | KDateComboBox::EditDate; } if ((m_options & KDateTimeEdit::SelectDate) == KDateTimeEdit::SelectDate) { options = options | KDateComboBox::SelectDate; } if ((m_options & KDateTimeEdit::DatePicker) == KDateTimeEdit::DatePicker) { options = options | KDateComboBox::DatePicker; } if ((m_options & KDateTimeEdit::DateKeywords) == KDateTimeEdit::DateKeywords) { options = options | KDateComboBox::DateKeywords; } ui.m_dateCombo->setOptions(options); ui.m_dateCombo->blockSignals(false); } void KDateTimeEditPrivate::initTimeWidget() { ui.m_timeCombo->blockSignals(true); ui.m_timeCombo->setVisible((m_options & KDateTimeEdit::ShowTime) == KDateTimeEdit::ShowTime); KTimeComboBox::Options options; if ((m_options & KDateTimeEdit::EditTime) == KDateTimeEdit::EditTime) { options = options | KTimeComboBox::EditTime; } if ((m_options & KDateTimeEdit::SelectTime) == KDateTimeEdit::SelectTime) { options = options | KTimeComboBox::SelectTime; } if ((m_options & KDateTimeEdit::ForceTime) == KDateTimeEdit::ForceTime) { options = options | KTimeComboBox::ForceTime; } ui.m_timeCombo->setOptions(options); ui.m_timeCombo->blockSignals(false); } void KDateTimeEditPrivate::initCalendarWidget() { ui.m_calendarCombo->blockSignals(true); ui.m_calendarCombo->clear(); - foreach (const QLocale &calendarLocale, m_calendarLocales) { + for (const QLocale &calendarLocale : qAsConst(m_calendarLocales)) { ui.m_calendarCombo->addItem(calendarLocale.name(), calendarLocale); } ui.m_calendarCombo->setCurrentIndex(ui.m_calendarCombo->findData(q->locale())); ui.m_calendarCombo->setVisible((m_options & KDateTimeEdit::ShowCalendar) == KDateTimeEdit::ShowCalendar); ui.m_calendarCombo->setEnabled((m_options & KDateTimeEdit::SelectCalendar) == KDateTimeEdit::SelectCalendar); ui.m_calendarCombo->setEditable(false); ui.m_calendarCombo->blockSignals(false); } void KDateTimeEditPrivate::updateCalendarWidget() { ui.m_calendarCombo->blockSignals(true); ui.m_calendarCombo->setCurrentIndex(ui.m_calendarCombo->findData(q->locale())); ui.m_calendarCombo->blockSignals(false); } void KDateTimeEditPrivate::selectCalendar(int index) { enterCalendar(ui.m_calendarCombo->itemData(index).toLocale()); } void KDateTimeEditPrivate::enterCalendar(const QLocale &calendarLocale) { q->setLocale(calendarLocale); emit q->calendarEntered(q->locale()); } void KDateTimeEditPrivate::initTimeZoneWidget() { ui.m_timeZoneCombo->blockSignals(true); ui.m_timeZoneCombo->clear(); ui.m_timeZoneCombo->addItem(KDateTimeEdit::tr("UTC", "UTC time zone"), QByteArray("UTC")); ui.m_timeZoneCombo->addItem(KDateTimeEdit::tr("Floating", "No specific time zone"), QByteArray()); - foreach (const QTimeZone &zone, m_zones) { + for (const QTimeZone &zone : qAsConst(m_zones)) { ui.m_timeZoneCombo->addItem(QString::fromUtf8(zone.id()), zone.id()); } ui.m_timeZoneCombo->setVisible((m_options & KDateTimeEdit::ShowTimeZone) == KDateTimeEdit::ShowTimeZone); ui.m_timeZoneCombo->setEnabled((m_options & KDateTimeEdit::SelectTimeZone) == KDateTimeEdit::SelectTimeZone); ui.m_timeZoneCombo->setEditable(false); ui.m_timeZoneCombo->blockSignals(false); } void KDateTimeEditPrivate::updateTimeZoneWidget() { ui.m_timeZoneCombo->blockSignals(true); ui.m_timeZoneCombo->blockSignals(false); } void KDateTimeEditPrivate::selectTimeZone(int index) { enterTimeZone(ui.m_timeCombo->itemData(index).toByteArray()); } void KDateTimeEditPrivate::enterTimeZone(const QByteArray &zoneId) { q->setTimeZone(QTimeZone(zoneId)); emit q->dateTimeEntered(m_dateTime); emit q->timeZoneEntered(m_dateTime.timeZone()); } void KDateTimeEditPrivate::warnDateTime() { if (!q->isValid() && (m_options & KDateTimeEdit::WarnOnInvalid) == KDateTimeEdit::WarnOnInvalid) { QString warnMsg; if (!m_dateTime.isValid()) { //TODO Add missing string //warnMsg = tr("The date or time you entered is invalid"); } else if (m_minDateTime.isValid() && m_dateTime < m_minDateTime) { if (m_minWarnMsg.isEmpty()) { //TODO Add datetime to string //warnMsg = q->tr("Date and time cannot be earlier than %1", "@info").arg(formatDate(m_minDate)); warnMsg = KDateTimeEdit::tr("The entered date and time is before the minimum allowed date and time.", "@info"); } else { warnMsg = m_minWarnMsg; //TODO localize properly warnMsg.replace(QLatin1String("%1"), q->locale().toString(m_minDateTime)); } } else if (m_maxDateTime.isValid() && m_dateTime > m_maxDateTime) { if (m_maxWarnMsg.isEmpty()) { //TODO Add datetime to string //warnMsg = q->tr("Date cannot be later than %1", "@info").arg(formatDate(m_maxDate)); warnMsg = KDateTimeEdit::tr("The entered date and time is after the maximum allowed date and time.", "@info"); } else { warnMsg = m_maxWarnMsg; warnMsg.replace(QLatin1String("%1"), q->locale().toString(m_maxDateTime)); } } KMessageBox::sorry(q, warnMsg); } } KDateTimeEdit::KDateTimeEdit(QWidget *parent) : QWidget(parent), d(new KDateTimeEditPrivate(this)) { d->ui.setupUi(this); //Need to do the min/max defaults here and not in private init as need to wait for ui to init //the KDateComboBox which holds the calendar object. Revisit this??? d->m_minDateTime = d->defaultMinDateTime(); d->m_maxDateTime = d->defaultMaxDateTime(); d->ui.m_calendarCombo->installEventFilter(this); d->ui.m_dateCombo->installEventFilter(this); d->ui.m_timeCombo->installEventFilter(this); d->ui.m_timeZoneCombo->installEventFilter(this); d->initWidgets(); connect(d->ui.m_dateCombo, &KDateComboBox::dateChanged, this, &KDateTimeEdit::setDate); connect(d->ui.m_timeCombo, &KTimeComboBox::timeChanged, this, &KDateTimeEdit::setTime); connect(d->ui.m_calendarCombo, SIGNAL(activated(int)), this, SLOT(selectCalendar(int))); connect(d->ui.m_timeZoneCombo, SIGNAL(activated(int)), this, SLOT(selectTimeZone(int))); } KDateTimeEdit::~KDateTimeEdit() { delete d; } QDateTime KDateTimeEdit::dateTime() const { return d->m_dateTime; } QDate KDateTimeEdit::date() const { return d->m_dateTime.date(); } QTime KDateTimeEdit::time() const { return d->m_dateTime.time(); } QTimeZone KDateTimeEdit::timeZone() const { return d->m_dateTime.timeZone(); } bool KDateTimeEdit::isValid() const { return d->m_dateTime.isValid() && (!d->m_minDateTime.isValid() || d->m_dateTime >= d->m_minDateTime) && (!d->m_maxDateTime.isValid() || d->m_dateTime <= d->m_maxDateTime); } bool KDateTimeEdit::isNull() const { return isNullDate() && isNullTime(); } bool KDateTimeEdit::isValidDate() const { return d->ui.m_dateCombo->isValid(); } bool KDateTimeEdit::isNullDate() const { return d->ui.m_dateCombo->isNull(); } bool KDateTimeEdit::isValidTime() const { return d->ui.m_timeCombo->isValid(); } bool KDateTimeEdit::isNullTime() const { return d->ui.m_timeCombo->isNull(); } void KDateTimeEdit::setOptions(Options options) { if (options != d->m_options) { d->m_options = options; d->initWidgets(); } } KDateTimeEdit::Options KDateTimeEdit::options() const { return d->m_options; } void KDateTimeEdit::setDateTime(const QDateTime &dateTime) { if (dateTime != d->m_dateTime) { assignDateTime(dateTime); emit dateTimeChanged(d->m_dateTime); emit dateChanged(d->m_dateTime.date()); emit timeChanged(d->m_dateTime.time()); } } void KDateTimeEdit::assignDateTime(const QDateTime &dateTime) { d->m_dateTime = dateTime; d->ui.m_dateCombo->setDate(dateTime.date()); d->ui.m_timeCombo->setTime(dateTime.time()); } void KDateTimeEdit::setDate(const QDate &date) { if (date != d->m_dateTime.date()) { assignDate(date); emit dateTimeChanged(d->m_dateTime); emit dateChanged(d->m_dateTime.date()); } } void KDateTimeEdit::assignDate(const QDate &date) { d->m_dateTime.setDate(date); d->ui.m_dateCombo->setDate(date); } void KDateTimeEdit::setTime(const QTime &time) { if (time != d->m_dateTime.time()) { assignTime(time); emit dateTimeChanged(d->m_dateTime); emit timeChanged(d->m_dateTime.time()); } } void KDateTimeEdit::assignTime(const QTime &time) { d->m_dateTime.setTime(time); d->ui.m_timeCombo->setTime(time); } void KDateTimeEdit::setTimeZone(const QTimeZone &zone) { if (zone == d->m_dateTime.timeZone() || !zone.isValid()) { return; } assignTimeZone(zone); emit dateTimeChanged(d->m_dateTime); emit timeZoneChanged(d->m_dateTime.timeZone()); } void KDateTimeEdit::assignTimeZone(const QTimeZone &zone) { d->m_dateTime.setTimeZone(zone); d->updateTimeZoneWidget(); } void KDateTimeEdit::setMinimumDateTime(const QDateTime &minDateTime, const QString &minWarnMsg) { setDateTimeRange(minDateTime, maximumDateTime(), minWarnMsg, d->m_maxWarnMsg); } QDateTime KDateTimeEdit::minimumDateTime() const { return d->m_minDateTime; } void KDateTimeEdit::resetMinimumDateTime() { d->m_minDateTime = d->defaultMinDateTime(); } void KDateTimeEdit::setMaximumDateTime(const QDateTime &maxDateTime, const QString &maxWarnMsg) { setDateTimeRange(minimumDateTime(), maxDateTime, d->m_minWarnMsg, maxWarnMsg); } QDateTime KDateTimeEdit::maximumDateTime() const { return d->m_maxDateTime; } void KDateTimeEdit::resetMaximumDateTime() { d->m_maxDateTime = d->defaultMaxDateTime(); } void KDateTimeEdit::setDateTimeRange(const QDateTime &minDateTime, const QDateTime &maxDateTime, const QString &minErrorMsg, const QString &maxErrorMsg) { if (minDateTime.isValid() && maxDateTime.isValid() && minDateTime <= maxDateTime) { d->m_minDateTime = minDateTime; d->m_minWarnMsg = minErrorMsg; d->m_maxDateTime = maxDateTime; d->m_maxWarnMsg = maxErrorMsg; } } void KDateTimeEdit::resetDateTimeRange() { setDateTimeRange(d->defaultMinDateTime(), d->defaultMaxDateTime()); } void KDateTimeEdit::setCalendarLocalesList(const QList &calendarLocales) { if (calendarLocales != d->m_calendarLocales) { d->m_calendarLocales = calendarLocales; d->updateCalendarWidget(); } } QList KDateTimeEdit::calendarLocalesList() const { return d->m_calendarLocales; } void KDateTimeEdit::setDateDisplayFormat(QLocale::FormatType format) { d->ui.m_dateCombo->setDisplayFormat(format); } QLocale::FormatType KDateTimeEdit::dateDisplayFormat() const { return d->ui.m_dateCombo->displayFormat(); } void KDateTimeEdit::setDateMap(QMap dateMap) { d->ui.m_dateCombo->setDateMap(dateMap); } QMap KDateTimeEdit::dateMap() const { return d->ui.m_dateCombo->dateMap(); } void KDateTimeEdit::setTimeDisplayFormat(QLocale::FormatType formatOptions) { d->ui.m_timeCombo->setDisplayFormat(formatOptions); } QLocale::FormatType KDateTimeEdit::timeDisplayFormat() const { return d->ui.m_timeCombo->displayFormat(); } void KDateTimeEdit::setTimeListInterval(int minutes) { d->ui.m_timeCombo->setTimeListInterval(minutes); } int KDateTimeEdit::timeListInterval() const { return d->ui.m_timeCombo->timeListInterval(); } void KDateTimeEdit::setTimeList(QList timeList, const QString &minWarnMsg, const QString &maxWarnMsg) { d->ui.m_timeCombo->setTimeList(timeList, minWarnMsg, maxWarnMsg); } QList KDateTimeEdit::timeList() const { return d->ui.m_timeCombo->timeList(); } void KDateTimeEdit::setTimeZones(const QList &zones) { if (zones != d->m_zones) { d->m_zones = zones; d->updateTimeZoneWidget(); } } QList KDateTimeEdit::timeZones() const { return d->m_zones; } bool KDateTimeEdit::eventFilter(QObject *object, QEvent *event) { return QWidget::eventFilter(object, event); } void KDateTimeEdit::focusInEvent(QFocusEvent *event) { QWidget::focusInEvent(event); } void KDateTimeEdit::focusOutEvent(QFocusEvent *event) { d->warnDateTime(); QWidget::focusOutEvent(event); } void KDateTimeEdit::resizeEvent(QResizeEvent *event) { QWidget::resizeEvent(event); } #include "moc_kdatetimeedit.cpp" diff --git a/src/kfontaction.cpp b/src/kfontaction.cpp index 7541638..11c7deb 100644 --- a/src/kfontaction.cpp +++ b/src/kfontaction.cpp @@ -1,213 +1,215 @@ /* This file is part of the KDE libraries Copyright (C) 1999 Reginald Stadlbauer (C) 1999 Simon Hausmann (C) 2000 Nicolas Hadacek (C) 2000 Kurt Granroth (C) 2000 Michael Koch (C) 2001 Holger Freyther (C) 2002 Ellis Whitehead (C) 2002 Joseph Wenninger (C) 2003 Andras Mantia (C) 2005-2006 Hamish Rodda (C) 2007 Clarence Dang This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kfontaction.h" #include #include class Q_DECL_HIDDEN KFontAction::KFontActionPrivate { public: KFontActionPrivate(KFontAction *parent) : q(parent), settingFont(0), fontFilters(QFontComboBox::AllFonts) { } void _k_slotFontChanged(const QFont &font) { // qCDebug(KWidgetsAddonsLog) << "QFontComboBox - slotFontChanged(" // << font.family() << ") settingFont=" << settingFont; if (settingFont) { return; } q->setFont(font.family()); emit q->triggered(font.family()); // qCDebug(KWidgetsAddonsLog) << "\tslotFontChanged done"; } KFontAction *q; int settingFont; QFontComboBox::FontFilters fontFilters; }; QStringList _k_fontList(const QFontComboBox::FontFilters &fontFilters = QFontComboBox::AllFonts) { QFontDatabase dbase; QStringList families; if (fontFilters == QFontComboBox::AllFonts) { families = dbase.families(); } else { const QFontComboBox::FontFilters scalableMask = (QFontComboBox::ScalableFonts | QFontComboBox::NonScalableFonts); const QFontComboBox::FontFilters spacingMask = (QFontComboBox::ProportionalFonts | QFontComboBox::MonospacedFonts); - foreach (const QString &family, dbase.families()) { + const auto allFamilies = dbase.families(); + for (const QString &family : allFamilies) { if ((fontFilters & scalableMask) && (fontFilters & scalableMask) != scalableMask) { if (bool(fontFilters & QFontComboBox::ScalableFonts) != dbase.isSmoothlyScalable(family)) { continue; } } if ((fontFilters & spacingMask) && (fontFilters & spacingMask) != spacingMask) { if (bool(fontFilters & QFontComboBox::MonospacedFonts) != dbase.isFixedPitch(family)) { continue; } } families << family; } } families.sort(); return families; } KFontAction::KFontAction(uint fontListCriteria, QObject *parent) : KSelectAction(parent), d(new KFontActionPrivate(this)) { if (fontListCriteria & KFontChooser::FixedWidthFonts) { d->fontFilters |= QFontComboBox::MonospacedFonts; } if (fontListCriteria & KFontChooser::SmoothScalableFonts) { d->fontFilters |= QFontComboBox::ScalableFonts; } KSelectAction::setItems(_k_fontList(d->fontFilters)); setEditable(true); } KFontAction::KFontAction(QObject *parent) : KSelectAction(parent), d(new KFontActionPrivate(this)) { KSelectAction::setItems(_k_fontList()); setEditable(true); } KFontAction::KFontAction(const QString &text, QObject *parent) : KSelectAction(text, parent), d(new KFontActionPrivate(this)) { KSelectAction::setItems(_k_fontList()); setEditable(true); } KFontAction::KFontAction(const QIcon &icon, const QString &text, QObject *parent) : KSelectAction(icon, text, parent), d(new KFontActionPrivate(this)) { KSelectAction::setItems(_k_fontList()); setEditable(true); } KFontAction::~KFontAction() { delete d; } QString KFontAction::font() const { return currentText(); } QWidget *KFontAction::createWidget(QWidget *parent) { // qCDebug(KWidgetsAddonsLog) << "KFontAction::createWidget()"; #ifdef __GNUC__ #warning FIXME: items need to be converted #endif // This is the visual element on the screen. This method overrides // the KSelectAction one, preventing KSelectAction from creating its // regular KComboBox. QFontComboBox *cb = new QFontComboBox(parent); cb->setFontFilters(d->fontFilters); // qCDebug(KWidgetsAddonsLog) << "\tset=" << font(); // Do this before connecting the signal so that nothing will fire. cb->setCurrentFont(QFont(font().toLower())); // qCDebug(KWidgetsAddonsLog) << "\tspit back=" << cb->currentFont().family(); connect(cb, &QFontComboBox::currentFontChanged, this, [this](const QFont &ft) { d->_k_slotFontChanged(ft); }); cb->setMinimumWidth(cb->sizeHint().width()); return cb; } /* * Maintenance note: Keep in sync with QFontComboBox::setCurrentFont() */ void KFontAction::setFont(const QString &family) { // qCDebug(KWidgetsAddonsLog) << "KFontAction::setFont(" << family << ")"; // Suppress triggered(QString) signal and prevent recursive call to ourself. d->settingFont++; - foreach (QWidget *w, createdWidgets()) { + const auto createdWidgets = this->createdWidgets(); + for (QWidget *w : createdWidgets) { QFontComboBox *cb = qobject_cast(w); // qCDebug(KWidgetsAddonsLog) << "\tw=" << w << "cb=" << cb; if (!cb) { continue; } cb->setCurrentFont(QFont(family.toLower())); // qCDebug(KWidgetsAddonsLog) << "\t\tw spit back=" << cb->currentFont().family(); } d->settingFont--; // qCDebug(KWidgetsAddonsLog) << "\tcalling setCurrentAction()"; QString lowerName = family.toLower(); if (setCurrentAction(lowerName, Qt::CaseInsensitive)) { return; } int i = lowerName.indexOf(QLatin1String(" [")); if (i > -1) { lowerName.truncate(i); i = 0; if (setCurrentAction(lowerName, Qt::CaseInsensitive)) { return; } } lowerName += QLatin1String(" ["); if (setCurrentAction(lowerName, Qt::CaseInsensitive)) { return; } // TODO: Inconsistent state if QFontComboBox::setCurrentFont() succeeded // but setCurrentAction() did not and vice-versa. // qCDebug(KWidgetsAddonsLog) << "Font not found " << family.toLower(); } #include "moc_kfontaction.cpp" diff --git a/src/kfontrequester.cpp b/src/kfontrequester.cpp index 282fc4c..eb1c903 100644 --- a/src/kfontrequester.cpp +++ b/src/kfontrequester.cpp @@ -1,236 +1,236 @@ /* Copyright (C) 2003 Nadeem Hasan This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kfontrequester.h" #include "fonthelpers_p.h" #include #include #include #include #include #include #include #include // Determine if the font with given properties is available on the system, // otherwise find and return the best fitting combination. static QFont nearestExistingFont(const QFont &font) { QFontDatabase dbase; // Initialize font data according to given font object. QString family = font.family(); QString style = dbase.styleString(font); qreal size = font.pointSizeF(); // Check if the family exists. const QStringList families = dbase.families(); if (!families.contains(family)) { // Chose another family. family = QFontInfo(font).family(); // the nearest match if (!families.contains(family)) { family = families.count() ? families.at(0) : QStringLiteral("fixed"); } } // Check if the family has the requested style. // Easiest by piping it through font selection in the database. QString retStyle = dbase.styleString(dbase.font(family, style, 10)); style = retStyle; // Check if the family has the requested size. // Only for bitmap fonts. if (!dbase.isSmoothlyScalable(family, style)) { - QList sizes = dbase.smoothSizes(family, style); + const QList sizes = dbase.smoothSizes(family, style); if (!sizes.contains(size)) { // Find nearest available size. int mindiff = 1000; int refsize = size; - Q_FOREACH (int lsize, sizes) { + for (int lsize : sizes) { int diff = qAbs(refsize - lsize); if (mindiff > diff) { mindiff = diff; size = lsize; } } } } // Select the font with confirmed properties. QFont result = dbase.font(family, style, int(size)); if (dbase.isSmoothlyScalable(family, style) && result.pointSize() == floor(size)) { result.setPointSizeF(size); } return result; } class Q_DECL_HIDDEN KFontRequester::KFontRequesterPrivate { public: KFontRequesterPrivate(KFontRequester *q): q(q) {} void displaySampleText(); void setToolTip(); void _k_buttonClicked(); KFontRequester *q; bool m_onlyFixed; QString m_sampleText, m_title; QLabel *m_sampleLabel = nullptr; QPushButton *m_button = nullptr; QFont m_selFont; }; KFontRequester::KFontRequester(QWidget *parent, bool onlyFixed) : QWidget(parent), d(new KFontRequesterPrivate(this)) { d->m_onlyFixed = onlyFixed; QHBoxLayout *layout = new QHBoxLayout(this); layout->setContentsMargins(0, 0, 0, 0); d->m_sampleLabel = new QLabel(this); d->m_button = new QPushButton(QIcon::fromTheme(QStringLiteral("document-edit")), QString(), this); d->m_sampleLabel->setFrameStyle(QFrame::StyledPanel | QFrame::Sunken); setFocusProxy(d->m_button); setFocusPolicy(d->m_button->focusPolicy()); layout->addWidget(d->m_sampleLabel, 1); layout->addWidget(d->m_button); connect(d->m_button, &QPushButton::clicked, this, [this] { d->_k_buttonClicked(); }); d->displaySampleText(); d->setToolTip(); } KFontRequester::~KFontRequester() { delete d; } QFont KFontRequester::font() const { return d->m_selFont; } bool KFontRequester::isFixedOnly() const { return d->m_onlyFixed; } QString KFontRequester::sampleText() const { return d->m_sampleText; } QString KFontRequester::title() const { return d->m_title; } QLabel *KFontRequester::label() const { return d->m_sampleLabel; } QPushButton *KFontRequester::button() const { return d->m_button; } void KFontRequester::setFont(const QFont &font, bool onlyFixed) { d->m_selFont = nearestExistingFont(font); d->m_onlyFixed = onlyFixed; d->displaySampleText(); emit fontSelected(d->m_selFont); } void KFontRequester::setSampleText(const QString &text) { d->m_sampleText = text; d->displaySampleText(); } void KFontRequester::setTitle(const QString &title) { d->m_title = title; d->setToolTip(); } void KFontRequester::KFontRequesterPrivate::_k_buttonClicked() { QFontDialog::FontDialogOptions flags; if (m_onlyFixed) { flags = QFontDialog::MonospacedFonts; } bool ok = false; QFont font = QFontDialog::getFont(&ok, m_selFont, q->parentWidget(), QString(), flags); if (ok) { m_selFont = font; displaySampleText(); emit q->fontSelected(m_selFont); } } void KFontRequester::KFontRequesterPrivate::displaySampleText() { m_sampleLabel->setFont(m_selFont); qreal size = m_selFont.pointSizeF(); if (size == -1) { size = m_selFont.pixelSize(); } if (m_sampleText.isEmpty()) { QString family = translateFontName(m_selFont.family()); m_sampleLabel->setText(QStringLiteral("%1 %2").arg(family).arg(size)); } else { m_sampleLabel->setText(m_sampleText); } } void KFontRequester::KFontRequesterPrivate::setToolTip() { m_button->setToolTip(tr("Choose Font...")); m_sampleLabel->setToolTip(QString()); m_sampleLabel->setWhatsThis(QString()); if (m_title.isNull()) { m_sampleLabel->setToolTip(tr("Preview of the selected font")); m_sampleLabel->setWhatsThis(tr("This is a preview of the selected font. You can change it" " by clicking the \"Choose Font...\" button.")); } else { m_sampleLabel->setToolTip(tr("Preview of the \"%1\" font").arg(m_title)); m_sampleLabel->setWhatsThis(tr("This is a preview of the \"%1\" font. You can change it" " by clicking the \"Choose Font...\" button.").arg(m_title)); } } #include "moc_kfontrequester.cpp" diff --git a/src/kfontsizeaction.cpp b/src/kfontsizeaction.cpp index 4db264c..9395666 100644 --- a/src/kfontsizeaction.cpp +++ b/src/kfontsizeaction.cpp @@ -1,140 +1,141 @@ /* This file is part of the KDE libraries Copyright (C) 1999 Reginald Stadlbauer (C) 1999 Simon Hausmann (C) 2000 Nicolas Hadacek (C) 2000 Kurt Granroth (C) 2000 Michael Koch (C) 2001 Holger Freyther (C) 2002 Ellis Whitehead (C) 2002 Joseph Wenninger (C) 2003 Andras Mantia (C) 2005-2006 Hamish Rodda This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kfontsizeaction.h" #include "loggingcategory.h" #include class Q_DECL_HIDDEN KFontSizeAction::Private { public: Private(KFontSizeAction *parent) : q(parent) { } void init(); KFontSizeAction *q; }; // BEGIN KFontSizeAction KFontSizeAction::KFontSizeAction(QObject *parent) : KSelectAction(parent), d(new Private(this)) { d->init(); } KFontSizeAction::KFontSizeAction(const QString &text, QObject *parent) : KSelectAction(text, parent), d(new Private(this)) { d->init(); } KFontSizeAction::KFontSizeAction(const QIcon &icon, const QString &text, QObject *parent) : KSelectAction(icon, text, parent), d(new Private(this)) { d->init(); } KFontSizeAction::~KFontSizeAction() { delete d; } void KFontSizeAction::Private::init() { q->setEditable(true); QFontDatabase fontDB; const QList sizes = fontDB.standardSizes(); QStringList lst; lst.reserve(sizes.count()); for (QList::ConstIterator it = sizes.begin(), total = sizes.end(); it != total; ++it) { lst.append(QString::number(*it)); } q->setItems(lst); } void KFontSizeAction::setFontSize(int size) { if (size == fontSize()) { const QString test = QString::number(size); - Q_FOREACH (QAction *action, actions()) { + const auto actions = this->actions(); + for (QAction *action : actions) { if (action->text() == test) { setCurrentAction(action); return; } } } if (size < 1) { qCWarning(KWidgetsAddonsLog) << "KFontSizeAction: Size " << size << " is out of range"; return; } QAction *a = action(QString::number(size)); if (!a) { // Insert at the correct position in the list (to keep sorting) QList lst; // Convert to list of ints QStringListIterator itemsIt(items()); while (itemsIt.hasNext()) { lst.append(itemsIt.next().toInt()); } // New size lst.append(size); // Sort the list std::sort(lst.begin(), lst.end()); for (int it : qAsConst(lst)) { QAction *const action = addAction(QString::number(it)); if (it == size) { setCurrentAction(action); } } } else { setCurrentAction(a); } } int KFontSizeAction::fontSize() const { return currentText().toInt(); } void KFontSizeAction::actionTriggered(QAction *action) { emit fontSizeChanged(action->text().toInt()); KSelectAction::actionTriggered(action); } diff --git a/src/kmessagebox_p.cpp b/src/kmessagebox_p.cpp index 6ec5a2a..6fde60b 100644 --- a/src/kmessagebox_p.cpp +++ b/src/kmessagebox_p.cpp @@ -1,154 +1,155 @@ /* This file is part of the KDE libraries * Copyright 2012 David Faure * Copyright 2013 Aurélien Gâteau * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2 of the License or ( at * your option ) version 3 or, at the discretion of KDE e.V. ( which shall * act as a proxy as in section 14 of the GPLv3 ), any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "kmessagebox_p.h" #include "loggingcategory.h" #include #include #include #include namespace KMessageBox { class KMessageBoxDontAskAgainQSettingsStorage : public KMessageBoxDontAskAgainInterface { public: KMessageBoxDontAskAgainQSettingsStorage() { m_filePath = QStandardPaths::writableLocation(QStandardPaths::ConfigLocation) + QLatin1Char('/') + QCoreApplication::instance()->applicationName() + QLatin1String(".kmessagebox"); QSettings s(m_filePath, QSettings::IniFormat); - foreach (const QString &key, s.allKeys()) { + const auto keys = s.allKeys(); + for (const QString &key : keys) { m_saved.insert(key, static_cast(s.value(key).toInt())); } } virtual ~KMessageBoxDontAskAgainQSettingsStorage() { QSettings s(m_filePath, QSettings::IniFormat); for (auto it = m_saved.constBegin(); it != m_saved.constEnd(); ++it) { s.setValue(it.key(), static_cast(it.value())); } } bool shouldBeShownYesNo(const QString &dontShowAgainName, KMessageBox::ButtonCode &result) override { KMessageBox::ButtonCode code = m_saved.value(dontShowAgainName, KMessageBox::ButtonCode(0)); if (code == KMessageBox::Yes || code == KMessageBox::No) { result = code; return false; } return true; } bool shouldBeShownContinue(const QString &dontShowAgainName) override { KMessageBox::ButtonCode code = m_saved.value(dontShowAgainName, KMessageBox::Yes); return code == KMessageBox::Yes; } void saveDontShowAgainYesNo(const QString &dontShowAgainName, KMessageBox::ButtonCode result) override { m_saved[dontShowAgainName] = result; } void saveDontShowAgainContinue(const QString &dontShowAgainName) override { m_saved[dontShowAgainName] = KMessageBox::No; } void enableAllMessages() override { m_saved.clear(); } void enableMessage(const QString &dontShowAgainName) override { m_saved.remove(dontShowAgainName); } void setConfig(KConfig *) override { qCWarning(KWidgetsAddonsLog) << "Using QSettings based KMessageBoxDontAskAgainInterface. KMessageBox::setDontShowAgainConfig ignored"; } private: QString m_filePath; QHash m_saved; }; class KMessageBoxNotifyDummy : public KMessageBoxNotifyInterface { public: void sendNotification(QMessageBox::Icon /*notificationType*/, const QString &/*message*/, QWidget * /*parent*/) override {} }; Q_GLOBAL_STATIC(KMessageBoxDontAskAgainQSettingsStorage, s_defaultDontAskAgainInterface) Q_GLOBAL_STATIC(KMessageBoxNotifyDummy, s_defaultNotifyInterface) static KMessageBoxDontAskAgainInterface *s_dontAskAgainInterface = nullptr; static KMessageBoxNotifyInterface *s_notifyInterface = nullptr; static void loadKMessageBoxPlugin() { static bool triedLoadingPlugin = false; if (!triedLoadingPlugin) { triedLoadingPlugin = true; QPluginLoader lib(QStringLiteral("kf5/FrameworkIntegrationPlugin")); QObject *rootObj = lib.instance(); if (rootObj) { s_dontAskAgainInterface = rootObj->property(KMESSAGEBOXDONTASKAGAIN_PROPERTY).value(); s_notifyInterface = rootObj->property(KMESSAGEBOXNOTIFY_PROPERTY).value(); } } if (!s_dontAskAgainInterface) { s_dontAskAgainInterface = s_defaultDontAskAgainInterface; } if (!s_notifyInterface) { s_notifyInterface = s_defaultNotifyInterface; } } KMessageBoxDontAskAgainInterface *dontAskAgainInterface() { if (!s_dontAskAgainInterface) { loadKMessageBoxPlugin(); } return s_dontAskAgainInterface; } KMessageBoxNotifyInterface *notifyInterface() { if (!s_notifyInterface) { loadKMessageBoxPlugin(); } return s_notifyInterface; } void setDontShowAgainInterface(KMessageBoxDontAskAgainInterface *dontAskAgainInterface) { Q_ASSERT(dontAskAgainInterface != nullptr); s_dontAskAgainInterface = dontAskAgainInterface; } void setNotifyInterface(KMessageBoxNotifyInterface *notifyInterface) { Q_ASSERT(notifyInterface != nullptr); s_notifyInterface = notifyInterface; } } // KMessageBox namespace diff --git a/src/kmessagewidget.cpp b/src/kmessagewidget.cpp index 22a787d..5c2266c 100644 --- a/src/kmessagewidget.cpp +++ b/src/kmessagewidget.cpp @@ -1,514 +1,516 @@ /* This file is part of the KDE libraries * * Copyright (c) 2011 Aurélien Gâteau * Copyright (c) 2014 Dominik Haumann * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA */ #include "kmessagewidget.h" #include #include #include #include #include #include #include #include #include #include #include #include //--------------------------------------------------------------------- // KMessageWidgetPrivate //--------------------------------------------------------------------- class KMessageWidgetPrivate { public: void init(KMessageWidget *); KMessageWidget *q; QFrame *content = nullptr; QLabel *iconLabel = nullptr; QLabel *textLabel = nullptr; QToolButton *closeButton = nullptr; QTimeLine *timeLine = nullptr; QIcon icon; bool ignoreShowEventDoingAnimatedShow = false; KMessageWidget::MessageType messageType; bool wordWrap; QList buttons; QPixmap contentSnapShot; void createLayout(); void applyStyleSheet(); void updateSnapShot(); void updateLayout(); void slotTimeLineChanged(qreal); void slotTimeLineFinished(); int bestContentHeight() const; }; void KMessageWidgetPrivate::init(KMessageWidget *q_ptr) { q = q_ptr; q->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed); // Note: when changing the value 500, also update KMessageWidgetTest timeLine = new QTimeLine(500, q); QObject::connect(timeLine, SIGNAL(valueChanged(qreal)), q, SLOT(slotTimeLineChanged(qreal))); QObject::connect(timeLine, SIGNAL(finished()), q, SLOT(slotTimeLineFinished())); content = new QFrame(q); content->setObjectName(QStringLiteral("contentWidget")); content->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); wordWrap = false; iconLabel = new QLabel(content); iconLabel->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); iconLabel->hide(); textLabel = new QLabel(content); textLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); textLabel->setTextInteractionFlags(Qt::TextBrowserInteraction); QObject::connect(textLabel, &QLabel::linkActivated, q, &KMessageWidget::linkActivated); QObject::connect(textLabel, &QLabel::linkHovered, q, &KMessageWidget::linkHovered); QAction *closeAction = new QAction(q); closeAction->setText(KMessageWidget::tr("&Close")); closeAction->setToolTip(KMessageWidget::tr("Close message")); QStyleOptionFrame opt; opt.initFrom(q); closeAction->setIcon(q->style()->standardIcon(QStyle::SP_DialogCloseButton, &opt, q)); QObject::connect(closeAction, &QAction::triggered, q, &KMessageWidget::animatedHide); closeButton = new QToolButton(content); closeButton->setAutoRaise(true); closeButton->setDefaultAction(closeAction); q->setMessageType(KMessageWidget::Information); } void KMessageWidgetPrivate::createLayout() { delete content->layout(); content->resize(q->size()); qDeleteAll(buttons); buttons.clear(); - Q_FOREACH (QAction *action, q->actions()) { + const auto actions = q->actions(); + buttons.reserve(actions.size()); + for (QAction *action : actions) { QToolButton *button = new QToolButton(content); button->setDefaultAction(action); button->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); buttons.append(button); } // AutoRaise reduces visual clutter, but we don't want to turn it on if // there are other buttons, otherwise the close button will look different // from the others. closeButton->setAutoRaise(buttons.isEmpty()); if (wordWrap) { QGridLayout *layout = new QGridLayout(content); // Set alignment to make sure icon does not move down if text wraps layout->addWidget(iconLabel, 0, 0, 1, 1, Qt::AlignHCenter | Qt::AlignTop); layout->addWidget(textLabel, 0, 1); if (buttons.isEmpty()) { // Use top-vertical alignment like the icon does. layout->addWidget(closeButton, 0, 2, 1, 1, Qt::AlignHCenter | Qt::AlignTop); } else { // Use an additional layout in row 1 for the buttons. QHBoxLayout *buttonLayout = new QHBoxLayout; buttonLayout->addStretch(); - Q_FOREACH (QToolButton *button, buttons) { + for (QToolButton *button : qAsConst(buttons)) { // For some reason, calling show() is necessary if wordwrap is true, // otherwise the buttons do not show up. It is not needed if // wordwrap is false. button->show(); buttonLayout->addWidget(button); } buttonLayout->addWidget(closeButton); layout->addItem(buttonLayout, 1, 0, 1, 2); } } else { QHBoxLayout *layout = new QHBoxLayout(content); layout->addWidget(iconLabel); layout->addWidget(textLabel); for (QToolButton *button : qAsConst(buttons)) { layout->addWidget(button); } layout->addWidget(closeButton); }; if (q->isVisible()) { q->setFixedHeight(content->sizeHint().height()); } q->updateGeometry(); } void KMessageWidgetPrivate::applyStyleSheet() { QColor bgBaseColor; // We have to hardcode colors here because KWidgetsAddons is a tier 1 framework // and therefore can't depend on any other KDE Frameworks // The following RGB color values come from the "default" scheme in kcolorscheme.cpp switch (messageType) { case KMessageWidget::Positive: bgBaseColor.setRgb(39, 174, 96); // Window: ForegroundPositive break; case KMessageWidget::Information: bgBaseColor.setRgb(61, 174, 233); // Window: ForegroundActive break; case KMessageWidget::Warning: bgBaseColor.setRgb(246, 116, 0); // Window: ForegroundNeutral break; case KMessageWidget::Error: bgBaseColor.setRgb(218, 68, 83); // Window: ForegroundNegative break; } const qreal bgBaseColorAlpha = 0.2; bgBaseColor.setAlphaF(bgBaseColorAlpha); const QPalette palette = QGuiApplication::palette(); const QColor windowColor = palette.window().color(); const QColor textColor = palette.text().color(); const QColor border = bgBaseColor; // Generate a final background color from overlaying bgBaseColor over windowColor const int newRed = (bgBaseColor.red() * bgBaseColorAlpha) + (windowColor.red() * (1 - bgBaseColorAlpha)); const int newGreen = (bgBaseColor.green() * bgBaseColorAlpha) + (windowColor.green() * (1 - bgBaseColorAlpha)); const int newBlue = (bgBaseColor.blue() * bgBaseColorAlpha) + (windowColor.blue() * (1 - bgBaseColorAlpha)); const QColor bgFinalColor = QColor(newRed, newGreen, newBlue); content->setStyleSheet( QString::fromLatin1(".QFrame {" "background-color: %1;" "border-radius: 4px;" "border: 2px solid %2;" "margin: %3px;" "}" ".QLabel { color: %4; }" ) .arg(bgFinalColor.name()) .arg(border.name()) // DefaultFrameWidth returns the size of the external margin + border width. We know our border is 1px, so we subtract this from the frame normal QStyle FrameWidth to get our margin .arg(q->style()->pixelMetric(QStyle::PM_DefaultFrameWidth, nullptr, q) - 1) .arg(textColor.name()) ); } void KMessageWidgetPrivate::updateLayout() { if (content->layout()) { createLayout(); } } void KMessageWidgetPrivate::updateSnapShot() { // Attention: updateSnapShot calls QWidget::render(), which causes the whole // window layouts to be activated. Calling this method from resizeEvent() // can lead to infinite recursion, see: // https://bugs.kde.org/show_bug.cgi?id=311336 contentSnapShot = QPixmap(content->size() * q->devicePixelRatio()); contentSnapShot.setDevicePixelRatio(q->devicePixelRatio()); contentSnapShot.fill(Qt::transparent); content->render(&contentSnapShot, QPoint(), QRegion(), QWidget::DrawChildren); } void KMessageWidgetPrivate::slotTimeLineChanged(qreal value) { q->setFixedHeight(qMin(value * 2, qreal(1.0)) * content->height()); q->update(); } void KMessageWidgetPrivate::slotTimeLineFinished() { if (timeLine->direction() == QTimeLine::Forward) { // Show // We set the whole geometry here, because it may be wrong if a // KMessageWidget is shown right when the toplevel window is created. content->setGeometry(0, 0, q->width(), bestContentHeight()); // notify about finished animation emit q->showAnimationFinished(); } else { // hide and notify about finished animation q->hide(); emit q->hideAnimationFinished(); } } int KMessageWidgetPrivate::bestContentHeight() const { int height = content->heightForWidth(q->width()); if (height == -1) { height = content->sizeHint().height(); } return height; } //--------------------------------------------------------------------- // KMessageWidget //--------------------------------------------------------------------- KMessageWidget::KMessageWidget(QWidget *parent) : QFrame(parent) , d(new KMessageWidgetPrivate) { d->init(this); } KMessageWidget::KMessageWidget(const QString &text, QWidget *parent) : QFrame(parent) , d(new KMessageWidgetPrivate) { d->init(this); setText(text); } KMessageWidget::~KMessageWidget() { delete d; } QString KMessageWidget::text() const { return d->textLabel->text(); } void KMessageWidget::setText(const QString &text) { d->textLabel->setText(text); updateGeometry(); } KMessageWidget::MessageType KMessageWidget::messageType() const { return d->messageType; } void KMessageWidget::setMessageType(KMessageWidget::MessageType type) { d->messageType = type; d->applyStyleSheet(); } QSize KMessageWidget::sizeHint() const { ensurePolished(); return d->content->sizeHint(); } QSize KMessageWidget::minimumSizeHint() const { ensurePolished(); return d->content->minimumSizeHint(); } bool KMessageWidget::event(QEvent *event) { if (event->type() == QEvent::Polish && !d->content->layout()) { d->createLayout(); } else if (event->type() == QEvent::PaletteChange) { d->applyStyleSheet(); } else if (event->type() == QEvent::Show && !d->ignoreShowEventDoingAnimatedShow) { if ((height() != d->content->height()) || (d->content->pos().y() != 0)) { d->content->move(0, 0); setFixedHeight(d->content->height()); } } return QFrame::event(event); } void KMessageWidget::resizeEvent(QResizeEvent *event) { QFrame::resizeEvent(event); if (d->timeLine->state() == QTimeLine::NotRunning) { d->content->resize(width(), d->bestContentHeight()); } } int KMessageWidget::heightForWidth(int width) const { ensurePolished(); return d->content->heightForWidth(width); } void KMessageWidget::paintEvent(QPaintEvent *event) { QFrame::paintEvent(event); if (d->timeLine->state() == QTimeLine::Running) { QPainter painter(this); painter.setOpacity(d->timeLine->currentValue() * d->timeLine->currentValue()); painter.drawPixmap(0, 0, d->contentSnapShot); } } bool KMessageWidget::wordWrap() const { return d->wordWrap; } void KMessageWidget::setWordWrap(bool wordWrap) { d->wordWrap = wordWrap; d->textLabel->setWordWrap(wordWrap); QSizePolicy policy = sizePolicy(); policy.setHeightForWidth(wordWrap); setSizePolicy(policy); d->updateLayout(); // Without this, when user does wordWrap -> !wordWrap -> wordWrap, a minimum // height is set, causing the widget to be too high. // Mostly visible in test programs. if (wordWrap) { setMinimumHeight(0); } } bool KMessageWidget::isCloseButtonVisible() const { return d->closeButton->isVisible(); } void KMessageWidget::setCloseButtonVisible(bool show) { d->closeButton->setVisible(show); updateGeometry(); } void KMessageWidget::addAction(QAction *action) { QFrame::addAction(action); d->updateLayout(); } void KMessageWidget::removeAction(QAction *action) { QFrame::removeAction(action); d->updateLayout(); } void KMessageWidget::animatedShow() { // Test before styleHint, as there might have been a style change while animation was running if (isHideAnimationRunning()) { d->timeLine->stop(); emit hideAnimationFinished(); } if (!style()->styleHint(QStyle::SH_Widget_Animate, nullptr, this) || (parentWidget() && !parentWidget()->isVisible())) { show(); emit showAnimationFinished(); return; } if (isVisible() && (d->timeLine->state() == QTimeLine::NotRunning) && (height() == d->bestContentHeight()) && (d->content->pos().y() == 0)) { emit showAnimationFinished(); return; } d->ignoreShowEventDoingAnimatedShow = true; show(); d->ignoreShowEventDoingAnimatedShow = false; setFixedHeight(0); int wantedHeight = d->bestContentHeight(); d->content->setGeometry(0, -wantedHeight, width(), wantedHeight); d->updateSnapShot(); d->timeLine->setDirection(QTimeLine::Forward); if (d->timeLine->state() == QTimeLine::NotRunning) { d->timeLine->start(); } } void KMessageWidget::animatedHide() { // test this before isVisible, as animatedShow might have been called directly before, // so the first timeline event is not yet done and the widget is still hidden // And before styleHint, as there might have been a style change while animation was running if (isShowAnimationRunning()) { d->timeLine->stop(); emit showAnimationFinished(); } if (!style()->styleHint(QStyle::SH_Widget_Animate, nullptr, this)) { hide(); emit hideAnimationFinished(); return; } if (!isVisible()) { // explicitly hide it, so it stays hidden in case it is only not visible due to the parents hide(); emit hideAnimationFinished(); return; } d->content->move(0, -d->content->height()); d->updateSnapShot(); d->timeLine->setDirection(QTimeLine::Backward); if (d->timeLine->state() == QTimeLine::NotRunning) { d->timeLine->start(); } } bool KMessageWidget::isHideAnimationRunning() const { return (d->timeLine->direction() == QTimeLine::Backward) && (d->timeLine->state() == QTimeLine::Running); } bool KMessageWidget::isShowAnimationRunning() const { return (d->timeLine->direction() == QTimeLine::Forward) && (d->timeLine->state() == QTimeLine::Running); } QIcon KMessageWidget::icon() const { return d->icon; } void KMessageWidget::setIcon(const QIcon &icon) { d->icon = icon; if (d->icon.isNull()) { d->iconLabel->hide(); } else { const int size = style()->pixelMetric(QStyle::PM_ToolBarIconSize); d->iconLabel->setPixmap(d->icon.pixmap(size)); d->iconLabel->show(); } } #include "moc_kmessagewidget.cpp" diff --git a/src/kmimetypechooser.cpp b/src/kmimetypechooser.cpp index da5dc3d..4489e94 100644 --- a/src/kmimetypechooser.cpp +++ b/src/kmimetypechooser.cpp @@ -1,366 +1,367 @@ /* This file is part of the KDE libraries Copyright (C) 2001 - 2004 Anders Lund This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kmimetypechooser.h" #include "kmimetypeeditor.h" #include #include #include #include #include #include #include //BEGIN KMimeTypeChooserPrivate class KMimeTypeChooserPrivate { public: KMimeTypeChooserPrivate(KMimeTypeChooser *parent) : q(parent) { } void loadMimeTypes(const QStringList &selected = QStringList()); void _k_editMimeType(); void _k_slotCurrentChanged(QTreeWidgetItem *); void _k_slotSycocaDatabaseChanged(const QStringList &); KMimeTypeChooser *q; QTreeWidget *mimeTypeTree = nullptr; QPushButton *btnEditMimeType = nullptr; QString defaultgroup; QStringList groups; int visuals; }; //END static const char s_keditfiletypeExecutable[] = "keditfiletype5"; //BEGIN KMimeTypeChooser KMimeTypeChooser::KMimeTypeChooser(const QString &text, const QStringList &selMimeTypes, const QString &defaultGroup, const QStringList &groupsToShow, int visuals, QWidget *parent) : QWidget(parent), d(new KMimeTypeChooserPrivate(this)) { d->defaultgroup = defaultGroup; d->groups = groupsToShow; if (visuals & EditButton) { if (QStandardPaths::findExecutable(QString::fromLatin1(s_keditfiletypeExecutable)).isEmpty()) { visuals &= ~EditButton; } } d->visuals = visuals; QVBoxLayout *vboxLayout = new QVBoxLayout(this); vboxLayout->setContentsMargins(0, 0, 0, 0); if (!text.isEmpty()) { vboxLayout->addWidget(new QLabel(text, this)); } d->mimeTypeTree = new QTreeWidget(this); vboxLayout->addWidget(d->mimeTypeTree); QStringList headerLabels; headerLabels.append(tr("Mime Type")); if (visuals & Comments) { headerLabels.append(tr("Comment")); } if (visuals & Patterns) { headerLabels.append(tr("Patterns")); } d->mimeTypeTree->setColumnCount(headerLabels.count()); d->mimeTypeTree->setHeaderLabels(headerLabels); QFontMetrics fm(d->mimeTypeTree->fontMetrics()); d->mimeTypeTree->setColumnWidth(0, 20 * fm.height()); // big enough for most names, but not for the insanely long ones d->loadMimeTypes(selMimeTypes); if (visuals & EditButton) { QHBoxLayout *buttonLayout = new QHBoxLayout(); buttonLayout->addStretch(1); d->btnEditMimeType = new QPushButton(tr("&Edit..."), this); buttonLayout->addWidget(d->btnEditMimeType); connect(d->btnEditMimeType, &QPushButton::clicked, this, [this]() { d->_k_editMimeType(); }); d->btnEditMimeType->setEnabled(false); connect(d->mimeTypeTree, &QTreeWidget::itemDoubleClicked, this, [this]() { d->_k_editMimeType(); }); connect(d->mimeTypeTree, SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)), this, SLOT(_k_slotCurrentChanged(QTreeWidgetItem*))); d->btnEditMimeType->setWhatsThis(tr( "Click this button to display the familiar KDE mime type editor.")); vboxLayout->addLayout(buttonLayout); } setLayout(vboxLayout); } KMimeTypeChooser::~KMimeTypeChooser() { delete d; } void KMimeTypeChooserPrivate::loadMimeTypes(const QStringList &_selectedMimeTypes) { QStringList selMimeTypes; if (!_selectedMimeTypes.isEmpty()) { selMimeTypes = _selectedMimeTypes; } else { selMimeTypes = q->mimeTypes(); } mimeTypeTree->clear(); QMap groupItems; QMimeDatabase db; const QList mimetypes = db.allMimeTypes(); bool agroupisopen = false; QTreeWidgetItem *idefault = nullptr; //open this, if all other fails QTreeWidgetItem *firstChecked = nullptr; // make this one visible after the loop - foreach (const QMimeType &mt, mimetypes) { + for (const QMimeType &mt : mimetypes) { const QString mimetype = mt.name(); const int index = mimetype.indexOf(QLatin1Char('/')); const QString maj = mimetype.left(index); if (!groups.isEmpty() && !groups.contains(maj)) { continue; } QTreeWidgetItem *groupItem; QMap::Iterator mit = groupItems.find(maj); if (mit == groupItems.end()) { groupItem = new QTreeWidgetItem(mimeTypeTree, QStringList(maj)); groupItems.insert(maj, groupItem); if (maj == defaultgroup) { idefault = groupItem; } } else { groupItem = mit.value(); } const QString min = mimetype.mid(index + 1); QTreeWidgetItem *item = new QTreeWidgetItem(groupItem, QStringList(min)); item->setIcon(0, QIcon::fromTheme(mt.iconName())); int cl = 1; if (visuals & KMimeTypeChooser::Comments) { item->setText(cl, mt.comment()); cl++; } if (visuals & KMimeTypeChooser::Patterns) { item->setText(cl, mt.globPatterns().join(QLatin1String("; "))); } if (selMimeTypes.contains(mimetype)) { item->setCheckState(0, Qt::Checked); groupItem->setExpanded(true); agroupisopen = true; if (!firstChecked) { firstChecked = item; } } else { item->setCheckState(0, Qt::Unchecked); } } if (firstChecked) { mimeTypeTree->scrollToItem(firstChecked); } if (!agroupisopen && idefault) { idefault->setExpanded(true); mimeTypeTree->scrollToItem(idefault); } mimeTypeTree->resizeColumnToContents(1); } void KMimeTypeChooserPrivate::_k_editMimeType() { QTreeWidgetItem *item = mimeTypeTree->currentItem(); if (!item || !item->parent()) { return; } QString mt = (item->parent())->text(0) + QLatin1Char('/') + item->text(0); // KF5 TODO: use a QFileSystemWatcher on one of the shared-mime-info generated files, instead. //q->connect( KSycoca::self(), SIGNAL(databaseChanged(QStringList)), // q, SLOT(_k_slotSycocaDatabaseChanged(QStringList)) ); #pragma message("KF5 TODO: use QFileSystemWatcher to be told when keditfiletype changed a mimetype") // or a better idea: a QMimeDatabaseWatcher class in Qt itself KMimeTypeEditor::editMimeType(mt, q); } void KMimeTypeChooserPrivate::_k_slotCurrentChanged(QTreeWidgetItem *item) { if (btnEditMimeType) { btnEditMimeType->setEnabled(item && item->parent()); } } // TODO: see _k_editMimeType void KMimeTypeChooserPrivate::_k_slotSycocaDatabaseChanged(const QStringList &changedResources) { if (changedResources.contains(QLatin1String("xdgdata-mime"))) { loadMimeTypes(); } } // recursive helper for mimeTypes() static void getCheckedItems(QList &lst, QTreeWidgetItem *parent) { for (int i = 0; i < parent->childCount(); ++i) { QTreeWidgetItem *item = parent->child(i); if (item->checkState(0) == Qt::Checked) { lst.append(item); } getCheckedItems(lst, item); } } static void getCheckedItems(QList &lst, QTreeWidget *tree) { for (int i = 0; i < tree->topLevelItemCount(); ++i) { QTreeWidgetItem *topItem = tree->topLevelItem(i); //if (topItem->checkState(0) == Qt::Checked) // lst.append(topItem); getCheckedItems(lst, topItem); } } QStringList KMimeTypeChooser::mimeTypes() const { QStringList mimeList; QList checkedItems; getCheckedItems(checkedItems, d->mimeTypeTree); - foreach (QTreeWidgetItem *item, checkedItems) { + mimeList.reserve(checkedItems.size()); + for (QTreeWidgetItem *item : qAsConst(checkedItems)) { mimeList.append(item->parent()->text(0) + QLatin1Char('/') + item->text(0)); } return mimeList; } QStringList KMimeTypeChooser::patterns() const { QStringList patternList; QList checkedItems; getCheckedItems(checkedItems, d->mimeTypeTree); QMimeDatabase db; - foreach (QTreeWidgetItem *item, checkedItems) { + for (QTreeWidgetItem *item : qAsConst(checkedItems)) { QMimeType mime = db.mimeTypeForName(item->parent()->text(0) + QLatin1Char('/') + item->text(0)); Q_ASSERT(mime.isValid()); patternList += mime.globPatterns(); } return patternList; } //END //BEGIN KMimeTypeChooserDialog::Private class Q_DECL_HIDDEN KMimeTypeChooserDialog::Private { public: Private(KMimeTypeChooserDialog *parent) : q(parent) { } void init(); KMimeTypeChooserDialog *q; KMimeTypeChooser *m_chooser; }; //END //BEGIN KMimeTypeChooserDialog KMimeTypeChooserDialog::KMimeTypeChooserDialog( const QString &caption, const QString &text, const QStringList &selMimeTypes, const QString &defaultGroup, const QStringList &groupsToShow, int visuals, QWidget *parent) : QDialog(parent), d(new Private(this)) { setWindowTitle(caption); d->m_chooser = new KMimeTypeChooser(text, selMimeTypes, defaultGroup, groupsToShow, visuals, this); d->init(); } KMimeTypeChooserDialog::KMimeTypeChooserDialog( const QString &caption, const QString &text, const QStringList &selMimeTypes, const QString &defaultGroup, QWidget *parent) : QDialog(parent), d(new Private(this)) { setWindowTitle(caption); d->m_chooser = new KMimeTypeChooser(text, selMimeTypes, defaultGroup, QStringList(), KMimeTypeChooser::Comments | KMimeTypeChooser::Patterns | KMimeTypeChooser::EditButton, this); d->init(); } KMimeTypeChooser *KMimeTypeChooserDialog::chooser() { return d->m_chooser; } void KMimeTypeChooserDialog::Private::init() { QVBoxLayout *layout = new QVBoxLayout; q->setLayout(layout); layout->addWidget(m_chooser); QDialogButtonBox *buttonBox = new QDialogButtonBox(q); buttonBox->setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); QObject::connect(buttonBox, &QDialogButtonBox::accepted, q, &QDialog::accept); QObject::connect(buttonBox, &QDialogButtonBox::rejected, q, &QDialog::reject); layout->addWidget(buttonBox); } KMimeTypeChooserDialog::~KMimeTypeChooserDialog() { delete d; } //END KMimeTypeChooserDialog #include "moc_kmimetypechooser.cpp" diff --git a/src/kselectaction.cpp b/src/kselectaction.cpp index be9e10b..72d560e 100644 --- a/src/kselectaction.cpp +++ b/src/kselectaction.cpp @@ -1,779 +1,783 @@ /* This file is part of the KDE libraries Copyright (C) 1999 Reginald Stadlbauer (C) 1999 Simon Hausmann (C) 2000 Nicolas Hadacek (C) 2000 Kurt Granroth (C) 2000 Michael Koch (C) 2001 Holger Freyther (C) 2002 Ellis Whitehead (C) 2002 Joseph Wenninger (C) 2003 Andras Mantia (C) 2005-2006 Hamish Rodda (C) 2006 Albert Astals Cid (C) 2006 Clarence Dang (C) 2006 Michel Hermier (C) 2007 Nick Shaforostoff This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kselectaction.h" #include "kselectaction_p.h" #include "loggingcategory.h" #include #include #include #include #include #include // QAction::setText("Hi") and then KPopupAccelManager exec'ing, causes // QAction::text() to return "&Hi" :( Comboboxes don't have accels and // display ampersands literally. static QString DropAmpersands(const QString &text) { QString label = text; int p = label.indexOf(QLatin1Char('&')); while (p >= 0 && p < label.length() - 1) { if (label[p + 1].isLetterOrNumber() // Valid accelerator. || label[p + 1] == QLatin1Char('&')) { // Escaped accelerator marker. label.remove(p, 1); } p = label.indexOf(QLatin1Char('&'), p + 1); } return label; } KSelectAction::KSelectAction(QObject *parent) : QWidgetAction(parent) , d_ptr(new KSelectActionPrivate()) { Q_D(KSelectAction); d->init(this); } KSelectAction::KSelectAction(const QString &text, QObject *parent) : QWidgetAction(parent) , d_ptr(new KSelectActionPrivate()) { Q_D(KSelectAction); d->init(this); setText(text); } KSelectAction::KSelectAction(const QIcon &icon, const QString &text, QObject *parent) : QWidgetAction(parent) , d_ptr(new KSelectActionPrivate()) { Q_D(KSelectAction); setIcon(icon); setText(text); d->init(this); } KSelectAction::KSelectAction(KSelectActionPrivate &dd, QObject *parent) : QWidgetAction(parent) , d_ptr(&dd) { Q_D(KSelectAction); d->init(this); } KSelectAction::~KSelectAction() { menu()->deleteLater(); delete d_ptr; } void KSelectActionPrivate::init(KSelectAction *q) { q_ptr = q; QObject::connect(q_ptr->selectableActionGroup(), &QActionGroup::triggered, q_ptr, &KSelectAction::actionTriggered); QObject::connect(q_ptr, &QAction::toggled, q_ptr, &KSelectAction::slotToggled); q_ptr->setMenu(new QMenu()); q_ptr->setEnabled(false); } QActionGroup *KSelectAction::selectableActionGroup() const { Q_D(const KSelectAction); return d->m_actionGroup; } QList KSelectAction::actions() const { return selectableActionGroup()->actions(); } QAction *KSelectAction::currentAction() const { return selectableActionGroup()->checkedAction(); } int KSelectAction::currentItem() const { return selectableActionGroup()->actions().indexOf(currentAction()); } QString KSelectAction::currentText() const { if (QAction *a = currentAction()) { return ::DropAmpersands(a->text()); } return QString(); } bool KSelectAction::setCurrentAction(QAction *action) { //qCDebug(KWidgetsAddonsLog) << "KSelectAction::setCurrentAction(" << action << ")"; if (action) { if (actions().contains(action)) { if (action->isVisible() && action->isEnabled() && action->isCheckable()) { action->setChecked(true); if (isCheckable()) { setChecked(true); } return true; } else { qCWarning(KWidgetsAddonsLog) << "Action does not have the correct properties to be current:" << action->text(); } } else { qCWarning(KWidgetsAddonsLog) << "Action does not belong to group:" << action->text(); } return false; } if (currentAction()) { currentAction()->setChecked(false); } return false; } bool KSelectAction::setCurrentItem(int index) { //qCDebug(KWidgetsAddonsLog) << "KSelectAction::setCurrentIndex(" << index << ")"; return setCurrentAction(action(index)); } QAction *KSelectAction::action(int index) const { if (index >= 0 && index < selectableActionGroup()->actions().count()) { return selectableActionGroup()->actions().at(index); } return nullptr; } QAction *KSelectAction::action(const QString &text, Qt::CaseSensitivity cs) const { QString compare; if (cs == Qt::CaseSensitive) { compare = text; } else { compare = text.toLower(); } - foreach (QAction *action, selectableActionGroup()->actions()) { + const auto selectableActions = selectableActionGroup()->actions(); + for (QAction *action : selectableActions) { const QString text = ::DropAmpersands(action->text()); if (cs == Qt::CaseSensitive) { if (text == compare) { return action; } } else if (cs == Qt::CaseInsensitive) { if (text.toLower() == compare) { return action; } } } return nullptr; } bool KSelectAction::setCurrentAction(const QString &text, Qt::CaseSensitivity cs) { //qCDebug(KWidgetsAddonsLog) << "KSelectAction::setCurrentAction(" << text << ",cs=" << cs << ")"; return setCurrentAction(action(text, cs)); } void KSelectAction::setComboWidth(int width) { Q_D(KSelectAction); if (width < 0) { return; } d->m_comboWidth = width; - foreach (QComboBox *box, d->m_comboBoxes) { + for (QComboBox *box : qAsConst(d->m_comboBoxes)) { box->setMaximumWidth(d->m_comboWidth); } emit changed(); } void KSelectAction::setMaxComboViewCount(int n) { Q_D(KSelectAction); d->m_maxComboViewCount = n; - foreach (QComboBox *box, d->m_comboBoxes) + for (QComboBox *box : qAsConst(d->m_comboBoxes)) { if (d->m_maxComboViewCount != -1) { box->setMaxVisibleItems(d->m_maxComboViewCount); } else // hardcoded qt default { box->setMaxVisibleItems(10); } + } emit changed(); } void KSelectAction::addAction(QAction *action) { insertAction(nullptr, action); } QAction *KSelectAction::addAction(const QString &text) { Q_D(KSelectAction); QAction *newAction = new QAction(parent()); newAction->setText(text); newAction->setCheckable(true); newAction->setProperty("isShortcutConfigurable", false); if (!d->m_menuAccelsEnabled) { newAction->setText(text); newAction->setShortcut(QKeySequence()); } addAction(newAction); return newAction; } QAction *KSelectAction::addAction(const QIcon &icon, const QString &text) { QAction *newAction = addAction(text); newAction->setIcon(icon); return newAction; } QAction *KSelectAction::removeAction(QAction *action) { Q_D(KSelectAction); //qCDebug(KWidgetsAddonsLog) << "KSelectAction::removeAction(" << action << ")"; //int index = selectableActionGroup()->actions().indexOf(action); //qCDebug(KWidgetsAddonsLog) << "\tindex=" << index; // Removes the action from the group and sets its parent to null. d->m_actionGroup->removeAction(action); // Disable when no action is in the group bool hasActions = selectableActionGroup()->actions().isEmpty(); setEnabled(!hasActions); - foreach (QToolButton *button, d->m_buttons) { + for (QToolButton *button : qAsConst(d->m_buttons)) { button->setEnabled(!hasActions); button->removeAction(action); } - foreach (QComboBox *comboBox, d->m_comboBoxes) { + for (QComboBox *comboBox : qAsConst(d->m_comboBoxes)) { comboBox->setEnabled(!hasActions); comboBox->removeAction(action); } menu()->removeAction(action); return action; } void KSelectAction::insertAction(QAction *before, QAction *action) { Q_D(KSelectAction); action->setActionGroup(selectableActionGroup()); // Re-Enable when an action is added setEnabled(true); // Keep in sync with createToolBarWidget() - foreach (QToolButton *button, d->m_buttons) { + for (QToolButton *button : qAsConst(d->m_buttons)) { button->setEnabled(true); button->insertAction(before, action); } - foreach (QComboBox *comboBox, d->m_comboBoxes) { + for (QComboBox *comboBox : qAsConst(d->m_comboBoxes)) { comboBox->setEnabled(true); comboBox->insertAction(before, action); } menu()->insertAction(before, action); } void KSelectAction::actionTriggered(QAction *action) { // cache values so we don't need access to members in the action // after we've done an emit() const QString text = ::DropAmpersands(action->text()); const int index = selectableActionGroup()->actions().indexOf(action); //qCDebug(KWidgetsAddonsLog) << "KSelectAction::actionTriggered(" << action << ") text=" << text // << " index=" << index << " emitting triggered()" << endl; if (isCheckable()) { // if this is subsidiary of other KSelectAction-derived class trigger(); // then imitate usual QAction behaviour so that other submenus (and their items) become unchecked } emit triggered(action); emit triggered(index); emit triggered(text); } QStringList KSelectAction::items() const { Q_D(const KSelectAction); QStringList ret; - foreach (QAction *action, d->m_actionGroup->actions()) { + const auto actions = d->m_actionGroup->actions(); + ret.reserve(actions.size()); + for (QAction *action : actions) { ret << ::DropAmpersands(action->text()); } return ret; } void KSelectAction::changeItem(int index, const QString &text) { Q_D(KSelectAction); if (index < 0 || index >= actions().count()) { qCWarning(KWidgetsAddonsLog) << "KSelectAction::changeItem Index out of scope"; return; } actions()[index]->setText(d->makeMenuText(text)); } void KSelectAction::setItems(const QStringList &lst) { Q_D(KSelectAction); //qCDebug(KWidgetsAddonsLog) << "KSelectAction::setItems(" << lst << ")"; clear(); - foreach (const QString &string, lst) { + for (const QString &string : lst) { if (!string.isEmpty()) { addAction(string); } else { QAction *action = new QAction(this); action->setSeparator(true); addAction(action); } } // Disable if empty and not editable setEnabled(lst.count() > 0 || d->m_edit); } int KSelectAction::comboWidth() const { Q_D(const KSelectAction); return d->m_comboWidth; } void KSelectAction::clear() { Q_D(KSelectAction); //qCDebug(KWidgetsAddonsLog) << "KSelectAction::clear()"; // we need to delete the actions later since we may get a call to clear() // from a method called due to a triggered(...) signal const QList actions = d->m_actionGroup->actions(); for (int i = 0; i < actions.count(); ++i) { // deleteLater() only removes us from the actions() list (among // other things) on the next entry into the event loop. Until then, // e.g. action() and setCurrentItem() will be working on items // that are supposed to have been deleted. So detach the action to // prevent this from happening. removeAction(actions[i]); actions[i]->deleteLater(); } } void KSelectAction::removeAllActions() { Q_D(KSelectAction); while (d->m_actionGroup->actions().count()) { removeAction(d->m_actionGroup->actions().first()); } } void KSelectAction::setEditable(bool edit) { Q_D(KSelectAction); d->m_edit = edit; - foreach (QComboBox *comboBox, d->m_comboBoxes) { + for (QComboBox *comboBox : qAsConst(d->m_comboBoxes)) { comboBox->setEditable(edit); } emit changed(); } bool KSelectAction::isEditable() const { Q_D(const KSelectAction); return d->m_edit; } void KSelectAction::slotToggled(bool checked) { //if (checked && selectableActionGroup()->checkedAction()) if (!checked && currentAction()) { // other's submenu item has been selected currentAction()->setChecked(false); } } KSelectAction::ToolBarMode KSelectAction::toolBarMode() const { Q_D(const KSelectAction); return d->m_toolBarMode; } void KSelectAction::setToolBarMode(ToolBarMode mode) { Q_D(KSelectAction); d->m_toolBarMode = mode; } QToolButton::ToolButtonPopupMode KSelectAction::toolButtonPopupMode() const { Q_D(const KSelectAction); return d->m_toolButtonPopupMode; } void KSelectAction::setToolButtonPopupMode(QToolButton::ToolButtonPopupMode mode) { Q_D(KSelectAction); d->m_toolButtonPopupMode = mode; } void KSelectActionPrivate::_k_comboBoxDeleted(QObject *object) { - foreach (QComboBox *comboBox, m_comboBoxes) - if (object == comboBox) { - m_comboBoxes.removeAll(static_cast(object)); - break; - } + m_comboBoxes.removeAll(static_cast(object)); } void KSelectActionPrivate::_k_comboBoxCurrentIndexChanged(int index) { Q_Q(KSelectAction); //qCDebug(KWidgetsAddonsLog) << "KSelectActionPrivate::_k_comboBoxCurrentIndexChanged(" << index << ")"; QComboBox *triggeringCombo = qobject_cast (q->sender()); QAction *a = q->action(index); //qCDebug(KWidgetsAddonsLog) << "\ta=" << a; if (a) { //qCDebug(KWidgetsAddonsLog) << "\t\tsetting as current action"; a->trigger(); } else if (q->isEditable() && triggeringCombo && triggeringCombo->count() > 0 && index == triggeringCombo->count() - 1) { // User must have added a new item by typing and pressing enter. const QString newItemText = triggeringCombo->currentText(); //qCDebug(KWidgetsAddonsLog) << "\t\tuser typed new item '" << newItemText << "'"; // Only 1 combobox contains this and it's not a proper action. bool blocked = triggeringCombo->blockSignals(true); triggeringCombo->removeItem(index); triggeringCombo->blockSignals(blocked); QAction *newAction = q->addAction(newItemText); newAction->trigger(); } else { if (q->selectableActionGroup()->checkedAction()) { q->selectableActionGroup()->checkedAction()->setChecked(false); } } } // TODO: DropAmpersands() certainly makes sure this doesn't work. But I don't // think it did anyway esp. in the presence KCheckAccelerator - Clarence. void KSelectAction::setMenuAccelsEnabled(bool b) { Q_D(KSelectAction); d->m_menuAccelsEnabled = b; } bool KSelectAction::menuAccelsEnabled() const { Q_D(const KSelectAction); return d->m_menuAccelsEnabled; } QWidget *KSelectAction::createWidget(QWidget *parent) { Q_D(KSelectAction); QMenu *menu = qobject_cast(parent); if (menu) { // If used in a menu want to return 0 and use only the text, not a widget return nullptr; } ToolBarMode mode = toolBarMode(); QToolBar *toolBar = qobject_cast(parent); if (!toolBar && mode != ComboBoxMode) { // we can return a combobox just fine. return nullptr; } switch (mode) { case MenuMode: { QToolButton *button = new QToolButton(toolBar); button->setToolTip(toolTip()); button->setWhatsThis(whatsThis()); button->setStatusTip(statusTip()); button->setAutoRaise(true); button->setFocusPolicy(Qt::NoFocus); button->setIconSize(toolBar->iconSize()); button->setToolButtonStyle(toolBar->toolButtonStyle()); QObject::connect(toolBar, &QToolBar::iconSizeChanged, button, &QAbstractButton::setIconSize); QObject::connect(toolBar, &QToolBar::toolButtonStyleChanged, button, &QToolButton::setToolButtonStyle); button->setDefaultAction(this); QObject::connect(button, &QToolButton::triggered, toolBar, &QToolBar::actionTriggered); button->setPopupMode(toolButtonPopupMode()); button->addActions(selectableActionGroup()->actions()); d->m_buttons.append(button); return button; } case ComboBoxMode: { QComboBox *comboBox = new QComboBox(parent); comboBox->installEventFilter(this); if (d->m_maxComboViewCount != -1) { comboBox->setMaxVisibleItems(d->m_maxComboViewCount); } if (d->m_comboWidth > 0) { comboBox->setMaximumWidth(d->m_comboWidth); } comboBox->setEditable(isEditable()); comboBox->setToolTip(toolTip()); comboBox->setWhatsThis(whatsThis()); comboBox->setStatusTip(statusTip()); - foreach (QAction *action, selectableActionGroup()->actions()) { + const auto selectableActions = selectableActionGroup()->actions(); + for (QAction *action : selectableActions) { comboBox->addAction(action); } - if (selectableActionGroup()->actions().isEmpty()) { + if (selectableActions.isEmpty()) { comboBox->setEnabled(false); } connect(comboBox, SIGNAL(destroyed(QObject*)), SLOT(_k_comboBoxDeleted(QObject*))); connect(comboBox, SIGNAL(currentIndexChanged(int)), SLOT(_k_comboBoxCurrentIndexChanged(int))); d->m_comboBoxes.append(comboBox); return comboBox; } } return nullptr; } void KSelectAction::deleteWidget(QWidget *widget) { Q_D(KSelectAction); if (QToolButton *toolButton = qobject_cast(widget)) { d->m_buttons.removeAll(toolButton); } else if (QComboBox *comboBox = qobject_cast(widget)) { d->m_comboBoxes.removeAll(comboBox); } QWidgetAction::deleteWidget(widget); } bool KSelectAction::event(QEvent *event) { Q_D(KSelectAction); if (event->type() == QEvent::ActionChanged) { - Q_FOREACH (QComboBox *comboBox, d->m_comboBoxes) { + for (QComboBox *comboBox : qAsConst(d->m_comboBoxes)) { comboBox->setToolTip(toolTip()); comboBox->setWhatsThis(whatsThis()); comboBox->setStatusTip(statusTip()); } - Q_FOREACH (QToolButton *toolButton, d->m_buttons) { + for (QToolButton *toolButton : qAsConst(d->m_buttons)) { toolButton->setToolTip(toolTip()); toolButton->setWhatsThis(whatsThis()); toolButton->setStatusTip(statusTip()); } } return QWidgetAction::event(event); } // KSelectAction::eventFilter() is called before action->setChecked() // invokes the signal to update QActionGroup so KSelectAction::currentItem() // returns an old value. There are 3 possibilities, where n actions will // report QAction::isChecked() where n is: // // 0: the checked action was unchecked // 1: the checked action did not change // 2: another action was checked but QActionGroup has not been invoked yet // to uncheck the one that was checked before // // TODO: we might want to cache this since QEvent::ActionChanged is fired // often. static int TrueCurrentItem(KSelectAction *sa) { QAction *curAction = sa->currentAction(); //qCDebug(KWidgetsAddonsLog) << "\tTrueCurrentItem(" << sa << ") curAction=" << curAction; - foreach (QAction *action, sa->actions()) { + const auto actions = sa->actions(); + int i = 0; + for (QAction *action : actions) { if (action->isChecked()) { //qCDebug(KWidgetsAddonsLog) << "\t\taction " << action << " (text=" << action->text () << ") isChecked"; // 2 actions checked case? if (action != curAction) { //qCDebug(KWidgetsAddonsLog) << "\t\t\tmust be newly selected one"; - return sa->actions().indexOf(action); + return i; } } + ++i; } //qCDebug(KWidgetsAddonsLog) << "\t\tcurrent action still selected? " << (curAction && curAction->isChecked ()); // 1 or 0 actions checked case (in that order)? return (curAction && curAction->isChecked()) ? sa->actions().indexOf(curAction) : -1; } bool KSelectAction::eventFilter(QObject *watched, QEvent *event) { QComboBox *comboBox = qobject_cast (watched); if (!comboBox) { return false/*propagate event*/; } // If focus is lost, replace any edited text with the currently selected // item. if (event->type() == QEvent::FocusOut) { QFocusEvent *const e = static_cast (event); //qCDebug(KWidgetsAddonsLog) << "KSelectAction::eventFilter(FocusOut)" // << " comboBox: ptr=" << comboBox // << " reason=" << e->reason () // << endl; if (e->reason() != Qt::ActiveWindowFocusReason/*switch window*/ && e->reason() != Qt::PopupFocusReason/*menu*/ && e->reason() != Qt::OtherFocusReason/*inconsistently reproduceable actions...*/) { //qCDebug(KWidgetsAddonsLog) << "\tkilling text"; comboBox->setEditText(comboBox->itemText(comboBox->currentIndex())); } return false/*propagate event*/; } bool blocked = comboBox->blockSignals(true); if (event->type() == QEvent::ActionAdded) { QActionEvent *const e = static_cast (event); const int index = e->before() ? comboBox->findData(QVariant::fromValue(e->before())) : comboBox->count(); const int newItem = ::TrueCurrentItem(this); //qCDebug(KWidgetsAddonsLog) << "KSelectAction::eventFilter(ActionAdded)" // << " comboBox: ptr=" << comboBox // << " currentItem=" << comboBox->currentIndex () // << " add index=" << index // << " action new: e->before=" << e->before () // << " ptr=" << e->action () // << " icon=" << e->action ()->icon () // << " text=" << e->action ()->text () // << " currentItem=" << newItem // << endl; comboBox->insertItem(index, e->action()->icon(), ::DropAmpersands(e->action()->text()), QVariant::fromValue(e->action())); if (QStandardItemModel *model = qobject_cast(comboBox->model())) { QStandardItem *item = model->item(index); item->setEnabled(e->action()->isEnabled()); } // Inserting an item into a combobox can change the current item so // make sure the item corresponding to the checked action is selected. comboBox->setCurrentIndex(newItem); } else if (event->type() == QEvent::ActionChanged) { QActionEvent *const e = static_cast (event); const int index = comboBox->findData(QVariant::fromValue(e->action())); const int newItem = ::TrueCurrentItem(this); //qCDebug(KWidgetsAddonsLog) << "KSelectAction::eventFilter(ActionChanged)" // << " comboBox: ptr=" << comboBox // << " currentItem=" << comboBox->currentIndex () // << " changed action's index=" << index // << " action new: ptr=" << e->action () // << " icon=" << e->action ()->icon () // << " text=" << e->action ()->text () // << " currentItem=" << newItem // << endl; comboBox->setItemIcon(index, e->action()->icon()); comboBox->setItemText(index, ::DropAmpersands(e->action()->text())); if (QStandardItemModel *model = qobject_cast(comboBox->model())) { QStandardItem *item = model->item(index); item->setEnabled(e->action()->isEnabled()); } // The checked action may have become unchecked so // make sure the item corresponding to the checked action is selected. comboBox->setCurrentIndex(newItem); } else if (event->type() == QEvent::ActionRemoved) { QActionEvent *const e = static_cast (event); const int index = comboBox->findData(QVariant::fromValue(e->action())); const int newItem = ::TrueCurrentItem(this); //qCDebug(KWidgetsAddonsLog) << "KSelectAction::eventFilter(ActionRemoved)" // << " comboBox: ptr=" << comboBox // << " currentItem=" << comboBox->currentIndex () // << " delete action index=" << index // << " new: currentItem=" << newItem // << endl; comboBox->removeItem(index); // Removing an item from a combobox can change the current item so // make sure the item corresponding to the checked action is selected. comboBox->setCurrentIndex(newItem); } comboBox->blockSignals(blocked); return false/*propagate event*/; } // END #include "moc_kselectaction.cpp" diff --git a/src/kselectaction_p.h b/src/kselectaction_p.h index 2610bb0..ced4e30 100644 --- a/src/kselectaction_p.h +++ b/src/kselectaction_p.h @@ -1,108 +1,108 @@ /* This file is part of the KDE libraries Copyright (C) 1999 Reginald Stadlbauer (C) 1999 Simon Hausmann (C) 2000 Nicolas Hadacek (C) 2000 Kurt Granroth (C) 2000 Michael Koch (C) 2001 Holger Freyther (C) 2002 Ellis Whitehead (C) 2002 Joseph Wenninger (C) 2003 Andras Mantia (C) 2005-2006 Hamish Rodda (C) 2006 Albert Astals Cid (C) 2006 Clarence Dang (C) 2006 Michel Hermier (C) 2007 Nick Shaforostoff This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KSELECTACTION_P_H #define KSELECTACTION_P_H #include class KSelectActionPrivate { Q_DECLARE_PUBLIC(KSelectAction) public: KSelectActionPrivate() : q_ptr(nullptr) { m_edit = false; m_menuAccelsEnabled = true; m_comboWidth = -1; m_maxComboViewCount = -1; m_toolBarMode = KSelectAction::ComboBoxMode; m_toolButtonPopupMode = QToolButton::InstantPopup; //InstantPopup by default because there is no default action m_actionGroup = new QActionGroup(nullptr); } virtual ~KSelectActionPrivate() { // unhook the event filter, as the deletion of the actiongroup // will trigger it - Q_FOREACH (QComboBox *box, m_comboBoxes) { + for (QComboBox *box : qAsConst(m_comboBoxes)) { box->removeEventFilter(q_ptr); } - Q_FOREACH (QToolButton *button, m_buttons) { + for (QToolButton *button : qAsConst(m_buttons)) { button->removeEventFilter(q_ptr); } delete m_actionGroup; } void _k_comboBoxDeleted(QObject *); void _k_comboBoxCurrentIndexChanged(int); void init(KSelectAction *); bool m_edit : 1; bool m_menuAccelsEnabled : 1; int m_comboWidth; int m_maxComboViewCount; KSelectAction::ToolBarMode m_toolBarMode; QToolButton::ToolButtonPopupMode m_toolButtonPopupMode; QActionGroup *m_actionGroup; QList m_buttons; QList m_comboBoxes; QString makeMenuText(const QString &_text) { if (m_menuAccelsEnabled) { return _text; } QString text = _text; int i = 0; while (i < text.length()) { if (text[ i ] == QLatin1Char('&')) { text.insert(i, QLatin1Char('&')); i += 2; } else { ++i; } } return text; } KSelectAction *q_ptr; }; #endif // KSELECTACTION_P_H diff --git a/src/ksqueezedtextlabel.cpp b/src/ksqueezedtextlabel.cpp index 728aa0b..9557c67 100644 --- a/src/ksqueezedtextlabel.cpp +++ b/src/ksqueezedtextlabel.cpp @@ -1,259 +1,261 @@ /* This file is part of the KDE libraries Copyright (C) 2000 Ronny Standtke This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "ksqueezedtextlabel.h" #include #include #include #include #include #include #include class KSqueezedTextLabelPrivate { public: void _k_copyFullText() { QApplication::clipboard()->setText(fullText); } QString fullText; Qt::TextElideMode elideMode; }; KSqueezedTextLabel::KSqueezedTextLabel(const QString &text, QWidget *parent) : QLabel(parent), d(new KSqueezedTextLabelPrivate) { setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed)); d->fullText = text; d->elideMode = Qt::ElideMiddle; squeezeTextToLabel(); } KSqueezedTextLabel::KSqueezedTextLabel(QWidget *parent) : QLabel(parent), d(new KSqueezedTextLabelPrivate) { setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed)); d->elideMode = Qt::ElideMiddle; } KSqueezedTextLabel::~KSqueezedTextLabel() { delete d; } void KSqueezedTextLabel::resizeEvent(QResizeEvent *) { squeezeTextToLabel(); } QSize KSqueezedTextLabel::minimumSizeHint() const { QSize sh = QLabel::minimumSizeHint(); sh.setWidth(-1); return sh; } QSize KSqueezedTextLabel::sizeHint() const { int maxWidth = QApplication::desktop()->screenGeometry(this).width() * 3 / 4; QFontMetrics fm(fontMetrics()); int textWidth = fm.width(d->fullText); if (textWidth > maxWidth) { textWidth = maxWidth; } const int chromeWidth = width() - contentsRect().width(); return QSize(textWidth + chromeWidth, QLabel::sizeHint().height()); } void KSqueezedTextLabel::setIndent(int indent) { QLabel::setIndent(indent); squeezeTextToLabel(); } void KSqueezedTextLabel::setMargin(int margin) { QLabel::setMargin(margin); squeezeTextToLabel(); } void KSqueezedTextLabel::setText(const QString &text) { d->fullText = text; squeezeTextToLabel(); } void KSqueezedTextLabel::clear() { d->fullText.clear(); QLabel::clear(); } void KSqueezedTextLabel::squeezeTextToLabel() { QFontMetrics fm(fontMetrics()); const int labelWidth = contentsRect().width(); QStringList squeezedLines; bool squeezed = false; - Q_FOREACH (const QString &line, d->fullText.split(QLatin1Char('\n'))) { + const auto textLines = d->fullText.split(QLatin1Char('\n')); + squeezedLines.reserve(textLines.size()); + for (const QString &line : textLines) { int lineWidth = fm.width(line); if (lineWidth > labelWidth) { squeezed = true; squeezedLines << fm.elidedText(line, d->elideMode, labelWidth); } else { squeezedLines << line; } } if (squeezed) { QLabel::setText(squeezedLines.join(QLatin1Char('\n'))); setToolTip(d->fullText); } else { QLabel::setText(d->fullText); setToolTip(QString()); } } QRect KSqueezedTextLabel::contentsRect() const { // calculation according to API docs for QLabel::indent const int margin = this->margin(); int indent = this->indent(); if (indent < 0) { if (frameWidth() == 0) { indent = 0; } else { indent = fontMetrics().width(QLatin1Char('x')) / 2 - margin; } } QRect result = QLabel::contentsRect(); if (indent > 0) { const int alignment = this->alignment(); if (alignment & Qt::AlignLeft) { result.setLeft(result.left() + indent); } if (alignment & Qt::AlignTop) { result.setTop(result.top() + indent); } if (alignment & Qt::AlignRight) { result.setRight(result.right() - indent); } if (alignment & Qt::AlignBottom) { result.setBottom(result.bottom() - indent); } } result.adjust(margin, margin, -margin, -margin); return result; } void KSqueezedTextLabel::setAlignment(Qt::Alignment alignment) { // save fullText and restore it QString tmpFull(d->fullText); QLabel::setAlignment(alignment); d->fullText = tmpFull; } Qt::TextElideMode KSqueezedTextLabel::textElideMode() const { return d->elideMode; } void KSqueezedTextLabel::setTextElideMode(Qt::TextElideMode mode) { d->elideMode = mode; squeezeTextToLabel(); } QString KSqueezedTextLabel::fullText() const { return d->fullText; } bool KSqueezedTextLabel::isSqueezed() const { return d->fullText != text(); } void KSqueezedTextLabel::contextMenuEvent(QContextMenuEvent *ev) { // We want to reimplement "Copy" to include the elided text. // But this means reimplementing the full popup menu, so no more // copy-link-address or copy-selection support anymore, since we // have no access to the QTextDocument. // Maybe we should have a boolean flag in KSqueezedTextLabel itself for // whether to show the "Copy Full Text" custom popup? // For now I chose to show it when the text is squeezed; when it's not, the // standard popup menu can do the job (select all, copy). if (isSqueezed()) { QMenu menu(this); QAction *act = new QAction(tr("&Copy Full Text"), &menu); connect(act, &QAction::triggered, this, [this]() { d->_k_copyFullText(); }); menu.addAction(act); ev->accept(); menu.exec(ev->globalPos()); } else { QLabel::contextMenuEvent(ev); } } void KSqueezedTextLabel::mouseReleaseEvent(QMouseEvent *ev) { if (QApplication::clipboard()->supportsSelection() && textInteractionFlags() != Qt::NoTextInteraction && ev->button() == Qt::LeftButton && !d->fullText.isEmpty() && hasSelectedText()) { // Expand "..." when selecting with the mouse QString txt = selectedText(); const QChar ellipsisChar(0x2026); // from qtextengine.cpp const int dotsPos = txt.indexOf(ellipsisChar); if (dotsPos > -1) { // Ex: abcde...yz, selecting de...y (selectionStart=3) // charsBeforeSelection = selectionStart = 2 (ab) // charsAfterSelection = 1 (z) // final selection length= 26 - 2 - 1 = 23 const int start = selectionStart(); int charsAfterSelection = text().length() - start - selectedText().length(); txt = d->fullText; // Strip markup tags if (textFormat() == Qt::RichText || (textFormat() == Qt::AutoText && Qt::mightBeRichText(txt))) { txt.remove(QRegExp(QStringLiteral("<[^>]*>"))); // account for stripped characters charsAfterSelection -= d->fullText.length() - txt.length(); } txt = txt.mid(selectionStart(), txt.length() - start - charsAfterSelection); } QApplication::clipboard()->setText(txt, QClipboard::Selection); } else { QLabel::mouseReleaseEvent(ev); } } #include "moc_ksqueezedtextlabel.cpp" diff --git a/src/ktimecombobox.cpp b/src/ktimecombobox.cpp index f2bbf2f..b056456 100644 --- a/src/ktimecombobox.cpp +++ b/src/ktimecombobox.cpp @@ -1,549 +1,549 @@ /* Copyright 2011 John Layt This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "ktimecombobox.h" #include #include #include #include "kmessagebox.h" class KTimeComboBoxPrivate { public: KTimeComboBoxPrivate(KTimeComboBox *q); virtual ~KTimeComboBoxPrivate(); QTime defaultMinTime(); QTime defaultMaxTime(); QString timeFormatToInputMask(const QString &format, bool nullMask = false); QTime nearestIntervalTime(const QTime &time); QString formatTime(const QTime &time); void initTimeWidget(); void updateTimeWidget(); // Q_PRIVATE_SLOTs void selectTime(int index); void editTime(const QString &text); void enterTime(const QTime &time); void parseTime(); void warnTime(); KTimeComboBox *const q; QTime m_time; KTimeComboBox::Options m_options; QTime m_minTime; QTime m_maxTime; QString m_minWarnMsg; QString m_maxWarnMsg; QString m_nullString; bool m_warningShown; QLocale::FormatType m_displayFormat; int m_timeListInterval; QList m_timeList; }; KTimeComboBoxPrivate::KTimeComboBoxPrivate(KTimeComboBox *q) : q(q), m_time(QTime(0, 0, 0)), m_warningShown(false), m_displayFormat(QLocale::ShortFormat), m_timeListInterval(15) { m_options = KTimeComboBox::EditTime | KTimeComboBox::SelectTime; m_minTime = defaultMinTime(); m_maxTime = defaultMaxTime(); } KTimeComboBoxPrivate::~KTimeComboBoxPrivate() { } QTime KTimeComboBoxPrivate::defaultMinTime() { return QTime(0, 0, 0, 0); } QTime KTimeComboBoxPrivate::defaultMaxTime() { return QTime(23, 59, 59, 999); } QString KTimeComboBoxPrivate::timeFormatToInputMask(const QString &format, bool nullMask) { const QLocale locale = q->locale(); //TODO not sure this will always work, does it support DigitSets, am/pm is dodgy? QString mask = formatTime(QTime(12, 34, 56, 789)); QString null = mask; mask.replace(locale.toString(12), QLatin1String("09")); null.remove(locale.toString(12)); mask.replace(locale.toString(34), QLatin1String("99")); null.remove(locale.toString(34)); mask.replace(locale.toString(56), QLatin1String("99")); null.remove(locale.toString(56)); mask.replace(locale.toString(789), QLatin1String("900")); null.remove(locale.toString(789)); // See if this time format contains a specifier for // AM/PM, regardless of case. int ampmPos = format.indexOf(QLatin1String("AP"), 0, Qt::CaseInsensitive); if (ampmPos != -1) { // Get the locale aware am/pm strings QString am = locale.amText(); QString pm = locale.pmText(); // Convert the am/pm strings to the same case // as the input format. This is necessary to // provide a correct mask to the line edit. if (format[ampmPos].isUpper()) { am = am.toUpper(); pm = pm.toUpper(); } else { am = am.toLower(); pm = pm.toLower(); } int ampmLen = qMax(am.length(), pm.length()); const QString ampmMask(ampmLen, QLatin1Char('a')); mask.replace(pm, ampmMask); null.remove(pm); } if (nullMask) { return null; } else { return mask; } } QTime KTimeComboBoxPrivate::nearestIntervalTime(const QTime &time) { int i = 0; while (q->itemData(i).toTime() < time) { ++i; } QTime before = q->itemData(i).toTime(); QTime after = q->itemData(i + 1).toTime(); if (before.secsTo(time) <= time.secsTo(after)) { return before; } else { return after; } } QString KTimeComboBoxPrivate::formatTime(const QTime &time) { return q->locale().toString(time, m_displayFormat); } void KTimeComboBoxPrivate::initTimeWidget() { q->blockSignals(true); q->clear(); // Set the input mask from the current format const QLocale locale = q->locale(); q->lineEdit()->setInputMask(timeFormatToInputMask(locale.timeFormat(m_displayFormat))); m_nullString = timeFormatToInputMask(locale.timeFormat(m_displayFormat), true); // If EditTime then set the line edit q->lineEdit()->setReadOnly((m_options & KTimeComboBox::EditTime) != KTimeComboBox::EditTime); // If SelectTime then make list items visible if ((m_options & KTimeComboBox::SelectTime) == KTimeComboBox::SelectTime) { q->setMaxVisibleItems(10); } else { q->setMaxVisibleItems(0); } // Populate the drop-down time list // If no time list set the use the time interval if (m_timeList.isEmpty()) { QTime startTime = m_minTime; QTime thisTime(startTime.hour(), 0, 0, 0); while (thisTime.isValid() && thisTime <= startTime) { thisTime = thisTime.addSecs(m_timeListInterval * 60); } QTime endTime = m_maxTime; q->addItem(formatTime(startTime), startTime); while (thisTime.isValid() && thisTime < endTime) { q->addItem(formatTime(thisTime), thisTime); QTime newTime = thisTime.addSecs(m_timeListInterval * 60); if (newTime.isValid() && newTime > thisTime) { thisTime = newTime; } else { thisTime = QTime(); } } q->addItem(formatTime(endTime), endTime); } else { - foreach (const QTime &thisTime, m_timeList) { + for (const QTime &thisTime : qAsConst(m_timeList)) { if (thisTime.isValid() && thisTime >= m_minTime && thisTime <= m_maxTime) { q->addItem(formatTime(thisTime), thisTime); } } } q->blockSignals(false); } void KTimeComboBoxPrivate::updateTimeWidget() { q->blockSignals(true); int pos = q->lineEdit()->cursorPosition(); //Set index before setting text otherwise it overwrites int i = 0; if (!m_time.isValid() || m_time < m_minTime) { i = 0; } else if (m_time > m_maxTime) { i = q->count() - 1; } else { while (q->itemData(i).toTime() < m_time && i < q->count() - 1) { ++i; } } q->setCurrentIndex(i); if (m_time.isValid()) { q->lineEdit()->setText(formatTime(m_time)); } else { q->lineEdit()->setText(QString()); } q->lineEdit()->setCursorPosition(pos); q->blockSignals(false); } void KTimeComboBoxPrivate::selectTime(int index) { enterTime(q->itemData(index).toTime()); } void KTimeComboBoxPrivate::editTime(const QString &text) { m_warningShown = false; emit q->timeEdited(q->locale().toTime(text, m_displayFormat)); } void KTimeComboBoxPrivate::parseTime() { m_time = q->locale().toTime(q->lineEdit()->text(), m_displayFormat); } void KTimeComboBoxPrivate::enterTime(const QTime &time) { q->setTime(time); warnTime(); emit q->timeEntered(m_time); } void KTimeComboBoxPrivate::warnTime() { if (!m_warningShown && !q->isValid() && (m_options & KTimeComboBox::WarnOnInvalid) == KTimeComboBox::WarnOnInvalid) { QString warnMsg; if (!m_time.isValid()) { warnMsg = KTimeComboBox::tr("The time you entered is invalid", "@info"); } else if (m_time < m_minTime) { if (m_minWarnMsg.isEmpty()) { warnMsg = KTimeComboBox::tr("Time cannot be earlier than %1", "@info").arg(formatTime(m_minTime)); } else { warnMsg = m_minWarnMsg; warnMsg.replace(QLatin1String("%1"), formatTime(m_minTime)); } } else if (m_time > m_maxTime) { if (m_maxWarnMsg.isEmpty()) { warnMsg = KTimeComboBox::tr("Time cannot be later than %1", "@info").arg(formatTime(m_maxTime)); } else { warnMsg = m_maxWarnMsg; warnMsg.replace(QLatin1String("%1"), formatTime(m_maxTime)); } } m_warningShown = true; KMessageBox::sorry(q, warnMsg); } } KTimeComboBox::KTimeComboBox(QWidget *parent) : QComboBox(parent), d(new KTimeComboBoxPrivate(this)) { setEditable(true); setInsertPolicy(QComboBox::NoInsert); setSizeAdjustPolicy(QComboBox::AdjustToContents); d->initTimeWidget(); d->updateTimeWidget(); connect(this, SIGNAL(activated(int)), this, SLOT(selectTime(int))); connect(this, &KTimeComboBox::editTextChanged, this, [this](const QString &str) { d->editTime(str); }); } KTimeComboBox::~KTimeComboBox() { delete d; } QTime KTimeComboBox::time() const { d->parseTime(); return d->m_time; } void KTimeComboBox::setTime(const QTime &time) { if (time == d->m_time) { return; } if ((d->m_options & KTimeComboBox::ForceTime) == KTimeComboBox::ForceTime) { assignTime(d->nearestIntervalTime(time)); } else { assignTime(time); } d->updateTimeWidget(); emit timeChanged(d->m_time); } void KTimeComboBox::assignTime(const QTime &time) { d->m_time = time; } bool KTimeComboBox::isValid() const { d->parseTime(); return d->m_time.isValid() && d->m_time >= d->m_minTime && d->m_time <= d->m_maxTime; } bool KTimeComboBox::isNull() const { return lineEdit()->text() == d->m_nullString; } KTimeComboBox::Options KTimeComboBox::options() const { return d->m_options; } void KTimeComboBox::setOptions(Options options) { if (options != d->m_options) { d->m_options = options; d->initTimeWidget(); d->updateTimeWidget(); } } QTime KTimeComboBox::minimumTime() const { return d->m_minTime; } void KTimeComboBox::setMinimumTime(const QTime &minTime, const QString &minWarnMsg) { setTimeRange(minTime, d->m_maxTime, minWarnMsg, d->m_maxWarnMsg); } void KTimeComboBox::resetMinimumTime() { setTimeRange(d->defaultMinTime(), d->m_maxTime, QString(), d->m_maxWarnMsg); } QTime KTimeComboBox::maximumTime() const { return d->m_maxTime; } void KTimeComboBox::setMaximumTime(const QTime &maxTime, const QString &maxWarnMsg) { setTimeRange(d->m_minTime, maxTime, d->m_minWarnMsg, maxWarnMsg); } void KTimeComboBox::resetMaximumTime() { setTimeRange(d->m_minTime, d->defaultMaxTime(), d->m_minWarnMsg, QString()); } void KTimeComboBox::setTimeRange(const QTime &minTime, const QTime &maxTime, const QString &minWarnMsg, const QString &maxWarnMsg) { if (!minTime.isValid() || !maxTime.isValid() || minTime > maxTime) { return; } if (minTime != d->m_minTime || maxTime != d->m_maxTime || minWarnMsg != d->m_minWarnMsg || maxWarnMsg != d->m_maxWarnMsg) { d->m_minTime = minTime; d->m_maxTime = maxTime; d->m_minWarnMsg = minWarnMsg; d->m_maxWarnMsg = maxWarnMsg; d->initTimeWidget(); d->updateTimeWidget(); } } void KTimeComboBox::resetTimeRange() { setTimeRange(d->defaultMinTime(), d->defaultMaxTime(), QString(), QString()); } QLocale::FormatType KTimeComboBox::displayFormat() const { return d->m_displayFormat; } void KTimeComboBox::setDisplayFormat(QLocale::FormatType format) { if (format != d->m_displayFormat) { d->m_displayFormat = format; d->initTimeWidget(); d->updateTimeWidget(); } } int KTimeComboBox::timeListInterval() const { return d->m_timeListInterval; } void KTimeComboBox::setTimeListInterval(int minutes) { if (minutes != d->m_timeListInterval) { //Must be able to exactly divide the valid time period int lowMins = (d->m_minTime.hour() * 60) + d->m_minTime.minute(); int hiMins = (d->m_maxTime.hour() * 60) + d->m_maxTime.minute(); if (d->m_minTime.minute() == 0 && d->m_maxTime.minute() == 59) { ++hiMins; } if ((hiMins - lowMins) % minutes == 0) { d->m_timeListInterval = minutes; d->m_timeList.clear(); } else { return; } d->initTimeWidget(); } } QList KTimeComboBox::timeList() const { //Return the drop down list as it is what can be selected currently QList list; int c = count(); list.reserve(c); for (int i = 0; i < c; ++i) { list.append(itemData(i).toTime()); } return list; } void KTimeComboBox::setTimeList(QList timeList, const QString &minWarnMsg, const QString &maxWarnMsg) { if (timeList != d->m_timeList) { d->m_timeList.clear(); - foreach (const QTime &time, timeList) { + for (const QTime &time : qAsConst(timeList)) { if (time.isValid() && !d->m_timeList.contains(time)) { d->m_timeList.append(time); } } std::sort(d->m_timeList.begin(), d->m_timeList.end()); // Does the updateTimeWidget call for us setTimeRange(d->m_timeList.first(), d->m_timeList.last(), minWarnMsg, maxWarnMsg); } } bool KTimeComboBox::eventFilter(QObject *object, QEvent *event) { return QComboBox::eventFilter(object, event); } void KTimeComboBox::keyPressEvent(QKeyEvent *keyEvent) { QTime temp; switch (keyEvent->key()) { case Qt::Key_Down: temp = d->m_time.addSecs(-60); break; case Qt::Key_Up: temp = d->m_time.addSecs(60); break; case Qt::Key_PageDown: temp = d->m_time.addSecs(-3600); break; case Qt::Key_PageUp: temp = d->m_time.addSecs(3600); break; default: QComboBox::keyPressEvent(keyEvent); return; } if (temp.isValid() && temp >= d->m_minTime && temp <= d->m_maxTime) { d->enterTime(temp); } } void KTimeComboBox::focusOutEvent(QFocusEvent *event) { d->parseTime(); d->warnTime(); QComboBox::focusOutEvent(event); } void KTimeComboBox::showPopup() { QComboBox::showPopup(); } void KTimeComboBox::hidePopup() { QComboBox::hidePopup(); } void KTimeComboBox::mousePressEvent(QMouseEvent *event) { QComboBox::mousePressEvent(event); } void KTimeComboBox::wheelEvent(QWheelEvent *event) { QComboBox::wheelEvent(event); } void KTimeComboBox::focusInEvent(QFocusEvent *event) { QComboBox::focusInEvent(event); } void KTimeComboBox::resizeEvent(QResizeEvent *event) { QComboBox::resizeEvent(event); } #include "moc_ktimecombobox.cpp" diff --git a/src/ktoolbarlabelaction.cpp b/src/ktoolbarlabelaction.cpp index 2328e6e..0701b0a 100644 --- a/src/ktoolbarlabelaction.cpp +++ b/src/ktoolbarlabelaction.cpp @@ -1,131 +1,135 @@ /* This file is part of the KDE libraries Copyright (C) 2004 Felix Berger This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "ktoolbarlabelaction.h" #include #include #include #include class Q_DECL_HIDDEN KToolBarLabelAction::Private { public: QPointer buddy; QPointer label; }; KToolBarLabelAction::KToolBarLabelAction(const QString &text, QObject *parent) : QWidgetAction(parent), d(new Private) { setText(text); d->label = nullptr; } KToolBarLabelAction::KToolBarLabelAction(QAction *buddy, const QString &text, QObject *parent) : QWidgetAction(parent), d(new Private) { setBuddy(buddy); setText(text); d->label = nullptr; } KToolBarLabelAction::~KToolBarLabelAction() { delete d; } void KToolBarLabelAction::setBuddy(QAction *buddy) { d->buddy = buddy; QList labels; - Q_FOREACH (QWidget *widget, associatedWidgets()) + const auto associatedWidgets = this->associatedWidgets(); + for (QWidget *widget : associatedWidgets) { if (QToolBar *toolBar = qobject_cast(widget)) if (QLabel *label = qobject_cast(toolBar->widgetForAction(this))) { labels.append(label); } - - Q_FOREACH (QWidget *widget, buddy->associatedWidgets()) + } + const auto buddysAssociatedWidgets = buddy->associatedWidgets(); + for (QWidget *widget : buddysAssociatedWidgets) { if (QToolBar *toolBar = qobject_cast(widget)) { QWidget *newBuddy = toolBar->widgetForAction(buddy); for (QLabel *label : qAsConst(labels)) { label->setBuddy(newBuddy); } return; } + } } QAction *KToolBarLabelAction::buddy() const { return d->buddy; } bool KToolBarLabelAction::event(QEvent *event) { if (event->type() == QEvent::ActionChanged) { if (d->label && text() != d->label->text()) { emit textChanged(text()); d->label->setText(text()); } } return QWidgetAction::event(event); } bool KToolBarLabelAction::eventFilter(QObject *watched, QEvent *event) { if (d->label && d->buddy && event->type() == QEvent::PolishRequest && watched == d->label) { - Q_FOREACH (QWidget *widget, d->buddy->associatedWidgets()) { + const auto buddysAssociatedWidgets = d->buddy->associatedWidgets(); + for (QWidget *widget : buddysAssociatedWidgets) { if (QToolBar *toolBar = qobject_cast(widget)) { QWidget *newBuddy = toolBar->widgetForAction(d->buddy); d->label->setBuddy(newBuddy); } } } return QWidgetAction::eventFilter(watched, event); } QWidget *KToolBarLabelAction::createWidget(QWidget *_parent) { QToolBar *parent = qobject_cast(_parent); if (!parent) { return QWidgetAction::createWidget(_parent); } if (!d->label) { d->label = new QLabel(parent); /** * These lines were copied from Konqueror's KonqDraggableLabel class in * konq_misc.cc */ d->label->setBackgroundRole(QPalette::Button); d->label->setAlignment((QApplication::isRightToLeft() ? Qt::AlignRight : Qt::AlignLeft) | Qt::AlignVCenter); d->label->adjustSize(); d->label->setText(text()); d->label->installEventFilter(this); } return d->label; } diff --git a/src/kviewstateserializer.h b/src/kviewstateserializer.h index 17517b3..13f418b 100644 --- a/src/kviewstateserializer.h +++ b/src/kviewstateserializer.h @@ -1,265 +1,265 @@ /* Copyright (C) 2010 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.net, author Stephen Kelly This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KVIEWSTATESERIALIZER_H #define KVIEWSTATESERIALIZER_H #include #include #include "kwidgetsaddons_export.h" class QAbstractItemView; class QItemSelectionModel; class QAbstractItemModel; class QAbstractScrollArea; class QModelIndex; class QStringList; class KViewStateSerializerPrivate; /** @class KViewStateSerializer kviewstateserializer.h KViewStateSerializer @brief Object for saving and restoring state in QTreeViews and QItemSelectionModels Implement the indexFromConfigString and indexToConfigString methods to handle the model in the view whose state is being saved. These implementations can be quite trivial: @code QModelIndex DynamicTreeStateSaver::indexFromConfigString(const QAbstractItemModel* model, const QString& key) const { QModelIndexList list = model->match(model->index(0, 0), DynamicTreeModel::DynamicTreeModelId, key.toInt(), 1, Qt::MatchRecursive); if (list.isEmpty()) return QModelIndex(); return list.first(); } QString DynamicTreeStateSaver::indexToConfigString(const QModelIndex& index) const { return index.data(DynamicTreeModel::DynamicTreeModelId).toString(); } @endcode It is possible to restore the state of a QTreeView (that is, the expanded state and selected state of all indexes as well as the horizontal and vertical scroll state) by using setTreeView. If there is no tree view state to restore (for example if using QML), the selection state of a QItemSelectionModel can be saved or restored instead. The state of any QAbstractScrollArea can also be saved and restored. A KViewStateSerializer should be created on the stack when saving and on the heap when restoring. The model may be populated dynamically between several event loops, so it may not be immediate for the indexes that should be selected to be in the model. The saver should *not* be persisted as a member. The saver will destroy itself when it has completed the restoration specified in the config group, or a small amount of time has elapsed. @code MyWidget::MyWidget(Qobject *parent) : QWidget(parent) { ... m_view = new QTreeView(splitter); m_view->setModel(model); connect( model, SIGNAL(modelAboutToBeReset()), SLOT(saveState()) ); connect( model, SIGNAL(modelReset()), SLOT(restoreState()) ); connect( qApp, SIGNAL(aboutToQuit()), SLOT(saveState()) ); restoreState(); } void StateSaverWidget::saveState() { ConcreteStateSaver saver; saver.setTreeView(m_view); KConfigGroup cfg( KSharedConfig::openConfig(), "ExampleViewState" ); saver.saveState( cfg ); cfg.sync(); } void StateSaverWidget::restoreState() { // Will delete itself. ConcreteTreeStateSaver *saver = new ConcreteStateSaver(); saver->setTreeView(m_view); KConfigGroup cfg( KSharedConfig::openConfig(), "ExampleViewState" ); saver->restoreState( cfg ); } @endcode After creating a saver, the state can be saved using a KConfigGroup. It is also possible to save and restore state directly by using the restoreSelection, restoreExpanded etc methods. Note that the implementation of these methods should return strings that the indexFromConfigString implementation can handle. @code class DynamicTreeStateSaver : public KViewStateSerializer { Q_OBJECT public: // ... void selectItems(const QList &items) { QStringList itemStrings; - Q_FOREACH(qint64 item, items) + for (qint64 item : items) itemStrings << QString::number(item); restoreSelection(itemStrings); } void expandItems(const QList &items) { QStringList itemStrings; - Q_FOREACH(qint64 item, items) + for (qint64 item : items) itemStrings << QString::number(item); restoreSelection(itemStrings); } }; @endcode Note that a single instance of this class should be used with only one widget. That is don't do this: @code saver->setTreeView(treeView1); saver->setSelectionModel(treeView2->selectionModel()); saver->setScrollArea(treeView3); @endcode To save the state of 3 different widgets, use three savers, even if they operate on the same root model. @code saver1->setTreeView(treeView1); saver2->setSelectionModel(treeView2->selectionModel()); saver3->setScrollArea(treeView3); @endcode @note The KViewStateSerializer does not take ownership of any widgets set on it. It is recommended to restore the state on application startup and after the model has been reset, and to save the state on application close and before the model has been reset. @see QAbstractItemModel::modelAboutToBeReset QAbstractItemModel::modelReset @author Stephen Kelly @since 4.5 */ class KWIDGETSADDONS_EXPORT KViewStateSerializer : public QObject { Q_OBJECT public: /** Constructor */ explicit KViewStateSerializer(QObject *parent = nullptr); /** Destructor */ ~KViewStateSerializer(); /** * The view whose state is persisted. */ QAbstractItemView *view() const; /** * Sets the view whose state is persisted. */ void setView(QAbstractItemView *view); /** The QItemSelectionModel whose state is persisted. */ QItemSelectionModel *selectionModel() const; /** Sets the QItemSelectionModel whose state is persisted. */ void setSelectionModel(QItemSelectionModel *selectionModel); /** * Returns a QStringList describing the selection in the selectionModel. */ QStringList selectionKeys() const; /** * Returns a QStringList representing the expanded indexes in the QTreeView. */ QStringList expansionKeys() const; /** * Returns a QString describing the current index in the selection model. */ QString currentIndexKey() const; /** * Returns the vertical and horizontal scroll of the QAbstractScrollArea. */ QPair scrollState() const; /** * Select the indexes described by @p indexStrings */ void restoreSelection(const QStringList &indexStrings); /** * Make the index described by @p indexString the currentIndex in the selectionModel. */ void restoreCurrentItem(const QString &indexString); /** * Expand the indexes described by @p indexStrings in the QTreeView. */ void restoreExpanded(const QStringList &indexStrings); /** * Restores the scroll state of the QAbstractScrollArea to the @p verticalScoll * and @p horizontalScroll */ void restoreScrollState(int verticalScoll, int horizontalScroll); protected: /** Reimplement to return an index in the @p model described by the unique key @p key */ virtual QModelIndex indexFromConfigString(const QAbstractItemModel *model, const QString &key) const = 0; /** Reimplement to return a unique string for the @p index. */ virtual QString indexToConfigString(const QModelIndex &index) const = 0; void restoreState(); private: //@cond PRIVATE Q_DECLARE_PRIVATE(KViewStateSerializer) KViewStateSerializerPrivate *const d_ptr; Q_PRIVATE_SLOT(d_func(), void rowsInserted(const QModelIndex &, int, int)) Q_PRIVATE_SLOT(d_func(), void restoreScrollBarState()) //@endcond }; #endif