diff --git a/autotests/kactioncategorytest.cpp b/autotests/kactioncategorytest.cpp index 2b39305..dbd6b81 100644 --- a/autotests/kactioncategorytest.cpp +++ b/autotests/kactioncategorytest.cpp @@ -1,156 +1,156 @@ #include #include "kactioncategorytest.h" #include "kactioncollection.h" #include "kactioncategory.h" #include "kselectaction.h" #include "kstandardaction.h" void tst_KActionCategory::tstCreation() { - KActionCollection collection((QObject *)NULL); + KActionCollection collection((QObject *)nullptr); KActionCategory category1(QStringLiteral("category1"), &collection); KActionCategory category2(QStringLiteral("category2"), &collection); // Check that the name is correct QCOMPARE(category1.text(), QStringLiteral("category1")); QCOMPARE(category2.text(), QStringLiteral("category2")); // Check that the parent is correct QCOMPARE(category1.collection(), &collection); QCOMPARE(category2.collection(), &collection); // Check that the category is available as a child of the collection QList categories = collection.findChildren(); QCOMPARE(categories.size(), 2); QCOMPARE(categories.count(&category1), 1); QCOMPARE(categories.count(&category2), 1); // Change the text category1.setText(QStringLiteral("Other Text")); QCOMPARE(category1.text(), QStringLiteral("Other Text")); } void tst_KActionCategory::tstSynchronization() { - KActionCollection collection((QObject *)NULL); + KActionCollection collection((QObject *)nullptr); KActionCategory category1(QStringLiteral("category1"), &collection); KActionCategory category2(QStringLiteral("category2"), &collection); // The collection is empty QCOMPARE(collection.count(), 0); // Now add a action to category1 QAction *action1 = category1.addAction(QStringLiteral("action1")); // Check it was added to the category. QCOMPARE(category1.actions().count(), 1); QCOMPARE(category1.actions().count(action1), 1); // Check it was added to the collection QCOMPARE(collection.actions().count(), 1); QCOMPARE(collection.actions().count(action1), 1); // Short intermezzo. Add the action a second time category1.addAction(QStringLiteral("action1_new"), action1); QCOMPARE(category1.actions().count(), 1); QCOMPARE(category1.actions().count(action1), 1); QCOMPARE(collection.actions().count(), 1); QCOMPARE(collection.actions().count(action1), 1); // Now add a action to category2 QAction *action2 = category2.addAction(QStringLiteral("action2")); // Check it was added to the category. QCOMPARE(category2.actions().count(), 1); QCOMPARE(category2.actions().count(action2), 1); // Check it was added to the collection QCOMPARE(collection.actions().count(), 2); QCOMPARE(collection.actions().count(action2), 1); // Delete action1 delete action1; // Check it was removed from the collection QCOMPARE(collection.actions().count(), 1); QCOMPARE(collection.actions().count(action1), 0); // Check it was removed from the category. QCOMPARE(category1.actions().count(), 0); QCOMPARE(category1.actions().count(action1), 0); // Remove action2 from the collection collection.removeAction(action2); // Check it was removed from the collection QCOMPARE(collection.actions().count(), 0); QCOMPARE(collection.actions().count(action2), 0); // Check it was removed from the category. QCOMPARE(category2.actions().count(), 0); QCOMPARE(category2.actions().count(action2), 0); // Create another category, add a action, delete the category and check // if the action is still part of the collection. KActionCategory *category3 = new KActionCategory(QStringLiteral("category3"), &collection); QAction *action3 = category3->addAction(QStringLiteral("action3")); // Check it was added to the collection QCOMPARE(collection.actions().count(action3), 1); // delete the category delete category3; // Make sure the action is still there. QCOMPARE(collection.actions().count(action3), 1); } void tst_KActionCategory::tstActionCreation() { - KActionCollection collection((QObject *)NULL); + KActionCollection collection((QObject *)nullptr); KActionCategory category(QStringLiteral("category"), &collection); // QAction * addAction(const QString &name, QAction *action); - QAction *action1 = new QAction(NULL); + QAction *action1 = new QAction(nullptr); category.addAction(QStringLiteral("action1"), action1); QCOMPARE(category.actions().count(action1), 1); QCOMPARE(collection.actions().count(action1), 1); // QAction * addAction(const QString &name, QAction *action); - QAction *action2 = new QAction(NULL); + QAction *action2 = new QAction(nullptr); category.addAction(QStringLiteral("action2"), action2); QCOMPARE(category.actions().count(action2), 1); QCOMPARE(collection.actions().count(action2), 1); // QAction * addAction( // KStandardAction::StandardAction actionType, // const QObject *receiver = NULL, // const char *member = NULL); QAction *action3 = category.addAction(KStandardAction::Revert); QCOMPARE(category.actions().count(action3), 1); QCOMPARE(collection.actions().count(action3), 1); // QAction * addAction( // KStandardAction::StandardAction actionType, // const QString &name, // const QObject *receiver = NULL, // const char *member = NULL); QAction *action4 = category.addAction(KStandardAction::Quit, QStringLiteral("myownname")); QCOMPARE(action4->objectName(), QStringLiteral("myownname")); QCOMPARE(category.actions().count(action4), 1); QCOMPARE(collection.actions().count(action4), 1); // QAction *addAction( // const QString &name, // const QObject *receiver = NULL, // const char *member = NULL); QAction *action5 = category.addAction(QStringLiteral("action5")); QCOMPARE(category.actions().count(action5), 1); QCOMPARE(collection.actions().count(action5), 1); // template // ActionType *add( // const QString &name, // const QObject *receiver = NULL, // const char *member = NULL) KSelectAction *action6 = category.add(QStringLiteral("action6")); QCOMPARE(category.actions().count(action6), 1); QCOMPARE(collection.actions().count(action6), 1); // There should be 6 actions inside the collection and category QCOMPARE(category.actions().count(), 6); QCOMPARE(collection.actions().count(), 6); } QTEST_MAIN(tst_KActionCategory) diff --git a/autotests/kactioncollectiontest.cpp b/autotests/kactioncollectiontest.cpp index 1dcb4b1..eff11b2 100644 --- a/autotests/kactioncollectiontest.cpp +++ b/autotests/kactioncollectiontest.cpp @@ -1,248 +1,248 @@ #include #include "kactioncollectiontest.h" #include #include #include #include void tst_KActionCollection::init() { - collection = new KActionCollection(static_cast(0)); + collection = new KActionCollection(static_cast(nullptr)); } void tst_KActionCollection::cleanup() { delete collection; - collection = 0; + collection = nullptr; } void tst_KActionCollection::clear() { QPointer action1 = collection->add(QStringLiteral("test1")); QPointer action2 = collection->add(QStringLiteral("test2")); QPointer action3 = collection->add(QStringLiteral("test3")); QPointer action4 = collection->add(QStringLiteral("test4")); QPointer action5 = collection->add(QStringLiteral("test5")); QPointer action6 = collection->add(QStringLiteral("test6")); QPointer action7 = collection->add(QStringLiteral("test7")); collection->clear(); QVERIFY(collection->isEmpty()); QVERIFY(action1.isNull()); QVERIFY(action2.isNull()); QVERIFY(action3.isNull()); QVERIFY(action4.isNull()); QVERIFY(action5.isNull()); QVERIFY(action6.isNull()); QVERIFY(action7.isNull()); } void tst_KActionCollection::deleted() { // Delete action -> automatically removed from collection QAction *a = collection->add(QStringLiteral("test")); delete a; QVERIFY(collection->isEmpty()); // Delete action's parent -> automatically removed from collection - QWidget *myWidget = new QWidget(0); + QWidget *myWidget = new QWidget(nullptr); QPointer action = new QAction(/*i18n()*/ QStringLiteral("Foo"), myWidget); collection->addAction(QStringLiteral("foo"), action); delete myWidget; QVERIFY(collection->isEmpty()); QVERIFY(action.isNull()); // Delete action's parent, but the action was added to another widget with setAssociatedWidget // and that widget gets deleted first. - myWidget = new QWidget(0); + myWidget = new QWidget(nullptr); QWidget *myAssociatedWidget = new QWidget(myWidget); // child widget action = new QAction(/*i18n()*/ QStringLiteral("Foo"), myWidget); // child action collection->addAction(QStringLiteral("foo"), action); collection->addAssociatedWidget(myAssociatedWidget); QVERIFY(myAssociatedWidget->actions().contains(action)); delete myAssociatedWidget; // would be done by the line below, but let's make sure it happens first delete myWidget; QVERIFY(collection->isEmpty()); QVERIFY(action.isNull()); } void tst_KActionCollection::take() { QAction *a = collection->add(QStringLiteral("test")); collection->takeAction(a); QVERIFY(collection->isEmpty()); delete a; } void tst_KActionCollection::writeSettings() { KConfigGroup cfg = clearConfig(); QList defaultShortcut; defaultShortcut << Qt::Key_A << Qt::Key_B; QList temporaryShortcut; temporaryShortcut << Qt::Key_C << Qt::Key_D; QAction *actionWithDifferentShortcut = new QAction(this); collection->setDefaultShortcuts(actionWithDifferentShortcut, defaultShortcut); actionWithDifferentShortcut->setShortcuts(temporaryShortcut); collection->addAction(QStringLiteral("actionWithDifferentShortcut"), actionWithDifferentShortcut); QAction *immutableAction = new QAction(this); collection->setDefaultShortcuts(immutableAction, defaultShortcut); immutableAction->setShortcuts(temporaryShortcut); collection->setShortcutsConfigurable(immutableAction, false); collection->addAction(QStringLiteral("immutableAction"), immutableAction); QAction *actionWithSameShortcut = new QAction(this); collection->setDefaultShortcuts(actionWithSameShortcut, defaultShortcut); collection->addAction(QStringLiteral("actionWithSameShortcut"), actionWithSameShortcut); cfg.writeEntry("actionToDelete", QStringLiteral("Foobar")); QAction *actionToDelete = new QAction(this); collection->setDefaultShortcuts(actionToDelete, defaultShortcut); collection->addAction(QStringLiteral("actionToDelete"), actionToDelete); collection->writeSettings(&cfg); QCOMPARE(cfg.readEntry("actionWithDifferentShortcut", QString()), QKeySequence::listToString(actionWithDifferentShortcut->shortcuts())); QCOMPARE(cfg.readEntry("immutableAction", QString()), QString()); QCOMPARE(cfg.readEntry("actionWithSameShortcut", QString()), QString()); QCOMPARE(cfg.readEntry("actionToDelete", QString()), QString()); qDeleteAll(collection->actions()); } void tst_KActionCollection::readSettings() { KConfigGroup cfg = clearConfig(); QList defaultShortcut; defaultShortcut << Qt::Key_A << Qt::Key_B; QList temporaryShortcut; temporaryShortcut << Qt::Key_C << Qt::Key_D; cfg.writeEntry("normalAction", QKeySequence::listToString(defaultShortcut)); cfg.writeEntry("immutable", QKeySequence::listToString(defaultShortcut)); cfg.writeEntry("empty", QString()); QAction *normal = new QAction(this); collection->addAction(QStringLiteral("normalAction"), normal); QAction *immutable = new QAction(this); immutable->setShortcuts(temporaryShortcut); collection->setDefaultShortcuts(immutable, temporaryShortcut); collection->setShortcutsConfigurable(immutable, false); collection->addAction(QStringLiteral("immutable"), immutable); QAction *empty = new QAction(this); collection->addAction(QStringLiteral("empty"), empty); collection->setDefaultShortcuts(empty, defaultShortcut); empty->setShortcuts(temporaryShortcut); QCOMPARE(QKeySequence::listToString(empty->shortcuts()), QKeySequence::listToString(temporaryShortcut)); collection->readSettings(&cfg); QCOMPARE(QKeySequence::listToString(normal->shortcuts()), QKeySequence::listToString(defaultShortcut)); QCOMPARE(QKeySequence::listToString(empty->shortcuts()), QKeySequence::listToString(defaultShortcut)); QCOMPARE(QKeySequence::listToString(immutable->shortcuts()), QKeySequence::listToString(temporaryShortcut)); qDeleteAll(collection->actions()); } void tst_KActionCollection::insertReplaces1() { - QAction *a = new QAction(0); - QAction *b = new QAction(0); + QAction *a = new QAction(nullptr); + QAction *b = new QAction(nullptr); collection->addAction(QStringLiteral("a"), a); QVERIFY(collection->actions().contains(a)); QVERIFY(collection->action(QStringLiteral("a")) == a); collection->addAction(QStringLiteral("a"), b); QVERIFY(!collection->actions().contains(a)); QVERIFY(collection->actions().contains(b)); QVERIFY(collection->action(QStringLiteral("a")) == b); delete a; delete b; } /** * Check that a action added twice under different names only ends up once in * the collection */ void tst_KActionCollection::insertReplaces2() { - QAction *a = new QAction(0); + QAction *a = new QAction(nullptr); collection->addAction(QStringLiteral("a"), a); QVERIFY(collection->actions().contains(a)); QVERIFY(collection->action(QStringLiteral("a")) == a); // Simple test: Just add it twice collection->addAction(QStringLiteral("b"), a); QVERIFY(collection->actions().contains(a)); QVERIFY(!collection->action(QStringLiteral("a"))); QVERIFY(collection->action(QStringLiteral("b")) == a); // Complex text: Mesh with the objectname a->setObjectName(QStringLiteral("c")); collection->addAction(QStringLiteral("d"), a); QVERIFY(collection->actions().contains(a)); QVERIFY(!collection->action(QStringLiteral("b"))); QVERIFY(!collection->action(QStringLiteral("c"))); QVERIFY(collection->action(QStringLiteral("d")) == a); delete a; } KConfigGroup tst_KActionCollection::clearConfig() { KSharedConfig::Ptr cfg = KSharedConfig::openConfig(); cfg->deleteGroup(collection->configGroup()); return KConfigGroup(cfg, collection->configGroup()); } void tst_KActionCollection::testSetShortcuts() { QAction *action = new QAction(/*i18n*/(QStringLiteral("Next Unread &Folder")), this); collection->addAction(QStringLiteral("go_next_unread_folder"), action); collection->setDefaultShortcut(action, QKeySequence(Qt::ALT + Qt::Key_Plus)); QList shortcut = action->shortcuts(); shortcut << QKeySequence(Qt::CTRL + Qt::Key_Plus); action->setShortcuts(shortcut); QCOMPARE(QKeySequence::listToString(action->shortcuts()), QStringLiteral("Alt++; Ctrl++")); // Simpler way: QList shortcut2; shortcut2 << QKeySequence(Qt::ALT + Qt::Key_Plus) << QKeySequence(Qt::CTRL + Qt::Key_Plus); QCOMPARE(QKeySequence::listToString(shortcut2), QStringLiteral("Alt++; Ctrl++")); } void tst_KActionCollection::implicitStandardActionInsertionUsingCreate() { - KActionCollection collection(static_cast(0)); + KActionCollection collection(static_cast(nullptr)); QAction *a = KStandardAction::create(KStandardAction::Undo, qApp, SLOT(quit()), &collection); QVERIFY(a); QVERIFY(a->parent() == &collection); QVERIFY(collection.action(KStandardAction::name(KStandardAction::Undo)) == a); } void tst_KActionCollection::implicitStandardActionInsertionUsingCut() { - KActionCollection collection(static_cast(0)); + KActionCollection collection(static_cast(nullptr)); QAction *cut = KStandardAction::cut(&collection); QAction *a = collection.action(KStandardAction::name(KStandardAction::Cut)); QVERIFY(a); QVERIFY(a == cut); } QTEST_MAIN(tst_KActionCollection) diff --git a/autotests/kxmlgui_unittest.cpp b/autotests/kxmlgui_unittest.cpp index c79d696..2ecd8ae 100644 --- a/autotests/kxmlgui_unittest.cpp +++ b/autotests/kxmlgui_unittest.cpp @@ -1,1061 +1,1061 @@ /* This file is part of the KDE libraries Copyright 2007-2009 David Faure 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 "kxmlgui_unittest.h" #include "testxmlguiwindow.h" #include "testguiclient.h" #include #include #include #include #include #include #include #include #include #include #include #include #include // it's not exported, so we need to include the code here #include QTEST_MAIN(KXmlGui_UnitTest) enum Flags { NoFlags = 0, AddToolBars = 1, AddModifiedToolBars = 2, AddActionProperties = 4, AddModifiedMenus = 8 // next item is 16 }; static void createXmlFile(QFile &file, int version, int flags, const QByteArray &toplevelTag = "gui") { const QByteArray xml = "\n" "\n" "<" + toplevelTag + " version=\"" + QByteArray::number(version) + "\" name=\"foo\" >\n" "\n"; file.write(xml); if (flags & AddModifiedMenus) { file.write( "\n" "&File\n" "\n" "\n" ); } file.write("\n"); if (flags & AddToolBars) { file.write( "\n" " Main Toolbar\n" " \n" "\n" "\n" " Bookmark Toolbar\n" "\n" ); } if (flags & AddModifiedToolBars) { file.write( "\n" " Main Toolbar\n" " \n" "\n" "\n" " Modified toolbars\n" "\n" ); } if (flags & AddActionProperties) { file.write( "\n" " \n" "\n" ); } file.write("\n"); } static void clickApply(KEditToolBar *dialog) { QDialogButtonBox *box = dialog->findChild(); - Q_ASSERT(box != 0); + Q_ASSERT(box != nullptr); box->button(QDialogButtonBox::Apply)->setEnabled(true); box->button(QDialogButtonBox::Apply)->click(); } void KXmlGui_UnitTest::initTestCase() { QStandardPaths::enableTestMode(true); // Leftover configuration breaks testAutoSaveSettings const QString configFile = QStandardPaths::locate(QStandardPaths::GenericConfigLocation, KSharedConfig::openConfig()->name()); if (!configFile.isEmpty()) { qDebug() << "Removing old config file"; QFile::remove(configFile); KSharedConfig::openConfig()->reparseConfiguration(); } } void KXmlGui_UnitTest::testFindVersionNumber_data() { QTest::addColumn("xml"); QTest::addColumn("version"); QTest::newRow("simple test") << "\n" "\n" "\n" << "3"; QTest::newRow("two digits") << "\n" "\n" << "42"; QTest::newRow("with spaces") << // as found in dirfilterplugin.rc for instance "\n" "\n" << "1"; QTest::newRow("with a dot") << // as was found in autorefresh.rc "\n" "\n" << QString() /*error*/; QTest::newRow("with a comment") << // as found in kmail.rc "\n" "\n" "\n" << "452"; } void KXmlGui_UnitTest::testFindVersionNumber() { QFETCH(QString, xml); QFETCH(QString, version); QCOMPARE(KXmlGuiVersionHandler::findVersionNumber(xml), version); } void KXmlGui_UnitTest::testVersionHandlerSameVersion() { // This emulates the case where the user has modified stuff locally // and the application hasn't changed since, so the version number is unchanged. QTemporaryFile userFile; QVERIFY(userFile.open()); createXmlFile(userFile, 2, AddActionProperties | AddModifiedToolBars); const QString firstFile = userFile.fileName(); QTemporaryFile appFile; QVERIFY(appFile.open()); createXmlFile(appFile, 2, AddToolBars); const QString secondFile = appFile.fileName(); QStringList files; files << firstFile << secondFile; userFile.close(); appFile.close(); KXmlGuiVersionHandler versionHandler(files); QCOMPARE(versionHandler.finalFile(), firstFile); QString finalDoc = versionHandler.finalDocument(); QVERIFY(finalDoc.startsWith(QStringLiteral(""))); QVERIFY(finalDoc.contains(QStringLiteral("sidebartng"))); // Check that the toolbars modified by the user were kept QVERIFY(finalDoc.contains(QStringLiteral(" fileToVersionMap; // makes QCOMPARE failures more readable than just temp filenames QDir().mkpath(QStandardPaths::writableLocation(QStandardPaths::DataLocation)); QFile fileV2(QStandardPaths::writableLocation(QStandardPaths::DataLocation) + QLatin1Char('/') + "testui.rc"); QVERIFY2(fileV2.open(QIODevice::WriteOnly), qPrintable(fileV2.fileName())); createXmlFile(fileV2, 2, NoFlags); fileToVersionMap.insert(fileV2.fileName(), 2); QTemporaryFile fileV5; QVERIFY(fileV5.open()); createXmlFile(fileV5, 5, NoFlags); fileToVersionMap.insert(fileV5.fileName(), 5); // The highest version is neither the first nor last one in the list, // to make sure the code really selects the highest version, not just by chance :) // (This is why we add the v1 version at the end of the list) QTemporaryFile fileV1; QVERIFY(fileV1.open()); createXmlFile(fileV1, 1, NoFlags); fileToVersionMap.insert(fileV1.fileName(), 1); QStringList files; files << fileV2.fileName() << fileV5.fileName() << fileV1.fileName(); fileV2.close(); fileV5.close(); fileV1.close(); KXmlGuiVersionHandler versionHandler(files); QCOMPARE(fileToVersionMap.value(versionHandler.finalFile()), 5); QString finalDoc = versionHandler.finalDocument(); QVERIFY(finalDoc.startsWith(QStringLiteral(" fileToVersionMap; // makes QCOMPARE failures more readable than just temp filenames // local file QFile fileV2(QStandardPaths::writableLocation(QStandardPaths::DataLocation) + QLatin1Char('/') + "testui.rc"); QVERIFY(fileV2.open(QIODevice::WriteOnly)); createXmlFile(fileV2, 2, AddActionProperties | AddModifiedToolBars); fileToVersionMap.insert(fileV2.fileName(), 2); // more-global file QTemporaryFile fileV5; QVERIFY(fileV5.open()); createXmlFile(fileV5, 5, AddToolBars | AddModifiedMenus, "kpartgui"); fileToVersionMap.insert(fileV5.fileName(), 5); // The highest version is neither the first nor last one in the list, // to make sure the code really selects the highest version, not just by chance :) // (This is why we add the v1 version at the end of the list) QTemporaryFile fileV1; QVERIFY(fileV1.open()); createXmlFile(fileV1, 1, AddToolBars); fileToVersionMap.insert(fileV1.fileName(), 1); QStringList files; files << fileV2.fileName() << fileV5.fileName() << fileV1.fileName(); fileV2.close(); fileV5.close(); fileV1.close(); KXmlGuiVersionHandler versionHandler(files); // We end up with the local file, so in our map it has version 2. // But of course by now it says "version=5" in it :) QCOMPARE(fileToVersionMap.value(versionHandler.finalFile()), 2); const QString finalDoc = versionHandler.finalDocument(); //qDebug() << finalDoc; QVERIFY(finalDoc.startsWith(QStringLiteral(""))); QVERIFY(finalDoc.contains(QStringLiteral("sidebartng"))); // Check that the menus modified by the app are still there QVERIFY(finalDoc.contains(QStringLiteral(" containers = factory.containers(QStringLiteral("Menu")); QStringList containerNames; Q_FOREACH (QWidget *w, containers) { containerNames << w->objectName(); } return containerNames; } void debugActions(const QList &actions) { Q_FOREACH (QAction *action, actions) { qDebug() << (action->isSeparator() ? QString("separator") : action->objectName()); } } static void checkActions(const QList &actions, const QStringList &expectedActions) { for (int i = 0; i < expectedActions.count(); ++i) { if (i >= actions.count()) break; QAction *action = actions.at(i); if (action->isSeparator()) { QCOMPARE(QStringLiteral("separator"), expectedActions[i]); } else { QCOMPARE(action->objectName(), expectedActions[i]); } } QCOMPARE(actions.count(), expectedActions.count()); } void KXmlGui_UnitTest::testPartMerging() { const QByteArray hostXml = "\n" "\n" "\n" "\n" " &Go\n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " Section title\n" " \n" " \n" " &File\n" " \n" " \n" "\n" "\n"; TestGuiClient hostClient; hostClient.createActions(QStringList() << QStringLiteral("go_up") << QStringLiteral("go_back") << QStringLiteral("go_forward") << QStringLiteral("go_home") << QStringLiteral("host_after_merge") << QStringLiteral("host_after_merge_2") << QStringLiteral("last_from_host") << QStringLiteral("file_new") << QStringLiteral("file_open") << QStringLiteral("file_quit")); hostClient.createGUI(hostXml, true /*ui_standards.rc*/); KMainWindow mainWindow; KXMLGUIBuilder builder(&mainWindow); KXMLGUIFactory factory(&builder); factory.addClient(&hostClient); const QString hostDomDoc = hostClient.domDocument().toString(); QWidget *goMenuW = factory.container(QStringLiteral("go"), &hostClient); QVERIFY(goMenuW); QMenu *goMenu = qobject_cast(goMenuW); QVERIFY(goMenu); QMenu *fileMenu = qobject_cast(factory.container(QStringLiteral("file"), &hostClient)); //debugActions(goMenu->actions()); checkActions(goMenu->actions(), QStringList() << QStringLiteral("go_up") << QStringLiteral("go_back") << QStringLiteral("go_forward") << QStringLiteral("go_home") << QStringLiteral("separator") << QStringLiteral("host_after_merge") << QStringLiteral("host_after_merge_2") << QStringLiteral("separator") << QStringLiteral("separator") // separator << QStringLiteral("last_from_host")); checkActions(fileMenu->actions(), QStringList() << QStringLiteral("file_new") << QStringLiteral("file_open") << QStringLiteral("separator") << QStringLiteral("file_quit")); qDebug() << "Now merging the part"; const QByteArray partXml = "<!DOCTYPE kpartgui SYSTEM \"kpartgui.dtd\">\n" "<kpartgui version=\"1\" name=\"part\" >\n" "<MenuBar>\n" " <Menu name=\"go\"><text>&Go</text>\n" " <Action name=\"go_previous\" group=\"before_merge\"/>\n" " <Action name=\"go_next\" group=\"before_merge\"/>\n" " <Separator/>\n" " <Action name=\"first_page\"/>\n" " <Action name=\"last_page\"/>\n" " <Title>Part Section\n" " \n" " \n" " \n" " \n" " \n" " &File\n" " \n" " \n" " \n" "\n" "\n"; TestGuiClient partClient(partXml); partClient.createActions(QStringList() << QStringLiteral("go_previous") << QStringLiteral("go_next") << QStringLiteral("first_page") << QStringLiteral("last_page") << QStringLiteral("last_from_part") << QStringLiteral("action_in_merge_group") << QStringLiteral("undefined_group") << QStringLiteral("action_in_placed_merge") << QStringLiteral("other_file_action") << QStringLiteral("action1") << QStringLiteral("action2")); const QList actionList = { partClient.actionCollection()->action(QStringLiteral("action1")), partClient.actionCollection()->action(QStringLiteral("action2")) }; for (int i = 0 ; i < 5 ; ++i) { //qDebug() << "addClient, iteration" << i; factory.addClient(&partClient); partClient.plugActionList(QStringLiteral("action_list"), actionList); //debugActions(goMenu->actions()); checkActions(goMenu->actions(), QStringList() << QStringLiteral("go_up") << QStringLiteral("go_back") << QStringLiteral("go_forward") << QStringLiteral("go_home") << QStringLiteral("separator") << QStringLiteral("go_previous") << QStringLiteral("go_next") // Contents of the : << QStringLiteral("separator") << QStringLiteral("first_page") << QStringLiteral("last_page") << QStringLiteral("separator") // in the part << QStringLiteral("action1") << QStringLiteral("action2") << QStringLiteral("undefined_group") << QStringLiteral("last_from_part") // End of <Merge> << QStringLiteral("host_after_merge") << QStringLiteral("host_after_merge_2") << QStringLiteral("separator") // Contents of <DefineGroup> << QStringLiteral("action_in_merge_group") // End of <DefineGroup> << QStringLiteral("separator") // <title> is a separator qaction with text << QStringLiteral("last_from_host") ); checkActions(fileMenu->actions(), QStringList() << QStringLiteral("file_new") << QStringLiteral("action_in_placed_merge") << QStringLiteral("file_open") << QStringLiteral("separator") << QStringLiteral("file_quit") << QStringLiteral("other_file_action")); factory.removeClient(&partClient); QCOMPARE(hostClient.domDocument().toString(), hostDomDoc); } factory.removeClient(&hostClient); } void KXmlGui_UnitTest::testPartMergingSettings() // #252911 { const QByteArray hostXml = "<?xml version = '1.0'?>\n" "<!DOCTYPE gui SYSTEM \"kpartgui.dtd\">\n" "<gui version=\"1\" name=\"foo\" >\n" "<MenuBar>\n" // The solution was to remove the duplicated definition // " <Menu name=\"settings\"><text>&Settings</text>\n" // " <Action name=\"options_configure_keybinding\"/>\n" // " <Action name=\"options_configure_toolbars\"/>\n" // " <Merge name=\"configure_merge\"/>\n" // " <Separator/>\n" // " <Merge/>\n" // " </Menu>\n" "</MenuBar></gui>\n"; TestGuiClient hostClient; hostClient.createActions(QStringList() << QStringLiteral("options_configure_keybinding") << QStringLiteral("options_configure_toolbars")); hostClient.createGUI(hostXml, true /*ui_standards.rc*/); //qDebug() << hostClient.domDocument().toString(); KMainWindow mainWindow; KXMLGUIBuilder builder(&mainWindow); KXMLGUIFactory factory(&builder); factory.addClient(&hostClient); QWidget *settingsMenu = qobject_cast<QMenu *>(factory.container(QStringLiteral("settings"), &hostClient)); QVERIFY(settingsMenu); //debugActions(settingsMenu->actions()); const QByteArray partXml = "<?xml version = '1.0'?>\n" "<!DOCTYPE gui SYSTEM \"kpartgui.dtd\">\n" "<gui version=\"1\" name=\"foo\" >\n" "<MenuBar>\n" " <Menu name=\"settings\"><text>&Settings</text>\n" " <Action name=\"configure_klinkstatus\"/>\n" " </Menu>\n" "</MenuBar></gui>\n"; TestGuiClient partClient(partXml); partClient.createActions(QStringList() << QStringLiteral("configure_klinkstatus")); factory.addClient(&partClient); //debugActions(settingsMenu->actions()); checkActions(settingsMenu->actions(), QStringList() << QStringLiteral("separator") // that's ok, QMenuPrivate::filterActions won't show it << QStringLiteral("options_configure_keybinding") << QStringLiteral("options_configure_toolbars") << QStringLiteral("configure_klinkstatus")); factory.removeClient(&partClient); factory.removeClient(&hostClient); } void KXmlGui_UnitTest::testUiStandardsMerging_data() { QTest::addColumn<QByteArray>("xml"); QTest::addColumn<QStringList>("actions"); QTest::addColumn<QStringList>("expectedMenus"); const QByteArray xmlBegin = "<!DOCTYPE gui SYSTEM \"kpartgui.dtd\">\n" "<gui version=\"1\" name=\"foo\" >\n" "<MenuBar>\n"; const QByteArray xmlEnd = "</MenuBar>\n" "</gui>"; // Merging an empty menu (or a menu with only non-existing actions) would make // the empty menu appear at the end after all other menus (fixed for KDE-4.2) QTest::newRow("empty file menu, implicit settings menu") << QByteArray(xmlBegin + "<Menu name=\"file\"/>\n" + xmlEnd) << (QStringList() << QStringLiteral("options_configure_toolbars")) << (QStringList() << QStringLiteral("settings")); QTest::newRow("file menu with non existing action, implicit settings menu") << QByteArray(xmlBegin + "<Menu name=\"file\"><Action name=\"foo\"/></Menu>\n" + xmlEnd) << (QStringList() << QStringLiteral("options_configure_toolbars")) << (QStringList() << QStringLiteral("settings")); QTest::newRow("file menu with existing action, implicit settings menu") << QByteArray(xmlBegin + "<Menu name=\"file\"><Action name=\"open\"/></Menu>\n" + xmlEnd) << (QStringList() << QStringLiteral("open") << QStringLiteral("options_configure_toolbars")) << (QStringList() << QStringLiteral("file") << QStringLiteral("settings")); QTest::newRow("implicit file and settings menu") << QByteArray(xmlBegin + xmlEnd) << (QStringList() << QStringLiteral("file_open") << QStringLiteral("options_configure_toolbars")) << (QStringList() << QStringLiteral("file") << QStringLiteral("settings")); // we could check that file_open is in the mainToolBar, too // Check that unknown non-empty menus are added at the "MergeLocal" position (before settings). QTest::newRow("foo menu added at the end") << QByteArray(xmlBegin + "<Menu name=\"foo\"><Action name=\"foo_action\"/></Menu>\n" + xmlEnd) << (QStringList() << QStringLiteral("file_open") << QStringLiteral("options_configure_toolbars") << QStringLiteral("foo_action")) << (QStringList() << QStringLiteral("file") << QStringLiteral("foo") << QStringLiteral("settings")); QTest::newRow("Bille's testcase: menu patch + menu edit") << QByteArray(xmlBegin + "<Menu name=\"patch\"><Action name=\"patch_generate\"/></Menu>\n" + "<Menu name=\"edit\"><Action name=\"edit_foo\"/></Menu>\n" + xmlEnd) << (QStringList() << QStringLiteral("file_open") << QStringLiteral("patch_generate") << QStringLiteral("edit_foo")) << (QStringList() << QStringLiteral("file") << QStringLiteral("edit") << QStringLiteral("patch")); QTest::newRow("Bille's testcase: menu patch + menu edit, lowercase tag") << QByteArray(xmlBegin + "<Menu name=\"patch\"><Action name=\"patch_generate\"/></Menu>\n" + "<menu name=\"edit\"><Action name=\"edit_foo\"/></menu>\n" + xmlEnd) << (QStringList() << QStringLiteral("file_open") << QStringLiteral("patch_generate") << QStringLiteral("edit_foo")) << (QStringList() << QStringLiteral("file") << QStringLiteral("edit") << QStringLiteral("patch")); // Check that <Menu append="..."> allows to insert menus at specific positions QTest::newRow("Menu append") << QByteArray(xmlBegin + "<Menu name=\"foo\" append=\"settings_merge\"><Action name=\"foo_action\"/></Menu>\n" + xmlEnd) << (QStringList() << QStringLiteral("file_open") << QStringLiteral("options_configure_toolbars") << QStringLiteral("foo_action") << QStringLiteral("help_contents")) << (QStringList() << QStringLiteral("file") << QStringLiteral("settings") << QStringLiteral("foo") << QStringLiteral("help")); QTest::newRow("Custom first menu") << QByteArray(xmlBegin + "<Menu name=\"foo\" append=\"first_menu\"><Action name=\"foo_action\"/></Menu>\n" + xmlEnd) << (QStringList() << QStringLiteral("edit_undo") << QStringLiteral("foo_action") << QStringLiteral("help_contents")) << (QStringList() << QStringLiteral("foo") << QStringLiteral("edit") << QStringLiteral("help")); // Tests for noMerge="1" QTest::newRow("noMerge empty file menu, implicit settings menu") << QByteArray(xmlBegin + "<Menu name=\"file\" noMerge=\"1\"/>\n" + xmlEnd) << (QStringList() << QStringLiteral("file_open") << QStringLiteral("options_configure_toolbars")) << (QStringList() << QStringLiteral("file") << QStringLiteral("settings")); // we keep empty menus, see #186382 QTest::newRow("noMerge empty file menu, file_open moved elsewhere") << QByteArray(xmlBegin + "<Menu name=\"file\" noMerge=\"1\"/>\n<Menu name=\"foo\"><Action name=\"file_open\"/></Menu>" + xmlEnd) << (QStringList() << QStringLiteral("file_open")) << (QStringList() << QStringLiteral("file") << QStringLiteral("foo")); QTest::newRow("noMerge file menu with open before new") << QByteArray(xmlBegin + "<Menu name=\"file\" noMerge=\"1\"><Action name=\"file_open\"/><Action name=\"file_new\"/></Menu>" + xmlEnd) << (QStringList() << QStringLiteral("file_open") << QStringLiteral("file_new")) << (QStringList() << QStringLiteral("file")); // TODO check the order of the actions in the menu? how? // Tests for deleted="true" QTest::newRow("deleted file menu, implicit settings menu") << QByteArray(xmlBegin + "<Menu name=\"file\" deleted=\"true\"/>\n" + xmlEnd) << (QStringList() << QStringLiteral("file_open") << QStringLiteral("options_configure_toolbars")) << (QStringList() << QStringLiteral("settings")); QTest::newRow("deleted file menu, file_open moved elsewhere") << QByteArray(xmlBegin + "<Menu name=\"file\" deleted=\"true\"/>\n<Menu name=\"foo\"><Action name=\"file_open\"/></Menu>" + xmlEnd) << (QStringList() << QStringLiteral("file_open")) << (QStringList() << QStringLiteral("foo")); QTest::newRow("deleted file menu with actions (contradiction)") << QByteArray(xmlBegin + "<Menu name=\"file\" deleted=\"true\"><Action name=\"file_open\"/><Action name=\"file_new\"/></Menu>" + xmlEnd) << (QStringList() << QStringLiteral("file_open") << QStringLiteral("file_new")) << (QStringList()); } void KXmlGui_UnitTest::testUiStandardsMerging() { QFETCH(QByteArray, xml); QFETCH(QStringList, actions); QFETCH(QStringList, expectedMenus); TestGuiClient client; client.createActions(actions); client.createGUI(xml, true /*ui_standards.rc*/); const QDomDocument domDocument = client.domDocument(); const QDomElement docElem = domDocument.documentElement(); QCOMPARE(docElem.attribute(QStringLiteral("name")), QStringLiteral("foo")); // not standard_containers from ui_standards.rc QCOMPARE(docElem.attribute(QStringLiteral("version")), QStringLiteral("1")); // not standard_containers from ui_standards.rc KMainWindow mainWindow; KXMLGUIBuilder builder(&mainWindow); KXMLGUIFactory factory(&builder); factory.addClient(&client); const QStringList containerNames = collectMenuNames(factory); //qDebug() << containerNames; QCOMPARE(containerNames, expectedMenus); factory.removeClient(&client); } void KXmlGui_UnitTest::testActionListAndSeparator() { const QByteArray xml = "<?xml version = '1.0'?>\n" "<!DOCTYPE gui SYSTEM \"kpartgui.dtd\">\n" "<gui version=\"1\" name=\"foo\" >\n" "<MenuBar>\n" " <Menu name=\"groups\"><text>Add to Group</text>\n" " <ActionList name=\"view_groups_list\"/>\n" " <Separator />" " <Action name=\"view_add_to_new_group\" />\n" " <ActionList name=\"second_list\"/>\n" " </Menu>\n" "</MenuBar>\n" "</gui>"; TestGuiClient client(xml); client.createActions(QStringList() << QStringLiteral("view_add_to_new_group")); KMainWindow mainWindow; KXMLGUIBuilder builder(&mainWindow); KXMLGUIFactory factory(&builder); factory.addClient(&client); QWidget *menuW = factory.container(QStringLiteral("groups"), &client); QVERIFY(menuW); QMenu *menu = qobject_cast<QMenu *>(menuW); QVERIFY(menu); //debugActions(menu->actions()); checkActions(menu->actions(), QStringList() << QStringLiteral("separator") // that's ok, QMenuPrivate::filterActions won't show it << QStringLiteral("view_add_to_new_group")); qDebug() << "Now plugging the actionlist"; QAction *action1 = new QAction(this); action1->setObjectName(QStringLiteral("action1")); client.actionCollection()->setDefaultShortcut(action1, QKeySequence(QStringLiteral("Ctrl+2"))); QAction *action2 = new QAction(this); action2->setObjectName(QStringLiteral("action2")); const QList<QAction *> actionList = { action1, action2 }; client.plugActionList(QStringLiteral("view_groups_list"), actionList); QCOMPARE(QKeySequence::listToString(action1->shortcuts()), QStringLiteral("Ctrl+2")); const QStringList expectedActionsOneList = { QStringLiteral("action1"), QStringLiteral("action2"), QStringLiteral("separator"), QStringLiteral("view_add_to_new_group") }; //debugActions(menu->actions()); checkActions(menu->actions(), expectedActionsOneList); QAction *action3 = new QAction(this); action3->setObjectName(QStringLiteral("action3")); const QList<QAction *> secondActionList = { action3 }; client.plugActionList(QStringLiteral("second_list"), secondActionList); QStringList expectedActions = expectedActionsOneList; expectedActions << QStringLiteral("action3"); checkActions(menu->actions(), expectedActions); qDebug() << "Now remove+add gui client"; // While I'm here, what happens with the action list if I remove+add the guiclient, // like KXmlGuiWindow::newToolBarConfig does? factory.removeClient(&client); factory.addClient(&client); // We need to get the container widget again, it was re-created. menuW = factory.container(QStringLiteral("groups"), &client); QVERIFY(menuW); menu = qobject_cast<QMenu *>(menuW); //debugActions(menu->actions()); checkActions(menu->actions(), QStringList() << QStringLiteral("separator") // yep, it removed the actionlist thing... << QStringLiteral("view_add_to_new_group")); qDebug() << "Now plugging the actionlist again"; client.plugActionList(QStringLiteral("second_list"), secondActionList); client.plugActionList(QStringLiteral("view_groups_list"), actionList); checkActions(menu->actions(), expectedActions); factory.removeClient(&client); } void KXmlGui_UnitTest::testHiddenToolBar() { const QByteArray xml = "<?xml version = '1.0'?>\n" "<!DOCTYPE gui SYSTEM \"kpartgui.dtd\">\n" "<gui version=\"1\" name=\"foo\" >\n" "<MenuBar>\n" "</MenuBar>\n" "<ToolBar hidden=\"true\" name=\"mainToolBar\">\n" " <Action name=\"go_up\"/>\n" "</ToolBar>\n" "<ToolBar name=\"visibleToolBar\">\n" " <Action name=\"go_up\"/>\n" "</ToolBar>\n" "<ToolBar hidden=\"true\" name=\"hiddenToolBar\">\n" " <Action name=\"go_up\"/>\n" "</ToolBar>\n" "</gui>\n"; KConfigGroup cg(KSharedConfig::openConfig(), "testHiddenToolBar"); TestXmlGuiWindow mainWindow(xml); mainWindow.setAutoSaveSettings(cg); mainWindow.createActions(QStringList() << QStringLiteral("go_up")); mainWindow.createGUI(); KToolBar *mainToolBar = mainWindow.toolBarByName(QStringLiteral("mainToolBar")); QVERIFY(mainToolBar->isHidden()); KXMLGUIFactory *factory = mainWindow.guiFactory(); QVERIFY(!factory->container(QStringLiteral("visibleToolBar"), &mainWindow)->isHidden()); KToolBar *hiddenToolBar = qobject_cast<KToolBar *>(factory->container(QStringLiteral("hiddenToolBar"), &mainWindow)); qDebug() << hiddenToolBar; QVERIFY(hiddenToolBar->isHidden()); // Now open KEditToolBar (#105525) KEditToolBar editToolBar(factory); // KEditToolBar loads the stuff in showEvent... QShowEvent ev; qApp->sendEvent(&editToolBar, &ev); clickApply(&editToolBar); QVERIFY(qobject_cast<KToolBar *>(factory->container(QStringLiteral("hiddenToolBar"), &mainWindow))->isHidden()); mainWindow.close(); } // taken from KMainWindow_UnitTest::testAutoSaveSettings() void KXmlGui_UnitTest::testAutoSaveSettings() { const QByteArray xml = "<?xml version = '1.0'?>\n" "<!DOCTYPE gui SYSTEM \"kpartgui.dtd\">\n" "<gui version=\"1\" name=\"foo\" >\n" "<MenuBar>\n" "</MenuBar>\n" "<ToolBar name=\"mainToolBar\">\n" " <Action name=\"go_up\"/>\n" "</ToolBar>\n" "<ToolBar name=\"secondToolBar\">\n" " <Action name=\"go_up\"/>\n" "</ToolBar>\n" "</gui>\n"; { // do not interfere with the "toolbarVisibility" unit test KConfigGroup cg(KSharedConfig::openConfig(), "testAutoSaveSettings"); TestXmlGuiWindow mw(xml); mw.show(); mw.setAutoSaveSettings(cg); // Test resizing first (like show() does). mw.reallyResize(400, 400); QTest::qWait(200); mw.createActions(QStringList() << QStringLiteral("go_up")); mw.createGUI(); // Resize again, should be saved mw.reallyResize(800, 600); QTest::qWait(200); KToolBar *mainToolBar = mw.toolBarByName(QStringLiteral("mainToolBar")); QCOMPARE(mw.toolBarArea(mainToolBar), Qt::TopToolBarArea); KToolBar *secondToolBar = mw.toolBarByName(QStringLiteral("secondToolBar")); QCOMPARE((int)mw.toolBarArea(secondToolBar), (int)Qt::TopToolBarArea); // REFERENCE #1 (see below) // Move second toolbar to bottom const QPoint oldPos = secondToolBar->pos(); mw.addToolBar(Qt::BottomToolBarArea, secondToolBar); const QPoint newPos = secondToolBar->pos(); QCOMPARE(mw.toolBarArea(secondToolBar), Qt::BottomToolBarArea); // Calling to addToolBar is not enough to trigger the event filter for move events // in KMainWindow, because there is no layouting happening in hidden mainwindows. QMoveEvent moveEvent(newPos, oldPos); QApplication::sendEvent(secondToolBar, &moveEvent); mw.close(); } { KConfigGroup cg(KSharedConfig::openConfig(), "testAutoSaveSettings"); TestXmlGuiWindow mw2(xml); mw2.show(); mw2.setAutoSaveSettings(cg); QTest::qWait(200); // Check window size was restored QCOMPARE(mw2.size(), QSize(800, 600)); mw2.createActions(QStringList() << QStringLiteral("go_up")); mw2.createGUI(); // Force window layout to happen mw2.reallyResize(800, 600); QTest::qWait(200); // Check toolbar positions were restored KToolBar *mainToolBar = mw2.toolBarByName(QStringLiteral("mainToolBar")); QCOMPARE(mw2.toolBarArea(mainToolBar), Qt::TopToolBarArea); KToolBar *secondToolBar = mw2.toolBarByName(QStringLiteral("secondToolBar")); QCOMPARE(mw2.toolBarArea(secondToolBar), Qt::BottomToolBarArea); mw2.applyMainWindowSettings(mw2.autoSaveConfigGroup()); QCOMPARE(mw2.toolBarArea(secondToolBar), Qt::BottomToolBarArea); } } void KXmlGui_UnitTest::testDeletedContainers() // deleted="true" { const QByteArray xml = "<?xml version = '1.0'?>\n" "<!DOCTYPE gui SYSTEM \"kpartgui.dtd\">\n" "<gui version=\"1\" name=\"foo\" >\n" "<MenuBar>\n" " <Menu deleted=\"true\" name=\"game\"/>\n" "</MenuBar>\n" "<ToolBar deleted=\"true\" name=\"mainToolBar\">\n" " <Action name=\"go_up\"/>\n" "</ToolBar>\n" "<ToolBar name=\"visibleToolBar\">\n" " <Action name=\"go_up\"/>\n" "</ToolBar>\n" "<ToolBar deleted=\"true\" name=\"deletedToolBar\">\n" " <Action name=\"go_up\"/>\n" "</ToolBar>\n" "</gui>\n"; KConfigGroup cg(KSharedConfig::openConfig(), "testDeletedToolBar"); TestXmlGuiWindow mainWindow(xml); mainWindow.setAutoSaveSettings(cg); mainWindow.createActions(QStringList() << QStringLiteral("go_up") << QStringLiteral("file_new") << QStringLiteral("game_new")); mainWindow.createGUI(); KXMLGUIFactory *factory = mainWindow.guiFactory(); //qDebug() << "containers:" << factory->containers("ToolBar"); QVERIFY(!factory->container(QStringLiteral("mainToolBar"), &mainWindow)); QVERIFY(!factory->container(QStringLiteral("visibleToolBar"), &mainWindow)->isHidden()); QVERIFY(!factory->container(QStringLiteral("deletedToolBar"), &mainWindow)); QVERIFY(factory->container(QStringLiteral("file"), &mainWindow)); // File menu was created QVERIFY(!factory->container(QStringLiteral("game"), &mainWindow)); // Game menu was not created // Now open KEditToolBar, just to check it doesn't crash. KEditToolBar editToolBar(factory); // KEditToolBar loads the stuff in showEvent... QShowEvent ev; qApp->sendEvent(&editToolBar, &ev); clickApply(&editToolBar); QVERIFY(!factory->container(QStringLiteral("mainToolBar"), &mainWindow)); QVERIFY(!factory->container(QStringLiteral("visibleToolBar"), &mainWindow)->isHidden()); QVERIFY(!factory->container(QStringLiteral("deletedToolBar"), &mainWindow)); QVERIFY(factory->container(QStringLiteral("file"), &mainWindow)); QVERIFY(!factory->container(QStringLiteral("game"), &mainWindow)); mainWindow.close(); } void KXmlGui_UnitTest::testTopLevelSeparator() { const QByteArray xml = "<?xml version = '1.0'?>\n" "<!DOCTYPE gui SYSTEM \"kpartgui.dtd\">\n" "<gui version=\"1\" name=\"foo\" >\n" "<MenuBar>\n" " <Menu name=\"before_separator\"><text>Before Separator</text></Menu>\n" " <Separator />\n" " <Menu name=\"after_separator\"><text>After Separator</text></Menu>\n" "</MenuBar>\n" "</gui>"; TestXmlGuiWindow mainWindow(xml); mainWindow.createGUI(); checkActions(mainWindow.menuBar()->actions(), QStringList() << QStringLiteral("before_separator") << QStringLiteral("separator") << QStringLiteral("after_separator") << QStringLiteral("separator") << QStringLiteral("help")); } // Check that the objectName() of the menus is set from the name in the XML file void KXmlGui_UnitTest::testMenuNames() { const QByteArray xml = "<?xml version = '1.0'?>\n" "<!DOCTYPE gui SYSTEM \"kpartgui.dtd\">\n" "<gui version=\"1\" name=\"foo\" >\n" "<MenuBar>\n" " <Menu name=\"filemenu\"><text>File Menu</text></Menu>\n" "</MenuBar>\n" "</gui>"; TestXmlGuiWindow mainWindow(xml); mainWindow.createGUI(); checkActions(mainWindow.menuBar()->actions(), QStringList() << QStringLiteral("filemenu") << QStringLiteral("separator") << QStringLiteral("help")); } // Test what happens when the application's rc file isn't found // We want a warning to be printed, but we don't want to see all menus from ui_standards.rc void KXmlGui_UnitTest::testMenusNoXmlFile() { TestXmlGuiWindow mainWindow; mainWindow.createGUIBad(); checkActions(mainWindow.menuBar()->actions(), QStringList() << QStringLiteral("help")); } void KXmlGui_UnitTest::testXMLFileReplacement() { // to differentiate "original" and replacement xml file, one is created with "modified" toolbars QTemporaryFile fileOrig; QVERIFY(fileOrig.open()); createXmlFile(fileOrig, 2, AddToolBars); const QString filenameOrig = fileOrig.fileName(); fileOrig.close(); QTemporaryFile fileReplace; QVERIFY(fileReplace.open()); createXmlFile(fileReplace, 2, AddModifiedToolBars); const QString filenameReplace = fileReplace.fileName(); fileReplace.close(); // finally, our local xml file has <ActionProperties/> QFile fileLocal(QStandardPaths::writableLocation(QStandardPaths::DataLocation) + QLatin1Char('/') + "testui.rc"); QVERIFY(fileLocal.open(QIODevice::WriteOnly)); createXmlFile(fileLocal, 1, AddActionProperties); const QString filenameLocal = fileLocal.fileName(); fileLocal.close(); TestGuiClient client; // first make sure that the "original" file is loaded, correctly client.setXMLFilePublic(filenameOrig); QString xml = client.domDocument().toString(); //qDebug() << xml; QVERIFY(xml.contains(QStringLiteral("<Action name=\"print\""))); QVERIFY(!xml.contains(QStringLiteral("<Action name=\"home\""))); QVERIFY(!xml.contains(QStringLiteral("<ActionProperties>"))); // now test the replacement (+ local file) client.replaceXMLFile(filenameReplace, filenameLocal); xml = client.domDocument().toString(); QVERIFY(!xml.contains(QStringLiteral("<Action name=\"print\""))); QVERIFY(xml.contains(QStringLiteral("<Action name=\"home\""))); QVERIFY(xml.contains(QStringLiteral("<ActionProperties>"))); // re-check after a reload client.reloadXML(); QString reloadedXml = client.domDocument().toString(); QVERIFY(!reloadedXml.contains(QStringLiteral("<Action name=\"print\""))); QVERIFY(reloadedXml.contains(QStringLiteral("<Action name=\"home\""))); QVERIFY(reloadedXml.contains(QStringLiteral("<ActionProperties>"))); // Check what happens when the local file doesn't exist TestGuiClient client2; QFile::remove(filenameLocal); client2.replaceXMLFile(filenameReplace, filenameLocal); xml = client2.domDocument().toString(); //qDebug() << xml; QVERIFY(!xml.contains(QStringLiteral("<Action name=\"print\""))); QVERIFY(xml.contains(QStringLiteral("<Action name=\"home\""))); // modified toolbars QVERIFY(!xml.contains(QStringLiteral("<ActionProperties>"))); // but no local xml file } void KXmlGui_UnitTest::testClientDestruction() // #170806 { const QByteArray hostXml = "<?xml version = '1.0'?>\n" "<!DOCTYPE gui SYSTEM \"kpartgui.dtd\">\n" "<gui version=\"1\" name=\"foo\" >\n" "<MenuBar>\n" " <Menu name=\"file\"><text>&File</text>\n" " </Menu>\n" " <Merge/>\n" "</MenuBar>\n" "</gui>"; const QByteArray xml = "<?xml version = '1.0'?>\n" "<!DOCTYPE gui SYSTEM \"kpartgui.dtd\">\n" "<gui version=\"1\" name=\"foo\" >\n" "<MenuBar>\n" " <Menu name=\"file\"><text>&File</text>\n" " <Action name=\"file_open\"/>\n" " <Action name=\"file_quit\"/>\n" " </Menu>\n" "</MenuBar>\n" "</gui>"; TestXmlGuiWindow mainWindow(hostXml); TestGuiClient *client = new TestGuiClient(xml); client->createActions(QStringList() << QStringLiteral("file_open") << QStringLiteral("file_quit")); mainWindow.insertChildClient(client); mainWindow.createGUI(); checkActions(mainWindow.menuBar()->actions(), QStringList() << QStringLiteral("file") << QStringLiteral("separator") << QStringLiteral("help")); QVERIFY(mainWindow.factory()->clients().contains(client)); delete client; QVERIFY(!mainWindow.factory()->clients().contains(client)); // No change, because deletion is fast, it doesn't do manual unplugging. checkActions(mainWindow.menuBar()->actions(), QStringList() << QStringLiteral("file") << QStringLiteral("separator") << QStringLiteral("help")); } void KXmlGui_UnitTest::testShortcuts() { const QByteArray xml = "<?xml version = '1.0'?>\n" "<!DOCTYPE gui SYSTEM \"kpartgui.dtd\">\n" "<gui version=\"1\" name=\"foo\" >\n" "<MenuBar>\n" " <Menu name=\"file\"><text>&File</text>\n" " <Action name=\"file_open\" shortcut=\"Ctrl+O\"/>\n" " <Action name=\"file_quit\" shortcut=\"Ctrl+Q; Ctrl+D\"/>\n" " </Menu>\n" "</MenuBar>\n" "<ActionProperties scheme=\"Default\">\n" " <Action shortcut=\"Ctrl+O\" name=\"file_open\"/>\n" " <Action shortcut=\"Ctrl+Q; Ctrl+D\" name=\"file_quit\"/>\n" "</ActionProperties>\n" "</gui>"; TestGuiClient client; client.createActions(QStringList() << QStringLiteral("file_open") << QStringLiteral("file_quit")); client.createGUI(xml, false /*ui_standards.rc*/); KMainWindow mainWindow; KXMLGUIBuilder builder(&mainWindow); KXMLGUIFactory factory(&builder); factory.addClient(&client); QAction* actionOpen = client.action("file_open"); QAction* actionQuit = client.action("file_quit"); QVERIFY(actionOpen && actionQuit); QCOMPARE(actionOpen->shortcuts(), QList<QKeySequence>() << QKeySequence(QStringLiteral("Ctrl+O"))); // #345411 QCOMPARE(actionQuit->shortcuts(), QList<QKeySequence>() << QKeySequence(QStringLiteral("Ctrl+Q")) << QKeySequence(QStringLiteral("Ctrl+D"))); factory.removeClient(&client); } diff --git a/src/kaboutapplicationdialog.h b/src/kaboutapplicationdialog.h index 79aa0dd..07afcd8 100644 --- a/src/kaboutapplicationdialog.h +++ b/src/kaboutapplicationdialog.h @@ -1,105 +1,105 @@ /* This file is part of the KDE libraries Copyright (C) 2007 Urs Wolfer <uwolfer at kde.org> Parts of this class have been take from the KAboutApplication class, which was Copyright (C) 2000 Waldo Bastian (bastian@kde.org) and Espen Sand (espen@kde.org) 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 KABOUT_APPLICATION_DIALOG_H #define KABOUT_APPLICATION_DIALOG_H #include <QDialog> #include <kxmlgui_export.h> #include <QtCore/QFlags> class KAboutData; /** * @short Standard "About Application" dialog box. * * This class provides the standard "About Application" dialog box * that is used by KHelpMenu. It uses the information of the global * KAboutData that is specified at the start of your program in * main(). Normally you should not use this class directly but rather * the KHelpMenu class or even better just subclass your toplevel * window from KMainWindow. If you do the latter, the help menu and * thereby this dialog box is available through the * KMainWindow::helpMenu() function. * * \image html kaboutapplicationdialog.png "KDE About Application Dialog" * * @author Urs Wolfer uwolfer @ kde.org */ class KXMLGUI_EXPORT KAboutApplicationDialog : public QDialog { Q_OBJECT public: /** * Defines some options which can be applied to the about dialog * * @since 4.4 */ enum Option { NoOptions = 0x0, ///< No options, show the standard about dialog HideTranslators = 0x1, ///< Don't show the translators tab HideKdeVersion = 0x2 ///< Don't show the KDE version next to the application name and version }; Q_DECLARE_FLAGS(Options, Option) Q_FLAG(Options) /** * Constructor. Creates a fully featured "About Application" dialog box. * * @param aboutData A KAboutData object which data * will be used for filling the dialog. * @param opts Additional options that can be applied, such as hiding the KDE version * or the translators tab. * @param parent The parent of the dialog box. You should use the * toplevel window so that the dialog becomes centered. * * @since 4.4 */ - explicit KAboutApplicationDialog(const KAboutData &aboutData, Options opts, QWidget *parent = 0); + explicit KAboutApplicationDialog(const KAboutData &aboutData, Options opts, QWidget *parent = nullptr); /** * Constructor. Creates a fully featured "About Application" dialog box. * * @param aboutData A pointer to a KAboutData object which data * will be used for filling the dialog. * @param parent The parent of the dialog box. You should use the * toplevel window so that the dialog becomes centered. */ - explicit KAboutApplicationDialog(const KAboutData &aboutData, QWidget *parent = 0); + explicit KAboutApplicationDialog(const KAboutData &aboutData, QWidget *parent = nullptr); virtual ~KAboutApplicationDialog(); private: class Private; Private *const d; Q_PRIVATE_SLOT(d, void _k_showLicense(const QString &)) Q_DISABLE_COPY(KAboutApplicationDialog) }; Q_DECLARE_OPERATORS_FOR_FLAGS(KAboutApplicationDialog::Options) #endif diff --git a/src/kaboutapplicationpersonlistdelegate_p.cpp b/src/kaboutapplicationpersonlistdelegate_p.cpp index 5c28591..ba315c5 100644 --- a/src/kaboutapplicationpersonlistdelegate_p.cpp +++ b/src/kaboutapplicationpersonlistdelegate_p.cpp @@ -1,305 +1,305 @@ /* This file is part of the KDE libraries Copyright (C) 2010 Teo Mrnjavac <teo@kde.org> 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, see <http://www.gnu.org/licenses/>. */ #include "kaboutapplicationpersonlistdelegate_p.h" #include "kaboutapplicationpersonmodel_p.h" #include "kaboutapplicationpersonlistview_p.h" #include "ktoolbar.h" #include <klocalizedstring.h> #include <QAction> #include <QApplication> #include <QDesktopServices> #include <QPainter> #include <QStandardPaths> namespace KDEPrivate { enum { AVATAR_HEIGHT = 50, AVATAR_WIDTH = 50, MAIN_LINKS_HEIGHT = 32, SOCIAL_LINKS_HEIGHT = 26, MAX_SOCIAL_LINKS = 9 }; KAboutApplicationPersonListDelegate::KAboutApplicationPersonListDelegate( QAbstractItemView *itemView, QObject *parent) : KWidgetItemDelegate(itemView, parent) { } QList< QWidget *> KAboutApplicationPersonListDelegate::createItemWidgets(const QModelIndex &index) const { Q_UNUSED(index); QList< QWidget *> list; QLabel *textLabel = new QLabel(itemView()); list.append(textLabel); KToolBar *mainLinks = new KToolBar(itemView(), false, false); QAction *emailAction = new QAction(QIcon::fromTheme(QStringLiteral("internet-mail")), i18nc("Action to send an email to a contributor", "Email contributor"), mainLinks); emailAction->setVisible(false); mainLinks->addAction(emailAction); QAction *homepageAction = new QAction(QIcon::fromTheme(QStringLiteral("applications-internet")), i18n("Visit contributor's homepage"), mainLinks); homepageAction->setVisible(false); mainLinks->addAction(homepageAction); QAction *visitProfileAction = new QAction(QIcon::fromTheme(QStringLiteral("get-hot-new-stuff")), QString(), mainLinks); visitProfileAction->setVisible(false); mainLinks->addAction(visitProfileAction); list.append(mainLinks); KToolBar *socialLinks = new KToolBar(itemView(), false, false); for (int i = 0; i < MAX_SOCIAL_LINKS; ++i) { QAction *action = new QAction(QIcon::fromTheme(QStringLiteral("applications-internet")), QString(), socialLinks); action->setVisible(false); socialLinks->addAction(action); } list.append(socialLinks); connect(mainLinks, SIGNAL(actionTriggered(QAction*)), this, SLOT(launchUrl(QAction*))); connect(socialLinks, SIGNAL(actionTriggered(QAction*)), this, SLOT(launchUrl(QAction*))); return list; } void KAboutApplicationPersonListDelegate::updateItemWidgets(const QList<QWidget *> widgets, const QStyleOptionViewItem &option, const QPersistentModelIndex &index) const { const int margin = option.fontMetrics.height() / 2; KAboutApplicationPersonProfile profile = index.data().value< KAboutApplicationPersonProfile >(); QRect wRect = widgetsRect(option, index); //Let's fill in the text first... QLabel *label = qobject_cast< QLabel * >(widgets.at(TextLabel)); label->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); QString text = buildTextForProfile(profile); label->move(wRect.left(), wRect.top()); label->resize(wRect.width(), heightForString(text, wRect.width() - margin, option) + margin); label->setWordWrap(true); label->setContentsMargins(0, 0, 0, 0); label->setAlignment(Qt::AlignBottom | Qt::AlignLeft); label->setForegroundRole(QPalette::WindowText); label->setText(text); //And now we fill in the main links (email + homepage + OCS profile)... KToolBar *mainLinks = qobject_cast< KToolBar * >(widgets.at(MainLinks)); mainLinks->setIconSize(QSize(22, 22)); mainLinks->setContentsMargins(0, 0, 0, 0); mainLinks->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); QAction *action; if (!profile.email().isEmpty()) { action = mainLinks->actions().at(EmailAction); action->setToolTip(i18nc("Action to send an email to a contributor", "Email contributor\n%1", profile.email())); action->setData(QStringLiteral("mailto:%1").arg(profile.email())); action->setVisible(true); } if (!profile.homepage().isEmpty()) { action = mainLinks->actions().at(HomepageAction); action->setToolTip(i18n("Visit contributor's homepage\n%1", profile.homepage().toString())); action->setData(profile.homepage().toString()); action->setVisible(true); } if (!profile.ocsProfileUrl().isEmpty()) { action = mainLinks->actions().at(VisitProfileAction); KAboutApplicationPersonModel *model = qobject_cast< KAboutApplicationPersonModel * >(itemView()->model()); action->setToolTip(i18n("Visit contributor's profile on %1\n%2", model->providerName(), profile.ocsProfileUrl())); action->setData(profile.ocsProfileUrl()); action->setVisible(true); } mainLinks->resize(QSize(mainLinks->sizeHint().width(), MAIN_LINKS_HEIGHT)); mainLinks->move(wRect.left(), wRect.top() + label->height()); //Finally, the social links... KToolBar *socialLinks = qobject_cast< KToolBar * >(widgets.at(SocialLinks)); socialLinks->setIconSize(QSize(16, 16)); socialLinks->setContentsMargins(0, 0, 0, 0); socialLinks->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); int currentSocialLinkAction = 0; Q_FOREACH (KAboutApplicationPersonProfileOcsLink link, profile.ocsLinks()) { if (!profile.homepage().isEmpty() && profile.homepage() == link.url()) { continue; //We skip it if it's the same as the homepage from KAboutData } action = socialLinks->actions().at(currentSocialLinkAction); if (link.type() == KAboutApplicationPersonProfileOcsLink::Other) { action->setToolTip(i18n("Visit contributor's page\n%1", link.url().toString())); } else if (link.type() == KAboutApplicationPersonProfileOcsLink::Blog) { action->setToolTip(i18n("Visit contributor's blog\n%1", link.url().toString())); } else if (link.type() == KAboutApplicationPersonProfileOcsLink::Homepage) { action->setToolTip(i18n("Visit contributor's homepage\n%1", link.url().toString())); } else { action->setToolTip(i18n("Visit contributor's profile on %1\n%2", link.prettyType(), link.url().toString())); } action->setIcon(link.icon()); action->setData(link.url().toString()); action->setVisible(true); ++currentSocialLinkAction; if (currentSocialLinkAction > MAX_SOCIAL_LINKS - 1) { break; } } socialLinks->resize(QSize(socialLinks->sizeHint().width(), SOCIAL_LINKS_HEIGHT)); socialLinks->move(wRect.left() + mainLinks->width(), wRect.top() + label->height() + (MAIN_LINKS_HEIGHT - SOCIAL_LINKS_HEIGHT) / 2); itemView()->reset(); } QSize KAboutApplicationPersonListDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const { KAboutApplicationPersonProfile profile = index.data().value< KAboutApplicationPersonProfile >(); bool hasAvatar = !profile.avatar().isNull(); int margin = option.fontMetrics.height() / 2; int height = hasAvatar ? qMax(widgetsRect(option, index).height(), AVATAR_HEIGHT + 2 * margin) : widgetsRect(option, index).height(); QSize metrics(option.fontMetrics.height() * 7, height); return metrics; } void KAboutApplicationPersonListDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const { int margin = option.fontMetrics.height() / 2; QStyle *style = QApplication::style(); - style->drawPrimitive(QStyle::PE_Widget, &option, painter, 0); + style->drawPrimitive(QStyle::PE_Widget, &option, painter, nullptr); const KAboutApplicationPersonModel *model = qobject_cast< const KAboutApplicationPersonModel * >(index.model()); if (model->hasAvatarPixmaps()) { int height = qMax(widgetsRect(option, index).height(), AVATAR_HEIGHT + 2 * margin); QPoint point(option.rect.left() + 2 * margin, option.rect.top() + ((height - AVATAR_HEIGHT) / 2)); KAboutApplicationPersonProfile profile = index.data().value< KAboutApplicationPersonProfile >(); if (!profile.avatar().isNull()) { QPixmap pixmap = profile.avatar(); point.setX((AVATAR_WIDTH - pixmap.width()) / 2 + 5); point.setY(option.rect.top() + ((height - pixmap.height()) / 2)); painter->drawPixmap(point, pixmap); QPoint framePoint(point.x() - 5, point.y() - 5); QPixmap framePixmap(QStringLiteral(":/kxmlgui5/thumb_frame.png")); painter->drawPixmap(framePoint, framePixmap.scaled(pixmap.width() + 10, pixmap.height() + 10)); } } } void KAboutApplicationPersonListDelegate::launchUrl(QAction *action) const { QString url = action->data().toString(); if (!url.isEmpty()) { QDesktopServices::openUrl(QUrl(url)); } } int KAboutApplicationPersonListDelegate::heightForString(const QString &string, int lineWidth, const QStyleOptionViewItem &option) const { QFontMetrics fm = option.fontMetrics; QRect boundingRect = fm.boundingRect(0, 0, lineWidth, 9999, Qt::AlignLeft | Qt::AlignBottom | Qt::TextWordWrap, string); return boundingRect.height(); } QString KAboutApplicationPersonListDelegate::buildTextForProfile(const KAboutApplicationPersonProfile &profile) const { QString text; text += QStringLiteral("<b>"); text += i18nc("@item Contributor name in about dialog.", "%1", profile.name()); text += QStringLiteral("</b>"); if (!profile.task().isEmpty()) { text += QStringLiteral("\n<br><i>"); text += profile.task(); text += QStringLiteral("</i>"); } if (!profile.location().isEmpty()) { text += QStringLiteral("\n<br>"); text += profile.location(); } return text; } QRect KAboutApplicationPersonListDelegate::widgetsRect(const QStyleOptionViewItem &option, const QPersistentModelIndex &index) const { KAboutApplicationPersonProfile profile = index.data().value< KAboutApplicationPersonProfile >(); int margin = option.fontMetrics.height() / 2; QRect widgetsRect; if (qobject_cast< const KAboutApplicationPersonModel * >(index.model())->hasAvatarPixmaps()) { widgetsRect = QRect(option.rect.left() + AVATAR_WIDTH + 3 * margin, margin / 2, option.rect.width() - AVATAR_WIDTH - 4 * margin, 0); } else { widgetsRect = QRect(option.rect.left() + margin, margin / 2, option.rect.width() - 2 * margin, 0); } int textHeight = heightForString(buildTextForProfile(profile), widgetsRect.width() - margin, option); widgetsRect.setHeight(textHeight + MAIN_LINKS_HEIGHT + 1.5 * margin); return widgetsRect; } } //namespace KDEPrivate diff --git a/src/kaboutapplicationpersonlistdelegate_p.h b/src/kaboutapplicationpersonlistdelegate_p.h index 8b72567..c183019 100644 --- a/src/kaboutapplicationpersonlistdelegate_p.h +++ b/src/kaboutapplicationpersonlistdelegate_p.h @@ -1,71 +1,71 @@ /* This file is part of the KDE libraries Copyright (C) 2010 Teo Mrnjavac <teo@kde.org> 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, see <http://www.gnu.org/licenses/>. */ #ifndef KABOUT_APPLICATION_PERSON_LIST_DELEGATE_H #define KABOUT_APPLICATION_PERSON_LIST_DELEGATE_H #include <kwidgetitemdelegate.h> #include <QLabel> namespace KDEPrivate { class KAboutApplicationPersonProfile; class KAboutApplicationPersonListDelegate : public KWidgetItemDelegate { Q_OBJECT public: KAboutApplicationPersonListDelegate(QAbstractItemView *itemView, - QObject *parent = 0); + QObject *parent = nullptr); ~KAboutApplicationPersonListDelegate() {} void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const Q_DECL_OVERRIDE; QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const Q_DECL_OVERRIDE; QList< QWidget *> createItemWidgets(const QModelIndex &index) const Q_DECL_OVERRIDE; void updateItemWidgets(const QList<QWidget *> widgets, const QStyleOptionViewItem &option, const QPersistentModelIndex &index) const Q_DECL_OVERRIDE; private Q_SLOTS: void launchUrl(QAction *action) const; private: int heightForString(const QString &string, int lineWidth, const QStyleOptionViewItem &option) const; QString buildTextForProfile(const KAboutApplicationPersonProfile &profile) const; QRect widgetsRect(const QStyleOptionViewItem &option, const QPersistentModelIndex &index) const; enum DelegateWidgets { TextLabel = 0, MainLinks, SocialLinks }; enum MainLinkActions { EmailAction = 0, HomepageAction, VisitProfileAction }; }; } //namespace KDEPrivate #endif // KABOUT_APPLICATION_PERSON_LIST_DELEGATE_H diff --git a/src/kaboutapplicationpersonlistview_p.h b/src/kaboutapplicationpersonlistview_p.h index cabbc34..26a94d4 100644 --- a/src/kaboutapplicationpersonlistview_p.h +++ b/src/kaboutapplicationpersonlistview_p.h @@ -1,38 +1,38 @@ /* This file is part of the KDE libraries Copyright (C) 2010 Teo Mrnjavac <teo@kde.org> 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, see <http://www.gnu.org/licenses/>. */ #ifndef KABOUT_APPLICATION_PERSON_LIST_VIEW_H #define KABOUT_APPLICATION_PERSON_LIST_VIEW_H #include <QListView> namespace KDEPrivate { class KAboutApplicationPersonListView : public QListView { public: - KAboutApplicationPersonListView(QWidget *parent = 0); + KAboutApplicationPersonListView(QWidget *parent = nullptr); protected: void wheelEvent(QWheelEvent *e) Q_DECL_OVERRIDE; }; } //namespace KDEPrivate #endif // KABOUT_APPLICATION_PERSON_LIST_VIEW_H diff --git a/src/kaboutapplicationpersonmodel_p.h b/src/kaboutapplicationpersonmodel_p.h index af9831e..0ce3570 100644 --- a/src/kaboutapplicationpersonmodel_p.h +++ b/src/kaboutapplicationpersonmodel_p.h @@ -1,312 +1,312 @@ /* This file is part of the KDE libraries Copyright (C) 2010 Teo Mrnjavac <teo@kde.org> 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, see <http://www.gnu.org/licenses/>. */ #ifndef KABOUT_APPLICATION_PERSON_MODEL_H #define KABOUT_APPLICATION_PERSON_MODEL_H #include "kaboutapplicationconfigattica_p.h" #if HAVE_ATTICA #include <attica/providermanager.h> #include <attica/provider.h> #endif //HAVE_ATTICA #include <kaboutdata.h> #include <QIcon> #include <QtCore/QAbstractListModel> #include <QPixmap> #include <QtNetwork/QNetworkReply> #include <QUrl> // Forward declarations to make Attica-related members work namespace Attica { class BaseJob; } namespace KDEPrivate { class KAboutApplicationPersonProfile; class KAboutApplicationPersonIconsJob; class KAboutApplicationPersonModel : public QAbstractListModel { Q_OBJECT public: KAboutApplicationPersonModel(const QList< KAboutPerson > &personList, const QString &providerUrl = QString(), - QObject *parent = 0); + QObject *parent = nullptr); int rowCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE; int columnCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE { Q_UNUSED(parent) return 1; } QVariant data(const QModelIndex &index, int role) const Q_DECL_OVERRIDE; Qt::ItemFlags flags(const QModelIndex &index) const Q_DECL_OVERRIDE; bool hasAvatarPixmaps() const { return m_hasAvatarPixmaps; } const QString &providerName() const { return m_providerName; } private Q_SLOTS: void onProvidersLoaded(); void onPersonJobFinished(Attica::BaseJob *job); void onAvatarJobFinished(QNetworkReply *reply); void onOcsLinksJobFinished(KAboutApplicationPersonIconsJob *job); private: void fetchOcsLinkIcons(int personProfileListIndex); QList< KAboutPerson > m_personList; QList< KAboutApplicationPersonProfile > m_profileList; QMap< int, QString > m_ocsLinkIconUrls; QMap< int, QPixmap > m_ocsLinkIcons; bool m_hasAvatarPixmaps; #if HAVE_ATTICA Attica::ProviderManager m_providerManager; Attica::Provider m_provider; #endif //HAVE_ATTICA QString m_providerUrl; QString m_providerName; friend class KAboutApplicationPersonIconsJob; }; //This list must be in sync with the one in KAboutApplicationPersonProfileOcsLink::prettyType() static const char s_personOcsLinkAtticaTypes[][16] = { { "other" }, { "Blog" }, { "delicious" }, { "Digg" }, { "Facebook" }, { "Homepage" }, { "identi.ca" }, { "libre.fm" }, { "LinkedIn" }, { "MySpace" }, { "Reddit" }, { "StackOverflow" }, { "Twitter" }, { "Wikipedia" }, { "Xing" }, { "YouTube" } }; class KAboutApplicationPersonProfileOcsLink { public: enum Type { Other = 0, Blog, Delicious, Digg, Facebook, Homepage, Identica, LibreFm, LinkedIn, MySpace, Reddit, StackOverflow, Twitter, Wikipedia, Xing, YouTube, NUM_ATTICA_LINK_TYPES }; static Type typeFromAttica(const QString &atticaType); KAboutApplicationPersonProfileOcsLink(Type type, const QUrl &url) : m_type(type) , m_url(url) {} Type type() const { return m_type; } QString prettyType() const; void setIcon(const QIcon &icon) { m_icon = icon; } const QIcon &icon() const { return m_icon; } const QUrl &url() const { return m_url; } private: Type m_type; QUrl m_url; QIcon m_icon; }; class KAboutApplicationPersonProfile { public: KAboutApplicationPersonProfile() : m_name() , m_task() , m_email() , m_ocsUsername() {} //needed for QVariant KAboutApplicationPersonProfile(const QString &name, const QString &task, const QString &email, const QString &ocsUsername = QString()) : m_name(name) , m_task(task) , m_email(email) , m_ocsUsername(ocsUsername) {} void setHomepage(const QUrl &url) { m_homepage = url; } void setAvatar(const QPixmap &pixmap) { m_avatar = pixmap; } void setLocation(const QString &location) { m_location = location; } void setOcsProfileUrl(const QString &url) { m_ocsProfileUrl = url; } void addAdditionalString(const QString &string) { m_additionalStrings << string; } void setOcsLinks(const QList< KAboutApplicationPersonProfileOcsLink > &ocsLinks) { m_ocsLinks = ocsLinks; } const QString &name() const { return m_name; } const QString &task() const { return m_task; } const QString &email() const { return m_email; } const QString &ocsUsername() const { return m_ocsUsername; } const QString &ocsProfileUrl() const { return m_ocsProfileUrl; } const QUrl &homepage() const { return m_homepage; } const QPixmap &avatar() const { return m_avatar; } const QString &location() const { return m_location; } const QStringList &additionalStrings() const { return m_additionalStrings; } const QList< KAboutApplicationPersonProfileOcsLink > &ocsLinks() const { return m_ocsLinks; } private: QString m_name; QString m_task; QString m_email; QString m_ocsUsername; QString m_ocsProfileUrl; QUrl m_homepage; QPixmap m_avatar; QString m_location; QStringList m_additionalStrings; QList< KAboutApplicationPersonProfileOcsLink > m_ocsLinks; }; class KAboutApplicationPersonIconsJob : public QObject { Q_OBJECT public: KAboutApplicationPersonIconsJob(KAboutApplicationPersonModel *model, int personProfileListIndex); void start(); int personProfileListIndex() const { return m_personProfileListIndex; } const QList< KAboutApplicationPersonProfileOcsLink > &ocsLinks() const { return m_ocsLinks; } Q_SIGNALS: void finished(KAboutApplicationPersonIconsJob *job); private Q_SLOTS: void onJobFinished(QNetworkReply *reply); private: void getIcons(int i); int m_personProfileListIndex; KAboutApplicationPersonModel *m_model; QList< KAboutApplicationPersonProfileOcsLink > m_ocsLinks; QNetworkAccessManager *m_manager; }; } //namespace KDEPrivate Q_DECLARE_METATYPE(KDEPrivate::KAboutApplicationPersonProfile) #endif // KABOUT_APPLICATION_PERSON_MODEL_H diff --git a/src/kaboutkdedialog_p.cpp b/src/kaboutkdedialog_p.cpp index fd71078..81a2f02 100644 --- a/src/kaboutkdedialog_p.cpp +++ b/src/kaboutkdedialog_p.cpp @@ -1,160 +1,160 @@ /* This file is part of the KDE libraries Copyright (C) 2007 Urs Wolfer <uwolfer at kde.org> Parts of this class have been take from the KAboutKDE class, which was Copyright (C) 2000 Espen Sand <espen@kde.org> 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 "kaboutkdedialog_p.h" #include <QDialogButtonBox> #include <QFrame> #include <QLabel> #include <QLayout> #include <QTabWidget> #include <QStandardPaths> #include <klocalizedstring.h> #include <ktitlewidget.h> #include "../kxmlgui_version.h" namespace KDEPrivate { KAboutKdeDialog::KAboutKdeDialog(QWidget *parent) : QDialog(parent), - d(0) + d(nullptr) { setWindowTitle(i18n("About KDE")); KTitleWidget *titleWidget = new KTitleWidget(this); titleWidget->setText(i18n("<html><font size=\"5\">KDE - Be Free!</font></html>")); titleWidget->setPixmap(QIcon::fromTheme(QStringLiteral("kde")).pixmap(48), KTitleWidget::ImageLeft); QLabel *about = new QLabel; about->setMargin(10); about->setAlignment(Qt::AlignTop); about->setWordWrap(true); about->setOpenExternalLinks(true); about->setTextInteractionFlags(Qt::TextBrowserInteraction); about->setText(i18n("<html>" "<b>KDE</b> is a world-wide network of software engineers, artists, writers, translators and facilitators " "who are committed to <a href=\"%1\">Free Software</a> development. " "This community has created hundreds of Free Software applications as part of the KDE " "frameworks, workspaces and applications.<br /><br />" "KDE is a cooperative enterprise in which no single entity controls the " "efforts or products of KDE to the exclusion of others. Everyone is welcome to join and " "contribute to KDE, including you.<br /><br />" "Visit <a href=\"%2\">%2</a> for " "more information about the KDE community and the software we produce.</html>", QStringLiteral("http://www.gnu.org/philosophy/free-sw.html"), QStringLiteral("http://www.kde.org/"))); QLabel *report = new QLabel; report->setMargin(10); report->setAlignment(Qt::AlignTop); report->setWordWrap(true); report->setOpenExternalLinks(true); report->setTextInteractionFlags(Qt::TextBrowserInteraction); report->setText(i18n("<html>" "Software can always be improved, and the KDE team is ready to " "do so. However, you - the user - must tell us when " "something does not work as expected or could be done better.<br /><br />" "KDE has a bug tracking system. Visit " "<a href=\"%1\">%1</a> or " "use the \"Report Bug...\" dialog from the \"Help\" menu to report bugs.<br /><br />" "If you have a suggestion for improvement then you are welcome to use " "the bug tracking system to register your wish. Make sure you use the " "severity called \"Wishlist\".</html>", QStringLiteral("https://bugs.kde.org/"))); QLabel *join = new QLabel; join->setMargin(10); join->setAlignment(Qt::AlignTop); join->setWordWrap(true); join->setOpenExternalLinks(true); join->setTextInteractionFlags(Qt::TextBrowserInteraction); join->setText(i18n("<html>" "You do not have to be a software developer to be a member of the " "KDE team. You can join the national teams that translate " "program interfaces. You can provide graphics, themes, sounds, and " "improved documentation. You decide!" "<br /><br />" "Visit " "<a href=\"%1\">%1</a> " "for information on some projects in which you can participate." "<br /><br />" "If you need more information or documentation, then a visit to " "<a href=\"%2\">%2</a> " "will provide you with what you need.</html>", QStringLiteral("http://www.kde.org/community/getinvolved/"), QStringLiteral("http://techbase.kde.org/"))); QLabel *support = new QLabel; support->setMargin(10); support->setAlignment(Qt::AlignTop); support->setWordWrap(true); support->setOpenExternalLinks(true); support->setTextInteractionFlags(Qt::TextBrowserInteraction); support->setText(i18n("<html>" "KDE software is and will always be available free of charge, however creating it is not free.<br /><br />" "To support development the KDE community has formed the KDE e.V., a non-profit organization " "legally founded in Germany. KDE e.V. represents the KDE community in legal and financial matters. " "See <a href=\"%1\">%1</a>" " for information on KDE e.V.<br /><br />" "KDE benefits from many kinds of contributions, including financial. " "We use the funds to reimburse members and others for expenses " "they incur when contributing. Further funds are used for legal " "support and organizing conferences and meetings. <br /> <br />" "We would like to encourage you to support our efforts with a " "financial donation, using one of the ways described at " "<a href=\"%2\">%2</a>." "<br /><br />Thank you very much in advance for your support.</html>", QStringLiteral("http://ev.kde.org/"), QStringLiteral("http://www.kde.org/community/donations/"))); support->setMinimumSize(support->sizeHint()); QTabWidget *tabWidget = new QTabWidget; tabWidget->setUsesScrollButtons(false); tabWidget->addTab(about, i18nc("About KDE", "&About")); tabWidget->addTab(report, i18n("&Report Bugs or Wishes")); tabWidget->addTab(join, i18n("&Join KDE")); tabWidget->addTab(support, i18n("&Support KDE")); QLabel *image = new QLabel; image->setPixmap(QStringLiteral(":/kxmlgui5/aboutkde.png")); QHBoxLayout *midLayout = new QHBoxLayout; midLayout->addWidget(image); midLayout->addWidget(tabWidget); QDialogButtonBox *buttonBox = new QDialogButtonBox; buttonBox->setStandardButtons(QDialogButtonBox::Close); connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept())); connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); QVBoxLayout *mainLayout = new QVBoxLayout; mainLayout->addWidget(titleWidget); mainLayout->addLayout(midLayout); mainLayout->addWidget(buttonBox); setLayout(mainLayout); } } diff --git a/src/kaboutkdedialog_p.h b/src/kaboutkdedialog_p.h index d29772d..8d8b0f2 100644 --- a/src/kaboutkdedialog_p.h +++ b/src/kaboutkdedialog_p.h @@ -1,67 +1,67 @@ /* This file is part of the KDE libraries Copyright (C) 2007 Urs Wolfer <uwolfer at kde.org> Parts of this class have been take from the KAboutKDE class, which was Copyright (C) 2000 Espen Sand <espen@kde.org> 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 KABOUT_KDE_DIALOG_H #define KABOUT_KDE_DIALOG_H #include <QDialog> namespace KDEPrivate { /** * @short Standard "About KDE" dialog box * * This class provides the standard "About KDE" dialog box that is used * in KHelpMenu. Normally you should not use this class directly, but * rather the KHelpMenu class or even better just subclass your * toplevel window from KMainWindow. If you do the latter, the help * menu and thereby this dialog box is available through the * KMainWindow::helpMenu() function. * * @author Urs Wolfer uwolfer @ kde.org * @internal */ class KAboutKdeDialog : public QDialog { Q_OBJECT public: /** * Constructor. Creates a fully featured "About KDE" dialog box. * Note that this dialog is made modeless in the KHelpMenu class so * the users may expect a modeless dialog. * * @param parent The parent of the dialog box. You should use the * toplevel window so that the dialog becomes centered. */ - explicit KAboutKdeDialog(QWidget *parent = 0); + explicit KAboutKdeDialog(QWidget *parent = nullptr); private: class Private; Private *const d; Q_DISABLE_COPY(KAboutKdeDialog) }; } #endif diff --git a/src/kactioncategory.h b/src/kactioncategory.h index be7281b..1a3ee37 100644 --- a/src/kactioncategory.h +++ b/src/kactioncategory.h @@ -1,191 +1,191 @@ /* This file is part of the KDE libraries Copyright (C) 2008 Michael Jansen <kde@michael-jansen.biz> 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 KACTIONCATEGORY_H #define KACTIONCATEGORY_H #include <kxmlgui_export.h> #include <QtCore/QObject> #include <QtCore/QString> #include <QtCore/QList> #include <kstandardaction.h> #include "kactioncollection.h" struct KActionCategoryPrivate; class QAction; /** * Categorize actions for KShortcutsEditor. * * KActionCategory provides a second level to organize the actions in * KShortcutsEditor. * * The first possibility is using more than one action collection. Each * actions collection becomes a top level node. * * + action collection 1 * + first action * + second action * + third action * + action collection 2 * + first action * + second action * + third action * * Using KActionCategory it's possible to group the actions of one collection. * + action collection 1 * + first action * + first category * + action 1 in category * + action 2 in category * + second action * * \section Usage * * The usage is analog to action collections. Just create a category and use * it instead of the collection to create the actions. * * The synchronization between KActionCollection and KActionCategory is done * internally. There is for example no need to remove actions from a category. * It is done implicitely if the action is removed from the associated * collection. * * \code * * KActionCategory *file = new KActionCategory(i18n("File"), actionCollection()); * file->addAction( * KStandardAction::New, //< see KStandardAction * this, //< Receiver * SLOT(fileNew())); //< SLOT * * ... more actions added to file ... * * KActionCategory *edit = new KActionCategory(i18n("Edit"), actionCollection()); * edit->addAction( * KStandardAction::Copy, //< see KStandardAction * this, //< Receiver * SLOT(fileNew())); //< SLOT * * ... * * \endcode */ class KXMLGUI_EXPORT KActionCategory : public QObject { Q_OBJECT Q_PROPERTY(QString text READ text WRITE setText) public: /** * Default constructor */ - explicit KActionCategory(const QString &text, KActionCollection *parent = NULL); + explicit KActionCategory(const QString &text, KActionCollection *parent = nullptr); /** * Destructor */ virtual ~KActionCategory(); /** * \name Adding Actions * * Add a action to the category. * * This methods are provided for your convenience. They call the * corresponding method of KActionCollection. */ //@{ QAction *addAction(const QString &name, QAction *action); QAction *addAction( KStandardAction::StandardAction actionType, - const QObject *receiver = NULL, - const char *member = NULL); + const QObject *receiver = nullptr, + const char *member = nullptr); QAction *addAction( KStandardAction::StandardAction actionType, const QString &name, - const QObject *receiver = NULL, - const char *member = NULL); + const QObject *receiver = nullptr, + const char *member = nullptr); QAction *addAction( const QString &name, - const QObject *receiver = NULL, - const char *member = NULL); + const QObject *receiver = nullptr, + const char *member = nullptr); template<class ActionType> ActionType *add( const QString &name, - const QObject *receiver = NULL, - const char *member = NULL) + const QObject *receiver = nullptr, + const char *member = nullptr) { ActionType *action = collection()->add<ActionType>(name, receiver, member); addAction(action); return action; } //@} /** * Returns the actions belonging to this category */ const QList<QAction *> actions() const; /** * The action collection this category is associated with. */ KActionCollection *collection() const; /** * The action categorys descriptive text */ QString text() const; /** * Set the action categorys descriptive text. */ void setText(const QString &text); private: /** * Remove \action from this category if found. */ void unlistAction(QAction *action); /** * Add action to category */ void addAction(QAction *action); //! KActionCollection needs access to some of our helper methods friend class KActionCollectionPrivate; //! Implementation details KActionCategoryPrivate *const d; }; #endif /* #ifndef KACTIONCATEGORY_H */ diff --git a/src/kactioncollection.cpp b/src/kactioncollection.cpp index 7e009dc..80082e4 100644 --- a/src/kactioncollection.cpp +++ b/src/kactioncollection.cpp @@ -1,873 +1,873 @@ /* This file is part of the KDE libraries Copyright (C) 1999 Reginald Stadlbauer <reggie@kde.org> (C) 1999 Simon Hausmann <hausmann@kde.org> (C) 2000 Nicolas Hadacek <haadcek@kde.org> (C) 2000 Kurt Granroth <granroth@kde.org> (C) 2000 Michael Koch <koch@kde.org> (C) 2001 Holger Freyther <freyther@kde.org> (C) 2002 Ellis Whitehead <ellis@kde.org> (C) 2002 Joseph Wenninger <jowenn@kde.org> (C) 2005-2007 Hamish Rodda <rodda@kde.org> 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 "config-xmlgui.h" #include "kactioncollection.h" #include "kactioncategory.h" #include "kxmlguiclient.h" #include "kxmlguifactory.h" #include "debug.h" #include <kauthorized.h> #include <kconfiggroup.h> #if HAVE_GLOBALACCEL # include <kglobalaccel.h> #endif #include <ksharedconfig.h> #include <QtXml/QDomDocument> #include <QtCore/QSet> #include <QGuiApplication> #include <QtCore/QMap> #include <QtCore/QList> #include <QAction> #include <QMetaMethod> #include <stdio.h> class KActionCollectionPrivate { public: KActionCollectionPrivate() - : m_parentGUIClient(0L), + : m_parentGUIClient(nullptr), configGroup(QStringLiteral("Shortcuts")), configIsGlobal(false), connectTriggered(false), connectHovered(false), - q(0) + q(nullptr) { } void setComponentForAction(QAction *action) { #if HAVE_GLOBALACCEL bool hasGlobalShortcut = KGlobalAccel::self()->hasShortcut(action); #else bool hasGlobalShortcut = false; #endif if (!hasGlobalShortcut) { action->setProperty("componentName", m_componentName); action->setProperty("componentDisplayName", m_componentDisplayName); } } static QList<KActionCollection *> s_allCollections; void _k_associatedWidgetDestroyed(QObject *obj); void _k_actionDestroyed(QObject *obj); bool writeKXMLGUIConfigFile(); QString m_componentName; QString m_componentDisplayName; //! Remove a action from our internal bookkeeping. Returns NULL if the //! action doesn't belong to us. QAction *unlistAction(QAction *); QMap<QString, QAction *> actionByName; QList<QAction *> actions; const KXMLGUIClient *m_parentGUIClient; QString configGroup; bool configIsGlobal : 1; bool connectTriggered : 1; bool connectHovered : 1; KActionCollection *q; QList<QWidget *> associatedWidgets; }; QList<KActionCollection *> KActionCollectionPrivate::s_allCollections; KActionCollection::KActionCollection(QObject *parent, const QString &cName) : QObject(parent) , d(new KActionCollectionPrivate) { d->q = this; KActionCollectionPrivate::s_allCollections.append(this); setComponentName(cName); } KActionCollection::KActionCollection(const KXMLGUIClient *parent) - : QObject(0) + : QObject(nullptr) , d(new KActionCollectionPrivate) { d->q = this; KActionCollectionPrivate::s_allCollections.append(this); d->m_parentGUIClient = parent; d->m_componentName = parent->componentName(); } KActionCollection::~KActionCollection() { KActionCollectionPrivate::s_allCollections.removeAll(this); delete d; } void KActionCollection::clear() { d->actionByName.clear(); qDeleteAll(d->actions); d->actions.clear(); } QAction *KActionCollection::action(const QString &name) const { - QAction *action = 0L; + QAction *action = nullptr; if (!name.isEmpty()) { action = d->actionByName.value(name); } return action; } QAction *KActionCollection::action(int index) const { // ### investigate if any apps use this at all return actions().value(index); } int KActionCollection::count() const { return d->actions.count(); } bool KActionCollection::isEmpty() const { return count() == 0; } void KActionCollection::setComponentName(const QString &cName) { if (count() > 0) { // Its component name is part of an action's signature in the context of // global shortcuts and the semantics of changing an existing action's // signature are, as it seems, impossible to get right. // As of now this only matters for global shortcuts. We could // thus relax the requirement and only refuse to change the component data // if we have actions with global shortcuts in this collection. qWarning() << "this does not work on a KActionCollection containing actions!"; } if (!cName.isEmpty()) { d->m_componentName = cName; } else { d->m_componentName = QCoreApplication::applicationName(); } } QString KActionCollection::componentName() const { return d->m_componentName; } void KActionCollection::setComponentDisplayName(const QString &displayName) { d->m_componentDisplayName = displayName; } QString KActionCollection::componentDisplayName() const { if (!d->m_componentDisplayName.isEmpty()) { return d->m_componentDisplayName; } if (!QGuiApplication::applicationDisplayName().isEmpty()) { return QGuiApplication::applicationDisplayName(); } return QCoreApplication::applicationName(); } const KXMLGUIClient *KActionCollection::parentGUIClient() const { return d->m_parentGUIClient; } QList<QAction *> KActionCollection::actions() const { return d->actions; } const QList< QAction * > KActionCollection::actionsWithoutGroup() const { QList<QAction *> ret; Q_FOREACH (QAction *action, d->actions) if (!action->actionGroup()) { ret.append(action); } return ret; } const QList< QActionGroup * > KActionCollection::actionGroups() const { QSet<QActionGroup *> set; Q_FOREACH (QAction *action, d->actions) if (action->actionGroup()) { set.insert(action->actionGroup()); } return set.toList(); } QAction *KActionCollection::addAction(const QString &name, QAction *action) { if (!action) { return action; } const QString objectName = action->objectName(); QString indexName = name; if (indexName.isEmpty()) { // No name provided. Use the objectName. indexName = objectName; } else { // A name was provided. Check against objectName. if ((!objectName.isEmpty()) && (objectName != indexName)) { // The user specified a new name and the action already has a // different one. The objectName is used for saving shortcut // settings to disk. Both for local and global shortcuts. qCDebug(DEBUG_KXMLGUI) << "Registering action " << objectName << " under new name " << indexName; // If there is a global shortcuts it's a very bad idea. #if HAVE_GLOBALACCEL if (KGlobalAccel::self()->hasShortcut(action)) { // In debug mode assert Q_ASSERT(!KGlobalAccel::self()->hasShortcut(action)); // In release mode keep the old name qCritical() << "Changing action name from " << objectName << " to " << indexName << "\nignored because of active global shortcut."; indexName = objectName; } #endif } // Set the new name action->setObjectName(indexName); } // No name provided and the action had no name. Make one up. This will not // work when trying to save shortcuts. Both local and global shortcuts. if (indexName.isEmpty()) { indexName = indexName.sprintf("unnamed-%p", (void *)action); action->setObjectName(indexName); } // From now on the objectName has to have a value. Else we cannot safely // remove actions. Q_ASSERT(!action->objectName().isEmpty()); // look if we already have THIS action under THIS name ;) - if (d->actionByName.value(indexName, 0) == action) { + if (d->actionByName.value(indexName, nullptr) == action) { // This is not a multi map! Q_ASSERT(d->actionByName.count(indexName) == 1); return action; } if (!KAuthorized::authorizeAction(indexName)) { // Disable this action action->setEnabled(false); action->setVisible(false); action->blockSignals(true); } // Check if we have another action under this name if (QAction *oldAction = d->actionByName.value(indexName)) { takeAction(oldAction); } // Check if we have this action under a different name. // Not using takeAction because we don't want to remove it from categories, // and because it has the new name already. const int oldIndex = d->actions.indexOf(action); if (oldIndex != -1) { d->actionByName.remove(d->actionByName.key(action)); d->actions.removeAt(oldIndex); } // Add action to our lists. d->actionByName.insert(indexName, action); d->actions.append(action); Q_FOREACH (QWidget *widget, d->associatedWidgets) { widget->addAction(action); } connect(action, SIGNAL(destroyed(QObject*)), SLOT(_k_actionDestroyed(QObject*))); d->setComponentForAction(action); if (d->connectHovered) { connect(action, SIGNAL(hovered()), SLOT(slotActionHovered())); } if (d->connectTriggered) { connect(action, SIGNAL(triggered(bool)), SLOT(slotActionTriggered())); } emit inserted(action); return action; } void KActionCollection::addActions(const QList<QAction *> &actions) { Q_FOREACH (QAction *action, actions) { addAction(action->objectName(), action); } } void KActionCollection::removeAction(QAction *action) { delete takeAction(action); } QAction *KActionCollection::takeAction(QAction *action) { if (!d->unlistAction(action)) { - return NULL; + return nullptr; } // Remove the action from all widgets Q_FOREACH (QWidget *widget, d->associatedWidgets) { widget->removeAction(action); } action->disconnect(this); emit removed(action); //deprecated return action; } QAction *KActionCollection::addAction(KStandardAction::StandardAction actionType, const QObject *receiver, const char *member) { QAction *action = KStandardAction::create(actionType, receiver, member, this); return action; } QAction *KActionCollection::addAction(KStandardAction::StandardAction actionType, const QString &name, const QObject *receiver, const char *member) { // pass 0 as parent, because if the parent is a KActionCollection KStandardAction::create automatically // adds the action to it under the default name. We would trigger the // warning about renaming the action then. - QAction *action = KStandardAction::create(actionType, receiver, member, 0); + QAction *action = KStandardAction::create(actionType, receiver, member, nullptr); // Give it a parent for gc. action->setParent(this); // Remove the name to get rid of the "rename action" warning above action->setObjectName(name); // And now add it with the desired name. return addAction(name, action); } QAction *KActionCollection::addAction(const QString &name, const QObject *receiver, const char *member) { QAction *a = new QAction(this); if (receiver && member) { connect(a, SIGNAL(triggered(bool)), receiver, member); } return addAction(name, a); } QKeySequence KActionCollection::defaultShortcut(QAction *action) const { const QList<QKeySequence> shortcuts = defaultShortcuts(action); return shortcuts.isEmpty() ? QKeySequence() : shortcuts.first(); } QList<QKeySequence> KActionCollection::defaultShortcuts(QAction *action) const { return action->property("defaultShortcuts").value<QList<QKeySequence> >(); } void KActionCollection::setDefaultShortcut(QAction *action, const QKeySequence &shortcut) { setDefaultShortcuts(action, QList<QKeySequence>() << shortcut); } void KActionCollection::setDefaultShortcuts(QAction *action, const QList<QKeySequence> &shortcuts) { action->setShortcuts(shortcuts); action->setProperty("defaultShortcuts", QVariant::fromValue(shortcuts)); } bool KActionCollection::isShortcutsConfigurable(QAction *action) const { // Considered as true by default const QVariant value = action->property("isShortcutConfigurable"); return value.isValid() ? value.toBool() : true; } void KActionCollection::setShortcutsConfigurable(QAction *action, bool configurable) { action->setProperty("isShortcutConfigurable", configurable); } QString KActionCollection::configGroup() const { return d->configGroup; } void KActionCollection::setConfigGroup(const QString &group) { d->configGroup = group; } bool KActionCollection::configIsGlobal() const { return d->configIsGlobal; } void KActionCollection::setConfigGlobal(bool global) { d->configIsGlobal = global; } void KActionCollection::importGlobalShortcuts(KConfigGroup *config) { #if HAVE_GLOBALACCEL Q_ASSERT(config); if (!config || !config->exists()) { return; } for (QMap<QString, QAction *>::ConstIterator it = d->actionByName.constBegin(); it != d->actionByName.constEnd(); ++it) { QAction *action = it.value(); if (!action) { continue; } QString actionName = it.key(); if (isShortcutsConfigurable(action)) { QString entry = config->readEntry(actionName, QString()); if (!entry.isEmpty()) { KGlobalAccel::self()->setShortcut(action, QKeySequence::listFromString(entry), KGlobalAccel::NoAutoloading); } else { QList<QKeySequence> defaultShortcut = KGlobalAccel::self()->defaultShortcut(action); KGlobalAccel::self()->setShortcut(action, defaultShortcut, KGlobalAccel::NoAutoloading); } } } #else Q_UNUSED(config); #endif } void KActionCollection::readSettings(KConfigGroup *config) { KConfigGroup cg(KSharedConfig::openConfig(), configGroup()); if (!config) { config = &cg; } if (!config->exists()) { return; } for (QMap<QString, QAction *>::ConstIterator it = d->actionByName.constBegin(); it != d->actionByName.constEnd(); ++it) { QAction *action = it.value(); if (!action) { continue; } if (isShortcutsConfigurable(action)) { QString actionName = it.key(); QString entry = config->readEntry(actionName, QString()); if (!entry.isEmpty()) { action->setShortcuts(QKeySequence::listFromString(entry)); } else { action->setShortcuts(defaultShortcuts(action)); } } } //qCDebug(DEBUG_KXMLGUI) << " done"; } void KActionCollection::exportGlobalShortcuts(KConfigGroup *config, bool writeAll) const { #if HAVE_GLOBALACCEL Q_ASSERT(config); if (!config) { return; } QList<QAction *> writeActions = actions(); for (QMap<QString, QAction *>::ConstIterator it = d->actionByName.constBegin(); it != d->actionByName.constEnd(); ++it) { QAction *action = it.value(); if (!action) { continue; } QString actionName = it.key(); // If the action name starts with unnamed- spit out a warning. That name // will change at will and will break loading writing if (actionName.startsWith(QLatin1String("unnamed-"))) { qCritical() << "Skipped exporting Shortcut for action without name " << action->text() << "!"; continue; } if (isShortcutsConfigurable(action) && KGlobalAccel::self()->hasShortcut(action)) { bool bConfigHasAction = !config->readEntry(actionName, QString()).isEmpty(); bool bSameAsDefault = (KGlobalAccel::self()->shortcut(action) == KGlobalAccel::self()->defaultShortcut(action)); // If we're using a global config or this setting // differs from the default, then we want to write. KConfigGroup::WriteConfigFlags flags = KConfigGroup::Persistent; if (configIsGlobal()) { flags |= KConfigGroup::Global; } if (writeAll || !bSameAsDefault) { QString s = QKeySequence::listToString(KGlobalAccel::self()->shortcut(action)); if (s.isEmpty()) { s = QStringLiteral("none"); } qCDebug(DEBUG_KXMLGUI) << "\twriting " << actionName << " = " << s; config->writeEntry(actionName, s, flags); } // Otherwise, this key is the same as default // but exists in config file. Remove it. else if (bConfigHasAction) { qCDebug(DEBUG_KXMLGUI) << "\tremoving " << actionName << " because == default"; config->deleteEntry(actionName, flags); } } } config->sync(); #else Q_UNUSED(config); Q_UNUSED(writeAll); #endif } bool KActionCollectionPrivate::writeKXMLGUIConfigFile() { const KXMLGUIClient *kxmlguiClient = q->parentGUIClient(); // return false if there is no KXMLGUIClient if (!kxmlguiClient || kxmlguiClient->xmlFile().isEmpty()) { return false; } qCDebug(DEBUG_KXMLGUI) << "xmlFile=" << kxmlguiClient->xmlFile(); QString attrShortcut = QStringLiteral("shortcut"); // Read XML file QString sXml(KXMLGUIFactory::readConfigFile(kxmlguiClient->xmlFile(), q->componentName())); QDomDocument doc; doc.setContent(sXml); // Process XML data // Get hold of ActionProperties tag QDomElement elem = KXMLGUIFactory::actionPropertiesElement(doc); // now, iterate through our actions for (QMap<QString, QAction *>::ConstIterator it = actionByName.constBegin(); it != actionByName.constEnd(); ++it) { QAction *action = it.value(); if (!action) { continue; } QString actionName = it.key(); // If the action name starts with unnamed- spit out a warning and ignore // it. That name will change at will and will break loading writing if (actionName.startsWith(QLatin1String("unnamed-"))) { qCritical() << "Skipped writing shortcut for action " << actionName << "(" << action->text() << ")!"; continue; } bool bSameAsDefault = (action->shortcuts() == q->defaultShortcuts(action)); qCDebug(DEBUG_KXMLGUI) << "name = " << actionName << " shortcut = " << QKeySequence::listToString(action->shortcuts()) #if HAVE_GLOBALACCEL << " globalshortcut = " << QKeySequence::listToString(KGlobalAccel::self()->shortcut(action)) #endif << " def = " << QKeySequence::listToString(q->defaultShortcuts(action)); // now see if this element already exists // and create it if necessary (unless bSameAsDefault) QDomElement act_elem = KXMLGUIFactory::findActionByName(elem, actionName, !bSameAsDefault); if (act_elem.isNull()) { continue; } if (bSameAsDefault) { act_elem.removeAttribute(attrShortcut); //qCDebug(DEBUG_KXMLGUI) << "act_elem.attributes().count() = " << act_elem.attributes().count(); if (act_elem.attributes().count() == 1) { elem.removeChild(act_elem); } } else { act_elem.setAttribute(attrShortcut, QKeySequence::listToString(action->shortcuts())); } } // Write back to XML file KXMLGUIFactory::saveConfigFile(doc, kxmlguiClient->localXMLFile(), q->componentName()); return true; } void KActionCollection::writeSettings(KConfigGroup *config, bool writeAll, QAction *oneAction) const { // If the caller didn't provide a config group we try to save the KXMLGUI // Configuration file. If that succeeds we are finished. - if (config == 0 && d->writeKXMLGUIConfigFile()) { + if (config == nullptr && d->writeKXMLGUIConfigFile()) { return; } KConfigGroup cg(KSharedConfig::openConfig(), configGroup()); if (!config) { config = &cg; } QList<QAction *> writeActions; if (oneAction) { writeActions.append(oneAction); } else { writeActions = actions(); } for (QMap<QString, QAction *>::ConstIterator it = d->actionByName.constBegin(); it != d->actionByName.constEnd(); ++it) { QAction *action = it.value(); if (!action) { continue; } QString actionName = it.key(); // If the action name starts with unnamed- spit out a warning and ignore // it. That name will change at will and will break loading writing if (actionName.startsWith(QLatin1String("unnamed-"))) { qCritical() << "Skipped saving Shortcut for action without name " << action->text() << "!"; continue; } // Write the shortcut if (isShortcutsConfigurable(action)) { bool bConfigHasAction = !config->readEntry(actionName, QString()).isEmpty(); bool bSameAsDefault = (action->shortcuts() == defaultShortcuts(action)); // If we're using a global config or this setting // differs from the default, then we want to write. KConfigGroup::WriteConfigFlags flags = KConfigGroup::Persistent; // Honor the configIsGlobal() setting if (configIsGlobal()) { flags |= KConfigGroup::Global; } if (writeAll || !bSameAsDefault) { // We are instructed to write all shortcuts or the shortcut is // not set to its default value. Write it QString s = QKeySequence::listToString(action->shortcuts()); if (s.isEmpty()) { s = QStringLiteral("none"); } qCDebug(DEBUG_KXMLGUI) << "\twriting " << actionName << " = " << s; config->writeEntry(actionName, s, flags); } else if (bConfigHasAction) { // Otherwise, this key is the same as default but exists in // config file. Remove it. qCDebug(DEBUG_KXMLGUI) << "\tremoving " << actionName << " because == default"; config->deleteEntry(actionName, flags); } } } config->sync(); } void KActionCollection::slotActionTriggered() { QAction *action = qobject_cast<QAction *>(sender()); if (action) { emit actionTriggered(action); } } void KActionCollection::slotActionHighlighted() { slotActionHovered(); } void KActionCollection::slotActionHovered() { QAction *action = qobject_cast<QAction *>(sender()); if (action) { emit actionHighlighted(action); emit actionHovered(action); } } void KActionCollectionPrivate::_k_actionDestroyed(QObject *obj) { // obj isn't really a QAction anymore. So make sure we don't do fancy stuff // with it. QAction *action = static_cast<QAction *>(obj); if (!unlistAction(action)) { return; } //HACK the object we emit is partly destroyed emit q->removed(action); //deprecated. remove in KDE5 } void KActionCollection::connectNotify(const QMetaMethod &signal) { if (d->connectHovered && d->connectTriggered) { return; } if (signal.methodSignature() == "actionHighlighted(QAction*)" || signal.methodSignature() == "actionHovered(QAction*)") { if (!d->connectHovered) { d->connectHovered = true; Q_FOREACH (QAction *action, actions()) { connect(action, SIGNAL(hovered()), SLOT(slotActionHovered())); } } } else if (signal.methodSignature() == "actionTriggered(QAction*)") { if (!d->connectTriggered) { d->connectTriggered = true; Q_FOREACH (QAction *action, actions()) { connect(action, SIGNAL(triggered(bool)), SLOT(slotActionTriggered())); } } } QObject::connectNotify(signal); } const QList< KActionCollection * > &KActionCollection::allCollections() { return KActionCollectionPrivate::s_allCollections; } void KActionCollection::associateWidget(QWidget *widget) const { Q_FOREACH (QAction *action, actions()) { if (!widget->actions().contains(action)) { widget->addAction(action); } } } void KActionCollection::addAssociatedWidget(QWidget *widget) { if (!d->associatedWidgets.contains(widget)) { widget->addActions(actions()); d->associatedWidgets.append(widget); connect(widget, SIGNAL(destroyed(QObject*)), this, SLOT(_k_associatedWidgetDestroyed(QObject*))); } } void KActionCollection::removeAssociatedWidget(QWidget *widget) { Q_FOREACH (QAction *action, actions()) { widget->removeAction(action); } d->associatedWidgets.removeAll(widget); disconnect(widget, SIGNAL(destroyed(QObject*)), this, SLOT(_k_associatedWidgetDestroyed(QObject*))); } QAction *KActionCollectionPrivate::unlistAction(QAction *action) { // ATTENTION: // This method is called with an QObject formerly known as a QAction // during _k_actionDestroyed(). So don't do fancy stuff here that needs a // real QAction! // Get the index for the action int index = actions.indexOf(action); // Action not found. if (index == -1) { - return NULL; + return nullptr; } // An action collection can't have the same action twice. Q_ASSERT(actions.indexOf(action, index + 1) == -1); // Get the actions name const QString name = action->objectName(); // Remove the action actionByName.remove(name); actions.removeAt(index); // Remove the action from the categories. Should be only one QList<KActionCategory *> categories = q->findChildren<KActionCategory *>(); Q_FOREACH (KActionCategory *category, categories) { category->unlistAction(action); } return action; } QList< QWidget * > KActionCollection::associatedWidgets() const { return d->associatedWidgets; } void KActionCollection::clearAssociatedWidgets() { Q_FOREACH (QWidget *widget, d->associatedWidgets) Q_FOREACH (QAction *action, actions()) { widget->removeAction(action); } d->associatedWidgets.clear(); } void KActionCollectionPrivate::_k_associatedWidgetDestroyed(QObject *obj) { associatedWidgets.removeAll(static_cast<QWidget *>(obj)); } #include "moc_kactioncollection.cpp" diff --git a/src/kactioncollection.h b/src/kactioncollection.h index 5b1543f..a9cb9bf 100644 --- a/src/kactioncollection.h +++ b/src/kactioncollection.h @@ -1,560 +1,560 @@ /* This file is part of the KDE libraries Copyright (C) 1999 Reginald Stadlbauer <reggie@kde.org> (C) 1999 Simon Hausmann <hausmann@kde.org> (C) 2000 Nicolas Hadacek <haadcek@kde.org> (C) 2000 Kurt Granroth <granroth@kde.org> (C) 2000 Michael Koch <koch@kde.org> (C) 2001 Holger Freyther <freyther@kde.org> (C) 2002 Ellis Whitehead <ellis@kde.org> (C) 2005-2006 Hamish Rodda <rodda@kde.org> 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 KACTIONCOLLECTION_H #define KACTIONCOLLECTION_H #include <kxmlgui_export.h> #include <kstandardaction.h> #include <QObject> #include <QAction> class QAction; class KXMLGUIClient; class KConfigGroup; class QActionGroup; class QString; /** * \short A container for a set of QAction objects. * * KActionCollection manages a set of QAction objects. It * allows them to be grouped for organized presentation of configuration to the user, * saving + loading of configuration, and optionally for automatic plugging into * specified widget(s). * * Additionally, KActionCollection provides several convenience functions for locating * named actions, and actions grouped by QActionGroup. * * \note If you create your own action collection and need to assign shortcuts * to the actions within, you have to call associateWidget() or * addAssociatedWidget() to have them working. */ class KXMLGUI_EXPORT KActionCollection : public QObject { friend class KXMLGUIClient; Q_OBJECT Q_PROPERTY(QString configGroup READ configGroup WRITE setConfigGroup) Q_PROPERTY(bool configIsGlobal READ configIsGlobal WRITE setConfigGlobal) public: /** * Constructor. Allows specification of a component name other than the default * application name, where needed (remember to call setComponentDisplayName() too). */ explicit KActionCollection(QObject *parent, const QString &cName = QString()); /** * Destructor. */ virtual ~KActionCollection(); /** * Access the list of all action collections in existence for this app */ static const QList<KActionCollection *> &allCollections(); /** * Clears the entire action collection, deleting all actions. */ void clear(); /** * Associate all actions in this collection to the given \a widget. * Unlike addAssociatedWidget, this method only adds all current actions * in the collection to the given widget. Any action added after this call * will not be added to the given widget automatically. * So this is just a shortcut for a foreach loop and a widget->addAction call. */ void associateWidget(QWidget *widget) const; /** * Associate all actions in this collection to the given \a widget, including any actions * added after this association is made. * * This does not change the action's shortcut context, so if you need to have the actions only * trigger when the widget has focus, you'll need to set the shortcut context on each action * to Qt::WidgetShortcut (or better still, Qt::WidgetWithChildrenShortcut with Qt 4.4+) */ void addAssociatedWidget(QWidget *widget); /** * Remove an association between all actions in this collection and the given \a widget, i.e. * remove those actions from the widget, and stop associating newly added actions as well. */ void removeAssociatedWidget(QWidget *widget); /** * Return a list of all associated widgets. */ QList<QWidget *> associatedWidgets() const; /** * Clear all associated widgets and remove the actions from those widgets. */ void clearAssociatedWidgets(); /** * Returns the KConfig group with which settings will be loaded and saved. */ QString configGroup() const; /** * Returns whether this action collection's configuration should be global to KDE ( \e true ), * or specific to the application ( \e false ). */ bool configIsGlobal() const; /** * Sets \a group as the KConfig group with which settings will be loaded and saved. */ void setConfigGroup(const QString &group); /** * Set whether this action collection's configuration should be global to KDE ( \e true ), * or specific to the application ( \e false ). */ void setConfigGlobal(bool global); /** * Read all key associations from @p config. * * If @p config is zero, read all key associations from the * application's configuration file KSharedConfig::openConfig(), * in the group set by setConfigGroup(). */ - void readSettings(KConfigGroup *config = 0); + void readSettings(KConfigGroup *config = nullptr); /** * Import from @p config all configurable global key associations. * * \since 4.1 * * \param config Config object to read from */ void importGlobalShortcuts(KConfigGroup *config); /** * Export the current configurable global key associations to @p config. * * \since 4.1 * * \param config Config object to save to * \param writeDefaults set to true to write settings which are already at defaults. */ void exportGlobalShortcuts(KConfigGroup *config, bool writeDefaults = false) const; /** * Write the current configurable key associations to @a config. What the * function does if @a config is zero depends. If this action collection * belongs to a KXMLGuiClient the setting are saved to the kxmlgui * definition file. If not the settings are written to the applications * config file. * * \note oneAction() and writeDefaults() have no meaning for the kxmlgui * configuration file. * * \param config Config object to save to, or null (see above) * \param writeDefaults set to true to write settings which are already at defaults. * \param oneAction pass an action here if you just want to save the values for one action, eg. * if you know that action is the only one which has changed. */ - void writeSettings(KConfigGroup *config = 0, bool writeDefaults = false, QAction *oneAction = 0) const; + void writeSettings(KConfigGroup *config = nullptr, bool writeDefaults = false, QAction *oneAction = nullptr) const; /** * Returns the number of actions in the collection. * * This is equivalent to actions().count(). */ int count() const; /** * Returns whether the action collection is empty or not. */ bool isEmpty() const; /** * Return the QAction* at position "index" in the action collection. * * This is equivalent to actions().value(index); */ QAction *action(int index) const; /** * Get the action with the given \a name from the action collection. * * @param name Name of the QAction * @return A pointer to the QAction in the collection which matches the parameters or * null if nothing matches. */ QAction *action(const QString &name) const; /** * Returns the list of QActions which belong to this action collection. * * The list is guaranteed to be in the same order the action were put into * the collection. */ QList<QAction *> actions() const; /** * Returns the list of QActions without an QAction::actionGroup() which belong to this action collection. */ const QList<QAction *> actionsWithoutGroup() const; /** * Returns the list of all QActionGroups associated with actions in this action collection. */ const QList<QActionGroup *> actionGroups() const; /** * Set the \a componentName associated with this action collection. * * \warning Don't call this method on a KActionCollection that contains * actions. This is not supported. * * \param componentData the name which is to be associated with this action collection, * or QString() to indicate the app name. This is used to load/save settings into XML files. * KXmlGuiClient::setComponentName takes care of calling this. */ void setComponentName(const QString &componentName); /** The component name with which this class is associated. */ QString componentName() const; /** * Set the component display name associated with this action collection. * (e.g. for the toolbar editor) * KXmlGuiClient::setComponentName takes care of calling this. */ void setComponentDisplayName(const QString &displayName); /** The display name for the associated component. */ QString componentDisplayName() const; /** * The parent KXMLGUIClient, or null if not available. */ const KXMLGUIClient *parentGUIClient() const; Q_SIGNALS: /** * Indicates that \a action was inserted into this action collection. */ void inserted(QAction *action); /** * Indicates that \a action was removed from this action collection. * @deprecated */ QT_MOC_COMPAT void removed(QAction *action); /** * Indicates that \a action was highlighted (hovered over). * @deprecated Replaced by actionHovered(QAction* action); */ QT_MOC_COMPAT void actionHighlighted(QAction *action); /** * Indicates that \a action was hovered. */ void actionHovered(QAction *action); /** * Indicates that \a action was triggered */ void actionTriggered(QAction *action); protected: /// Overridden to perform connections when someone wants to know whether an action was highlighted or triggered void connectNotify(const QMetaMethod &signal) Q_DECL_OVERRIDE; protected Q_SLOTS: virtual void slotActionTriggered(); /** * @internal * @deprecated Replaced by slotActionHovered(); */ QT_MOC_COMPAT virtual void slotActionHighlighted(); private Q_SLOTS: void slotActionHovered(); public: /** * Add an action under the given name to the collection. * * Inserting an action that was previously inserted under a different name will replace the * old entry, i.e. the action will not be available under the old name anymore but only under * the new one. * * Inserting an action under a name that is already used for another action will replace * the other action in the collection (but will not delete it). * * If KAuthorized::authorizeAction() reports that the action is not * authorized, it will be disabled and hidden. * * @param name The name by which the action be retrieved again from the collection. * @param action The action to add. * @return the same as the action given as parameter. This is just for convenience * (chaining calls) and consistency with the other addAction methods, you can also * simply ignore the return value. */ Q_INVOKABLE QAction *addAction(const QString &name, QAction *action); /** * Adds a list of actions to the collection. * * The objectName of the actions is used as their internal name in the collection. * * Uses addAction(QString, QAction*). * * @param actions the list of the actions to add. * * @see addAction() * @since 5.0 */ void addActions(const QList<QAction *> &actions); /** * Removes an action from the collection and deletes it. * @param action The action to remove. */ void removeAction(QAction *action); /** * Removes an action from the collection. * @param action the action to remove. */ QAction *takeAction(QAction *action); /** * Creates a new standard action, adds it to the collection and connects the * action's triggered(bool) signal to the specified receiver/member. The * newly created action is also returned. * * Note: Using KStandardAction::OpenRecent will cause a different signal than * triggered(bool) to be used, see KStandardAction for more information. * * The action can be retrieved later from the collection by its standard name as per * KStandardAction::stdName. * * @param actionType The standard action type of the action to create. * @param receiver The QObject to connect the triggered(bool) signal to. Leave 0 if no * connection is desired. * @param member The SLOT to connect the triggered(bool) signal to. Leave 0 if no * connection is desired. * @return new action of the given type ActionType. */ - QAction *addAction(KStandardAction::StandardAction actionType, const QObject *receiver = 0, const char *member = 0); + QAction *addAction(KStandardAction::StandardAction actionType, const QObject *receiver = nullptr, const char *member = nullptr); /** * Creates a new standard action, adds to the collection under the given name * and connects the action's triggered(bool) signal to the specified * receiver/member. The newly created action is also returned. * * Note: Using KStandardAction::OpenRecent will cause a different signal than * triggered(bool) to be used, see KStandardAction for more information. * * The action can be retrieved later from the collection by the specified name. * * @param actionType The standard action type of the action to create. * @param name The name by which the action be retrieved again from the collection. * @param receiver The QObject to connect the triggered(bool) signal to. Leave 0 if no * connection is desired. * @param member The SLOT to connect the triggered(bool) signal to. Leave 0 if no * connection is desired. * @return new action of the given type ActionType. */ QAction *addAction(KStandardAction::StandardAction actionType, const QString &name, - const QObject *receiver = 0, const char *member = 0); + const QObject *receiver = nullptr, const char *member = nullptr); /** * Creates a new action under the given name to the collection and connects * the action's triggered(bool) signal to the specified receiver/member. The * newly created action is returned. * * NOTE: KDE prior to 4.2 used the triggered() signal instead of the triggered(bool) * signal. * * Inserting an action that was previously inserted under a different name will replace the * old entry, i.e. the action will not be available under the old name anymore but only under * the new one. * * Inserting an action under a name that is already used for another action will replace * the other action in the collection. * * @param name The name by which the action be retrieved again from the collection. * @param receiver The QObject to connect the triggered(bool) signal to. Leave 0 if no * connection is desired. * @param member The SLOT to connect the triggered(bool) signal to. Leave 0 if no * connection is desired. * @return new action of the given type ActionType. */ - QAction *addAction(const QString &name, const QObject *receiver = 0, const char *member = 0); + QAction *addAction(const QString &name, const QObject *receiver = nullptr, const char *member = nullptr); /** * Creates a new action under the given name, adds it to the collection and connects the action's triggered(bool) * signal to the specified receiver/member. The receiver slot may accept either a bool or no * parameters at all (i.e. slotTriggered(bool) or slotTriggered() ). * The type of the action is specified by the template parameter ActionType. * * NOTE: KDE prior to 4.2 connected the triggered() signal instead of the triggered(bool) * signal. * * @param name The internal name of the action (e.g. "file-open"). * @param receiver The QObject to connect the triggered(bool) signal to. Leave 0 if no * connection is desired. * @param member The SLOT to connect the triggered(bool) signal to. Leave 0 if no * connection is desired. * @return new action of the given type ActionType. * * @see addAction() */ template<class ActionType> - ActionType *add(const QString &name, const QObject *receiver = 0, const char *member = 0) + ActionType *add(const QString &name, const QObject *receiver = nullptr, const char *member = nullptr) { ActionType *a = new ActionType(this); if (receiver && member) { connect(a, SIGNAL(triggered(bool)), receiver, member); } addAction(name, a); return a; } /** * This is the same as add(const QString &name, const QObject *receiver = 0, const char *member = 0) using * new style connect syntax * * @param name The internal name of the action (e.g. "file-open"). * @param receiver The QObject to connect the triggered(bool) signal to. * @param slot The slot or lambda to connect the triggered(bool) signal to. * @return new action of the given type ActionType. * * @see add(const QString &, const QObject *, const char *) * @since 5.28 */ template<class ActionType, class Receiver, class Func> inline typename std::enable_if<!std::is_convertible<Func, const char*>::value, ActionType>::type *add( const QString &name, const Receiver *receiver, Func slot) { ActionType *a = new ActionType(this); connect(a, &QAction::triggered, receiver, slot); addAction(name, a); return a; } /** * This is the same as addAction(const QString &name, const QObject *receiver = 0, const char *member = 0) using * new style connect syntax * * @param name The internal name of the action (e.g. "file-open"). * @param receiver The QObject to connect the triggered(bool) signal to. * @param slot The slot or lambda to connect the triggered(bool) signal to. * @return new action of the given type ActionType. * * @see addAction(const QString &, const QObject *, const char *) * @since 5.28 */ template<class Receiver, class Func> inline typename std::enable_if<!std::is_convertible<Func, const char*>::value, QAction>::type *addAction( const QString &name, const Receiver *receiver, Func slot) { return add<QAction>(name, receiver, slot); } /** * Get the default primary shortcut for the given action. * * @param action the action for which the default primary shortcut should be returned. * @return the default primary shortcut of the given action * @since 5.0 */ QKeySequence defaultShortcut(QAction *action) const; /** * Get the default shortcuts for the given action. * * @param action the action for which the default shortcuts should be returned. * @return the default shortcuts of the given action * @since 5.0 */ QList<QKeySequence> defaultShortcuts(QAction *action) const; //TODO KF6: Make setDefaultShortcut static /** * Set the default shortcut for the given action. * Since 5.2, this also calls action->setShortcut(shortcut), i.e. the default shortcut is * made active initially. * * @param action the action for which the default shortcut should be set. * @param shortcut the shortcut to use for the given action in its specified shortcutContext() * @since 5.0 */ void setDefaultShortcut(QAction *action, const QKeySequence &shortcut); /** * Set the default shortcuts for the given action. * Since 5.2, this also calls action->setShortcuts(shortcuts), i.e. the default shortcut is * made active initially. * * @param action the action for which the default shortcut should be set. * @param shortcuts the shortcuts to use for the given action in its specified shortcutContext() * @since 5.0 */ Q_INVOKABLE void setDefaultShortcuts(QAction *action, const QList<QKeySequence> &shortcuts); /** * Returns true if the given action's shortcuts may be configured by the user. * * @param action the action for the hint should be verified. * @since 5.0 */ bool isShortcutsConfigurable(QAction *action) const; /** * Indicate whether the user may configure the action's shortcuts. * * @param action the action for the hint should be verified. * @param configurable set to true if the shortcuts of the given action may be configured by the user, otherwise false. * @since 5.0 */ void setShortcutsConfigurable(QAction *action, bool configurable); private: Q_PRIVATE_SLOT(d, void _k_actionDestroyed(QObject *)) Q_PRIVATE_SLOT(d, void _k_associatedWidgetDestroyed(QObject *)) KActionCollection(const KXMLGUIClient *parent); // used by KXMLGUIClient friend class KActionCollectionPrivate; class KActionCollectionPrivate *const d; }; #endif diff --git a/src/kactionconflictdetector.cpp b/src/kactionconflictdetector.cpp index 34eca51..174018c 100644 --- a/src/kactionconflictdetector.cpp +++ b/src/kactionconflictdetector.cpp @@ -1,68 +1,68 @@ /* This file is part of the KDE libraries Copyright (C) 1999 Reginald Stadlbauer <reggie@kde.org> (C) 1999 Simon Hausmann <hausmann@kde.org> (C) 2000 Nicolas Hadacek <haadcek@kde.org> (C) 2000 Kurt Granroth <granroth@kde.org> (C) 2000 Michael Koch <koch@kde.org> (C) 2001 Holger Freyther <freyther@kde.org> (C) 2002 Ellis Whitehead <ellis@kde.org> (C) 2002 Joseph Wenninger <jowenn@kde.org> (C) 2005-2006 Hamish Rodda <rodda@kde.org> 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 <QAction> #include <QCoreApplication> #include <QShortcutEvent> #include <klocalizedstring.h> #include <kmessagebox.h> class KActionConflictDetector : public QObject { public: - explicit KActionConflictDetector(QObject *parent = 0) + explicit KActionConflictDetector(QObject *parent = nullptr) : QObject(parent) { } bool eventFilter(QObject *watched, QEvent *event) Q_DECL_OVERRIDE { if (qobject_cast<QAction *>(watched) && (event->type() == QEvent::Shortcut)) { QShortcutEvent *se = static_cast<QShortcutEvent *>(event); if (se->isAmbiguous()) { KMessageBox::information( Q_NULLPTR, // No widget to be seen around here i18n("The key sequence '%1' is ambiguous. Use 'Configure Shortcuts'\n" "from the 'Settings' menu to solve the ambiguity.\n" "No action will be triggered.", se->key().toString(QKeySequence::NativeText)), i18n("Ambiguous shortcut detected")); return true; } } return QObject::eventFilter(watched, event); } }; void _k_installConflictDetector() { QCoreApplication *app = QCoreApplication::instance(); app->installEventFilter(new KActionConflictDetector(app)); } Q_COREAPP_STARTUP_FUNCTION(_k_installConflictDetector) diff --git a/src/kbugreport.cpp b/src/kbugreport.cpp index 7b18f8a..da91f3e 100644 --- a/src/kbugreport.cpp +++ b/src/kbugreport.cpp @@ -1,557 +1,557 @@ /* This file is part of the KDE project Copyright (C) 1999 David Faure <faure@kde.org> 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 "kbugreport.h" #include <QtCore/QProcess> #include <QtCore/QCoreApplication> #include <QDialogButtonBox> #include <QPushButton> #include <QLayout> #include <QRadioButton> #include <QGroupBox> #include <QLocale> #include <QCloseEvent> #include <QLabel> #include <QUrl> #include <QUrlQuery> #include <QDesktopServices> #include <QComboBox> #include <QLineEdit> #include <QStandardPaths> #include <QDebug> #include <kaboutdata.h> #include <kconfig.h> #include <kconfiggroup.h> #include <kemailsettings.h> #include <klocalizedstring.h> #include <kmessagebox.h> #include <ktextedit.h> #include <ktitlewidget.h> #include "kdepackages.h" #include "../kxmlgui_version.h" #include "systeminformation_p.h" #include "config-xmlgui.h" class KBugReportPrivate { public: KBugReportPrivate(KBugReport *q): q(q), m_aboutData(KAboutData::applicationData()) {} void _k_slotConfigureEmail(); void _k_slotSetFrom(); void _k_appChanged(int); void _k_updateUrl(); KBugReport *q; QProcess *m_process; KAboutData m_aboutData; KTextEdit *m_lineedit; QLineEdit *m_subject; QLabel *m_from; QLabel *m_version; QString m_strVersion; QGroupBox *m_bgSeverity; QPushButton *m_configureEmail; QComboBox *appcombo; QString lastError; QString kde_version; QString appname; QString os; QUrl url; QList<QRadioButton *> severityButtons; int currentSeverity() { for (int i = 0; i < severityButtons.count(); i++) if (severityButtons[i]->isChecked()) { return i; } return -1; } bool submitBugWeb; }; KBugReport::KBugReport(const KAboutData &aboutData, QWidget *_parent) : QDialog(_parent), d(new KBugReportPrivate(this)) { setWindowTitle(i18n("Submit Bug Report")); QDialogButtonBox *buttonBox = new QDialogButtonBox(this); buttonBox->setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept())); connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); d->m_aboutData = aboutData; - d->m_process = 0; + d->m_process = nullptr; d->submitBugWeb = false; if (d->m_aboutData.bugAddress() == QStringLiteral("submit@bugs.kde.org")) { // This is a core KDE application -> redirect to the web form d->submitBugWeb = true; KGuiItem::assign(buttonBox->button(QDialogButtonBox::Cancel), KStandardGuiItem::close()); } QLabel *tmpLabel; QVBoxLayout *lay = new QVBoxLayout; setLayout(lay); KTitleWidget *title = new KTitleWidget(this); title->setText(i18n("Submit Bug Report")); title->setPixmap(QIcon::fromTheme(QStringLiteral("tools-report-bug")).pixmap(32)); lay->addWidget(title); QGridLayout *glay = new QGridLayout(); lay->addLayout(glay); int row = 0; if (!d->submitBugWeb) { // From QString qwtstr = i18n("Your email address. If incorrect, use the Configure Email button to change it"); tmpLabel = new QLabel(i18nc("Email sender address", "From:"), this); glay->addWidget(tmpLabel, row, 0); tmpLabel->setWhatsThis(qwtstr); d->m_from = new QLabel(this); glay->addWidget(d->m_from, row, 1); d->m_from->setWhatsThis(qwtstr); // Configure email button d->m_configureEmail = new QPushButton(i18n("Configure Email..."), this); connect(d->m_configureEmail, SIGNAL(clicked()), this, SLOT(_k_slotConfigureEmail())); glay->addWidget(d->m_configureEmail, 0, 2, 3, 1, Qt::AlignTop | Qt::AlignRight); // To qwtstr = i18n("The email address this bug report is sent to."); tmpLabel = new QLabel(i18nc("Email receiver address", "To:"), this); glay->addWidget(tmpLabel, ++row, 0); tmpLabel->setWhatsThis(qwtstr); tmpLabel = new QLabel(d->m_aboutData.bugAddress(), this); tmpLabel->setTextInteractionFlags(Qt::TextBrowserInteraction); glay->addWidget(tmpLabel, row, 1); tmpLabel->setWhatsThis(qwtstr); KGuiItem::assign(buttonBox->button(QDialogButtonBox::Ok), KGuiItem(i18n("&Send"), QStringLiteral("mail-send"), i18n("Send bug report."), i18n("Send this bug report to %1.", d->m_aboutData.bugAddress()))); row++; } else { - d->m_configureEmail = 0; - d->m_from = 0; + d->m_configureEmail = nullptr; + d->m_from = nullptr; } // Program name QString qwtstr = i18n("The application for which you wish to submit a bug report - if incorrect, please use the Report Bug menu item of the correct application"); tmpLabel = new QLabel(i18n("Application: "), this); glay->addWidget(tmpLabel, row, 0); tmpLabel->setWhatsThis(qwtstr); d->appcombo = new QComboBox(this); d->appcombo->setWhatsThis(qwtstr); QStringList packageList; for (int c = 0; packages[c]; ++c) { packageList << QString::fromLatin1(packages[c]); } d->appcombo->addItems(packageList); connect(d->appcombo, SIGNAL(activated(int)), SLOT(_k_appChanged(int))); d->appname = d->m_aboutData.productName(); glay->addWidget(d->appcombo, row, 1); int index = 0; for (; index < d->appcombo->count(); index++) { if (d->appcombo->itemText(index) == d->appname) { break; } } if (index == d->appcombo->count()) { // not present d->appcombo->addItem(d->appname); } d->appcombo->setCurrentIndex(index); tmpLabel->setWhatsThis(qwtstr); // Version qwtstr = i18n("The version of this application - please make sure that no newer version is available before sending a bug report"); tmpLabel = new QLabel(i18n("Version:"), this); glay->addWidget(tmpLabel, ++row, 0); tmpLabel->setWhatsThis(qwtstr); d->m_strVersion = d->m_aboutData.version(); if (d->m_strVersion.isEmpty()) { d->m_strVersion = i18n("no version set (programmer error)"); } d->kde_version = QStringLiteral(KXMLGUI_VERSION_STRING); d->kde_version += QStringLiteral(", ") + QLatin1String(XMLGUI_DISTRIBUTION_TEXT); if (!d->submitBugWeb) { d->m_strVersion += QLatin1Char(' ') + d->kde_version; } d->m_version = new QLabel(d->m_strVersion, this); d->m_version->setTextInteractionFlags(Qt::TextBrowserInteraction); //glay->addWidget( d->m_version, row, 1 ); glay->addWidget(d->m_version, row, 1, 1, 2); d->m_version->setWhatsThis(qwtstr); tmpLabel = new QLabel(i18n("OS:"), this); glay->addWidget(tmpLabel, ++row, 0); d->os = SystemInformation::operatingSystemVersion(); tmpLabel = new QLabel(d->os, this); tmpLabel->setTextInteractionFlags(Qt::TextBrowserInteraction); glay->addWidget(tmpLabel, row, 1, 1, 2); tmpLabel = new QLabel(i18n("Compiler:"), this); glay->addWidget(tmpLabel, ++row, 0); tmpLabel = new QLabel(QLatin1String(XMLGUI_COMPILER_VERSION), this); tmpLabel->setTextInteractionFlags(Qt::TextBrowserInteraction); glay->addWidget(tmpLabel, row, 1, 1, 2); if (!d->submitBugWeb) { // Severity d->m_bgSeverity = new QGroupBox(i18n("Se&verity"), this); static const char *const sevNames[5] = { "critical", "grave", "normal", "wishlist", "i18n" }; const QString sevTexts[5] = { i18n("Critical"), i18n("Grave"), i18nc("normal severity", "Normal"), i18n("Wishlist"), i18n("Translation") }; QHBoxLayout *severityLayout = new QHBoxLayout(d->m_bgSeverity); for (int i = 0; i < 5; i++) { // Store the severity string as the name QRadioButton *rb = new QRadioButton(sevTexts[i], d->m_bgSeverity); rb->setObjectName(QLatin1String(sevNames[i])); d->severityButtons.append(rb); severityLayout->addWidget(rb); if (i == 2) { rb->setChecked(true); // default : "normal" } } lay->addWidget(d->m_bgSeverity); // Subject QHBoxLayout *hlay = new QHBoxLayout(); lay->addItem(hlay); tmpLabel = new QLabel(i18n("S&ubject: "), this); hlay->addWidget(tmpLabel); d->m_subject = new QLineEdit(this); d->m_subject->setClearButtonEnabled(true); d->m_subject->setFocus(); tmpLabel->setBuddy(d->m_subject); hlay->addWidget(d->m_subject); QString text = i18n("Enter the text (in English if possible) that you wish to submit for the " "bug report.\n" "If you press \"Send\", a mail message will be sent to the maintainer of " "this program.\n"); QLabel *label = new QLabel(this); label->setText(text); lay->addWidget(label); // The multiline-edit d->m_lineedit = new KTextEdit(this); d->m_lineedit->setMinimumHeight(180); // make it big d->m_lineedit->setWordWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere); d->m_lineedit->setLineWrapMode(QTextEdit::WidgetWidth); d->m_lineedit->setCheckSpellingEnabled(true); d->m_lineedit->setSpellCheckingLanguage(QStringLiteral("en")); lay->addWidget(d->m_lineedit, 10 /*stretch*/); d->_k_slotSetFrom(); } else { // Point to the web form lay->addSpacing(10); QString text = i18n("<qt>To submit a bug report, click on the button below. This will open a web browser " "window on <a href=\"http://bugs.kde.org\">http://bugs.kde.org</a> where you will find " "a form to fill in. The information displayed above will be transferred to that server.</qt>"); QLabel *label = new QLabel(text, this); label->setOpenExternalLinks(true); label->setTextInteractionFlags(Qt::LinksAccessibleByMouse | Qt::LinksAccessibleByKeyboard); label->setWordWrap(true); lay->addWidget(label); lay->addSpacing(10); d->appcombo->setFocus(); d->_k_updateUrl(); QPushButton *okButton = buttonBox->button(QDialogButtonBox::Ok); okButton->setText(i18n("&Launch Bug Report Wizard")); okButton->setIcon(QIcon::fromTheme(QStringLiteral("tools-report-bug"))); } lay->addWidget(buttonBox); setMinimumHeight(sizeHint().height() + 20); // WORKAROUND: prevent "cropped" qcombobox } KBugReport::~KBugReport() { delete d; } QString KBugReport::messageBody() const { if (!d->submitBugWeb) { return d->m_lineedit->toPlainText(); } else { return QString(); } } void KBugReport::setMessageBody(const QString &messageBody) { if (!d->submitBugWeb) { d->m_lineedit->setPlainText(messageBody); } } void KBugReportPrivate::_k_updateUrl() { url = QUrl(QStringLiteral("https://bugs.kde.org/enter_bug.cgi")); QUrlQuery query; query.addQueryItem(QStringLiteral("format"), QStringLiteral("guided")); // use the guided form // the string format is product/component, where component is optional QStringList list = appcombo->currentText().split(QLatin1Char('/')); query.addQueryItem(QStringLiteral("product"), list[0]); if (list.size() == 2) { query.addQueryItem(QStringLiteral("component"), list[1]); } query.addQueryItem(QStringLiteral("version"), m_strVersion); url.setQuery(query); // TODO: guess and fill OS(sys_os) and Platform(rep_platform) fields } void KBugReportPrivate::_k_appChanged(int i) { QString appName = appcombo->itemText(i); int index = appName.indexOf(QLatin1Char('/')); if (index > 0) { appName = appName.left(index); } //qDebug() << "appName " << appName; QString strDisplayVersion; //Version string to show in the UI if (appname == appName && !m_aboutData.version().isEmpty()) { m_strVersion = m_aboutData.version(); strDisplayVersion = m_strVersion; } else { m_strVersion = QStringLiteral("unknown"); //English string to put in the bug report strDisplayVersion = i18nc("unknown program name", "unknown"); } if (!submitBugWeb) { m_strVersion += QLatin1Char(' ') + kde_version; strDisplayVersion += QLatin1Char(' ') + kde_version; } m_version->setText(strDisplayVersion); if (submitBugWeb) { _k_updateUrl(); } } void KBugReportPrivate::_k_slotConfigureEmail() { if (m_process) { return; } m_process = new QProcess; QObject::connect(m_process, SIGNAL(finished(int,QProcess::ExitStatus)), q, SLOT(_k_slotSetFrom())); m_process->start(QStringLiteral("kcmshell5"), QStringList() << QStringLiteral("kcm_useraccount")); if (!m_process->waitForStarted()) { //qDebug() << "Couldn't start kcmshell5.."; delete m_process; - m_process = 0; + m_process = nullptr; return; } m_configureEmail->setEnabled(false); } void KBugReportPrivate::_k_slotSetFrom() { delete m_process; - m_process = 0; + m_process = nullptr; m_configureEmail->setEnabled(true); KEMailSettings emailSettings; QString fromaddr = emailSettings.getSetting(KEMailSettings::EmailAddress); if (fromaddr.isEmpty()) { fromaddr = SystemInformation::userName(); } else { QString name = emailSettings.getSetting(KEMailSettings::RealName); if (!name.isEmpty()) { fromaddr = name + QLatin1String(" <") + fromaddr + QLatin1String(">"); } } m_from->setText(fromaddr); } void KBugReport::accept() { if (d->submitBugWeb) { QDesktopServices::openUrl(d->url); return; } if (d->m_lineedit->toPlainText().isEmpty() || d->m_subject->text().isEmpty()) { QString msg = i18n("You must specify both a subject and a description " "before the report can be sent."); KMessageBox::error(this, msg); return; } switch (d->currentSeverity()) { case 0: // critical if (KMessageBox::questionYesNo(this, i18n( "<p>You chose the severity <b>Critical</b>. " "Please note that this severity is intended only for bugs that:</p>" "<ul><li>break unrelated software on the system (or the whole system)</li>" "<li>cause serious data loss</li>" "<li>introduce a security hole on the system where the affected package is installed</li></ul>\n" "<p>Does the bug you are reporting cause any of the above damage? " "If it does not, please select a lower severity. Thank you.</p>"), QString(), KStandardGuiItem::cont(), KStandardGuiItem::cancel()) == KMessageBox::No) { return; } break; case 1: // grave if (KMessageBox::questionYesNo(this, i18n( "<p>You chose the severity <b>Grave</b>. " "Please note that this severity is intended only for bugs that:</p>" "<ul><li>make the package in question unusable or mostly so</li>" "<li>cause data loss</li>" "<li>introduce a security hole allowing access to the accounts of users who use the affected package</li></ul>\n" "<p>Does the bug you are reporting cause any of the above damage? " "If it does not, please select a lower severity. Thank you.</p>"), QString(), KStandardGuiItem::cont(), KStandardGuiItem::cancel()) == KMessageBox::No) { return; } break; default: break; } if (!sendBugReport()) { QString msg = i18n("Unable to send the bug report.\n" "Please submit a bug report manually....\n" "See http://bugs.kde.org/ for instructions."); KMessageBox::error(this, msg + QStringLiteral("\n\n") + d->lastError); return; } KMessageBox::information(this, i18n("Bug report sent, thank you for your input.")); QDialog::accept(); } void KBugReport::closeEvent(QCloseEvent *e) { if (!d->submitBugWeb && ((d->m_lineedit->toPlainText().length() > 0) || d->m_subject->isModified())) { int rc = KMessageBox::warningYesNo(this, i18n("Close and discard\nedited message?"), i18n("Close Message"), KStandardGuiItem::discard(), KStandardGuiItem::cont()); if (rc == KMessageBox::No) { e->ignore(); return; } } QDialog::closeEvent(e); } QString KBugReport::text() const { //qDebug() << d->severityButtons[d->currentSeverity()]->objectName(); // Prepend the pseudo-headers to the contents of the mail QString severity = d->severityButtons[d->currentSeverity()]->objectName(); QString appname = d->appcombo->currentText(); QString os = QStringLiteral("OS: %1 (%2)\n"). arg(QStringLiteral(XMLGUI_COMPILING_OS), QStringLiteral(XMLGUI_DISTRIBUTION_TEXT)); QString bodyText; /* for(int i = 0; i < m_lineedit->numLines(); i++) { QString line = m_lineedit->textLine(i); if (!line.endsWith("\n")) line += '\n'; bodyText += line; } */ bodyText = d->m_lineedit->toPlainText(); if (bodyText.length() > 0) if (bodyText[bodyText.length() - 1] != QLatin1Char('\n')) { bodyText += QLatin1Char('\n'); } if (severity == QStringLiteral("i18n") && QLocale().language() != QLocale::system().language()) { // Case 1 : i18n bug QString package = QStringLiteral("i18n_%1").arg(QLocale::languageToString(QLocale().language())); package = package.replace(QLatin1Char('_'), QLatin1Char('-')); return QStringLiteral("Package: %1").arg(package) + QStringLiteral("\nApplication: %1\nVersion: %2\n").arg(appname, d->m_strVersion) + // not really i18n's version, so better here IMHO os + QLatin1String("\n") + bodyText; } else { appname = appname.replace(QLatin1Char('_'), QLatin1Char('-')); // Case 2 : normal bug return QStringLiteral("Package: %1\nVersion: %2\nSeverity: %3\n") .arg(appname, d->m_strVersion, severity) + QStringLiteral("Compiler: %1\n").arg(QStringLiteral(XMLGUI_COMPILER_VERSION)) + os + QLatin1String("\n") + bodyText; } } bool KBugReport::sendBugReport() { QString recipient = d->m_aboutData.bugAddress(); if (recipient.isEmpty()) { recipient = QStringLiteral("submit@bugs.kde.org"); } QString command = QStandardPaths::findExecutable(QStringLiteral("ksendbugmail")); if (command.isEmpty()) { command = QFile::decodeName(CMAKE_INSTALL_PREFIX "/" KF5_LIBEXEC_INSTALL_DIR "/ksendbugmail"); } QProcess proc; QStringList args; args << QStringLiteral("--subject") << d->m_subject->text() << QStringLiteral("--recipient") << recipient; proc.start(command, args); //qDebug() << command << args; if (!proc.waitForStarted()) { qCritical() << "Unable to open a pipe to " << command << endl; return false; } proc.write(text().toUtf8()); proc.closeWriteChannel(); proc.waitForFinished(); //qDebug() << "kbugreport: sendbugmail exit, status " << proc.exitStatus() << " code " << proc.exitCode(); QByteArray line; if (proc.exitStatus() == QProcess::NormalExit && proc.exitCode() != 0) { // XXX not stderr? while (!proc.atEnd()) { line = proc.readLine(); } d->lastError = QString::fromUtf8(line); return false; } return true; } #include "moc_kbugreport.cpp" diff --git a/src/kbugreport.h b/src/kbugreport.h index a84884e..67cba67 100644 --- a/src/kbugreport.h +++ b/src/kbugreport.h @@ -1,120 +1,120 @@ /* This file is part of the KDE project Copyright (C) 1999 David Faure <faure@kde.org> 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 KBUGREPORT_H #define KBUGREPORT_H #include <QDialog> #include <kxmlgui_export.h> class KAboutData; class KBugReportPrivate; /** * @short A dialog box for sending bug reports. * * All the information needed by the dialog box * (program name, version, bug-report address, etc.) * comes from the KAboutData class. * Make sure you create an instance of KAboutData and call * KAboutData::setApplicationData(<aboutData>). * * \image html kbugreport.png "KDE Bug Report Dialog" * * @author David Faure <faure@kde.org> */ class KXMLGUI_EXPORT KBugReport : public QDialog { Q_OBJECT public: /** * Creates a bug-report dialog. * Note that you shouldn't have to do this manually, * since KHelpMenu takes care of the menu item * for "Report Bug..." and of creating a KBugReport dialog. */ - explicit KBugReport(const KAboutData &aboutData, QWidget *parent = 0L); + explicit KBugReport(const KAboutData &aboutData, QWidget *parent = nullptr); /** * Destructor */ virtual ~KBugReport(); /** * The message body of the bug report * @return The message body of the bug report. */ QString messageBody() const; /** * Sets the message body of the bug report. */ void setMessageBody(const QString &messageBody); /** * OK has been clicked */ void accept() Q_DECL_OVERRIDE; private: /** * "Configure email" has been clicked - this calls kcmshell5 System/email */ Q_PRIVATE_SLOT(d, void _k_slotConfigureEmail()) /** * Sets the "From" field from the e-mail configuration * Called at creation time, but also after "Configure email" is closed. */ Q_PRIVATE_SLOT(d, void _k_slotSetFrom()) /** * Application combo selection changed (and was activated) */ Q_PRIVATE_SLOT(d, void _k_appChanged(int)) /** * Update the url to match the current os, compiler, selected app, etc */ Q_PRIVATE_SLOT(d, void _k_updateUrl()) protected: /** * A complete copy of the bug report * @return QString copy of the bug report. */ QString text() const; /** * Attempt to e-mail the bug report. * @return true on success */ bool sendBugReport(); void closeEvent(QCloseEvent *e) Q_DECL_OVERRIDE; private: friend class KBugReportPrivate; KBugReportPrivate *const d; Q_DISABLE_COPY(KBugReport) }; #endif diff --git a/src/kcheckaccelerators.cpp b/src/kcheckaccelerators.cpp index 3b26128..1156fb5 100644 --- a/src/kcheckaccelerators.cpp +++ b/src/kcheckaccelerators.cpp @@ -1,317 +1,317 @@ /* This file is part of the KDE libraries Copyright (C) 1997 Matthias Kalle Dalheimer (kalle@kde.org) Copyright (C) 1998, 1999, 2000 KDE Team Copyright (C) 2008 Nick Shaforostoff <shaforostoff@kde.ru> 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 "kcheckaccelerators.h" #include <QApplication> #include <QCheckBox> #include <QDialog> #include <QShortcutEvent> #include <QMouseEvent> #include <QLayout> #include <QMenuBar> #include <QPushButton> #include <QTabBar> #include <QTextBrowser> #include <QChar> #include <QLabel> #include <QComboBox> #include <QGroupBox> #include <QClipboard> #include <QProcess> #include <QDialogButtonBox> #include <kconfig.h> #include <kconfiggroup.h> #include <ksharedconfig.h> #include <klocalizedstring.h> #include <kacceleratormanager.h> class KCheckAcceleratorsInitializer : public QObject { Q_OBJECT public: explicit KCheckAcceleratorsInitializer(QObject *parent = Q_NULLPTR) : QObject(parent) { } public Q_SLOTS: void initiateIfNeeded() { KConfigGroup cg(KSharedConfig::openConfig(), "Development"); QString sKey = cg.readEntry("CheckAccelerators").trimmed(); int key = 0; if (!sKey.isEmpty()) { QList<QKeySequence> cuts = QKeySequence::listFromString(sKey); if (!cuts.isEmpty()) { key = cuts.first()[0]; } } const bool autoCheck = cg.readEntry("AutoCheckAccelerators", true); const bool copyWidgetText = cg.readEntry("CopyWidgetText", false); if (!copyWidgetText && key == 0 && !autoCheck) { deleteLater(); return; } new KCheckAccelerators(qApp, key, autoCheck, copyWidgetText); deleteLater(); } }; static void startupFunc() { // Call initiateIfNeeded once we're in the event loop // This is to prevent using KSharedConfig before main() can set the app name QCoreApplication *app = QCoreApplication::instance(); KCheckAcceleratorsInitializer *initializer = new KCheckAcceleratorsInitializer(app); QMetaObject::invokeMethod(initializer, "initiateIfNeeded", Qt::QueuedConnection); } Q_COREAPP_STARTUP_FUNCTION(startupFunc) KCheckAccelerators::KCheckAccelerators(QObject *parent, int key_, bool autoCheck_, bool copyWidgetText_) : QObject(parent) , key(key_) , block(false) , autoCheck(autoCheck_) , copyWidgetText(copyWidgetText_) - , drklash(0) + , drklash(nullptr) { setObjectName(QStringLiteral("kapp_accel_filter")); KConfigGroup cg(KSharedConfig::openConfig(), "Development"); alwaysShow = cg.readEntry("AlwaysShowCheckAccelerators", false); copyWidgetTextCommand = cg.readEntry("CopyWidgetTextCommand", QString()); parent->installEventFilter(this); connect(&autoCheckTimer, SIGNAL(timeout()), SLOT(autoCheckSlot())); } bool KCheckAccelerators::eventFilter(QObject *obj, QEvent *e) { if (block) { return false; } switch (e->type()) { // just simplify debuggin case QEvent::ShortcutOverride: if (key && (static_cast<QKeyEvent *>(e)->key() == key)) { block = true; checkAccelerators(false); block = false; e->accept(); return true; } break; case QEvent::ChildAdded: case QEvent::ChildRemoved: // Only care about widgets; this also avoids starting the timer in other threads if (!static_cast<QChildEvent *>(e)->child()->isWidgetType()) { break; } // fall-through case QEvent::Resize: case QEvent::LayoutRequest: case QEvent::WindowActivate: case QEvent::WindowDeactivate: if (autoCheck) { autoCheckTimer.setSingleShot(true); autoCheckTimer.start(20); // 20 ms } break; //case QEvent::MouseButtonDblClick: case QEvent::MouseButtonPress: if (copyWidgetText && static_cast<QMouseEvent *>(e)->button() == Qt::MidButton) { //kWarning()<<"obj"<<obj; QWidget *w = static_cast<QWidget *>(obj)->childAt(static_cast<QMouseEvent *>(e)->pos()); if (!w) { w = static_cast<QWidget *>(obj); } if (!w) { return false; } //kWarning()<<"MouseButtonDblClick"<<w; QString text; if (qobject_cast<QLabel *>(w)) { text = static_cast<QLabel *>(w)->text(); } else if (qobject_cast<QAbstractButton *>(w)) { text = static_cast<QAbstractButton *>(w)->text(); } else if (qobject_cast<QComboBox *>(w)) { text = static_cast<QComboBox *>(w)->currentText(); } else if (qobject_cast<QTabBar *>(w)) { text = static_cast<QTabBar *>(w)->tabText(static_cast<QTabBar *>(w)->tabAt(static_cast<QMouseEvent *>(e)->pos())); } else if (qobject_cast<QGroupBox *>(w)) { text = static_cast<QGroupBox *>(w)->title(); } else if (qobject_cast<QMenu *>(obj)) { QAction *a = static_cast<QMenu *>(obj)->actionAt(static_cast<QMouseEvent *>(e)->pos()); if (!a) { return false; } text = a->text(); if (text.isEmpty()) { text = a->iconText(); } } if (text.isEmpty()) { return false; } if (static_cast<QMouseEvent *>(e)->modifiers() == Qt::ControlModifier) { text.remove(QChar::fromLatin1('&')); } //kWarning()<<KGlobal::activeComponent().catalogName()<<text; if (copyWidgetTextCommand.isEmpty()) { QClipboard *clipboard = QApplication::clipboard(); clipboard->setText(text); } else { QProcess *script = new QProcess(this); script->start(copyWidgetTextCommand.arg(text, QFile::decodeName(KLocalizedString::applicationDomain()))); connect(script, SIGNAL(finished(int,QProcess::ExitStatus)), script, SLOT(deleteLater())); } e->accept(); return true; //kWarning()<<"MouseButtonDblClick"<<static_cast<QWidget*>(obj)->childAt(static_cast<QMouseEvent*>(e)->globalPos()); } return false; case QEvent::Timer: case QEvent::MouseMove: case QEvent::Paint: return false; default: // qDebug() << "KCheckAccelerators::eventFilter " << e->type() << " " << autoCheck; break; } return false; } void KCheckAccelerators::autoCheckSlot() { if (block) { autoCheckTimer.setSingleShot(true); autoCheckTimer.start(20); return; } block = true; checkAccelerators(!alwaysShow); block = false; } void KCheckAccelerators::createDialog(QWidget *actWin, bool automatic) { if (drklash) { return; } drklash = new QDialog(actWin); drklash->setAttribute(Qt::WA_DeleteOnClose); drklash->setObjectName(QStringLiteral("kapp_accel_check_dlg")); drklash->setWindowTitle(i18nc("@title:window", "Dr. Klash' Accelerator Diagnosis")); drklash->resize(500, 460); QVBoxLayout *layout = new QVBoxLayout(drklash); drklash_view = new QTextBrowser(drklash); layout->addWidget(drklash_view); - QCheckBox *disableAutoCheck = NULL; + QCheckBox *disableAutoCheck = nullptr; if (automatic) { disableAutoCheck = new QCheckBox(i18nc("@option:check", "Disable automatic checking"), drklash); connect(disableAutoCheck, SIGNAL(toggled(bool)), SLOT(slotDisableCheck(bool))); layout->addWidget(disableAutoCheck); } QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Close, drklash); layout->addWidget(buttonBox); connect(buttonBox, SIGNAL(rejected()), drklash, SLOT(close())); if (disableAutoCheck) { disableAutoCheck->setFocus(); } else { drklash_view->setFocus(); } } void KCheckAccelerators::slotDisableCheck(bool on) { autoCheck = !on; if (!on) { autoCheckSlot(); } } void KCheckAccelerators::checkAccelerators(bool automatic) { QWidget *actWin = qApp->activeWindow(); if (!actWin) { return; } KAcceleratorManager::manage(actWin); QString a, c, r; KAcceleratorManager::last_manage(a, c, r); if (automatic) { // for now we only show dialogs on F12 checks return; } if (c.isEmpty() && r.isEmpty() && (automatic || a.isEmpty())) { return; } QString s; if (! c.isEmpty()) { s += i18n("<h2>Accelerators changed</h2>"); s += QStringLiteral("<table border><tr><th><b>"); s += i18n("Old Text"); s += QStringLiteral("</b></th><th><b>"); s += i18n("New Text"); s += QStringLiteral("</b></th></tr>"); s += c; s += QStringLiteral("</table>"); } if (! r.isEmpty()) { s += i18n("<h2>Accelerators removed</h2>"); s += QStringLiteral("<table border><tr><th><b>"); s += i18n("Old Text"); s += QStringLiteral("</b></th></tr>"); s += r; s += QStringLiteral("</table>"); } if (! a.isEmpty()) { s += i18n("<h2>Accelerators added (just for your info)</h2>"); s += QStringLiteral("<table border><tr><th><b>"); s += i18n("New Text"); s += QStringLiteral("</b></th></tr>"); s += a; s += QStringLiteral("</table>"); } createDialog(actWin, automatic); drklash_view->setHtml(s); drklash->show(); drklash->raise(); // dlg will be destroyed before returning } #include "kcheckaccelerators.moc" diff --git a/src/kdepackages.h b/src/kdepackages.h index edfe9cb..b1a5cbb 100644 --- a/src/kdepackages.h +++ b/src/kdepackages.h @@ -1,1913 +1,1913 @@ // DO NOT EDIT - EDIT products in bugs.kde.org and run ./make_kdepackages_updated.py in kxmlgui to update const char * const packages[] = { "abakus", "Active", "Active/Application launcher", "Active/Applications", "Active/Contour activity screen", "Active/Contour activity switcher", "Active/Contour recommendations manager", "Active/General", "Active/Keyboard", "Active/Plasma mobile shell", "Active/Share Like Connect", "Active/Task switcher", "Active/Top Bar", "Active/Widgets strip containment", "aki", "aki/libakicore", "akiirc", "akiirc/ChannelView", "akiirc/ChatInput", "akiirc/notifications", "akiirc/ServerView", "Akonadi", "Akonadi/Account Wizard", "Akonadi/akonadiconsole", "Akonadi/Archive Mail Agent", "Akonadi/Baloo Indexer", "Akonadi/Birthdays Resource", "Akonadi/Contacts resource", "Akonadi/DAV Resource", "Akonadi/Facebook Resource", "Akonadi/Gmail resource", "Akonadi/Google Resource", "Akonadi/GoogleData Resource", "Akonadi/ICal file resource", "Akonadi/IMAP resource", "Akonadi/KAlarm resource", "Akonadi/Knut resource", "Akonadi/Kolab Resource", "Akonadi/KResource compat bridges", "Akonadi/libakonadi", "Akonadi/Local Bookmark Resource", "Akonadi/Mail Dispatcher Agent", "Akonadi/Mail Filter Agent", "Akonadi/Maildir Resource", "Akonadi/MBox Resource", "Akonadi/Migration", "Akonadi/Mixed Maildir resource", "Akonadi/Nepomuk Feeder Agents", "Akonadi/New Mail Notifier", "Akonadi/OpenXchange Resource", "Akonadi/POP3 Resource", "Akonadi/SendLaterAgent", "Akonadi/server", "Akonadi/SingleFileResourceBase", "Akonadi/Tag Resource", "Akonadi/VCard dir resource", "Akonadi/VCard file resource", "akregator", "akregator/akonadi-port", "akregator/akregator konqueror plugin", "akregator/feed parser", "akregator/internal browser", "akregator/kontact plugin", "akregator/metakit plugin", "akunambol", "akunambol/syncronization", "amarok", "amarok/Collection Browser", "amarok/Collections/CD", "amarok/Collections/DAAP", "amarok/Collections/iPod iPhone", "amarok/Collections/Local", "amarok/Collections/Media Devices", "amarok/Collections/MTP player", "amarok/Collections/Nepomuk", "amarok/Collections/Organize Files Dialog", "amarok/Collections/Spotify", "amarok/Collections/UPnP", "amarok/Collections/USB mass storage and MSC", "amarok/Context View", "amarok/Context View/Albums", "amarok/Context View/Cover Bling", "amarok/Context View/Covergrid", "amarok/Context View/Current Track", "amarok/Context View/Info", "amarok/Context View/Labels", "amarok/Context View/Lyrics", "amarok/Context View/Photos", "amarok/Context View/Similar Artists", "amarok/Context View/Spectrum Analyzer", "amarok/Context View/Tabs", "amarok/Context View/Upcoming Events", "amarok/Context View/Video", "amarok/Context View/Wikipedia", "amarok/D-Bus interfaces", "amarok/Documentation", "amarok/File Browser", "amarok/Internet Services", "amarok/LikeBack", "amarok/Metadata Editing and Reading", "amarok/Moodbar", "amarok/Notifications", "amarok/Playback", "amarok/Playback/CUE sheet support", "amarok/Playback/Replay Gain", "amarok/Playback/Streams", "amarok/Playlist", "amarok/Playlist/Playlist Layout Editor", "amarok/Playlists/Automated Playlist Generator", "amarok/Playlists/Dynamic Playlists", "amarok/Playlists/Saved Playlists", "amarok/Podcast", "amarok/PopUpDropper", "amarok/Services/Amazon MP3 Store", "amarok/Services/Ampache", "amarok/Services/gpodder.net", "amarok/Services/Jamendo", "amarok/Services/Last.fm", "amarok/Services/Magnatune", "amarok/Services/MP3tunes", "amarok/Services/Podcast Directory", "amarok/Shortcuts", "amarok/Toolbar", "amarok/Tools/Bookmark Manager", "amarok/Tools/Cover Manager", "amarok/Tools/Equalizer", "amarok/Tools/Network Requests Viewer", "amarok/Tools/Script Manager", "amarok/Tools/Statistics Synchronization", "amarok/Transcoding", "amor", "analitza", "analitza/analitzaplot", "analitza/core", "apper", "ark", "Artikulate", "Artikulate/course files", "Artikulate/language specifications", "artwork", "attica", "AVKode", "AVKode/AVKode library", "Baloo", "Baloo/Files", "Baloo/General", "Baloo/KCM", "Baloo/Tags", "Baloo/Timeline", "Baloo/Widgets", "bangarang", "bangarang/Baloo", "bangarang/UI", "basket", "bindings", "bindings/pykde", "blinken", "blogilo", "Bluedevil", "Bluedevil/daemon", "Bluedevil/kcm", "Bluedevil/kio-bt", "Bluedevil/kio-obex", "Bluedevil/libbluedevil", "Bluedevil/system tray", "Bluedevil/wizard", "bodega", "bodega/Client library", "bodega/Manager", "bodega/Plasma Active client", "bodega/Server", "bodega/Web client", "bomber", "bovo", "braindump", "braindump/application", "Breeze", "Breeze/Icons", "Breeze/QStyle", "Breeze/QtQuickControls", "Breeze/window decoration", "bugs.kde.org", "bugs.kde.org/code customizations", "bugs.kde.org/database", "bugs.kde.org/product/component changes", "bugs.kde.org/templates", "buildsystem", "buildsystem/KDE3 (autotools)", "buildsystem/KDE4 (cmake)", "calligraactive", "calligraactive/usability", "calligraauthor", "calligrachart", "calligrachart/3D charts", "calligrachart/opendocument", "calligrachart/usability", "calligracommon", "calligracommon/embedded-documents", "calligracommon/filters", "calligracommon/flake", "calligracommon/flake-picture", "calligracommon/flake-plugins", "calligracommon/koreport", "calligracommon/path-shapes", "calligracommon/pigment", "calligracommon/text-layout", "calligracommon/text-tool", "calligracommon/text-tool-plugins", "calligracommon/usability", "calligraflow", "calligraflow/opendocument", "calligraflow/usability", "calligraformula", "calligraformula/filters", "calligraformula/usability", "calligragemini", "calligraplan", "calligraplan/usability", "calligraplan/work", "calligraservices", "calligraservices/Build Server", "calligraservices/quality.calligra-suite.org", "calligraservices/www.calligra-suite.org", "calligrasheets", "calligrasheets/filter/xls", "calligrasheets/filter/xlsx", "calligrasheets/filters", "calligrasheets/functions", "calligrasheets/opendocument", "calligrasheets/shape", "calligrasheets/usability", "calligrastage", "calligrastage/filter/ppt", "calligrastage/filter/pptx", "calligrastage/filters", "calligrastage/okularplugin", "calligrastage/opendocument", "calligrastage/usability", "calligrawords", "calligrawords/doc", "calligrawords/docx", "calligrawords/filters", "calligrawords/okularplugin", "calligrawords/opendocument", "calligrawords/review tool", "calligrawords/tables", "calligrawords/usability", "cantor", "cantor/kalgebra-backend", "cantor/maxima-backend", "cantor/octave-backend", "cantor/python2-backend", "cantor/qualculate-backend", "cantor/r-backend", "cantor/sage-backend", "cantor/scilab-backend", "cervisia", "choqok", "choqok/Gnu social (StatusNet)", "choqok/OCS", "choqok/Pump.io", "choqok/Twitter", "cirkuit", "cocoon", "colibri", "colord-kde", "colord-kde/Daemon Module (KDED)", "colord-kde/Systems Settings Module (KCM)", "conquirere", "datakiosk", "dekorator", "dekorator/kcm part", "digikam", "digikam/Advanced Rename", "digikam/Albums GUI", "digikam/Baloo Interface", "digikam/Batch Queue Manager", "digikam/Color Management", "digikam/Database", "digikam/Dates", "digikam/Documentation", "digikam/Export", "digikam/Face Management", "digikam/Filters", "digikam/Fuzzy Searches", "digikam/Geolocation", "digikam/Image Editor", "digikam/Import", "digikam/Kioslave", "digikam/Kipiinterface", "digikam/Labels", "digikam/libkdcraw", "digikam/libkexiv2", "digikam/libkface", "digikam/libkgeomap", "digikam/libkipi", "digikam/Light Table", "digikam/Maintenance", "digikam/Map Searches", "digikam/Metadata", "digikam/Portability", "digikam/Preview", "digikam/Progress Manager", "digikam/RAW files management", "digikam/Searches", "digikam/setup", "digikam/Sidecar Management", "digikam/SlideShow", "digikam/TableView", "digikam/Tags", "digikam/Thumbnails", "digikam/Time Line", "digikam/Usability", "digikam/Versioning", "digikam/Workflow", "digikamimageplugins", "digikamimageplugins/Adjust Curves", "digikamimageplugins/AdjustLevels", "digikamimageplugins/Anti-Vignetting", "digikamimageplugins/Aspect Ratio Crop", "digikamimageplugins/Auto-Colors", "digikamimageplugins/BCG correction", "digikamimageplugins/Black and White", "digikamimageplugins/Blur", "digikamimageplugins/Blur Fx", "digikamimageplugins/Border", "digikamimageplugins/Channel Mixer", "digikamimageplugins/Charcoal", "digikamimageplugins/Color Balance", "digikamimageplugins/Color Management", "digikamimageplugins/ColorFx", "digikamimageplugins/Distortion Fx", "digikamimageplugins/Emboss", "digikamimageplugins/Filmgrain", "digikamimageplugins/Free Rotation", "digikamimageplugins/Hot Pixels", "digikamimageplugins/HSL Correction", "digikamimageplugins/Inpainting", "digikamimageplugins/Insert Text", "digikamimageplugins/Lens Distortion", "digikamimageplugins/Liquid Rescale", "digikamimageplugins/Local Contrast", "digikamimageplugins/Noise Reduction", "digikamimageplugins/OilPaint", "digikamimageplugins/Perspective", "digikamimageplugins/Raindrops", "digikamimageplugins/Red Eyes", "digikamimageplugins/Refocus", "digikamimageplugins/Resize", "digikamimageplugins/Restoration", "digikamimageplugins/Sharpen", "digikamimageplugins/Shear", "digikamimageplugins/Texture", "digikamimageplugins/Unsharp", "digikamimageplugins/White Balance", "digikamimageplugins/Wish for new plugin", "docs", "docs/Corrections", "docs/docs.kde.org", "docs/faq", "docs/ksgmltools", "docs/Missing Content", "docs/Screenshots", "dolphin", "dolphin/bars: filter", "dolphin/bars: location", "dolphin/bars: status", "dolphin/panels: folders", "dolphin/panels: information", "dolphin/panels: places", "dolphin/panels: search", "dolphin/panels: terminal", "dolphin/plugins: bazaar", "dolphin/plugins: dropbox", "dolphin/plugins: git", "dolphin/plugins: mercurial", "dolphin/plugins: svn", "dolphin/search", "dolphin/split view", "dolphin/view-engine: columns mode", "dolphin/view-engine: compact mode", "dolphin/view-engine: details mode", "dolphin/view-engine: general", "dolphin/view-engine: icons mode", "dolphin/view-engine: tooltip", "dragonplayer", "drkonqi", "drkonqi/backtraceparsing", "extra-cmake-modules", "filelight", "forum.kde.org", "frameworks-frameworkintegration", "frameworks-kactivities", "frameworks-kactivities/fileitemplugin", "frameworks-kactivities/kio", "frameworks-kactivities/libkactivities", "frameworks-kactivities/settings", "frameworks-karchive", "frameworks-kauth", "frameworks-kbookmarks", "frameworks-kcmutils", "frameworks-kcodecs", "frameworks-kcompletion", "frameworks-kconfig", "frameworks-kconfigwidgets", "frameworks-kcoreaddons", "frameworks-kcrash", "frameworks-kdaemon", "frameworks-kdbusaddons", "frameworks-kdeclarative", "frameworks-kdelibs4support", "frameworks-kdesignerplugin", "frameworks-kdesu", "frameworks-kdewebkit", "frameworks-kdnssd", "frameworks-kdoctools", "frameworks-kemoticons", "frameworks-kfileaudiopreview", "frameworks-kglobalaccel", "frameworks-kguiaddons", "frameworks-khtml", "frameworks-ki18n", "frameworks-kiconthemes", "frameworks-kidletime", "frameworks-kimageformats", "frameworks-kinit", "frameworks-kio", "frameworks-kitemmodels", "frameworks-kitemviews", "frameworks-kjobwidgets", "frameworks-kjs", "frameworks-kjsembed", "frameworks-kmediaplayer", "frameworks-knewstuff", "frameworks-knotifications", "frameworks-knotifyconfig", "frameworks-kparts", "frameworks-kplotting", "frameworks-kpty", "frameworks-kross", "frameworks-kservice", "frameworks-ktexteditor", "frameworks-ktextwidgets", "frameworks-kunitconversion", "frameworks-kwallet", "frameworks-kwidgetsaddons", "frameworks-kwindowsystem", "frameworks-kxmlgui", "frameworks-kxmlrpcclient", "frameworks-modemmanager-qt", "frameworks-networkmanager-qt", "frameworks-package", "frameworks-package/default", "frameworks-plasma", "frameworks-plasma/components", "frameworks-plasma/libplasma", "frameworks-plasma/libplasmaquick", "frameworks-solid", "frameworks-sonnet", "frameworks-threadweaver", "gcompris", "gluon", "gluon/gluonaudio", "gluon/gluoncreator", "gluon/gluonengine", "gluon/gluongraphics", "gluon/gluoninput", "gluon/gluonplayer", "Granatier", "grantlee", "guidance", "gwenview", "gwenview/importer", "headerthemeeditor", "HIG", "homerun", "homerun/applet", "homerun/sources", "i18n", "i18n/af", "i18n/bg", "i18n/br", "i18n/ca", "i18n/cs", "i18n/cy", "i18n/da", "i18n/de", "i18n/el", "i18n/en_GB", "i18n/eo", "i18n/es", "i18n/et", "i18n/eu", "i18n/fi", "i18n/fr", "i18n/ga", "i18n/gl", "i18n/gu", "i18n/he", "i18n/hr", "i18n/hu", "i18n/is", "i18n/it", "i18n/ja", "i18n/ko", "i18n/lt", "i18n/lv", "i18n/mk", "i18n/nb", "i18n/nl", "i18n/nn", "i18n/oc", "i18n/pl", "i18n/pt", "i18n/pt-BR", "i18n/ro", "i18n/ru", "i18n/sk", "i18n/sl", "i18n/sr", "i18n/sv", "i18n/ta", "i18n/th", "i18n/tr", "i18n/uk", "i18n/wa", "i18n/zh_CN", "i18n/zh_TW", "jovie", "jovie/General", "juk", "k3b", "k3b/Audio Project", "k3b/Burning/Hardware", "k3b/Copying", "k3b/Data Project", "k3b/eMovix project", "k3b/GUI/Usability", "k3b/Image Formats", "k3b/K3bSetup", "k3b/Mixed Mode Project", "k3b/Plugins", "k3b/Verfication", "k3b/Video CD project", "k3b/Video DVD", "KAccounts", "KAccounts/Daemon", "KAccounts/KCM", "kaddressbook", "kaddressbook/ldap search", "kaddressbook/printing", "KAddressbook Mobile", "kaffeine", "kajongg", "kalarm", "kalarm/Akonadi", "kalarm/kalarmd", "kalarm/sound", "kalcul", "kalgebra", "kalzium", "kalzium/Calculator", "kamoso", "kanagram", "Kannasaver", "Kannasaver/General", "kapman", "kappfinder", "kapptemplate", "karbon", "karbon/opendocument", "karbon/usability", "kard", "kate", "kate/application", "kate/encoding", "kate/folding", "kate/indentation", "kate/kwrite", "kate/part", "kate/python plugins", "kate/scripting", "kate/search", "kate/sessions", "kate/smart", "kate/Snippets Plugin", "kate/syntax", "kate/Vi Input Mode", "katomic", "kaudiocreator", "kaveau", "kbackgammon", "KBibTeX", "KBibTeX/Compiling/packaging", "KBibTeX/General", "KBibTeX/Loading/saving files", "KBibTeX/Network/online search", "KBibTeX/User interface", "kblackbox", "kblocks", "kbounce", "kbreakout", "kbruch", "kcachegrind", "kcalc", "kcharselect", "kcm_grub2", "kcm_grub2/General", "kcoloredit", "kcron", "kde", "kde/clipboard", "kde/dualhead", "kde/xinerama", "KDE Config Driver Manager", "KDE Config Module for Whoopsie", "KDE PIM Mobile", "kde-cli-tools", "kde-gtk-config", "kde-windows", "kde-windows/buildsystem", "kde-windows/installer", "kde-windows/network and files", "kde-windows/other", "kde-windows/porting", "kdebugdialog", "kdeconnect", "kdeconnect/android-application", "kdeconnect/common", "kdeconnect/kcm", "kdeconnect/plasmoid", "kded-appmenu", "kded-appmenu/export", "kded-appmenu/menu popup", "kded-appmenu/top menubar", "kdelibs", "kdelibs/kbugreport", "kdelibs/kcertpart", "kdelibs/kdecore", "kdelibs/kded", "kdelibs/kdeinit", "kdelibs/kdesasl", "kdelibs/kdeui", "kdelibs/kdewebkit", "kdelibs/kedittoolbar", "kdelibs/kimgio", "kdelibs/klauncher", "kdelibs/klocale", "kdelibs/kmdi", "kdelibs/knetwork", "kdelibs/knewstuff", "kdelibs/knotify", "kdelibs/kpasswdserver", "kdelibs/kshareddatacache", "kdelibs/kspell", "kdelibs/kstyle", "kdelibs/ksycoca", "kdelibs/kunitconverter", "kdelibs/kwallet", "kdelibs/print-dialog", "kdelibs/qt", "kdelibs/shortcuts", "kdemultimedia", "kdemultimedia/ffmpegthumbs", "kdemultimedia/libkcompactdisc", "kdemultimedia/mplayerthumbs", "kdenlive", "kdenlive/Documentation", "kdenlive/Effects & Transitions", "kdenlive/Installation", "kdenlive/Translation", "kdenlive/User Interface", "kdenlive/Video Display & Export", "kdepasswd", "kdepim", "kdepim/backupmail", "kdepim/importwizard", "kdepim/libkdepim", "kdepim/libkleo", "kdepim/libkpgp", "kdepim/messagecomposer", "kdepim/messagecore", "kdepim/messagelist", "kdepim/messagetemplates", "kdepim/messageviewer", "kdepim/wizards", "kdepimlibs", "kdepimlibs/akonadi", "kdepimlibs/gpgme++", "kdepimlibs/kabc", "kdepimlibs/kblog", "kdepimlibs/kcal", "kdepimlibs/kcalcore", "kdepimlibs/kholidays", "kdepimlibs/kldap", "kdepimlibs/kmime", "kdepimlibs/kpimidentities", "kdepimlibs/kpimtextedit", "kdepimlibs/kpimutils", "kdepimlibs/mailtransport", "kdepimlibs/qgpgme", "kdepimlibs/syndication", "kdeplasma-addons", "kdeplasma-addons/calculator", "kdeplasma-addons/converter", "kdeplasma-addons/dictionary", "kdeplasma-addons/fuzzy-clock", "kdeplasma-addons/General", "kdeplasma-addons/haenau", "kdeplasma-addons/hunyango", "kdeplasma-addons/kimpanel-applet", "kdeplasma-addons/kimpanel-dataengine", "kdeplasma-addons/konsoleprofiles", "kdeplasma-addons/notes", "kdeplasma-addons/systemloadviewer", "kdeplasma-addons/timer", "kdesdk-scripts", "kdesdk-scripts/cmake", "kdesdk-scripts/Miscellaneous", "kdesdk-scripts/source_control", "kdesrc-build", "kdesrc-build/documentation", "kdesrc-build/project metadata", "kdesrc-build/setup wizard", "kdesrc-build/test suite", "kdesu", "kdesu/kdesu", "kdesu/kdesud", "kdesvn", "kdesvn/General", "kdev-clang", "kdev-clang/General", "kdev-python", "kdev-python/Code completion", "kdev-python/Documentation data", "kdev-python/Language support", "kdev-python/User interface", "kdev-qmljs", "kdev-qmljs/code completion", "kdev-qmljs/language support", "kdevelop", "kdevelop/abbreviation plugin", "kdevelop/all build tools", "kdevelop/all editors", "kdevelop/application templates", "kdevelop/astyle", "kdevelop/bookmarks part", "kdevelop/Build tools: ANT", "kdevelop/Build tools: Automake", "kdevelop/Build tools: CMake", "kdevelop/Build tools: Custom Makefiles", "kdevelop/Build tools: QMake", "kdevelop/BuildSystem: Custom BuildSystems", "kdevelop/Buildtools: Make", "kdevelop/code completion", "kdevelop/coverage", "kdevelop/CPP Debugger", "kdevelop/CTags", "kdevelop/distpart", "kdevelop/doctreeview", "kdevelop/documentation viewer", "kdevelop/documentview", "kdevelop/doxygen integration", "kdevelop/file create", "kdevelop/file groups", "kdevelop/file selector", "kdevelop/file tree", "kdevelop/ident", "kdevelop/kdevelop 2.x (obsolete)", "kdevelop/Language Support: Ada", "kdevelop/Language Support: CPP", "kdevelop/Language Support: Java", "kdevelop/Language Support: Pascal", "kdevelop/Language Support: Perl", "kdevelop/Language Support: PHP", "kdevelop/Language Support: Ruby", "kdevelop/okteta", "kdevelop/Output Views", "kdevelop/partexplorer", "kdevelop/problem reporter", "kdevelop/Profiles", "kdevelop/qeditor", "kdevelop/Qt Designer Integration", "kdevelop/scriptproject", "kdevelop/session support", "kdevelop/UI: all modes", "kdevelop/UI: childframe window", "kdevelop/UI: IDEAl", "kdevelop/UI: tabbed pages", "kdevelop/UI: toplevel window", "kdevelop/valgrind integration", "kdevelop/VCS: Clearcase", "kdevelop/VCS: Perforce", "kdevplatform", "kdevplatform/appwizard", "kdevplatform/classbrowser", "kdevplatform/contextbrowser", "kdevplatform/cvs", "kdevplatform/dashboard", "kdevplatform/debugger", "kdevplatform/documentswitcher", "kdevplatform/dvcs", "kdevplatform/editor integration", "kdevplatform/filemanager", "kdevplatform/git", "kdevplatform/grepview", "kdevplatform/konsole integration", "kdevplatform/language", "kdevplatform/mercurial", "kdevplatform/openwith", "kdevplatform/outputview", "kdevplatform/patchreview", "kdevplatform/problemreporter", "kdevplatform/project", "kdevplatform/quickopen", "kdevplatform/run support", "kdevplatform/session", "kdevplatform/shell", "kdevplatform/snippet", "kdevplatform/sourceformatter", "kdevplatform/sublime", "kdevplatform/subversion", "kdevplatform/templates", "kdevplatform/util", "kdevplatform/vcs", "kdevplatform/veritas", "kdf", "kdiagram", "kdiagram/KChart", "kdiagram/KGantt", "kdialog", "kdiamond", "kdiff3", "kdiff3/KDiff3Part", "kdm", "kdm/kdmdesktop", "kdm/krootimage", "kdots", "kdots/default", "keditbookmarks", "kexi", "kexi/Documentation", "kexi/Forms", "kexi/General", "kexi/KexiDB", "kexi/Migration/Import/Export", "kexi/Queries", "kexi/Reports and Printouts", "kexi/Scripting", "kexi/Tables", "kexi/Usability", "Keyboard status applet", "kfax", "kfile", "kfile/kfileplacesview", "kfile/kpropertiesdialog", "kfile/kurlnavigator", "kfile-plugins", "kfile-plugins/avi", "kfile-plugins/exr", "kfile-plugins/jpeg", "kfile-plugins/mp3", "kfile-plugins/mpeg", "kfile-plugins/ogg", "kfile-plugins/OOo", "kfile-plugins/pdf", "kfile-plugins/PNG", "kfile-plugins/ps", "kfile-plugins/wav", "kfilereplace", "kfind", "kfloppy", "kfontview", "kfourinline", "kfritz", "kfritz/Call list", "kfritz/Call notification", "kfritz/Documentation", "kfritz/General", "kfritz/Phone books", "kfritz/Setup", "kfritz/Usability", "kftpgrabber", "kgeography", "kget", "kget/Core", "kget/konqextension", "kget/metalink", "kget/Multisegkio", "kget/plasma widget", "kget/torrent", "kget/UI", "kget/webinterface", "kgoldrunner", "kgpg", "kgrapheditor", "kgraphviewer", "khangman", "khelpcenter", "khipu", "khipu/core", "khotkeys", "khotkeys/Voice recognition", "kig", "kigo", "kile", "kile/editor", "kile/user interface", "killbots", "kimagemapeditor", "kimtoy", "kinfocenter", "kinfocenter/All KCMs", "kinfocenter/Device Viewer", "kinfocenter/Energy Information", "kinfocenter/IEEE 1394 Device", "kinfocenter/Info", "kinfocenter/InfoSummary", "kinfocenter/KInfoCenter Viewer", "kinfocenter/Memory", "kinfocenter/Network Interfaces", "kinfocenter/OpenGL", "kinfocenter/PCI", "kinfocenter/Samba", "kinfocenter/USB Devices", "kio", "kio/audiocd", "kio/cgi", "kio/desktop", "kio/devices", "kio/file", "kio/fish", "kio/floppy", "kio/ftp", "kio/gopher", "kio/help", "kio/home", "kio/http", "kio/imap", "kio/info", "kio/kamera", "kio/kfile", "kio/kioslave", "kio/kssl", "kio/man", "kio/media", "kio/mtp", "kio/network", "kio/nfs", "kio/nntp", "kio/pop3", "kio/remote", "kio/settings", "kio/sftp", "kio/sieve", "kio/smb", "kio/smbro", "kio/smtp", "kio/svn", "kio/system", "kio/tar", "kio/thumbnail", "kio/trash", "kio/uiserver", "kio/webdav", "kio/zeroconf", "kio/zip", "kio-extras", "kio-extras/default", "kiosk", "kiosk/kiosktool", "kipiplugins", "kipiplugins/AcquireImages", "kipiplugins/AdvancedSlideshow", "kipiplugins/BatchProcessImages", "kipiplugins/Calendar", "kipiplugins/DLNAExport", "kipiplugins/DngConverter", "kipiplugins/DropBox", "kipiplugins/ExpoBlending", "kipiplugins/Facebook", "kipiplugins/FlashExport", "kipiplugins/FlickrExport", "kipiplugins/GalleryExport", "kipiplugins/GoogleDrive", "kipiplugins/GPSSync", "kipiplugins/HTMLExport", "kipiplugins/ImageShackExport", "kipiplugins/ImageViewer", "kipiplugins/ImgurExport", "kipiplugins/IpodExport", "kipiplugins/JPEGLossLess", "kipiplugins/KIOImportExport", "kipiplugins/KMLExport", "kipiplugins/MediaWiki", "kipiplugins/MetadataEdit", "kipiplugins/Panorama", "kipiplugins/PhotoLayoutsEditor", "kipiplugins/PicasaWeb", "kipiplugins/PiwigoExport", "kipiplugins/PrintWizard", "kipiplugins/RajceExport", "kipiplugins/RAWConverter", "kipiplugins/RemoveRedEyes", "kipiplugins/SendImages", "kipiplugins/Shwup", "kipiplugins/Smug", "kipiplugins/TimeAdjust", "kipiplugins/VideoSlideshow", "kipiplugins/vkontakte", "kipiplugins/Wallpaper", "kipiplugins/Wish For New Plugins", "kipiplugins/YandexFotki", "kiriki", "kiten", "kjots", "KJots Mobile", "kjsembed", "kjsembed/Core", "kjsembed/Docs and examples", "kjsembed/Plugins", "kjumpingcube", "kleopatra", "kleopatra/Assuan UI Server", "klettres", "klickety", "klines", "klinkstatus", "klipper", "klipper/plasma-widget", "kmag", "kmahjongg", "KMail Mobile", "kmail2", "kmail2/commands and actions", "kmail2/composer", "kmail2/composereditor-ng", "kmail2/config dialog", "kmail2/contact completion", "kmail2/crypto", "kmail2/filtering", "kmail2/folders", "kmail2/MDN", "kmail2/message list", "kmail2/misc", "kmail2/search", "kmail2/sieve", "kmail2/UI", "kmailcvt", "kmenuedit", "kmid", "kmines", "kmix", "kmix/Backend: OSS4", "kmix/Backend: Pulseaudio", "kmix/Documentation", "kmix/KMix Panel Docking", "kmix/Obsolete component. Do NOT use!!! (ex: KMix Panel Applet).", "kmix/On Hold / External issue", "kmix/On-Screen-Display (OSD)", "kmix/QA and Testing", "kmix/Soundcard specific", "kmix/Visual: Layout and GUI", "kmldonkey", "kmldonkey/libkmldonkey", "kmousetool", "kmouth", "kmplayer", "kmplot", "kmtrace", "kmymoney", "kmymoney/General", "kmymoney4", "kmymoney4/csvimporter", "kmymoney4/database", "kmymoney4/file", "kmymoney4/onlinebanking", "knavalbattle", "knemo", "knetattach", "knetwalk", "knights", "knipptasch", "knode", "knotes", "kobby", "kolf", "kollision", "kolor-manager", "kolor-manager/kolor-manager", "kolor-manager/kolor-server", "kolourpaint", "kolourpaint/general-kde4", "kolourpaint/imagelib", "kolourpaint/ui", "kolourpaint/ui-kde4", "kommander", "kommander/Kommander editor", "kommander/Kommander executor", "kompare", "kompare/navigationpart", "kompare/parser", "kompare/viewpart", "konqueror", "konqueror/bookmarks", "konqueror/dirfilterplugin", "konqueror/feedplugin", "konqueror/file icon view", "konqueror/file list view", "konqueror/fsview", "konqueror/htmlsettingsplugin", "konqueror/imagerotation", "konqueror/kcookiejar", "konqueror/khtml", "konqueror/khtml adblock", "konqueror/khtml ecma", "konqueror/khtml event", "konqueror/khtml forms", "konqueror/khtml image part", "konqueror/khtml parsing", "konqueror/khtml part", "konqueror/khtml printing", "konqueror/khtml renderer", "konqueror/khtml svg", "konqueror/khtml xml", "konqueror/kimgalleryplugin", "konqueror/kjava", "konqueror/kjs", "konqueror/konq-e", "konqueror/kuick", "konqueror/New Plugin wishes", "konqueror/nsplugins", "konqueror/nspluginscan", "konqueror/nspluginviewer", "konqueror/reaktivate", "konqueror/rellinks", "konqueror/searchbarplugin", "konqueror/servicemenus", "konqueror/session restore", "konqueror/shellcmdplugin", "konqueror/sidebar", "konqueror/sidebar media player", "konqueror/tabbing", "konqueror/webarchiverplugin", "konquest", "konsole", "konsole/bookmark", "konsole/copy-paste", "konsole/emulation", "konsole/font", "konsole/history", "konsole/keyboard", "konsole/kpart", "konsole/kwrited", "konsole/monitoring", "konsole/single-process", "konsole/split-view", "konsole/tabbar", "konsolekalendar", "kontact", "kontact/akregator", "kontact/calendar", "kontact/contacts", "kontact/journal", "kontact/ktimetracker", "kontact/mail", "kontact/new plugin wishes", "kontact/news", "kontact/newsticker", "kontact/notes", "kontact/planner summary", "kontact/special dates", "kontact/summary", "kontact/todo", "konversation", "konversation/dcc", "konversation/documentation", "konversation/encryption", "konversation/ignore", "konversation/inputline", "konversation/ircview", "konversation/logging", "konversation/notifications", "konversation/protocol", "konversation/systemcontacts", "konversation/tabcompletion", "konversation/upnp", "kooka", "kooka/libkscan", "kopete", "kopete/Add Contact Wizard", "kopete/Addressbook integration", "kopete/Alias Plugin", "kopete/Audio/Video Plugin", "kopete/Auto-Away Plugin", "kopete/Autoreplace plugin", "kopete/Bonjour Plugin", "kopete/Bookmarks plugin", "kopete/Chat Window", "kopete/Chatwindow Styles", "kopete/Connection Status Plugin", "kopete/Contact list", "kopete/Contact Notes Plugin", "kopete/Cryptography Plugin", "kopete/DBus", "kopete/DCOP Interface", "kopete/Facebook Plugin", "kopete/Gadu-Gadu Plugin", "kopete/History Plugin", "kopete/ICQ and AIM Plugins", "kopete/Importer plugin", "kopete/IRC Plugin", "kopete/Jabber Plugin", "kopete/Latex Plugin", "kopete/libkopete", "kopete/Lotus Sametime Plugin", "kopete/Main Application", "kopete/Meanwhile plugin", "kopete/MSN Plugin", "kopete/New plugin wishes", "kopete/Notifications", "kopete/Novell GroupWise Messenger Plugin", "kopete/Now Listening Plugin", "kopete/OTR Plugin", "kopete/Pipes Plugin", "kopete/Privacy Plugin", "kopete/QQ Plugin", "kopete/Skype Plugin", "kopete/SMS Plugin", "kopete/SpellCheck Plugin", "kopete/Statistics plugin", "kopete/Testbed Plugin", "kopete/Texteffect Plugin", "kopete/Translator Plugin", "kopete/Web Presence Plugin", "kopete/WinPopup Plugin", "kopete/WLM Plugin", "kopete/Yahoo Plugin", "korgac", "korganizer", "korganizer/agendaview (weekview)", "korganizer/embedded", "korganizer/exchangeplugin", "korganizer/groupware", "korganizer/html export", "korganizer/import/export", "korganizer/incidence editors", "korganizer/incidence viewer", "korganizer/invitations", "korganizer/journalview", "korganizer/monthview", "korganizer/otherviews", "korganizer/printing", "korganizer/recurrence", "korganizer/reminder daemon (korgac)", "korganizer/timezones", "korganizer/todoview", "KOrganizer Mobile", "KOrganizer Mobile/calendar", "KOrganizer Mobile/tasks", "kpat", "kpat/solver", "kpeople", "kpeople/PersonsModel", "kpeople/PersonsPresenceModel", "kpeople/ResourceWatcher", "kphotoalbum", "kphotoalbum/Android client", "kphotoalbum/Annotation Dialog", "kphotoalbum/Backend", "kphotoalbum/Browser", "kphotoalbum/Datebar", "kphotoalbum/EXIF viewer/search", "kphotoalbum/HTML generator", "kphotoalbum/Import/Export", "kphotoalbum/SQL backend", "kphotoalbum/Thumbnail Viewer", "kphotoalbum/Viewer", "kphotoalbum/XML backend", "kplayer", "kppp", "kqtquickcharts", "krazy", "krdc", "krdc/NX", "krdc/RDP", "krdc/VNC", "krecipes", "krecord", "kreenshot-editor", "kregexpeditor", "kremotecontrol", "kremotecontrol/daemon", "kremotecontrol/settings", "kremotecontrol/tray", "kresources", "kresources/birthdays", "kresources/blogging", "kresources/bugzilla", "kresources/egroupware", "kresources/exchange", "kresources/featureplan", "kresources/framework", "kresources/groupdav", "kresources/groupwise", "kresources/imap", "kresources/ldap", "kresources/local", "kresources/newexchange", "kresources/remote", "kresources/SLOX", "kreversi", "krfb", "krita", "krita/adjustment layers", "krita/Brush engine", "krita/Color models", "krita/color selectors", "krita/Dockers", "krita/Documentation", "krita/File formats", "krita/Filters", "krita/G'Mic for Krita", "krita/krita gemini", "krita/krita sketch", "krita/Layer Stack", "krita/tablet support", "krita/Tile manager", "krita/Tools", "krita/transform", "krita/usability", "krita/Vector Objects and Tools", "kronometer", "kruler", "krunner", "krunner/audioplayercontrol", "krunner/bookmarks", "krunner/calculator", "krunner/converter", "krunner/events", "krunner/filesearch", "krunner/locations", "krunner/places", "krunner/quicksand", "krunner/solid", "krunner/spellchecker", "krunner/webshortcuts", "krunner/windows", "krusader", "krusader/disk-usage", "krusader/krarc", "krusader/krviewer", "krusader/net-connection", "krusader/search", "krusader/shortcuts", "krusader/synchronize", "ksayit", "kscd", "KScreen", "KScreen/common", "KScreen/kcm", "KScreen/kded", "KScreen/libkscreen", "KScreen/Plasma Applet", "ksecretsservice", "ksecretsservice/Client library", "ksecretsservice/Daemon", "ksecretsservice/ksecretssync", "kshisen", "ksirk", "ksirkskineditor", "ksmserver", "ksmserver/lockscreen", "ksmserver/ui", "ksnapshot", "ksnapshot/send-to menu", "kspaceduel", "ksplash", "KSquares", "ksshaskpass", "kst", "kst/datasources", "kst/documentation", "kst/elog extension", "kst/equations", "kst/i18n", "kst/packaging", "kst/plotting", "kst/plugins", "kst/scripting", "kst/ui", "kst/view objects", "kstars", "kstart", "ksudoku", "ksvg2", "ksysguard", "ksysguard/ksysguard", "ksysguard/ksysguardd", "ksysguard/libksysguard", "ksysguard/Plasmoid / Applet", "ksysguard/Process Controller - krunner part", "KSystemLog", "ksystraycmd", "kte-collaborative", "kte-collaborative/General", "kte-collaborative/KIO slave", "kte-collaborative/Telepathy integration", "kte-collaborative/Text buffer", "kteatime", "ktimer", "ktimetracker", "ktimetracker/reporting", "ktnef", "ktorrent", "ktouch", "ktouchpadenabler", "ktouchpadenabler/General", "ktron", "kttt", "ktuberling", "kturtle", "kubrick", "Kubuntu Debug Installer", "Kubuntu Notifications Helper", "kuiviewer", "kuser", "kverbos", "kvkbd", "kvpnc", "kwalletmanager", "kwayland", "kwebkitpart", "kwikdisk", "kwin", "kwin/activities", "kwin/appmenu", "kwin/aurorae", "kwin/colorcorrection", "kwin/compatibility", "kwin/compositing", "kwin/core", "kwin/decorations", "kwin/effects-tabbox", "kwin/effects-various", "kwin/effects-window-management", "kwin/egl", "kwin/glx", "kwin/kdecorations", "kwin/multihead", "kwin/rules", "kwin/scene-opengl", "kwin/scene-xrender", "kwin/scripting", "kwin/tabbox", "kwin/tiling", "kwin/window-tabbing", "kwin/xinerama", "kwin/xrandr", "kwordquiz", "kwrited", "kxkb", "kxsldbg", "kxsldbg/kxsldbg", "kxsldbg/xsldbg", "KXStitch", "KXStitch/pattern-libraries", "KXStitch/schemes", "KXStitch/symbol-libraries", "LabPlot2", "LabPlot2/backend", "LabPlot2/frontend", "libalkimia", "libalkimia/General", "libechonest", "libkcddb", "libkdeedu", "libkdeedu/keduvocdocument", "libkdegames", "libkdegames/kgamerenderer", "libkeduvocdocument", "libkgapi", "libkgapi/blogger", "libkgapi/calendar", "libkgapi/contacts", "libkgapi/drive", "libkgapi/General", "libkgapi/latitude", "libkgapi/static maps", "libkgapi/tasks", "libksane", "LibKubuntu", "libkvkontakte", "libmediawiki", "libqgit2", "libqinfinity", "lightdm", "lightdm/config", "lightdm/greeter", "lightdm/theme-classic", "lightdm/theme-userbar", "lokalize", "lokalize/editor", "lokalize/glossary", "lokalize/project management", "lokalize/translation memory", "lskat", "magazynier", "mancala", "Mangonel", "Mangonel/mangonel", "marble", "marble/data", "marble/plasmoid", "marble/wallpaper", "marble/website", "massif-visualizer", "massif-visualizer/callgraph", "massif-visualizer/massif-parser", "massif-visualizer/memorychart", "muon", "muon/discover", "muon/installer", "muon/libqapt", "muon/misc", "muon/muon", "muon/notifier", "muon/qapt-batch", "muon/qapt-deb-installer", "muon/qaptworker", "muon/updater", "Necessitas", "Necessitas/Android Qt4", "Necessitas/Android Qt4 Mobility", "Necessitas/Android Qt4 WebKit", "Necessitas/Android QtCreator", "Necessitas/Ministro", "Necessitas/SDK Installer", "Necessitas/Web infrastructure", "okteta", "okteta/Structures Tool", "okular", "okular/CHM backend", "okular/Comicbook backend", "okular/DjVu backend", "okular/DVI backend", "okular/EPub backend", "okular/Fax backend", "okular/Image backend components", "okular/mobipocket backend", "okular/New backend wishes", "okular/ODT backend", "okular/PDF backend", "okular/printing", "okular/PS backend", "okular/TIFF backend", "okular/XPS backend", "Oxygen", "Oxygen/fonts", "Oxygen/gtk2-engine", "Oxygen/gtk3-engine", "Oxygen/icons", "Oxygen/QtQuickControls", "Oxygen/sound", "Oxygen/style", "Oxygen/win deco", "Pairs", "Pairs/editor", "palapeli", "palapeli/libpala", "palapeli/slicers", "parley", "parley/editor", "parley/scripts", "partitionmanager", "paste.kde.org", "Phonon", "Phonon/avKode backend", "Phonon/Directshow", "Phonon/frontend", "Phonon/KDE platform plugin", "Phonon/kded module for audio device listing", "Phonon/Mplayer backend", "Phonon/NMM backend", "Phonon/Pulsesupport", "Phonon/settings", "Phonon/Xine backend", "phonon-backend-gstreamer", "phonon-backend-vlc", "picmi", "pimsettingexporter", "Planet KDE", "Planet KDE/feeds", "Plasma SDK", "Plasma SDK/cuttlefish", "Plasma SDK/editor", "Plasma SDK/engineexplorer", "Plasma SDK/General", "Plasma SDK/kconfigxteditor", "Plasma SDK/konsolepreviewer", "Plasma SDK/metadataeditor", "Plasma SDK/plasma-remote-helper-widgets", "Plasma SDK/plasmathemeexplorer", "Plasma SDK/plasmawallpaperviewer", "Plasma SDK/plasmoidviewer", "Plasma SDK/publisher", "Plasma SDK/remoteinstaller", "Plasma Workspace Wallpapers", "plasma-mediacenter", "plasma-mediacenter/BrowsingBackends", "plasma-mediacenter/General", "plasma-mediacenter/Libs", "plasma-mediacenter/MediaBrowser", "plasma-mediacenter/MediaController", "plasma-mediacenter/MediaPlayer", "plasma-mediacenter/Playlist", "plasma-mediacenter/Shell", "plasma-nm", "plasma-nm/applet", "plasma-nm/editor", "plasma4", "plasma4/activities", "plasma4/containment-desktop", "plasma4/containment-panel", "plasma4/dashboard", "plasma4/dataengines", "plasma4/desktop", "plasma4/groupingdesktop", "plasma4/mouse actions", "plasma4/multihead", "plasma4/multiscreen", "plasma4/notifications", "plasma4/panel", "plasma4/plasma-netbook", "plasma4/qml-components", "plasma4/remotewidgets", "plasma4/screensaver overlay", "plasma4/scriptengines", "plasma4/visuals", "plasma4/wallpaper-image", "plasma4/wallpaper-marble", "plasma4/wallpaper-virus", "plasma4/wallpaper-weather", "plasma4/widget explorer", "plasma4/widget-activitybar", "plasma4/widget-analogclock", "plasma4/widget-battery", "plasma4/widget-bouncyball", "plasma4/widget-bubblemon", "plasma4/widget-calculator", "plasma4/widget-clock", "plasma4/widget-colorpicker", "plasma4/widget-comic", "plasma4/widget-current-application-control", "plasma4/widget-devicenotifier", "plasma4/widget-dictionary", "plasma4/widget-fifteenpuzzle", "plasma4/widget-filewatcher", "plasma4/widget-folderview", "plasma4/widget-frame", "plasma4/widget-fuzzyclock", "plasma4/widget-ggadgets", "plasma4/widget-icontasks", "plasma4/widget-incomingmessage", "plasma4/widget-kickoff", "plasma4/widget-konsoleprofiles", "plasma4/widget-lancelot", "plasma4/widget-luna", "plasma4/widget-mediaplayer", "plasma4/widget-microblogging", "plasma4/widget-misc", "plasma4/widget-networkmonitor", "plasma4/widget-news", "plasma4/widget-notes", "plasma4/widget-nowplaying", "plasma4/widget-opendesktop", "plasma4/widget-osx", "plasma4/widget-pager", "plasma4/widget-paste", "plasma4/widget-pastebin", "plasma4/widget-quicklaunch", "plasma4/widget-rememberthemilk", "plasma4/widget-rssnow", "plasma4/widget-shelf", "plasma4/widget-systemloadviewer", "plasma4/widget-systemmonitor", "plasma4/widget-systemtray", "plasma4/widget-taskbar", "plasma4/widget-timer", "plasma4/widget-trashcan", "plasma4/widget-weather", "plasma4/widget-webbrowser", "plasma4/widget-webslice", "plasma4/widget-windowlist", "plasma4/widget-worldclock", "plasmashell", "plasmashell/Activity Switcher", "plasmashell/Analogue Clock", "plasmashell/Application Launcher (Kickoff)", "plasmashell/Application Menu (Kicker)", "plasmashell/Battery Monitor", "plasmashell/Calendar", "plasmashell/DataEngines", "plasmashell/Desktop Containment", "plasmashell/Desktop Dashboard", "plasmashell/Device Notifier", "plasmashell/Digital Clock", "plasmashell/Folder", "plasmashell/Icons-only Task Manager", "plasmashell/Image Wallpaper", "plasmashell/Look & Feel package", "plasmashell/Media Controller", "plasmashell/Milou", "plasmashell/Multi-screen support", "plasmashell/Notifications", "plasmashell/Pager", "plasmashell/Panel", "plasmashell/System Tray", "plasmashell/Task Manager", "plasmashell/Theme - Breeze", "plasmashell/Widget Explorer", "policykit-kde-agent-1", "policykit-kde-agent-1/kcm_auth", "policykit-kde-agent-1/manager", "policykit-kde-agent-1/polkit-kde-authentication-agent-1", "Powerdevil", "Predicate", "Predicate/Driver: MySQL", "Predicate/Driver: PostgreSQL", "Predicate/Driver: SQLite", "Predicate/General", "Predicate/Migration", "print-manager", "qaccessibilityclient", "qca", "QmlWeb", "QmlWeb/QML-Engine", "QmlWeb/QML-Parser", "QmlWeb/QtQuick", "qtatspi", "QtCurve", "QtCurve/gtk2", "QtCurve/gtk3", "QtCurve/qt4", "QtCurve/qt5", "rekonq", "rekonq/security", "rekonq/sessions", "rekonq/user interface", "rekonq/web app", "rekonq/web content", "Rocs", "Rocs/script engine", "Rocs/UI", "Rocs/visual graph editor", "season.kde.org", "showfoto", "sieveeditor", "simon", "simon/afaras", "simon/julius", "simon/ksimond", "simon/sam", "simon/simon", "simon/simond", "simon/simonoid", "simon/ssc", "simon/sscd", "simon-tools", "simon-tools/panic", "simon-tools/simon-touch", "simon-tools/simone", "simon-tools/simonskype", "simon-tools/simonstatus", "simon-tools/speechcal", "Skanlite", "skrooge", "smaragd", "step", "step/stepcore", "storageservicemanager", "superkaramba", "sweeper", "SymbolEditor", "Systemd KCM", "systemsettings", "systemsettings/kcmshell", "systemsettings/kcm_accessibility", "systemsettings/kcm_audiocd", "systemsettings/kcm_autostart", "systemsettings/kcm_bell", "systemsettings/kcm_clock", "systemsettings/kcm_colors", "systemsettings/kcm_componentchooser", "systemsettings/kcm_desktoppath", "systemsettings/kcm_desktopsearch", "systemsettings/kcm_desktopthemedetails", "systemsettings/kcm_deviceautomounter", "systemsettings/kcm_emoticons", "systemsettings/kcm_energy", "systemsettings/kcm_fileshare", "systemsettings/kcm_filetypes", "systemsettings/kcm_fontinst", "systemsettings/kcm_fonts", "systemsettings/kcm_formats", "systemsettings/kcm_gtk", "systemsettings/kcm_icons", "systemsettings/kcm_joystick", "systemsettings/kcm_kamera", "systemsettings/kcm_kded", "systemsettings/kcm_kdm", "systemsettings/kcm_kdnssd", "systemsettings/kcm_keyboard", "systemsettings/kcm_keyboard_layout", "systemsettings/kcm_keys", "systemsettings/kcm_kgamma", "systemsettings/kcm_khotkeys", "systemsettings/kcm_ksmserver", "systemsettings/kcm_ksplashthememgr", "systemsettings/kcm_kthememanage", "systemsettings/kcm_language", "systemsettings/kcm_launch", "systemsettings/kcm_mouse", "systemsettings/kcm_netpref", "systemsettings/kcm_notify", "systemsettings/kcm_phonon", "systemsettings/kcm_proxy", "systemsettings/kcm_randr", "systemsettings/kcm_samba", "systemsettings/kcm_screensaver", "systemsettings/kcm_sddm", "systemsettings/kcm_solid-actions", "systemsettings/kcm_ssl", "systemsettings/kcm_standard_actions", "systemsettings/kcm_style", "systemsettings/kcm_useraccount", "systemsettings/krdb", "systemsettings/treeview", "taglib", "telepathy", "telepathy/accounts-kcm", "telepathy/approver", "telepathy/auth-handler", "telepathy/call-ui", "telepathy/chat-plasmoid", "telepathy/common-internals", "telepathy/contact-applet", "telepathy/contact-runner", "telepathy/contactlist", "telepathy/contactlist-applet", "telepathy/debugger", "telepathy/documentation", "telepathy/filetransfer", "telepathy/kded-module", "telepathy/kopete-migrator", "telepathy/kopete-protocol-telepathy", "telepathy/KPeople plugins", "telepathy/kwhiteboard", "telepathy/launcher-kded", "telepathy/libktelepathy", "telepathy/log-viewer", "telepathy/nepomuk-service", "telepathy/ontology", "telepathy/OTR", "telepathy/presence-applet", "telepathy/presence-dataengine", "telepathy/send-file", "telepathy/telepathy-logger-qt", "telepathy/testlib", "telepathy/text-ui", "telepathy/text-ui-message-filters", "tellico", "Touchpad-KCM", "Touchpad-KCM/daemon", "Touchpad-KCM/kcm", "Touchpad-KCM/plasma-applet", "trojita", "trojita/Core", "trojita/Desktop GUI", "trojita/Harmattan", "trojita/IMAP", "trojita/Message Composer", "trojita/Other", "trojita/SMTP", "trojita/Ubuntu", "umbrello", "umbrello/documentation", "unknown", "user-manager", "user-manager/kcontrol module", "valgrind", "valgrind/bbv", "valgrind/cachegrind", "valgrind/callgrind", "valgrind/dhat", "valgrind/drd", "valgrind/helgrind", "valgrind/massif", "valgrind/memcheck", "valgrind/sgcheck", "valgrind/vex", "valkyrie", "wacomtablet", "wicd-client-kde", "workout", "www.kde.org", "www.kde.org/jointhegame.kde.org", "www.kde.org/sprints.kde.org", "yakuake", "zanshin", -0 }; +nullptr }; diff --git a/src/kedittoolbar.cpp b/src/kedittoolbar.cpp index 2e39130..ca77a38 100644 --- a/src/kedittoolbar.cpp +++ b/src/kedittoolbar.cpp @@ -1,1728 +1,1728 @@ /* This file is part of the KDE libraries Copyright (C) 2000 Kurt Granroth <granroth@kde.org> Copyright (C) 2006 Hamish Rodda <rodda@kde.org> Copyright 2007 David Faure <faure@kde.org> 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 "kedittoolbar.h" #include "kedittoolbar_p.h" #include <QShowEvent> #include <QAction> #include <QDialogButtonBox> #include <QtXml/QDomDocument> #include <QLayout> #include <QtCore/QDir> #include <QtCore/QFile> #include <QHeaderView> #include <QToolButton> #include <QLabel> #include <QApplication> #include <QGridLayout> #include <QCheckBox> #include <QMimeData> #include <QPushButton> #include <QStandardPaths> #include <QComboBox> #include <QLineEdit> #include <QDebug> #include <kicondialog.h> #include <klistwidgetsearchline.h> #include <klocalizedstring.h> #include <kmessagebox.h> #include <kseparator.h> #include <kconfig.h> #include "kactioncollection.h" #include "kxmlguifactory.h" #include "ktoolbar.h" #include "../kxmlgui_version.h" static const char separatorstring[] = I18N_NOOP("--- separator ---"); #define SEPARATORSTRING i18n(separatorstring) //static const char *const s_XmlTypeToString[] = { "Shell", "Part", "Local", "Merged" }; typedef QList<QDomElement> ToolBarList; namespace KDEPrivate { /** * Return a list of toolbar elements given a toplevel element */ static ToolBarList findToolBars(const QDomElement &start) { ToolBarList list; for (QDomElement elem = start; !elem.isNull(); elem = elem.nextSiblingElement()) { if (elem.tagName() == QStringLiteral("ToolBar")) { if (elem.attribute(QStringLiteral("noEdit")) != QLatin1String("true")) { list.append(elem); } } else { if (elem.tagName() != QStringLiteral("MenuBar")) { // there are no toolbars inside the menubar :) list += findToolBars(elem.firstChildElement()); // recursive } } } return list; } class XmlData { public: enum XmlType { Shell = 0, Part, Local, Merged }; explicit XmlData(XmlType xmlType, const QString &xmlFile, KActionCollection *collection) : m_isModified(false), m_xmlFile(xmlFile), m_type(xmlType), m_actionCollection(collection) { } void dump() const { #if 0 qDebug(240) << "XmlData" << this << "type" << s_XmlTypeToString[m_type] << "xmlFile:" << m_xmlFile; foreach (const QDomElement &element, m_barList) { qDebug(240) << " ToolBar:" << toolBarText(element); } if (m_actionCollection) { qDebug(240) << " " << m_actionCollection->actions().count() << "actions in the collection."; } else { qDebug(240) << " no action collection."; } #endif } QString xmlFile() const { return m_xmlFile; } XmlType type() const { return m_type; } KActionCollection *actionCollection() const { return m_actionCollection; } void setDomDocument(const QDomDocument &domDoc) { m_document = domDoc.cloneNode().toDocument(); m_barList = findToolBars(m_document.documentElement()); } // Return reference, for e.g. actionPropertiesElement() to modify the document QDomDocument &domDocument() { return m_document; } const QDomDocument &domDocument() const { return m_document; } /** * Return the text (user-visible name) of a given toolbar */ QString toolBarText(const QDomElement &it) const; bool m_isModified; ToolBarList &barList() { return m_barList; } const ToolBarList &barList() const { return m_barList; } private: ToolBarList m_barList; QString m_xmlFile; QDomDocument m_document; XmlType m_type; KActionCollection *m_actionCollection; }; QString XmlData::toolBarText(const QDomElement &it) const { const QLatin1String attrName("name"); QString name; QByteArray txt(it.namedItem(QStringLiteral("text")).toElement().text().toUtf8()); if (txt.isEmpty()) { txt = it.namedItem(QStringLiteral("text")).toElement().text().toUtf8(); } if (txt.isEmpty()) { name = it.attribute(attrName); } else { QByteArray domain = it.namedItem(QStringLiteral("text")).toElement().attribute(QStringLiteral("translationDomain")).toUtf8(); if (domain.isEmpty()) { domain = it.ownerDocument().documentElement().attribute(QStringLiteral("translationDomain")).toUtf8(); if (domain.isEmpty()) { domain = KLocalizedString::applicationDomain(); } } name = i18nd(domain.constData(), txt.constData()); } // the name of the toolbar might depend on whether or not // it is in kparts if ((m_type == XmlData::Shell) || (m_type == XmlData::Part)) { QString doc_name(m_document.documentElement().attribute(attrName)); name += QStringLiteral(" <") + doc_name + QLatin1Char('>'); } return name; } typedef QList<XmlData> XmlDataList; class ToolBarItem : public QListWidgetItem { public: ToolBarItem(QListWidget *parent, const QString &tag = QString(), const QString &name = QString(), const QString &statusText = QString()) : QListWidgetItem(parent), m_internalTag(tag), m_internalName(name), m_statusText(statusText), m_isSeparator(false), m_isTextAlongsideIconHidden(false) { // Drop between items, not onto items setFlags((flags() | Qt::ItemIsDragEnabled) & ~Qt::ItemIsDropEnabled); } void setInternalTag(const QString &tag) { m_internalTag = tag; } void setInternalName(const QString &name) { m_internalName = name; } void setStatusText(const QString &text) { m_statusText = text; } void setSeparator(bool sep) { m_isSeparator = sep; } void setTextAlongsideIconHidden(bool hidden) { m_isTextAlongsideIconHidden = hidden; } QString internalTag() const { return m_internalTag; } QString internalName() const { return m_internalName; } QString statusText() const { return m_statusText; } bool isSeparator() const { return m_isSeparator; } bool isTextAlongsideIconHidden() const { return m_isTextAlongsideIconHidden; } int index() const { return listWidget()->row(const_cast<ToolBarItem *>(this)); } private: QString m_internalTag; QString m_internalName; QString m_statusText; bool m_isSeparator; bool m_isTextAlongsideIconHidden; }; static QDataStream &operator<< (QDataStream &s, const ToolBarItem &item) { s << item.internalTag(); s << item.internalName(); s << item.statusText(); s << item.isSeparator(); s << item.isTextAlongsideIconHidden(); return s; } static QDataStream &operator>> (QDataStream &s, ToolBarItem &item) { QString internalTag; s >> internalTag; item.setInternalTag(internalTag); QString internalName; s >> internalName; item.setInternalName(internalName); QString statusText; s >> statusText; item.setStatusText(statusText); bool sep; s >> sep; item.setSeparator(sep); bool hidden; s >> hidden; item.setTextAlongsideIconHidden(hidden); return s; } //// ToolBarListWidget::ToolBarListWidget(QWidget *parent) : QListWidget(parent), m_activeList(true) { setDragDropMode(QAbstractItemView::DragDrop); // no internal moves } QMimeData *ToolBarListWidget::mimeData(const QList<QListWidgetItem *> items) const { if (items.isEmpty()) { - return 0; + return nullptr; } QMimeData *mimedata = new QMimeData(); QByteArray data; { QDataStream stream(&data, QIODevice::WriteOnly); // we only support single selection ToolBarItem *item = static_cast<ToolBarItem *>(items.first()); stream << *item; } mimedata->setData(QStringLiteral("application/x-kde-action-list"), data); mimedata->setData(QStringLiteral("application/x-kde-source-treewidget"), m_activeList ? "active" : "inactive"); return mimedata; } bool ToolBarListWidget::dropMimeData(int index, const QMimeData *mimeData, Qt::DropAction action) { Q_UNUSED(action) const QByteArray data = mimeData->data(QStringLiteral("application/x-kde-action-list")); if (data.isEmpty()) { return false; } QDataStream stream(data); const bool sourceIsActiveList = mimeData->data(QStringLiteral("application/x-kde-source-treewidget")) == "active"; ToolBarItem *item = new ToolBarItem(this); // needs parent, use this temporarily stream >> *item; emit dropped(this, index, item, sourceIsActiveList); return true; } ToolBarItem *ToolBarListWidget::currentItem() const { return static_cast<ToolBarItem *>(QListWidget::currentItem()); } IconTextEditDialog::IconTextEditDialog(QWidget *parent) : QDialog(parent) { setWindowTitle(i18n("Change Text")); setModal(true); QVBoxLayout *layout = new QVBoxLayout; setLayout(layout); QGridLayout *grid = new QGridLayout; grid->setMargin(0); m_lineEdit = new QLineEdit(this); m_lineEdit->setClearButtonEnabled(true); QLabel *label = new QLabel(i18n("Icon te&xt:"), this); label->setBuddy(m_lineEdit); grid->addWidget(label, 0, 0); grid->addWidget(m_lineEdit, 0, 1); m_cbHidden = new QCheckBox(i18n("&Hide text when toolbar shows text alongside icons"), this); grid->addWidget(m_cbHidden, 1, 1); layout->addLayout(grid); m_buttonBox = new QDialogButtonBox(this); m_buttonBox->setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); connect(m_buttonBox, SIGNAL(accepted()), this, SLOT(accept())); connect(m_buttonBox, SIGNAL(rejected()), this, SLOT(reject())); layout->addWidget(m_buttonBox); connect(m_lineEdit, SIGNAL(textChanged(QString)), SLOT(slotTextChanged(QString))); m_lineEdit->setFocus(); setFixedHeight(sizeHint().height()); } void IconTextEditDialog::setIconText(const QString &text) { m_lineEdit->setText(text); } QString IconTextEditDialog::iconText() const { return m_lineEdit->text().trimmed(); } void IconTextEditDialog::setTextAlongsideIconHidden(bool hidden) { m_cbHidden->setChecked(hidden); } bool IconTextEditDialog::textAlongsideIconHidden() const { return m_cbHidden->isChecked(); } void IconTextEditDialog::slotTextChanged(const QString &text) { // Do not allow empty icon text m_buttonBox->button(QDialogButtonBox::Ok)->setEnabled(!text.trimmed().isEmpty()); } class KEditToolBarWidgetPrivate { public: /** * * @param collection In the old-style constructor, this is the collection passed * to the KEditToolBar constructor. * In the xmlguifactory-based constructor, we let KXMLGUIClient create a dummy one, * but it probably isn't used. */ KEditToolBarWidgetPrivate(KEditToolBarWidget *widget, const QString &cName, KActionCollection *collection) : m_collection(collection), m_widget(widget), - m_factory(NULL), + m_factory(nullptr), m_loadedOnce(false) { m_componentName = cName; m_isPart = false; - m_helpArea = 0L; + m_helpArea = nullptr; // We want items with an icon to align with items without icon // So we use an empty QPixmap for that const int iconSize = widget->style()->pixelMetric(QStyle::PM_SmallIconSize); m_emptyIcon = QPixmap(iconSize, iconSize); m_emptyIcon.fill(Qt::transparent); } ~KEditToolBarWidgetPrivate() { } // private slots void slotToolBarSelected(int index); void slotInactiveSelectionChanged(); void slotActiveSelectionChanged(); void slotInsertButton(); void slotRemoveButton(); void slotUpButton(); void slotDownButton(); void selectActiveItem(const QString &); void slotChangeIcon(); void slotChangeIconText(); void slotDropped(ToolBarListWidget *list, int index, ToolBarItem *item, bool sourceIsActiveList); void setupLayout(); void initOldStyle(const QString &file, bool global, const QString &defaultToolbar); void initFromFactory(KXMLGUIFactory *factory, const QString &defaultToolbar); void loadToolBarCombo(const QString &defaultToolbar); void loadActions(const QDomElement &elem); QString xmlFile(const QString &xml_file) const { return xml_file.isEmpty() ? m_componentName + QStringLiteral("ui.rc") : xml_file; } /** * Load in the specified XML file and dump the raw xml */ QString loadXMLFile(const QString &_xml_file) { QString raw_xml; QString xml_file = xmlFile(_xml_file); //qDebug() << "loadXMLFile xml_file=" << xml_file; if (!QDir::isRelativePath(xml_file)) { raw_xml = KXMLGUIFactory::readConfigFile(xml_file); } else { raw_xml = KXMLGUIFactory::readConfigFile(xml_file, m_componentName); } return raw_xml; } /** * Look for a given item in the current toolbar */ QDomElement findElementForToolBarItem(const ToolBarItem *item) const { //qDebug(240) << "looking for name=" << item->internalName() << "and tag=" << item->internalTag(); for (QDomNode n = m_currentToolBarElem.firstChild(); !n.isNull(); n = n.nextSibling()) { QDomElement elem = n.toElement(); if ((elem.attribute(QStringLiteral("name")) == item->internalName()) && (elem.tagName() == item->internalTag())) { return elem; } } //qDebug(240) << "no item found in the DOM with name=" << item->internalName() << "and tag=" << item->internalTag(); return QDomElement(); } void insertActive(ToolBarItem *item, ToolBarItem *before, bool prepend = false); void removeActive(ToolBarItem *item); void moveActive(ToolBarItem *item, ToolBarItem *before); void updateLocal(QDomElement &elem); #ifndef NDEBUG void dump() const { XmlDataList::const_iterator xit = m_xmlFiles.begin(); for (; xit != m_xmlFiles.end(); ++xit) { (*xit).dump(); } } #endif QComboBox *m_toolbarCombo; QToolButton *m_upAction; QToolButton *m_removeAction; QToolButton *m_insertAction; QToolButton *m_downAction; //QValueList<QAction*> m_actionList; KActionCollection *m_collection; KEditToolBarWidget *m_widget; KXMLGUIFactory *m_factory; QString m_componentName; QPixmap m_emptyIcon; XmlData *m_currentXmlData; QDomElement m_currentToolBarElem; QString m_xmlFile; QString m_globalFile; QString m_rcFile; QDomDocument m_localDoc; ToolBarList m_barList; ToolBarListWidget *m_inactiveList; ToolBarListWidget *m_activeList; XmlDataList m_xmlFiles; QLabel *m_comboLabel; KSeparator *m_comboSeparator; QLabel *m_helpArea; QPushButton *m_changeIcon; QPushButton *m_changeIconText; bool m_isPart : 1; bool m_loadedOnce : 1; }; } using namespace KDEPrivate; class KEditToolBarPrivate { public: KEditToolBarPrivate(KEditToolBar *q): q(q), m_accept(false), m_global(false), - m_collection(0), m_factory(0), m_widget(0) {} + m_collection(nullptr), m_factory(nullptr), m_widget(nullptr) {} void init(); void _k_slotButtonClicked(QAbstractButton *button); void _k_acceptOK(bool); void _k_enableApply(bool); void okClicked(); void applyClicked(); void defaultClicked(); KEditToolBar *q; bool m_accept; // Save parameters for recreating widget after resetting toolbar bool m_global; KActionCollection *m_collection; QString m_file; QString m_defaultToolBar; KXMLGUIFactory *m_factory; KEditToolBarWidget *m_widget; QVBoxLayout *m_layout; QDialogButtonBox *m_buttonBox; }; Q_GLOBAL_STATIC(QString, s_defaultToolBarName) KEditToolBar::KEditToolBar(KActionCollection *collection, QWidget *parent) : QDialog(parent), d(new KEditToolBarPrivate(this)) { d->m_widget = new KEditToolBarWidget(collection, this); d->init(); d->m_collection = collection; } KEditToolBar::KEditToolBar(KXMLGUIFactory *factory, QWidget *parent) : QDialog(parent), d(new KEditToolBarPrivate(this)) { d->m_widget = new KEditToolBarWidget(this); d->init(); d->m_factory = factory; } void KEditToolBarPrivate::init() { m_accept = false; - m_factory = 0; + m_factory = nullptr; q->setDefaultToolBar(QString()); q->setWindowTitle(i18n("Configure Toolbars")); q->setModal(false); m_layout = new QVBoxLayout; q->setLayout(m_layout); m_layout->addWidget(m_widget); m_buttonBox = new QDialogButtonBox(q); m_buttonBox->setStandardButtons(QDialogButtonBox::RestoreDefaults | QDialogButtonBox::Ok | QDialogButtonBox::Apply | QDialogButtonBox::Cancel); KGuiItem::assign(m_buttonBox->button(QDialogButtonBox::Ok), KStandardGuiItem::ok()); KGuiItem::assign(m_buttonBox->button(QDialogButtonBox::Apply), KStandardGuiItem::apply()); KGuiItem::assign(m_buttonBox->button(QDialogButtonBox::Cancel), KStandardGuiItem::cancel()); KGuiItem::assign(m_buttonBox->button(QDialogButtonBox::RestoreDefaults), KStandardGuiItem::defaults()); q->connect(m_buttonBox, SIGNAL(clicked(QAbstractButton*)), SLOT(_k_slotButtonClicked(QAbstractButton*))); q->connect(m_buttonBox, SIGNAL(rejected()), SLOT(reject())); m_layout->addWidget(m_buttonBox); q->connect(m_widget, SIGNAL(enableOk(bool)), SLOT(_k_acceptOK(bool))); q->connect(m_widget, SIGNAL(enableOk(bool)), SLOT(_k_enableApply(bool))); _k_enableApply(false); q->setMinimumSize(q->sizeHint()); } void KEditToolBar::setResourceFile(const QString &file, bool global) { d->m_file = file; d->m_global = global; d->m_widget->load(d->m_file, d->m_global, d->m_defaultToolBar); } KEditToolBar::~KEditToolBar() { delete d; s_defaultToolBarName()->clear(); } void KEditToolBar::setDefaultToolBar(const QString &toolBarName) { if (toolBarName.isEmpty()) { d->m_defaultToolBar = *s_defaultToolBarName(); } else { d->m_defaultToolBar = toolBarName; } } void KEditToolBarPrivate::_k_acceptOK(bool b) { m_buttonBox->button(QDialogButtonBox::Ok)->setEnabled(b); m_accept = b; } void KEditToolBarPrivate::_k_enableApply(bool b) { m_buttonBox->button(QDialogButtonBox::Apply)->setEnabled(b); } void KEditToolBarPrivate::defaultClicked() { if (KMessageBox::warningContinueCancel(q, i18n("Do you really want to reset all toolbars of this application to their default? The changes will be applied immediately."), i18n("Reset Toolbars"), KGuiItem(i18n("Reset"))) != KMessageBox::Continue) { return; } KEditToolBarWidget *oldWidget = m_widget; - m_widget = 0; + m_widget = nullptr; m_accept = false; if (m_factory) { foreach (KXMLGUIClient *client, m_factory->clients()) { const QString file = client->localXMLFile(); if (file.isEmpty()) { continue; } //qDebug(240) << "Deleting local xml file" << file; // << "for client" << client << typeid(*client).name(); if (QFile::exists(file)) if (!QFile::remove(file)) { qWarning() << "Could not delete" << file; } } // Reload the xml files in all clients, now that the local files are gone oldWidget->rebuildKXMLGUIClients(); m_widget = new KEditToolBarWidget(q); m_widget->load(m_factory, m_defaultToolBar); } else { int slash = m_file.lastIndexOf(QLatin1Char('/')) + 1; if (slash) { m_file = m_file.mid(slash); } const QString xml_file = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QStringLiteral("/kxmlgui5/") + QCoreApplication::instance()->applicationName() + QLatin1Char('/') + m_file; if (QFile::exists(xml_file)) if (!QFile::remove(xml_file)) { qWarning() << "Could not delete " << xml_file; } m_widget = new KEditToolBarWidget(m_collection, q); q->setResourceFile(m_file, m_global); } // Copy the geometry to minimize UI flicker m_widget->setGeometry(oldWidget->geometry()); delete oldWidget; m_layout->insertWidget(0, m_widget); q->connect(m_widget, SIGNAL(enableOk(bool)), SLOT(_k_acceptOK(bool))); q->connect(m_widget, SIGNAL(enableOk(bool)), SLOT(_k_enableApply(bool))); _k_enableApply(false); emit q->newToolBarConfig(); emit q->newToolbarConfig(); // compat } void KEditToolBarPrivate::_k_slotButtonClicked(QAbstractButton *button) { QDialogButtonBox::StandardButton type = m_buttonBox->standardButton(button); switch (type) { case QDialogButtonBox::Ok: okClicked(); break; case QDialogButtonBox::Apply: applyClicked(); break; case QDialogButtonBox::RestoreDefaults: defaultClicked(); break; default: break; } } void KEditToolBarPrivate::okClicked() { if (!m_accept) { q->reject(); return; } // Do not rebuild GUI and emit the "newToolBarConfig" signal again here if the "Apply" // button was already pressed and no further changes were made. if (m_buttonBox->button(QDialogButtonBox::Apply)->isEnabled()) { m_widget->save(); emit q->newToolBarConfig(); emit q->newToolbarConfig(); // compat } q->accept(); } void KEditToolBarPrivate::applyClicked() { (void)m_widget->save(); _k_enableApply(false); emit q->newToolBarConfig(); emit q->newToolbarConfig(); // compat } void KEditToolBar::setGlobalDefaultToolBar(const char *toolbarName) { *s_defaultToolBarName() = QString::fromLatin1(toolbarName); } KEditToolBarWidget::KEditToolBarWidget(KActionCollection *collection, QWidget *parent) : QWidget(parent), d(new KEditToolBarWidgetPrivate(this, componentName(), collection)) { d->setupLayout(); } KEditToolBarWidget::KEditToolBarWidget(QWidget *parent) : QWidget(parent), d(new KEditToolBarWidgetPrivate(this, componentName(), KXMLGUIClient::actionCollection() /*create new one*/)) { d->setupLayout(); } KEditToolBarWidget::~KEditToolBarWidget() { delete d; } void KEditToolBarWidget::load(const QString &file, bool global, const QString &defaultToolBar) { d->initOldStyle(file, global, defaultToolBar); } void KEditToolBarWidget::load(KXMLGUIFactory *factory, const QString &defaultToolBar) { d->initFromFactory(factory, defaultToolBar); } void KEditToolBarWidgetPrivate::initOldStyle(const QString &resourceFile, bool global, const QString &defaultToolBar) { //TODO: make sure we can call this multiple times? if (m_loadedOnce) { return; } m_loadedOnce = true; //d->m_actionList = collection->actions(); // handle the merging if (global) { m_widget->loadStandardsXmlFile(); // ui_standards.rc } const QString localXML = loadXMLFile(resourceFile); m_widget->setXML(localXML, global ? true /*merge*/ : false); // first, get all of the necessary info for our local xml XmlData local(XmlData::Local, xmlFile(resourceFile), m_collection); QDomDocument domDoc; domDoc.setContent(localXML); local.setDomDocument(domDoc); m_xmlFiles.append(local); // then, the merged one (ui_standards + local xml) XmlData merge(XmlData::Merged, QString(), m_collection); merge.setDomDocument(m_widget->domDocument()); m_xmlFiles.append(merge); #ifndef NDEBUG dump(); #endif // now load in our toolbar combo box loadToolBarCombo(defaultToolBar); m_widget->adjustSize(); m_widget->setMinimumSize(m_widget->sizeHint()); } void KEditToolBarWidgetPrivate::initFromFactory(KXMLGUIFactory *factory, const QString &defaultToolBar) { //TODO: make sure we can call this multiple times? if (m_loadedOnce) { return; } m_loadedOnce = true; m_factory = factory; // add all of the client data bool first = true; foreach (KXMLGUIClient *client, factory->clients()) { if (client->xmlFile().isEmpty()) { continue; } XmlData::XmlType type = XmlData::Part; if (first) { type = XmlData::Shell; first = false; Q_ASSERT(!client->localXMLFile().isEmpty()); // where would we save changes?? } XmlData data(type, client->localXMLFile(), client->actionCollection()); QDomDocument domDoc = client->domDocument(); data.setDomDocument(domDoc); m_xmlFiles.append(data); //d->m_actionList += client->actionCollection()->actions(); } #ifndef NDEBUG //d->dump(); #endif // now load in our toolbar combo box loadToolBarCombo(defaultToolBar); m_widget->adjustSize(); m_widget->setMinimumSize(m_widget->sizeHint()); m_widget->actionCollection()->addAssociatedWidget(m_widget); foreach (QAction *action, m_widget->actionCollection()->actions()) { action->setShortcutContext(Qt::WidgetWithChildrenShortcut); } } void KEditToolBarWidget::save() { //qDebug(240) << "KEditToolBarWidget::save"; XmlDataList::Iterator it = d->m_xmlFiles.begin(); for (; it != d->m_xmlFiles.end(); ++it) { // let's not save non-modified files if (!((*it).m_isModified)) { continue; } // let's also skip (non-existent) merged files if ((*it).type() == XmlData::Merged) { continue; } // Add noMerge="1" to all the menus since we are saving the merged data QDomNodeList menuNodes = (*it).domDocument().elementsByTagName(QStringLiteral("Menu")); for (int i = 0; i < menuNodes.length(); ++i) { QDomNode menuNode = menuNodes.item(i); QDomElement menuElement = menuNode.toElement(); if (menuElement.isNull()) { continue; } menuElement.setAttribute(QStringLiteral("noMerge"), QStringLiteral("1")); } //qDebug() << (*it).domDocument().toString(); //qDebug(240) << "Saving " << (*it).xmlFile(); // if we got this far, we might as well just save it KXMLGUIFactory::saveConfigFile((*it).domDocument(), (*it).xmlFile()); } if (!d->m_factory) { return; } rebuildKXMLGUIClients(); } void KEditToolBarWidget::rebuildKXMLGUIClients() { if (!d->m_factory) { return; } const QList<KXMLGUIClient *> clients = d->m_factory->clients(); //qDebug(240) << "factory: " << clients.count() << " clients"; // remove the elements starting from the last going to the first if (clients.isEmpty()) { return; } QListIterator<KXMLGUIClient *> clientIterator = clients; clientIterator.toBack(); while (clientIterator.hasPrevious()) { KXMLGUIClient *client = clientIterator.previous(); //qDebug(240) << "factory->removeClient " << client; d->m_factory->removeClient(client); } KXMLGUIClient *firstClient = clients.first(); // now, rebuild the gui from the first to the last //qDebug(240) << "rebuilding the gui"; foreach (KXMLGUIClient *client, clients) { //qDebug(240) << "updating client " << client << " " << client->componentName() << " xmlFile=" << client->xmlFile(); QString file(client->xmlFile()); // before setting ui_standards! if (!file.isEmpty()) { // passing an empty stream forces the clients to reread the XML client->setXMLGUIBuildDocument(QDomDocument()); // for the shell, merge in ui_standards.rc if (client == firstClient) { // same assumption as in the ctor: first==shell client->loadStandardsXmlFile(); } // and this forces it to use the *new* XML file client->setXMLFile(file, client == firstClient /* merge if shell */); // [we can't use reloadXML, it doesn't load ui_standards.rc] } } // Now we can add the clients to the factory // We don't do it in the loop above because adding a part automatically // adds its plugins, so we must make sure the plugins were updated first. foreach (KXMLGUIClient *client, clients) { d->m_factory->addClient(client); } } void KEditToolBarWidgetPrivate::setupLayout() { // the toolbar name combo m_comboLabel = new QLabel(i18n("&Toolbar:"), m_widget); m_toolbarCombo = new QComboBox(m_widget); m_comboLabel->setBuddy(m_toolbarCombo); m_comboSeparator = new KSeparator(m_widget); QObject::connect(m_toolbarCombo, SIGNAL(activated(int)), m_widget, SLOT(slotToolBarSelected(int))); // QPushButton *new_toolbar = new QPushButton(i18n("&New"), this); // new_toolbar->setPixmap(BarIcon("document-new", KIconLoader::SizeSmall)); // new_toolbar->setEnabled(false); // disabled until implemented // QPushButton *del_toolbar = new QPushButton(i18n("&Delete"), this); // del_toolbar->setPixmap(BarIcon("edit-delete", KIconLoader::SizeSmall)); // del_toolbar->setEnabled(false); // disabled until implemented // our list of inactive actions QLabel *inactive_label = new QLabel(i18n("A&vailable actions:"), m_widget); m_inactiveList = new ToolBarListWidget(m_widget); m_inactiveList->setDragEnabled(true); m_inactiveList->setActiveList(false); m_inactiveList->setMinimumSize(180, 250); m_inactiveList->setDropIndicatorShown(false); // #165663 inactive_label->setBuddy(m_inactiveList); QObject::connect(m_inactiveList, SIGNAL(itemSelectionChanged()), m_widget, SLOT(slotInactiveSelectionChanged())); QObject::connect(m_inactiveList, SIGNAL(itemDoubleClicked(QListWidgetItem*)), m_widget, SLOT(slotInsertButton())); QObject::connect(m_inactiveList, SIGNAL(dropped(ToolBarListWidget*,int,ToolBarItem*,bool)), m_widget, SLOT(slotDropped(ToolBarListWidget*,int,ToolBarItem*,bool))); KListWidgetSearchLine *inactiveListSearchLine = new KListWidgetSearchLine(m_widget, m_inactiveList); inactiveListSearchLine->setPlaceholderText(i18n("Filter")); // our list of active actions QLabel *active_label = new QLabel(i18n("Curr&ent actions:"), m_widget); m_activeList = new ToolBarListWidget(m_widget); m_activeList->setDragEnabled(true); m_activeList->setActiveList(true); // With Qt-4.1 only setting MiniumWidth results in a 0-width icon column ... m_activeList->setMinimumSize(m_inactiveList->minimumWidth(), 100); active_label->setBuddy(m_activeList); QObject::connect(m_activeList, SIGNAL(itemSelectionChanged()), m_widget, SLOT(slotActiveSelectionChanged())); QObject::connect(m_activeList, SIGNAL(itemDoubleClicked(QListWidgetItem*)), m_widget, SLOT(slotRemoveButton())); QObject::connect(m_activeList, SIGNAL(dropped(ToolBarListWidget*,int,ToolBarItem*,bool)), m_widget, SLOT(slotDropped(ToolBarListWidget*,int,ToolBarItem*,bool))); KListWidgetSearchLine *activeListSearchLine = new KListWidgetSearchLine(m_widget, m_activeList); activeListSearchLine->setPlaceholderText(i18n("Filter")); // "change icon" button m_changeIcon = new QPushButton(i18n("Change &Icon..."), m_widget); m_changeIcon->setIcon(QIcon::fromTheme(QStringLiteral("preferences-desktop-icons"))); m_changeIcon->setEnabled(m_activeList->currentItem()); QObject::connect(m_changeIcon, SIGNAL(clicked()), m_widget, SLOT(slotChangeIcon())); // "change icon text" button m_changeIconText = new QPushButton(i18n("Change Te&xt..."), m_widget); m_changeIconText->setIcon(QIcon::fromTheme(QStringLiteral("edit-rename"))); - m_changeIconText->setEnabled(m_activeList->currentItem() != 0); + m_changeIconText->setEnabled(m_activeList->currentItem() != nullptr); QObject::connect(m_changeIconText, SIGNAL(clicked()), m_widget, SLOT(slotChangeIconText())); // The buttons in the middle m_upAction = new QToolButton(m_widget); m_upAction->setIcon(QIcon::fromTheme(QStringLiteral("go-up"))); m_upAction->setEnabled(false); m_upAction->setAutoRepeat(true); QObject::connect(m_upAction, SIGNAL(clicked()), m_widget, SLOT(slotUpButton())); m_insertAction = new QToolButton(m_widget); m_insertAction->setIcon(QIcon::fromTheme(QApplication::isRightToLeft() ? QStringLiteral("go-previous") : QStringLiteral("go-next"))); m_insertAction->setEnabled(false); QObject::connect(m_insertAction, SIGNAL(clicked()), m_widget, SLOT(slotInsertButton())); m_removeAction = new QToolButton(m_widget); m_removeAction->setIcon(QIcon::fromTheme(QApplication::isRightToLeft() ? QStringLiteral("go-next") : QStringLiteral("go-previous"))); m_removeAction->setEnabled(false); QObject::connect(m_removeAction, SIGNAL(clicked()), m_widget, SLOT(slotRemoveButton())); m_downAction = new QToolButton(m_widget); m_downAction->setIcon(QIcon::fromTheme(QStringLiteral("go-down"))); m_downAction->setEnabled(false); m_downAction->setAutoRepeat(true); QObject::connect(m_downAction, SIGNAL(clicked()), m_widget, SLOT(slotDownButton())); m_helpArea = new QLabel(m_widget); m_helpArea->setWordWrap(true); // now start with our layouts QVBoxLayout *top_layout = new QVBoxLayout(m_widget); top_layout->setMargin(0); QVBoxLayout *name_layout = new QVBoxLayout(); QHBoxLayout *list_layout = new QHBoxLayout(); QVBoxLayout *inactive_layout = new QVBoxLayout(); QVBoxLayout *active_layout = new QVBoxLayout(); QHBoxLayout *changeIcon_layout = new QHBoxLayout(); QGridLayout *button_layout = new QGridLayout(); name_layout->addWidget(m_comboLabel); name_layout->addWidget(m_toolbarCombo); // name_layout->addWidget(new_toolbar); // name_layout->addWidget(del_toolbar); button_layout->setSpacing(0); button_layout->setRowStretch(0, 10); button_layout->addWidget(m_upAction, 1, 1); button_layout->addWidget(m_removeAction, 2, 0); button_layout->addWidget(m_insertAction, 2, 2); button_layout->addWidget(m_downAction, 3, 1); button_layout->setRowStretch(4, 10); inactive_layout->addWidget(inactive_label); inactive_layout->addWidget(inactiveListSearchLine); inactive_layout->addWidget(m_inactiveList, 1); active_layout->addWidget(active_label); active_layout->addWidget(activeListSearchLine); active_layout->addWidget(m_activeList, 1); active_layout->addLayout(changeIcon_layout); changeIcon_layout->addWidget(m_changeIcon); changeIcon_layout->addStretch(1); changeIcon_layout->addWidget(m_changeIconText); list_layout->addLayout(inactive_layout); list_layout->addLayout(button_layout); list_layout->addLayout(active_layout); top_layout->addLayout(name_layout); top_layout->addWidget(m_comboSeparator); top_layout->addLayout(list_layout, 10); top_layout->addWidget(m_helpArea); top_layout->addWidget(new KSeparator(m_widget)); } void KEditToolBarWidgetPrivate::loadToolBarCombo(const QString &defaultToolBar) { const QLatin1String attrName("name"); // just in case, we clear our combo m_toolbarCombo->clear(); int defaultToolBarId = -1; int count = 0; // load in all of the toolbar names into this combo box XmlDataList::const_iterator xit = m_xmlFiles.constBegin(); for (; xit != m_xmlFiles.constEnd(); ++xit) { // skip the merged one in favor of the local one, // so that we can change icons // This also makes the app-defined named for "mainToolBar" appear rather than the ui_standards-defined name. if ((*xit).type() == XmlData::Merged) { continue; } // each xml file may have any number of toolbars ToolBarList::const_iterator it = (*xit).barList().begin(); for (; it != (*xit).barList().constEnd(); ++it) { const QString text = (*xit).toolBarText(*it); m_toolbarCombo->addItem(text); const QString name = (*it).attribute(attrName); if (defaultToolBarId == -1 && name == defaultToolBar) { defaultToolBarId = count; } count++; } } const bool showCombo = (count > 1); m_comboLabel->setVisible(showCombo); m_comboSeparator->setVisible(showCombo); m_toolbarCombo->setVisible(showCombo); if (defaultToolBarId == -1) { defaultToolBarId = 0; } // we want to the specified item selected and its actions loaded m_toolbarCombo->setCurrentIndex(defaultToolBarId); slotToolBarSelected(m_toolbarCombo->currentIndex()); } void KEditToolBarWidgetPrivate::loadActions(const QDomElement &elem) { const QLatin1String tagSeparator("Separator"); const QLatin1String tagMerge("Merge"); const QLatin1String tagActionList("ActionList"); const QLatin1String tagAction("Action"); const QLatin1String attrName("name"); int sep_num = 0; QString sep_name(QStringLiteral("separator_%1")); // clear our lists m_inactiveList->clear(); m_activeList->clear(); m_insertAction->setEnabled(false); m_removeAction->setEnabled(false); m_upAction->setEnabled(false); m_downAction->setEnabled(false); // We'll use this action collection KActionCollection *actionCollection = m_currentXmlData->actionCollection(); // store the names of our active actions QSet<QString> active_list; // Filtering message requested by translators (scripting). KLocalizedString nameFilter = ki18nc("@item:intable Action name in toolbar editor", "%1"); // see if our current action is in this toolbar QDomNode n = elem.firstChild(); for (; !n.isNull(); n = n.nextSibling()) { QDomElement it = n.toElement(); if (it.isNull()) { continue; } if (it.tagName() == tagSeparator) { ToolBarItem *act = new ToolBarItem(m_activeList, tagSeparator, sep_name.arg(sep_num++), QString()); act->setSeparator(true); act->setText(SEPARATORSTRING); it.setAttribute(attrName, act->internalName()); continue; } if (it.tagName() == tagMerge) { // Merge can be named or not - use the name if there is one QString name = it.attribute(attrName); ToolBarItem *act = new ToolBarItem(m_activeList, tagMerge, name, i18n("This element will be replaced with all the elements of an embedded component.")); if (name.isEmpty()) { act->setText(i18n("<Merge>")); } else { act->setText(i18n("<Merge %1>", name)); } continue; } if (it.tagName() == tagActionList) { ToolBarItem *act = new ToolBarItem(m_activeList, tagActionList, it.attribute(attrName), i18n("This is a dynamic list of actions. You can move it, but if you remove it you will not be able to re-add it.")); act->setText(i18n("ActionList: %1", it.attribute(attrName))); continue; } // iterate through this client's actions // This used to iterate through _all_ actions, but we don't support // putting any action into any client... foreach (QAction *action, actionCollection->actions()) { // do we have a match? if (it.attribute(attrName) == action->objectName()) { // we have a match! ToolBarItem *act = new ToolBarItem(m_activeList, it.tagName(), action->objectName(), action->toolTip()); act->setText(nameFilter.subs(KLocalizedString::removeAcceleratorMarker(action->iconText())).toString()); act->setIcon(!action->icon().isNull() ? action->icon() : m_emptyIcon); act->setTextAlongsideIconHidden(action->priority() < QAction::NormalPriority); active_list.insert(action->objectName()); break; } } } // go through the rest of the collection foreach (QAction *action, actionCollection->actions()) { // skip our active ones if (active_list.contains(action->objectName())) { continue; } ToolBarItem *act = new ToolBarItem(m_inactiveList, tagAction, action->objectName(), action->toolTip()); act->setText(nameFilter.subs(KLocalizedString::removeAcceleratorMarker(action->text())).toString()); act->setIcon(!action->icon().isNull() ? action->icon() : m_emptyIcon); } m_inactiveList->sortItems(Qt::AscendingOrder); // finally, add default separators to the inactive list - ToolBarItem *act = new ToolBarItem(0L, tagSeparator, sep_name.arg(sep_num++), QString()); + ToolBarItem *act = new ToolBarItem(nullptr, tagSeparator, sep_name.arg(sep_num++), QString()); act->setSeparator(true); act->setText(SEPARATORSTRING); m_inactiveList->insertItem(0, act); } KActionCollection *KEditToolBarWidget::actionCollection() const { return d->m_collection; } void KEditToolBarWidgetPrivate::slotToolBarSelected(int index) { const QLatin1String attrName("name"); // We need to find the XmlData and toolbar element for this index // To do that, we do the same iteration as the one which filled in the combobox. int toolbarNumber = 0; XmlDataList::iterator xit = m_xmlFiles.begin(); for (; xit != m_xmlFiles.end(); ++xit) { // skip the merged one in favor of the local one, // so that we can change icons if ((*xit).type() == XmlData::Merged) { continue; } // each xml file may have any number of toolbars ToolBarList::Iterator it = (*xit).barList().begin(); for (; it != (*xit).barList().end(); ++it) { // is this our toolbar? if (toolbarNumber == index) { // save our current settings m_currentXmlData = & (*xit); m_currentToolBarElem = *it; //qDebug() << "found toolbar" << m_currentXmlData->toolBarText(*it) << "m_currentXmlData set to"; m_currentXmlData->dump(); // If this is a Merged xmldata, clicking the "change icon" button would assert... Q_ASSERT(m_currentXmlData->type() != XmlData::Merged); // load in our values loadActions(m_currentToolBarElem); if ((*xit).type() == XmlData::Part || (*xit).type() == XmlData::Shell) { m_widget->setDOMDocument((*xit).domDocument()); } return; } ++toolbarNumber; } } } void KEditToolBarWidgetPrivate::slotInactiveSelectionChanged() { if (!m_inactiveList->selectedItems().isEmpty()) { m_insertAction->setEnabled(true); QString statusText = static_cast<ToolBarItem *>(m_inactiveList->selectedItems().first())->statusText(); m_helpArea->setText(i18nc("@label Action tooltip in toolbar editor, below the action list", "%1", statusText)); } else { m_insertAction->setEnabled(false); m_helpArea->setText(QString()); } } void KEditToolBarWidgetPrivate::slotActiveSelectionChanged() { - ToolBarItem *toolitem = 0; + ToolBarItem *toolitem = nullptr; if (!m_activeList->selectedItems().isEmpty()) { toolitem = static_cast<ToolBarItem *>(m_activeList->selectedItems().first()); } m_removeAction->setEnabled(toolitem); m_changeIcon->setEnabled(toolitem && toolitem->internalTag() == QStringLiteral("Action")); m_changeIconText->setEnabled(toolitem && toolitem->internalTag() == QStringLiteral("Action")); if (toolitem) { m_upAction->setEnabled(toolitem->index() != 0); m_downAction->setEnabled(toolitem->index() != toolitem->listWidget()->count() - 1); QString statusText = toolitem->statusText(); m_helpArea->setText(i18nc("@label Action tooltip in toolbar editor, below the action list", "%1", statusText)); } else { m_upAction->setEnabled(false); m_downAction->setEnabled(false); m_helpArea->setText(QString()); } } void KEditToolBarWidgetPrivate::slotInsertButton() { QString internalName = static_cast<ToolBarItem *>(m_inactiveList->currentItem())->internalName(); insertActive(m_inactiveList->currentItem(), m_activeList->currentItem(), false); // we're modified, so let this change emit m_widget->enableOk(true); slotToolBarSelected(m_toolbarCombo->currentIndex()); selectActiveItem(internalName); } void KEditToolBarWidgetPrivate::selectActiveItem(const QString &internalName) { int activeItemCount = m_activeList->count(); for (int i = 0; i < activeItemCount; i++) { ToolBarItem *item = static_cast<ToolBarItem *>(m_activeList->item(i)); if (item->internalName() == internalName) { m_activeList->setCurrentItem(item); break; } } } void KEditToolBarWidgetPrivate::slotRemoveButton() { removeActive(m_activeList->currentItem()); slotToolBarSelected(m_toolbarCombo->currentIndex()); } void KEditToolBarWidgetPrivate::insertActive(ToolBarItem *item, ToolBarItem *before, bool prepend) { if (!item) { return; } QDomElement new_item; // let's handle the separator specially if (item->isSeparator()) { new_item = m_widget->domDocument().createElement(QStringLiteral("Separator")); } else { new_item = m_widget->domDocument().createElement(QStringLiteral("Action")); } new_item.setAttribute(QStringLiteral("name"), item->internalName()); Q_ASSERT(!m_currentToolBarElem.isNull()); if (before) { // we have the item in the active list which is before the new // item.. so let's try our best to add our new item right after it QDomElement elem = findElementForToolBarItem(before); Q_ASSERT(!elem.isNull()); m_currentToolBarElem.insertAfter(new_item, elem); } else { // simply put it at the beginning or the end of the list. if (prepend) { m_currentToolBarElem.insertBefore(new_item, m_currentToolBarElem.firstChild()); } else { m_currentToolBarElem.appendChild(new_item); } } // and set this container as a noMerge m_currentToolBarElem.setAttribute(QStringLiteral("noMerge"), QStringLiteral("1")); // update the local doc updateLocal(m_currentToolBarElem); } void KEditToolBarWidgetPrivate::removeActive(ToolBarItem *item) { if (!item) { return; } // we're modified, so let this change emit m_widget->enableOk(true); // now iterate through to find the child to nuke QDomElement elem = findElementForToolBarItem(item); if (!elem.isNull()) { // nuke myself! m_currentToolBarElem.removeChild(elem); // and set this container as a noMerge m_currentToolBarElem.setAttribute(QStringLiteral("noMerge"), QStringLiteral("1")); // update the local doc updateLocal(m_currentToolBarElem); } } void KEditToolBarWidgetPrivate::slotUpButton() { ToolBarItem *item = m_activeList->currentItem(); if (!item) { Q_ASSERT(false); return; } int row = item->listWidget()->row(item) - 1; // make sure we're not the top item already if (row < 0) { Q_ASSERT(false); return; } // we're modified, so let this change emit m_widget->enableOk(true); moveActive(item, static_cast<ToolBarItem *>(item->listWidget()->item(row - 1))); } void KEditToolBarWidgetPrivate::moveActive(ToolBarItem *item, ToolBarItem *before) { QDomElement e = findElementForToolBarItem(item); if (e.isNull()) { return; } // remove item m_activeList->takeItem(m_activeList->row(item)); // put it where it's supposed to go m_activeList->insertItem(m_activeList->row(before) + 1, item); // make it selected again m_activeList->setCurrentItem(item); // and do the real move in the DOM if (!before) { m_currentToolBarElem.insertBefore(e, m_currentToolBarElem.firstChild()); } else { m_currentToolBarElem.insertAfter(e, findElementForToolBarItem(before)); } // and set this container as a noMerge m_currentToolBarElem.setAttribute(QStringLiteral("noMerge"), QStringLiteral("1")); // update the local doc updateLocal(m_currentToolBarElem); } void KEditToolBarWidgetPrivate::slotDownButton() { ToolBarItem *item = m_activeList->currentItem(); if (!item) { Q_ASSERT(false); return; } // make sure we're not the bottom item already int newRow = item->listWidget()->row(item) + 1; if (newRow >= item->listWidget()->count()) { Q_ASSERT(false); return; } // we're modified, so let this change emit m_widget->enableOk(true); moveActive(item, static_cast<ToolBarItem *>(item->listWidget()->item(newRow))); } void KEditToolBarWidgetPrivate::updateLocal(QDomElement &elem) { XmlDataList::Iterator xit = m_xmlFiles.begin(); for (; xit != m_xmlFiles.end(); ++xit) { if ((*xit).type() == XmlData::Merged) { continue; } if ((*xit).type() == XmlData::Shell || (*xit).type() == XmlData::Part) { if (m_currentXmlData->xmlFile() == (*xit).xmlFile()) { (*xit).m_isModified = true; return; } continue; } (*xit).m_isModified = true; const QLatin1String attrName("name"); ToolBarList::Iterator it = (*xit).barList().begin(); for (; it != (*xit).barList().end(); ++it) { QString name((*it).attribute(attrName)); QString tag((*it).tagName()); if ((tag != elem.tagName()) || (name != elem.attribute(attrName))) { continue; } QDomElement toolbar = (*xit).domDocument().documentElement().toElement(); toolbar.replaceChild(elem, (*it)); return; } // just append it QDomElement toolbar = (*xit).domDocument().documentElement().toElement(); Q_ASSERT(!toolbar.isNull()); toolbar.appendChild(elem); } } void KEditToolBarWidgetPrivate::slotChangeIcon() { m_currentXmlData->dump(); Q_ASSERT(m_currentXmlData->type() != XmlData::Merged); QString icon = KIconDialog::getIcon(KIconLoader::Toolbar, KIconLoader::Action, false, 0, false, // all defaults m_widget, i18n("Change Icon")); if (icon.isEmpty()) { return; } ToolBarItem *item = m_activeList->currentItem(); //qDebug() << item; if (item) { item->setIcon(QIcon::fromTheme(icon)); m_currentXmlData->m_isModified = true; // Get hold of ActionProperties tag QDomElement elem = KXMLGUIFactory::actionPropertiesElement(m_currentXmlData->domDocument()); // Find or create an element for this action QDomElement act_elem = KXMLGUIFactory::findActionByName(elem, item->internalName(), true /*create*/); Q_ASSERT(!act_elem.isNull()); act_elem.setAttribute(QStringLiteral("icon"), icon); // we're modified, so let this change emit m_widget->enableOk(true); } } void KEditToolBarWidgetPrivate::slotChangeIconText() { m_currentXmlData->dump(); ToolBarItem *item = m_activeList->currentItem(); if (item) { QString iconText = item->text(); bool hidden = item->isTextAlongsideIconHidden(); IconTextEditDialog dialog(m_widget); dialog.setIconText(iconText); dialog.setTextAlongsideIconHidden(hidden); bool ok = dialog.exec() == QDialog::Accepted; iconText = dialog.iconText(); hidden = dialog.textAlongsideIconHidden(); bool hiddenChanged = hidden != item->isTextAlongsideIconHidden(); bool iconTextChanged = iconText != item->text(); if (!ok || (!hiddenChanged && !iconTextChanged)) { return; } item->setText(iconText); item->setTextAlongsideIconHidden(hidden); Q_ASSERT(m_currentXmlData->type() != XmlData::Merged); m_currentXmlData->m_isModified = true; // Get hold of ActionProperties tag QDomElement elem = KXMLGUIFactory::actionPropertiesElement(m_currentXmlData->domDocument()); // Find or create an element for this action QDomElement act_elem = KXMLGUIFactory::findActionByName(elem, item->internalName(), true /*create*/); Q_ASSERT(!act_elem.isNull()); if (iconTextChanged) { act_elem.setAttribute(QStringLiteral("iconText"), iconText); } if (hiddenChanged) { act_elem.setAttribute(QStringLiteral("priority"), hidden ? QAction::LowPriority : QAction::NormalPriority); } // we're modified, so let this change emit m_widget->enableOk(true); } } void KEditToolBarWidgetPrivate::slotDropped(ToolBarListWidget *list, int index, ToolBarItem *item, bool sourceIsActiveList) { //qDebug() << "slotDropped list=" << (list==m_activeList?"activeList":"inactiveList") // << "index=" << index << "sourceIsActiveList=" << sourceIsActiveList; if (list == m_activeList) { - ToolBarItem *after = index > 0 ? static_cast<ToolBarItem *>(list->item(index - 1)) : 0; + ToolBarItem *after = index > 0 ? static_cast<ToolBarItem *>(list->item(index - 1)) : nullptr; //qDebug() << "after" << after->text() << after->internalTag(); if (sourceIsActiveList) { // has been dragged within the active list (moved). moveActive(item, after); } else { // dragged from the inactive list to the active list insertActive(item, after, true); } } else if (list == m_inactiveList) { // has been dragged to the inactive list -> remove from the active list. removeActive(item); } delete item; // not needed anymore. must be deleted before slotToolBarSelected clears the lists // we're modified, so let this change emit m_widget->enableOk(true); slotToolBarSelected(m_toolbarCombo->currentIndex()); } void KEditToolBar::showEvent(QShowEvent *event) { if (!event->spontaneous()) { // The dialog has been shown, enable toolbar editing if (d->m_factory) { // call the xmlgui-factory version d->m_widget->load(d->m_factory, d->m_defaultToolBar); } else { // call the action collection version d->m_widget->load(d->m_file, d->m_global, d->m_defaultToolBar); } KToolBar::setToolBarsEditable(true); } QDialog::showEvent(event); } void KEditToolBar::hideEvent(QHideEvent *event) { // The dialog has been hidden, disable toolbar editing KToolBar::setToolBarsEditable(false); QDialog::hideEvent(event); } #include "moc_kedittoolbar.cpp" #include "moc_kedittoolbar_p.cpp" diff --git a/src/kedittoolbar.h b/src/kedittoolbar.h index 7f69586..bf00368 100644 --- a/src/kedittoolbar.h +++ b/src/kedittoolbar.h @@ -1,172 +1,172 @@ /* This file is part of the KDE libraries Copyright (C) 2000 Kurt Granroth <granroth@kde.org> Copyright (C) 2006 Hamish Rodda <rodda@kde.org> 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 KEDITTOOLBAR_H #define KEDITTOOLBAR_H #include <QDialog> #include <kxmlgui_export.h> class KActionCollection; class KEditToolBarPrivate; class KXMLGUIFactory; /** * @short A dialog used to customize or configure toolbars. * * This dialog only works if your application uses the XML UI * framework for creating menus and toolbars. It depends on the XML * files to describe the toolbar layouts and it requires the actions * to determine which buttons are active. * * Typically you do not need to use it directly as KXmlGuiWindow::setupGUI * takes care of it. * * If you use plugListAction you need to overload saveNewToolbarConfig() * to plug actions again: * * \code * void MyClass::saveNewToolbarConfig() * { * KXmlGuiWindow::saveNewToolbarConfig(); * plugActionList( "list1", list1Actions ); * plugActionList( "list2", list2Actions ); * } * \endcode * * When created, KEditToolBar takes a KXMLGUIFactory object, and uses it to * find all of the action collections and XML files (there is one of each for the * mainwindow, but there could be more, when adding other XMLGUI clients like * KParts or plugins). The editor aims to be semi-intelligent about where it * assigns any modifications. In other words, it will not write out part specific * changes to your application's main XML file. * * KXmlGuiWindow and KParts::MainWindow take care of creating KEditToolBar correctly * and connecting to its newToolBarConfig slot, but if you really really want to do it * yourself, see the KXmlGuiWindow::configureToolbars() and KXmlGuiWindow::saveNewToolbarConfig() code. * * \image html kedittoolbar.png "KDE Toolbar Editor (KWrite)" * * @author Kurt Granroth <granroth@kde.org> * @maintainer David Faure <faure@kde.org> */ class KXMLGUI_EXPORT KEditToolBar : public QDialog { Q_OBJECT public: /** * Old constructor for apps that do not use components. * This constructor is somewhat deprecated, since it doesn't work * with any KXMLGuiClient being added to the mainwindow. * You really want to use the other constructor. * * You @em must pass along your collection of actions (some of which appear in your toolbars). * * @param collection The collection of actions to work on. * @param parent The parent of the dialog. */ explicit KEditToolBar(KActionCollection *collection, - QWidget *parent = 0); + QWidget *parent = nullptr); /** * Main constructor. * * The main parameter, @p factory, is a pointer to the * XML GUI factory object for your application. It contains a list * of all of the GUI clients (along with the action collections and * xml files) and the toolbar editor uses that. * * Use this like so: * \code * KEditToolBar edit(factory()); * if (edit.exec()) * ... * \endcode * * @param factory Your application's factory object * @param parent The usual parent for the dialog. */ explicit KEditToolBar(KXMLGUIFactory *factory, - QWidget *parent = 0); + QWidget *parent = nullptr); /// destructor ~KEditToolBar(); /** * Sets the default toolbar that will be selected when the dialog is shown. * If not set, or QString() is passed in, the global default tool bar name * will be used. * @param toolBarName the name of the tool bar * @see setGlobalDefaultToolBar */ void setDefaultToolBar(const QString &toolBarName); /** * The name (absolute or relative) of your application's UI resource file * is assumed to be share/apps/appname/appnameui.rc though this can be * overridden by calling this method. * * The global parameter controls whether or not the * global resource file is used. If this is @p true, then you may * edit all of the actions in your toolbars -- global ones and * local one. If it is @p false, then you may edit only your * application's entries. The only time you should set this to * false is if your application does not use the global resource * file at all (very rare). * * @param xmlfile The application's local resource file. * @param global If @p true, then the global resource file will also * be parsed. */ void setResourceFile(const QString &file, bool global = true); /** * Sets the default toolbar which will be auto-selected for all * KEditToolBar instances. Can be overridden on a per-dialog basis * by calling setDefaultToolBar( const QString& ) on the dialog. * @param toolbarName the name of the tool bar */ static void setGlobalDefaultToolBar(const char *toolBarName); // TODO should be const QString& Q_SIGNALS: /** * Signal emitted when 'apply' or 'ok' is clicked or toolbars were reset. * Connect to it, to plug action lists and to call applyMainWindowSettings * (see sample code in this class's documentation) */ void newToolBarConfig(); QT_MOC_COMPAT void newToolbarConfig(); protected: void showEvent(QShowEvent *event) Q_DECL_OVERRIDE; void hideEvent(QHideEvent *event) Q_DECL_OVERRIDE; private: friend class KEditToolBarPrivate; KEditToolBarPrivate *const d; Q_PRIVATE_SLOT(d, void _k_slotButtonClicked(QAbstractButton *)) Q_PRIVATE_SLOT(d, void _k_acceptOK(bool)) Q_PRIVATE_SLOT(d, void _k_enableApply(bool)) Q_DISABLE_COPY(KEditToolBar) }; #endif // _KEDITTOOLBAR_H diff --git a/src/kedittoolbar_p.h b/src/kedittoolbar_p.h index 327e1fb..c65fae6 100644 --- a/src/kedittoolbar_p.h +++ b/src/kedittoolbar_p.h @@ -1,259 +1,259 @@ /* This file is part of the KDE libraries Copyright (C) 2000 Kurt Granroth <granroth@kde.org> Copyright (C) 2006 Hamish Rodda <rodda@kde.org> 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 KEDITTOOLBARP_H #define KEDITTOOLBARP_H #include "kxmlguiclient.h" #include <QDialog> #include <QListWidget> class QDialogButtonBox; class QLineEdit; class QCheckBox; namespace KDEPrivate { class ToolBarItem; class KEditToolBarWidgetPrivate; class ToolBarListWidget : public QListWidget { Q_OBJECT public: - ToolBarListWidget(QWidget *parent = 0); + ToolBarListWidget(QWidget *parent = nullptr); void makeVisible(QListWidgetItem *item) { scrollTo(indexFromItem(item)); } ToolBarItem *currentItem() const; void setActiveList(bool isActiveList) { m_activeList = isActiveList; } Q_SIGNALS: void dropped(ToolBarListWidget *list, int index, ToolBarItem *item, bool sourceIsActiveList); protected: Qt::DropActions supportedDropActions() const Q_DECL_OVERRIDE { return Qt::MoveAction; } QStringList mimeTypes() const Q_DECL_OVERRIDE { return QStringList() << QStringLiteral("application/x-kde-action-list"); } QMimeData *mimeData(const QList<QListWidgetItem *> items) const Q_DECL_OVERRIDE; bool dropMimeData(int index, const QMimeData *data, Qt::DropAction action) Q_DECL_OVERRIDE; // Skip internal dnd handling in QListWidget ---- how is one supposed to figure this out // without reading the QListWidget code !? void dropEvent(QDropEvent *ev) Q_DECL_OVERRIDE { QAbstractItemView::dropEvent(ev); } private: bool m_activeList; }; class IconTextEditDialog : public QDialog { Q_OBJECT public: - explicit IconTextEditDialog(QWidget *parent = 0); + explicit IconTextEditDialog(QWidget *parent = nullptr); public: void setIconText(const QString &text); QString iconText() const; void setTextAlongsideIconHidden(bool hidden); bool textAlongsideIconHidden() const; private Q_SLOTS: void slotTextChanged(const QString &text); private: QLineEdit *m_lineEdit; QCheckBox *m_cbHidden; QDialogButtonBox *m_buttonBox; }; /** * @short A widget used to customize or configure toolbars * * This is the widget that does all of the work for the * KEditToolBar dialog. In most cases, you will want to use the * dialog instead of this widget directly. * * Typically, you would use this widget only if you wanted to embed * the toolbar editing directly into your existing configure or * preferences dialog. * * This widget only works if your application uses the XML UI * framework for creating menus and toolbars. It depends on the XML * files to describe the toolbar layouts and it requires the actions * to determine which buttons are active. * * @author Kurt Granroth <granroth@kde.org> * @internal */ class KEditToolBarWidget : public QWidget, virtual public KXMLGUIClient { Q_OBJECT public: /** * Old constructor for apps that do not use components. * This constructor is somewhat deprecated, since it doesn't work * with any KXMLGuiClient being added to the mainwindow. * You really want to use the other constructor. * * You @em must pass along your collection of actions (some of which appear in your toolbars). * Then call old-style load. * * @param collection The collection of actions to work on * @param parent This widget's parent */ explicit KEditToolBarWidget(KActionCollection *collection, - QWidget *parent = 0L); + QWidget *parent = nullptr); /** * Main constructor. * * Use this like so: * \code * KEditToolBarWidget widget(this); * widget.load(factory()); * ... * \endcode * * @param factory Your application's factory object * @param parent This widget's parent */ - explicit KEditToolBarWidget(QWidget *parent = 0L); + explicit KEditToolBarWidget(QWidget *parent = nullptr); /** * Destructor. Note that any changes done in this widget will * @p NOT be saved in the destructor. You @p must call save() * to do that. */ virtual ~KEditToolBarWidget(); /** * Old-style load. * * Loads the toolbar configuration into the widget. Should be called before being shown. * * @param resourceFile the name (absolute or relative) of your application's UI * resource file. If it is left blank, then the resource file: share/apps/appname/appnameui.rc * is used. This is the same resource file that is used by the * default createGUI function in KMainWindow so you're usually * pretty safe in leaving it blank. * * @param global controls whether or not the * global resource file is used. If this is true, then you may * edit all of the actions in your toolbars -- global ones and * local one. If it is false, then you may edit only your * application's entries. The only time you should set this to * false is if your application does not use the global resource * file at all (very rare) * * @param defaultToolBar the default toolbar that will be selected when the dialog is shown. * If not set, or QString() is passed in, the global default tool bar name * will be used. * * @see KEditToolBar */ void load(const QString &resourceFile, bool global = true, const QString &defaultToolBar = QString()); /** * Loads the toolbar configuration into the widget. Should be called before being shown. * * @param factory pointer to the XML GUI factory object for your application. * It contains a list of all of the GUI clients (along with the action * collections and xml files) and the toolbar editor uses that. * * @param defaultToolBar the default toolbar that will be selected when the dialog is shown. * If not set, or QString() is passed in, the global default tool bar name * will be used. * * @see KEditToolBar */ void load(KXMLGUIFactory *factory, const QString &defaultToolBar = QString()); /** * @internal Reimplemented for internal purposes. */ KActionCollection *actionCollection() const Q_DECL_OVERRIDE; /** * Save any changes the user made. The file will be in the user's * local directory (usually $HOME/.kde/share/apps/\<appname\>). The * filename will be the one specified in the constructor.. or the * made up one if the filename was NULL. * */ void save(); /** * Remove and readd all KMXLGUIClients to update the GUI */ void rebuildKXMLGUIClients(); Q_SIGNALS: /** * Emitted whenever any modifications are made by the user. */ void enableOk(bool); private: Q_PRIVATE_SLOT(d, void slotToolBarSelected(int index)) Q_PRIVATE_SLOT(d, void slotInactiveSelectionChanged()) Q_PRIVATE_SLOT(d, void slotActiveSelectionChanged()) Q_PRIVATE_SLOT(d, void slotInsertButton()) Q_PRIVATE_SLOT(d, void slotRemoveButton()) Q_PRIVATE_SLOT(d, void slotUpButton()) Q_PRIVATE_SLOT(d, void slotDownButton()) Q_PRIVATE_SLOT(d, void slotChangeIcon()) Q_PRIVATE_SLOT(d, void slotChangeIconText()) Q_PRIVATE_SLOT(d, void slotDropped(ToolBarListWidget *, int, ToolBarItem *, bool)) private: friend class KEditToolBarWidgetPrivate; KEditToolBarWidgetPrivate *const d; Q_DISABLE_COPY(KEditToolBarWidget) }; } #endif diff --git a/src/kgesturemap.cpp b/src/kgesturemap.cpp index 2d79ba8..06ab37f 100644 --- a/src/kgesturemap.cpp +++ b/src/kgesturemap.cpp @@ -1,363 +1,363 @@ /* This file is part of the KDE libraries Copyright (C) 2006,2007 Andreas Hartmetz (ahartmetz@gmail.com) 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 "kgesturemap_p.h" #include <qapplication.h> #include <QAction> #include <QActionEvent> #include <QDebug> /* This is a class for internal use by the KDE libraries only. This class may change or go away without notice so don't try to use it in non-kdelibs code. */ class KGestureMapContainer { public: KGestureMap gestureMap; }; Q_GLOBAL_STATIC(KGestureMapContainer, g_instance) KGestureMap::~KGestureMap() { } KGestureMap *KGestureMap::self() { return &g_instance()->gestureMap; } KGestureMap::KGestureMap() { m_gestureTimeout.setSingleShot(true); connect(&m_gestureTimeout, SIGNAL(timeout()), this, SLOT(stopAcquisition())); //It would be nice to install the filter on demand. Unfortunately, //undesired behavior might result due to changing invocation //orders of different event filters. if (qApp) { qApp->installEventFilter(this); } } void KGestureMap::setShapeGesture(QAction *act, const KShapeGesture &gesture) { if (!gesture.isValid() || !act) { return; } qDebug() << "KGestureMap::addGesture(KShapeGesture ...)"; if (m_shapeGestures.contains(gesture)) { qWarning() << "Replacing an action for a gesture already taken"; } m_shapeGestures.insert(gesture, act); } void KGestureMap::setRockerGesture(QAction *act, const KRockerGesture &gesture) { if (!gesture.isValid() || !act) { return; } qDebug() << "KGestureMap::addGesture(KRockerGesture ...)"; if (m_rockerGestures.contains(gesture)) { qWarning() << "Replacing an action for a gesture already taken"; } m_rockerGestures.insert(gesture, act); } void KGestureMap::setDefaultShapeGesture(QAction *act, const KShapeGesture &gesture) { if (!gesture.isValid() || !act) { return; } qDebug() << "KGestureMap::addGesture(KShapeGesture ...)"; if (m_defaultShapeGestures.contains(gesture)) { qWarning() << "Replacing an action for a gesture already taken"; } m_defaultShapeGestures.insert(gesture, act); } void KGestureMap::setDefaultRockerGesture(QAction *act, const KRockerGesture &gesture) { if (!gesture.isValid() || !act) { return; } qDebug() << "KGestureMap::addGesture(KRockerGesture ...)"; if (m_defaultRockerGestures.contains(gesture)) { qWarning() << "Replacing an action for a gesture already taken"; } m_defaultRockerGestures.insert(gesture, act); } void KGestureMap::removeAllGestures(QAction *kact) { KShapeGesture activeGesture; ShapeGestureHash::iterator si = m_shapeGestures.begin(); ShapeGestureHash::iterator send = m_shapeGestures.end(); for (; si != send; ++si) { if (si.value() == kact) { m_shapeGestures.remove(si.key()); break; } } si = m_defaultShapeGestures.begin(); send = m_defaultShapeGestures.end(); for (; si != send; ++si) { if (si.value() == kact) { m_defaultShapeGestures.remove(si.key()); break; } } RockerGestureHash::iterator ri = m_rockerGestures.begin(); RockerGestureHash::iterator rend = m_rockerGestures.end(); for (; ri != rend; ++ri) { if (ri.value() == kact) { m_rockerGestures.remove(ri.key()); break; } } ri = m_defaultRockerGestures.begin(); rend = m_defaultRockerGestures.end(); for (; ri != rend; ++ri) { if (ri.value() == kact) { m_defaultRockerGestures.remove(ri.key()); break; } } } QAction *KGestureMap::findAction(const KShapeGesture &gesture) const { return m_shapeGestures.value(gesture); } QAction *KGestureMap::findAction(const KRockerGesture &gesture) const { return m_rockerGestures.value(gesture); } void KGestureMap::installEventFilterOnMe(QApplication *app) { app->installEventFilter(this); } KShapeGesture KGestureMap::shapeGesture(const QAction *kact) const { KShapeGesture activeGesture; ShapeGestureHash::const_iterator it = m_shapeGestures.constBegin(); ShapeGestureHash::const_iterator end = m_shapeGestures.constEnd(); for (; it != end; ++it) { if (it.value() == kact) { activeGesture = it.key(); break; } } return activeGesture; } KShapeGesture KGestureMap::defaultShapeGesture(const QAction *kact) const { KShapeGesture defaultGesture; ShapeGestureHash::const_iterator it = m_defaultShapeGestures.constBegin(); ShapeGestureHash::const_iterator end = m_defaultShapeGestures.constEnd(); for (; it != end; ++it) { if (it.value() == kact) { defaultGesture = it.key(); break; } } return defaultGesture; } KRockerGesture KGestureMap::rockerGesture(const QAction *kact) const { KRockerGesture activeGesture; RockerGestureHash::const_iterator it = m_rockerGestures.constBegin(); RockerGestureHash::const_iterator end = m_rockerGestures.constEnd(); for (; it != end; ++it) { if (it.value() == kact) { activeGesture = it.key(); break; } } return activeGesture; } KRockerGesture KGestureMap::defaultRockerGesture(const QAction *kact) const { KRockerGesture defaultGesture; RockerGestureHash::const_iterator it = m_defaultRockerGestures.constBegin(); RockerGestureHash::const_iterator end = m_defaultRockerGestures.constEnd(); for (; it != end; ++it) { if (it.value() == kact) { defaultGesture = it.key(); break; } } return defaultGesture; } inline int KGestureMap::bitCount(int n) { int count = 0; while (n) { n &= (n - 1); count++; } return count; } void KGestureMap::handleAction(QAction *kact) { if (!kact) { return; } qDebug() << "handleAction"; //TODO: only activate in the action's context, just like keyboard shortcuts kact->trigger(); return; } void KGestureMap::matchShapeGesture() { //TODO: tune and tweak until satisfied with result :) m_shapeGesture.setShape(m_points); float dist, minDist = 20.0; - QAction *bestMatch = 0; + QAction *bestMatch = nullptr; for (QHash<KShapeGesture, QAction *>::const_iterator it = m_shapeGestures.constBegin(); it != m_shapeGestures.constEnd(); ++it) { dist = m_shapeGesture.distance(it.key(), 1000.0); if (dist < minDist) { minDist = dist; bestMatch = it.value(); } } handleAction(bestMatch); } //slot void KGestureMap::stopAcquisition() { m_gestureTimeout.stop(); m_acquiring = false; } //TODO: Probably kwin, kded and others should not have a gesture map. //Maybe making them friends and providing a private "die()" function would work. /* * Act on rocker gestures immediately and collect movement data for evaluation. * The decision when to consume and when to relay an event is quite tricky. * I decided to only consume clicks that belong to completed rocker gestures. * A user might e.g. go back in a browser several times using rocker gestures, * thus changing what's under the cursor every time. This might lead to * unintended clicks on links where there was free space before. */ bool KGestureMap::eventFilter(QObject *obj, QEvent *e) { //disable until it does not interfere with other input any more return false; Q_UNUSED(obj); int type = e->type(); //catch right-clicks disguised as context menu events. if we ignore a //context menu event caused by a right-click, it should get resent //as a right-click event, according to documentation. //### this is preliminary if (type == QEvent::ContextMenu) { QContextMenuEvent *cme = static_cast<QContextMenuEvent *>(e); if (cme->reason() == QContextMenuEvent::Mouse) { cme->ignore(); return true; } return false; } if (type < QEvent::MouseButtonPress || type > QEvent::MouseMove) { return false; } QMouseEvent *me = static_cast<QMouseEvent *>(e); if (type == QEvent::MouseButtonPress) { int nButtonsDown = bitCount(me->buttons()); qDebug() << "number of buttons down:" << nButtonsDown; //right button down starts gesture acquisition if (nButtonsDown == 1 && me->button() == Qt::RightButton) { //"startAcquisition()" m_acquiring = true; m_gestureTimeout.start(4000); qDebug() << "========================"; m_points.clear(); m_points.append(me->pos()); return true; } else if (nButtonsDown != 2) { return false; } //rocker gestures. do not trigger any movement gestures from now on. stopAcquisition(); int buttonHeld = me->buttons() ^ me->button(); m_rockerGesture.setButtons(static_cast<Qt::MouseButton>(buttonHeld), me->button()); QAction *match = m_rockerGestures.value(m_rockerGesture); if (!match) { return false; } handleAction(match); return true; } if (m_acquiring) { if (type == QEvent::MouseMove) { m_points.append(me->pos()); //abort to avoid using too much memory. 1010 points should be enough //for everyone! :) //next reallocation of m_points would happen at 1012 items if (m_points.size() > 1010) { stopAcquisition(); } return true; } else if (type == QEvent::MouseButtonRelease && me->button() == Qt::RightButton) { stopAcquisition(); //TODO: pre-selection of gestures by length (optimization), if necessary //possibly apply other heuristics //then try all remaining gestures for sufficiently small distance int dist = 0; for (int i = 1; i < m_points.size(); i++) { dist += (m_points[i] - m_points[i - 1]).manhattanLength(); if (dist > 40) { matchShapeGesture(); return true; } //this was probably a small glitch while right-clicking if we get here. //TODO: open the context menu or do whatever happens on right-click (how?) } return false; } } return false; } diff --git a/src/khelpmenu.cpp b/src/khelpmenu.cpp index 4c01fd4..27b384f 100644 --- a/src/khelpmenu.cpp +++ b/src/khelpmenu.cpp @@ -1,391 +1,391 @@ /* * This file is part of the KDE Libraries * Copyright (C) 1999-2000 Espen Sand (espen@kde.org) * * 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. * */ // I (espen) prefer that header files are included alphabetically #include "khelpmenu.h" #include <QtCore/QTimer> #include <QAction> #include <QApplication> #include <QDialogButtonBox> #include <QLabel> #include <QMenu> #include <QStyle> #include <QWidget> #include <QWhatsThis> #include <QFile> #include <QDir> #include <QUrl> #include <QBoxLayout> #include <QDesktopServices> #include <QStandardPaths> #include "kaboutapplicationdialog.h" #include "kaboutkdedialog_p.h" #include "kbugreport.h" #include "kswitchlanguagedialog_p.h" #include <kaboutdata.h> #include <kauthorized.h> #include <kiconloader.h> #include <klocalizedstring.h> #include <kstandardaction.h> using namespace KDEPrivate; class KHelpMenuPrivate { public: KHelpMenuPrivate() - : mSwitchApplicationLanguage(0), + : mSwitchApplicationLanguage(nullptr), mActionsCreated(false), - mSwitchApplicationLanguageAction(0), + mSwitchApplicationLanguageAction(nullptr), mAboutData(KAboutData::applicationData()) { - mMenu = 0; - mAboutApp = 0; - mAboutKDE = 0; - mBugReport = 0; - mDonateAction = 0; - mHandBookAction = 0; - mWhatsThisAction = 0; - mReportBugAction = 0; - mAboutAppAction = 0; - mAboutKDEAction = 0; + mMenu = nullptr; + mAboutApp = nullptr; + mAboutKDE = nullptr; + mBugReport = nullptr; + mDonateAction = nullptr; + mHandBookAction = nullptr; + mWhatsThisAction = nullptr; + mReportBugAction = nullptr; + mAboutAppAction = nullptr; + mAboutKDEAction = nullptr; } ~KHelpMenuPrivate() { delete mMenu; delete mAboutApp; delete mAboutKDE; delete mBugReport; delete mSwitchApplicationLanguage; } void createActions(KHelpMenu *q); QMenu *mMenu; QDialog *mAboutApp; KAboutKdeDialog *mAboutKDE; KBugReport *mBugReport; QAction *mDonateAction; KSwitchLanguageDialog *mSwitchApplicationLanguage; // TODO evaluate if we use static_cast<QWidget*>(parent()) instead of mParent to win that bit of memory QWidget *mParent; QString mAboutAppText; bool mShowWhatsThis; bool mActionsCreated; QAction *mHandBookAction, *mWhatsThisAction; QAction *mReportBugAction, *mSwitchApplicationLanguageAction, *mAboutAppAction, *mAboutKDEAction; KAboutData mAboutData; }; KHelpMenu::KHelpMenu(QWidget *parent, const QString &aboutAppText, bool showWhatsThis) : QObject(parent), d(new KHelpMenuPrivate) { d->mAboutAppText = aboutAppText; d->mShowWhatsThis = showWhatsThis; d->mParent = parent; d->createActions(this); } KHelpMenu::KHelpMenu(QWidget *parent, const KAboutData &aboutData, bool showWhatsThis) : QObject(parent), d(new KHelpMenuPrivate) { d->mShowWhatsThis = showWhatsThis; d->mParent = parent; d->mAboutData = aboutData; d->createActions(this); } KHelpMenu::~KHelpMenu() { delete d; } void KHelpMenuPrivate::createActions(KHelpMenu *q) { if (mActionsCreated) { return; } mActionsCreated = true; if (KAuthorized::authorizeAction(QStringLiteral("help_contents"))) { mHandBookAction = KStandardAction::helpContents(q, SLOT(appHelpActivated()), q); } if (mShowWhatsThis && KAuthorized::authorizeAction(QStringLiteral("help_whats_this"))) { mWhatsThisAction = KStandardAction::whatsThis(q, SLOT(contextHelpActivated()), q); } if (KAuthorized::authorizeAction(QStringLiteral("help_report_bug")) && !mAboutData.bugAddress().isEmpty()) { mReportBugAction = KStandardAction::reportBug(q, SLOT(reportBug()), q); } if (KAuthorized::authorizeAction(QStringLiteral("help_donate")) && mAboutData.bugAddress() == QStringLiteral("submit@bugs.kde.org")) { mDonateAction = KStandardAction::donate(q, &KHelpMenu::donate, q); } if (KAuthorized::authorizeAction(QStringLiteral("switch_application_language"))) { if (KLocalizedString::availableApplicationTranslations().count() > 1) { mSwitchApplicationLanguageAction = KStandardAction::create(KStandardAction::SwitchApplicationLanguage, q, SLOT(switchApplicationLanguage()), q); } } if (KAuthorized::authorizeAction(QStringLiteral("help_about_app"))) { mAboutAppAction = KStandardAction::aboutApp(q, SLOT(aboutApplication()), q); } if (KAuthorized::authorizeAction(QStringLiteral("help_about_kde"))) { mAboutKDEAction = KStandardAction::aboutKDE(q, SLOT(aboutKDE()), q); } } // Used in the non-xml-gui case, like kfind or ksnapshot's help button. QMenu *KHelpMenu::menu() { if (!d->mMenu) { d->mMenu = new QMenu(); connect(d->mMenu, SIGNAL(destroyed()), this, SLOT(menuDestroyed())); d->mMenu->setTitle(i18n("&Help")); d->createActions(this); bool need_separator = false; if (d->mHandBookAction) { d->mMenu->addAction(d->mHandBookAction); need_separator = true; } if (d->mWhatsThisAction) { d->mMenu->addAction(d->mWhatsThisAction); need_separator = true; } if (d->mReportBugAction) { if (need_separator) { d->mMenu->addSeparator(); } d->mMenu->addAction(d->mReportBugAction); need_separator = true; } if (d->mDonateAction) { if (need_separator) { d->mMenu->addSeparator(); } d->mMenu->addAction(d->mDonateAction); need_separator = true; } if (d->mSwitchApplicationLanguageAction) { if (need_separator) { d->mMenu->addSeparator(); } d->mMenu->addAction(d->mSwitchApplicationLanguageAction); need_separator = true; } if (need_separator) { d->mMenu->addSeparator(); } if (d->mAboutAppAction) { d->mMenu->addAction(d->mAboutAppAction); } if (d->mAboutKDEAction) { d->mMenu->addAction(d->mAboutKDEAction); } } return d->mMenu; } QAction *KHelpMenu::action(MenuId id) const { switch (id) { case menuHelpContents: return d->mHandBookAction; break; case menuWhatsThis: return d->mWhatsThisAction; break; case menuReportBug: return d->mReportBugAction; break; case menuSwitchLanguage: return d->mSwitchApplicationLanguageAction; break; case menuAboutApp: return d->mAboutAppAction; break; case menuAboutKDE: return d->mAboutKDEAction; break; case menuDonate: return d->mDonateAction; break; } - return 0; + return nullptr; } void KHelpMenu::appHelpActivated() { QDesktopServices::openUrl(QUrl(QStringLiteral("help:/"))); } void KHelpMenu::aboutApplication() { if (receivers(SIGNAL(showAboutApplication())) > 0) { emit showAboutApplication(); } else { // if (d->mAboutData) if (!d->mAboutApp) { d->mAboutApp = new KAboutApplicationDialog(d->mAboutData, d->mParent); connect(d->mAboutApp, SIGNAL(finished(int)), this, SLOT(dialogFinished())); } d->mAboutApp->show(); } #if 0 // KF5: when can this happen? else { if (!d->mAboutApp) { d->mAboutApp = new QDialog(d->mParent, Qt::Dialog); QString caption = QGuiApplication::applicationDisplayName(); if (caption.isEmpty()) { caption = QCoreApplication::applicationName(); } d->mAboutApp->setWindowTitle(i18n("About %1", caption)); d->mAboutApp->setObjectName(QStringLiteral("about")); connect(d->mAboutApp, SIGNAL(finished(int)), this, SLOT(dialogFinished())); const int spacingHint = d->mAboutApp->style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing); const int marginHint = d->mAboutApp->style()->pixelMetric(QStyle::PM_DefaultChildMargin); QVBoxLayout *vbox = new QVBoxLayout; d->mAboutApp->setLayout(vbox); QHBoxLayout *hbox = new QHBoxLayout; hbox->setSpacing(spacingHint * 3); hbox->setMargin(marginHint * 1); const int size = IconSize(KIconLoader::Dialog); QLabel *label1 = new QLabel(d->mAboutApp); label1->setPixmap(qApp->windowIcon().pixmap(size, size)); QLabel *label2 = new QLabel(d->mAboutApp); label2->setText(d->mAboutAppText); hbox->addWidget(label1); hbox->addWidget(label2); vbox->addLayout(hbox); QDialogButtonBox *buttonBox = new QDialogButtonBox(d->mAboutApp); buttonBox->setStandardButtons(QDialogButtonBox::Close); connect(buttonBox, SIGNAL(accepted()), d->mAboutApp, SLOT(accept())); connect(buttonBox, SIGNAL(rejected()), d->mAboutApp, SLOT(reject())); vbox->addWidget(buttonBox); } d->mAboutApp->show(); } #endif } void KHelpMenu::aboutKDE() { if (!d->mAboutKDE) { d->mAboutKDE = new KAboutKdeDialog(d->mParent); connect(d->mAboutKDE, SIGNAL(finished(int)), this, SLOT(dialogFinished())); } d->mAboutKDE->show(); } void KHelpMenu::reportBug() { if (!d->mBugReport) { d->mBugReport = new KBugReport(d->mAboutData, d->mParent); connect(d->mBugReport, SIGNAL(finished(int)), this, SLOT(dialogFinished())); } d->mBugReport->show(); } void KHelpMenu::switchApplicationLanguage() { if (!d->mSwitchApplicationLanguage) { d->mSwitchApplicationLanguage = new KSwitchLanguageDialog(d->mParent); connect(d->mSwitchApplicationLanguage, SIGNAL(finished(int)), this, SLOT(dialogFinished())); } d->mSwitchApplicationLanguage->show(); } void KHelpMenu::donate() { QDesktopServices::openUrl(QUrl(QStringLiteral("https://www.kde.org/donate?app=%1").arg(d->mAboutData.componentName()))); } void KHelpMenu::dialogFinished() { QTimer::singleShot(0, this, SLOT(timerExpired())); } void KHelpMenu::timerExpired() { if (d->mAboutKDE && !d->mAboutKDE->isVisible()) { - delete d->mAboutKDE; d->mAboutKDE = 0; + delete d->mAboutKDE; d->mAboutKDE = nullptr; } if (d->mBugReport && !d->mBugReport->isVisible()) { - delete d->mBugReport; d->mBugReport = 0; + delete d->mBugReport; d->mBugReport = nullptr; } if (d->mSwitchApplicationLanguage && !d->mSwitchApplicationLanguage->isVisible()) { - delete d->mSwitchApplicationLanguage; d->mSwitchApplicationLanguage = 0; + delete d->mSwitchApplicationLanguage; d->mSwitchApplicationLanguage = nullptr; } if (d->mAboutApp && !d->mAboutApp->isVisible()) { - delete d->mAboutApp; d->mAboutApp = 0; + delete d->mAboutApp; d->mAboutApp = nullptr; } } void KHelpMenu::menuDestroyed() { - d->mMenu = 0; + d->mMenu = nullptr; } void KHelpMenu::contextHelpActivated() { QWhatsThis::enterWhatsThisMode(); } diff --git a/src/khelpmenu.h b/src/khelpmenu.h index dfe3297..8664458 100644 --- a/src/khelpmenu.h +++ b/src/khelpmenu.h @@ -1,274 +1,274 @@ /* * This file is part of the KDE Libraries * Copyright (C) 1999-2000 Espen Sand (espen@kde.org) * * 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 KHELPMENU_H #define KHELPMENU_H #include <kxmlgui_export.h> #include <QtCore/QObject> #include <QtCore/QString> class QMenu; class QWidget; class QAction; class KAboutData; class KHelpMenuPrivate; /** * @short Standard %KDE help menu with dialog boxes. * * This class provides the standard %KDE help menu with the default "about" * dialog boxes and help entry. * * This class is used in KMainWindow so * normally you don't need to use this class yourself. However, if you * need the help menu or any of its dialog boxes in your code that is * not subclassed from KMainWindow you should use this class. * * The usage is simple: * * \code * mHelpMenu = new KHelpMenu( this, <someText> ); * kmenubar->addMenu(mHelpMenu->menu() ); * \endcode * * or if you just want to open a dialog box: * * \code * mHelpMenu = new KHelpMenu( this, <someText> ); * connect( this, SIGNAL(someSignal()), mHelpMenu,SLOT(aboutKDE())); * \endcode * * IMPORTANT: * The first time you use KHelpMenu::menu(), a QMenu object is * allocated. Only one object is created by the class so if you call * KHelpMenu::menu() twice or more, the same pointer is returned. The class * will destroy the popupmenu in the destructor so do not delete this * pointer yourself. * * The KHelpMenu object will be deleted when its parent is destroyed but you * can delete it yourself if you want. The code below will always work. * * \code * MyClass::~MyClass() * { * delete mHelpMenu; * } * \endcode * * * Using your own "about application" dialog box: * * The standard "about application" dialog box is quite simple. If you * need a dialog box with more functionality you must design that one * yourself. When you want to display the dialog, you simply need to * connect the help menu signal showAboutApplication() to your slot. * * \code * void MyClass::myFunc() * { * .. * KHelpMenu *helpMenu = new KHelpMenu( this ); * connect( helpMenu, SIGNAL(showAboutApplication()), * this, SLOT(myDialogSlot())); * .. * } * * void MyClass::myDialogSlot() * { * <activate your custom dialog> * } * \endcode * * \image html khelpmenu.png "KDE Help Menu" * * KHelpMenu respects Kiosk settings (see the KAuthorized namespace in the * KConfig framework). In particular, system administrators can disable items * on this menu using some subset of the following configuration: * @verbatim [KDE Action Restrictions][$i] actions/help_contents=false actions/help_whats_this=false actions/help_report_bug=false actions/switch_application_language=false actions/help_about_app=false actions/help_about_kde=false @endverbatim * * @author Espen Sand (espen@kde.org) */ class KXMLGUI_EXPORT KHelpMenu : public QObject { Q_OBJECT public: /** * Constructor. * * @param parent The parent of the dialog boxes. The boxes are modeless * and will be centered with respect to the parent. * @param aboutAppText User definable string that is used in the * default application dialog box. * @param showWhatsThis Decides whether a "Whats this" entry will be * added to the dialog. * */ - explicit KHelpMenu(QWidget *parent = 0, const QString &aboutAppText = QString(), + explicit KHelpMenu(QWidget *parent = nullptr, const QString &aboutAppText = QString(), bool showWhatsThis = true); /** * Constructor. * * This alternative constructor is mainly useful if you want to * overide the standard actions (aboutApplication(), aboutKDE(), * helpContents(), reportBug, and optionally whatsThis). * * @param parent The parent of the dialog boxes. The boxes are modeless * and will be centered with respect to the parent. * @param aboutData User and app data used in the About app dialog * @param showWhatsThis Decides whether a "Whats this" entry will be * added to the dialog. */ KHelpMenu(QWidget *parent, const KAboutData &aboutData, bool showWhatsThis = true); /** * Destructor * * Destroys dialogs and the menu pointer retuned by menu */ ~KHelpMenu(); /** * Returns a popup menu you can use in the menu bar or where you * need it. * * The returned menu is configured with an icon, a title and * menu entries. Therefore adding the returned pointer to your menu * is enougth to have access to the help menu. * * Note: This method will only create one instance of the menu. If * you call this method twice or more the same pointer is returned. */ QMenu *menu(); enum MenuId { menuHelpContents = 0, menuWhatsThis = 1, menuAboutApp = 2, menuAboutKDE = 3, menuReportBug = 4, menuSwitchLanguage = 5, menuDonate = 6 //< @since 5.24 }; /** * Returns the QAction * associated with the given parameter * Will return NULL pointers if menu() has not been called * * @param id The id of the action of which you want to get QAction * */ QAction *action(MenuId id) const; public Q_SLOTS: /** * Opens the help page for the application. The application name is * used as a key to determine what to display and the system will attempt * to open \<appName\>/index.html. */ void appHelpActivated(); /** * Activates What's This help for the application. */ void contextHelpActivated(); /** * Opens an application specific dialog box. * * The method will try to open the about box using the following steps: * - If the showAboutApplication() signal is connected, then it will be called. * This means there is an application defined aboutBox. * - If the aboutData was set in the constructor a KAboutApplicationDialog will be created. * - Else a default about box using the aboutAppText from the constructor will be created. */ void aboutApplication(); /** * Opens the standard "About KDE" dialog box. */ void aboutKDE(); /** * Opens the standard "Report Bugs" dialog box. */ void reportBug(); /** * Opens the changing default application language dialog box. */ void switchApplicationLanguage(); /** * Opens the donate url. * @since 5.24 */ void donate(); private Q_SLOTS: /** * Connected to the menu pointer (if created) to detect a delete * operation on the pointer. You should not delete the pointer in your * code yourself. Let the KHelpMenu destructor do the job. */ void menuDestroyed(); /** * Connected to the dialogs (about kde and bug report) to detect * when they are finished. */ void dialogFinished(); /** * This slot will delete a dialog (about kde or bug report) if the * dialog pointer is not zero and the dialog is not visible. This * slot is activated by a one shot timer started in dialogHidden */ void timerExpired(); Q_SIGNALS: /** * This signal is emitted from aboutApplication() if no * "about application" string has been defined. The standard * application specific dialog box that is normally activated in * aboutApplication() will not be displayed when this signal * is emitted. */ void showAboutApplication(); private: KHelpMenuPrivate *const d; }; #endif diff --git a/src/kkeysequencewidget.cpp b/src/kkeysequencewidget.cpp index 7ecddcf..de02e2d 100644 --- a/src/kkeysequencewidget.cpp +++ b/src/kkeysequencewidget.cpp @@ -1,863 +1,863 @@ /* This file is part of the KDE libraries Copyright (C) 1998 Mark Donohoe <donohoe@kde.org> Copyright (C) 2001 Ellis Whitehead <ellis@kde.org> Copyright (C) 2007 Andreas Hartmetz <ahartmetz@gmail.com> 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 "config-xmlgui.h" #include "kkeysequencewidget.h" #include "kkeysequencewidget_p.h" #include <QAction> #include <QKeyEvent> #include <QTimer> #include <QtCore/QHash> #include <QHBoxLayout> #include <QToolButton> #include <QApplication> #include <QDebug> #include <klocalizedstring.h> #include <kmessagebox.h> #include <kkeyserver.h> #if HAVE_GLOBALACCEL # include <kglobalaccel.h> #endif #include "kactioncollection.h" #if HAVE_GLOBALACCEL && QT_VERSION < QT_VERSION_CHECK(5, 6, 0) static uint qHash(const QKeySequence &seq) { return qHash(seq.toString()); } #endif class KKeySequenceWidgetPrivate { public: KKeySequenceWidgetPrivate(KKeySequenceWidget *q); void init(); static QKeySequence appendToSequence(const QKeySequence &seq, int keyQt); static bool isOkWhenModifierless(int keyQt); void updateShortcutDisplay(); void startRecording(); /** * Conflicts the key sequence @a seq with a current standard * shortcut? */ bool conflictWithStandardShortcuts(const QKeySequence &seq); /** * Conflicts the key sequence @a seq with a current local * shortcut? */ bool conflictWithLocalShortcuts(const QKeySequence &seq); /** * Conflicts the key sequence @a seq with a current global * shortcut? */ bool conflictWithGlobalShortcuts(const QKeySequence &seq); /** * Get permission to steal the shortcut @seq from the standard shortcut @a std. */ bool stealStandardShortcut(KStandardShortcut::StandardShortcut std, const QKeySequence &seq); bool checkAgainstStandardShortcuts() const { return checkAgainstShortcutTypes & KKeySequenceWidget::StandardShortcuts; } bool checkAgainstGlobalShortcuts() const { return checkAgainstShortcutTypes & KKeySequenceWidget::GlobalShortcuts; } bool checkAgainstLocalShortcuts() const { return checkAgainstShortcutTypes & KKeySequenceWidget::LocalShortcuts; } void controlModifierlessTimout() { if (nKey != 0 && !modifierKeys) { // No modifier key pressed currently. Start the timout modifierlessTimeout.start(600); } else { // A modifier is pressed. Stop the timeout modifierlessTimeout.stop(); } } void cancelRecording() { keySequence = oldKeySequence; doneRecording(); } #if HAVE_GLOBALACCEL bool promptStealShortcutSystemwide( QWidget *parent, const QHash<QKeySequence, QList<KGlobalShortcutInfo> > &shortcuts, const QKeySequence &sequence) { if (shortcuts.isEmpty()) { // Usage error. Just say no return false; } QString clashingKeys; Q_FOREACH (const QKeySequence &seq, shortcuts.keys()) { Q_FOREACH (const KGlobalShortcutInfo &info, shortcuts[seq]) { clashingKeys += i18n("Shortcut '%1' in Application %2 for action %3\n", seq.toString(), info.componentFriendlyName(), info.friendlyName()); } } const int hashSize = shortcuts.size(); QString message = i18ncp("%1 is the number of conflicts (hidden), %2 is the key sequence of the shortcut that is problematic", "The shortcut '%2' conflicts with the following key combination:\n", "The shortcut '%2' conflicts with the following key combinations:\n", hashSize, sequence.toString()); message += clashingKeys; QString title = i18ncp("%1 is the number of shortcuts with which there is a conflict", "Conflict with Registered Global Shortcut", "Conflict with Registered Global Shortcuts", hashSize); return KMessageBox::warningContinueCancel(parent, message, title, KGuiItem(i18n("Reassign"))) == KMessageBox::Continue; } #endif //private slot void doneRecording(bool validate = true); //members KKeySequenceWidget *const q; QHBoxLayout *layout; KKeySequenceButton *keyButton; QToolButton *clearButton; QKeySequence keySequence; QKeySequence oldKeySequence; QTimer modifierlessTimeout; bool allowModifierless; uint nKey; uint modifierKeys; bool isRecording; bool multiKeyShortcutsAllowed; QString componentName; //! Check the key sequence against KStandardShortcut::find() KKeySequenceWidget::ShortcutTypes checkAgainstShortcutTypes; /** * The list of action to check against for conflict shortcut */ QList<QAction *> checkList; // deprecated /** * The list of action collections to check against for conflict shortcut */ QList<KActionCollection *> checkActionCollections; /** * The action to steal the shortcut from. */ QList<QAction *> stealActions; bool stealShortcuts(const QList<QAction *> &actions, const QKeySequence &seq); void wontStealShortcut(QAction *item, const QKeySequence &seq); }; KKeySequenceWidgetPrivate::KKeySequenceWidgetPrivate(KKeySequenceWidget *q) : q(q) - , layout(NULL) - , keyButton(NULL) - , clearButton(NULL) + , layout(nullptr) + , keyButton(nullptr) + , clearButton(nullptr) , allowModifierless(false) , nKey(0) , modifierKeys(0) , isRecording(false) , multiKeyShortcutsAllowed(true) , componentName() , checkAgainstShortcutTypes(KKeySequenceWidget::LocalShortcuts | KKeySequenceWidget::GlobalShortcuts) , stealActions() {} bool KKeySequenceWidgetPrivate::stealShortcuts( const QList<QAction *> &actions, const QKeySequence &seq) { const int listSize = actions.size(); QString title = i18ncp("%1 is the number of conflicts", "Shortcut Conflict", "Shortcut Conflicts", listSize); QString conflictingShortcuts; Q_FOREACH (const QAction *action, actions) { conflictingShortcuts += i18n("Shortcut '%1' for action '%2'\n", action->shortcut().toString(QKeySequence::NativeText), KLocalizedString::removeAcceleratorMarker(action->text())); } QString message = i18ncp("%1 is the number of ambigious shortcut clashes (hidden)", "The \"%2\" shortcut is ambiguous with the following shortcut.\n" "Do you want to assign an empty shortcut to this action?\n" "%3", "The \"%2\" shortcut is ambiguous with the following shortcuts.\n" "Do you want to assign an empty shortcut to these actions?\n" "%3", listSize, seq.toString(QKeySequence::NativeText), conflictingShortcuts); if (KMessageBox::warningContinueCancel(q, message, title, KGuiItem(i18n("Reassign"))) != KMessageBox::Continue) { return false; } return true; } void KKeySequenceWidgetPrivate::wontStealShortcut(QAction *item, const QKeySequence &seq) { QString title(i18n("Shortcut conflict")); QString msg(i18n("<qt>The '%1' key combination is already used by the <b>%2</b> action.<br>" "Please select a different one.</qt>", seq.toString(QKeySequence::NativeText), KLocalizedString::removeAcceleratorMarker(item->text()))); KMessageBox::sorry(q, msg, title); } KKeySequenceWidget::KKeySequenceWidget(QWidget *parent) : QWidget(parent), d(new KKeySequenceWidgetPrivate(this)) { d->init(); setFocusProxy(d->keyButton); connect(d->keyButton, SIGNAL(clicked()), this, SLOT(captureKeySequence())); connect(d->clearButton, SIGNAL(clicked()), this, SLOT(clearKeySequence())); connect(&d->modifierlessTimeout, SIGNAL(timeout()), this, SLOT(doneRecording())); //TODO: how to adopt style changes at runtime? /*QFont modFont = d->clearButton->font(); modFont.setStyleHint(QFont::TypeWriter); d->clearButton->setFont(modFont);*/ d->updateShortcutDisplay(); } void KKeySequenceWidgetPrivate::init() { layout = new QHBoxLayout(q); layout->setMargin(0); keyButton = new KKeySequenceButton(this, q); keyButton->setFocusPolicy(Qt::StrongFocus); keyButton->setIcon(QIcon::fromTheme(QStringLiteral("configure"))); keyButton->setToolTip(i18n("Click on the button, then enter the shortcut like you would in the program.\nExample for Ctrl+A: hold the Ctrl key and press A.")); layout->addWidget(keyButton); clearButton = new QToolButton(q); layout->addWidget(clearButton); if (qApp->isLeftToRight()) { clearButton->setIcon(QIcon::fromTheme(QStringLiteral("edit-clear-locationbar-rtl"))); } else { clearButton->setIcon(QIcon::fromTheme(QStringLiteral("edit-clear-locationbar-ltr"))); } } KKeySequenceWidget::~KKeySequenceWidget() { delete d; } KKeySequenceWidget::ShortcutTypes KKeySequenceWidget::checkForConflictsAgainst() const { return d->checkAgainstShortcutTypes; } void KKeySequenceWidget::setComponentName(const QString &componentName) { d->componentName = componentName; } bool KKeySequenceWidget::multiKeyShortcutsAllowed() const { return d->multiKeyShortcutsAllowed; } void KKeySequenceWidget::setMultiKeyShortcutsAllowed(bool allowed) { d->multiKeyShortcutsAllowed = allowed; } void KKeySequenceWidget::setCheckForConflictsAgainst(ShortcutTypes types) { d->checkAgainstShortcutTypes = types; } void KKeySequenceWidget::setModifierlessAllowed(bool allow) { d->allowModifierless = allow; } bool KKeySequenceWidget::isKeySequenceAvailable(const QKeySequence &keySequence) const { if (keySequence.isEmpty()) { return true; } return !(d->conflictWithLocalShortcuts(keySequence) || d->conflictWithGlobalShortcuts(keySequence) || d->conflictWithStandardShortcuts(keySequence)); } bool KKeySequenceWidget::isModifierlessAllowed() { return d->allowModifierless; } void KKeySequenceWidget::setClearButtonShown(bool show) { d->clearButton->setVisible(show); } #ifndef KXMLGUI_NO_DEPRECATED void KKeySequenceWidget::setCheckActionList(const QList<QAction *> &checkList) // deprecated { d->checkList = checkList; Q_ASSERT(d->checkActionCollections.isEmpty()); // don't call this method if you call setCheckActionCollections! } #endif void KKeySequenceWidget::setCheckActionCollections(const QList<KActionCollection *> &actionCollections) { d->checkActionCollections = actionCollections; } //slot void KKeySequenceWidget::captureKeySequence() { d->startRecording(); } QKeySequence KKeySequenceWidget::keySequence() const { return d->keySequence; } //slot void KKeySequenceWidget::setKeySequence(const QKeySequence &seq, Validation validate) { // oldKeySequence holds the key sequence before recording started, if setKeySequence() // is called while not recording then set oldKeySequence to the existing sequence so // that the keySequenceChanged() signal is emitted if the new and previous key // sequences are different if (!d->isRecording) { d->oldKeySequence = d->keySequence; } d->keySequence = seq; d->doneRecording(validate == Validate); } //slot void KKeySequenceWidget::clearKeySequence() { setKeySequence(QKeySequence()); } //slot void KKeySequenceWidget::applyStealShortcut() { QSet<KActionCollection *> changedCollections; Q_FOREACH (QAction *stealAction, d->stealActions) { // Stealing a shortcut means setting it to an empty one. stealAction->setShortcuts(QList<QKeySequence>()); // The following code will find the action we are about to // steal from and save it's actioncollection. - KActionCollection *parentCollection = 0; + KActionCollection *parentCollection = nullptr; foreach (KActionCollection *collection, d->checkActionCollections) { if (collection->actions().contains(stealAction)) { parentCollection = collection; break; } } // Remember the changed collection if (parentCollection) { changedCollections.insert(parentCollection); } } Q_FOREACH (KActionCollection *col, changedCollections) { col->writeSettings(); } d->stealActions.clear(); } void KKeySequenceWidgetPrivate::startRecording() { nKey = 0; modifierKeys = 0; oldKeySequence = keySequence; keySequence = QKeySequence(); isRecording = true; keyButton->grabKeyboard(); if (!QWidget::keyboardGrabber()) { qWarning() << "Failed to grab the keyboard! Most likely qt's nograb option is active"; } keyButton->setDown(true); updateShortcutDisplay(); } void KKeySequenceWidgetPrivate::doneRecording(bool validate) { modifierlessTimeout.stop(); isRecording = false; keyButton->releaseKeyboard(); keyButton->setDown(false); stealActions.clear(); if (keySequence == oldKeySequence) { // The sequence hasn't changed updateShortcutDisplay(); return; } if (validate && !q->isKeySequenceAvailable(keySequence)) { // The sequence had conflicts and the user said no to stealing it keySequence = oldKeySequence; } else { emit q->keySequenceChanged(keySequence); } updateShortcutDisplay(); } bool KKeySequenceWidgetPrivate::conflictWithGlobalShortcuts(const QKeySequence &keySequence) { #ifdef Q_OS_WIN //on windows F12 is reserved by the debugger at all times, so we can't use it for a global shortcut if (KKeySequenceWidget::GlobalShortcuts && keySequence.toString().contains(QStringLiteral("F12"))) { QString title = i18n("Reserved Shortcut"); QString message = i18n("The F12 key is reserved on Windows, so cannot be used for a global shortcut.\n" "Please choose another one."); KMessageBox::sorry(q, message, title); return false; } #endif #if HAVE_GLOBALACCEL if (!(checkAgainstShortcutTypes & KKeySequenceWidget::GlobalShortcuts)) { return false; } // Global shortcuts are on key+modifier shortcuts. They can clash with // each of the keys of a multi key shortcut. QHash<QKeySequence, QList<KGlobalShortcutInfo> > others; for (int i = 0; i < keySequence.count(); ++i) { QKeySequence tmp(keySequence[i]); if (!KGlobalAccel::isGlobalShortcutAvailable(tmp, componentName)) { others.insert(tmp, KGlobalAccel::getGlobalShortcutsByKey(tmp)); } } if (!others.isEmpty() && !promptStealShortcutSystemwide(q, others, keySequence)) { return true; } // The user approved stealing the shortcut. We have to steal // it immediately because KAction::setGlobalShortcut() refuses // to set a global shortcut that is already used. There is no // error it just silently fails. So be nice because this is // most likely the first action that is done in the slot // listening to keySequenceChanged(). for (int i = 0; i < keySequence.count(); ++i) { KGlobalAccel::stealShortcutSystemwide(keySequence[i]); } return false; #else Q_UNUSED(keySequence); return false; #endif } static bool shortcutsConflictWith(const QList<QKeySequence> &shortcuts, const QKeySequence &needle) { if (needle.isEmpty()) { return false; } foreach (const QKeySequence &sequence, shortcuts) { if (sequence.isEmpty()) { continue; } if (sequence.matches(needle) != QKeySequence::NoMatch || needle.matches(sequence) != QKeySequence::NoMatch) { return true; } } return false; } bool KKeySequenceWidgetPrivate::conflictWithLocalShortcuts(const QKeySequence &keySequence) { if (!(checkAgainstShortcutTypes & KKeySequenceWidget::LocalShortcuts)) { return false; } // We have actions both in the deprecated checkList and the // checkActionCollections list. Add all the actions to a single list to // be able to process them in a single loop below. // Note that this can't be done in setCheckActionCollections(), because we // keep pointers to the action collections, and between the call to // setCheckActionCollections() and this function some actions might already be // removed from the collection again. QList<QAction *> allActions; allActions += checkList; foreach (KActionCollection *collection, checkActionCollections) { allActions += collection->actions(); } // Because of multikey shortcuts we can have clashes with many shortcuts. // // Example 1: // // Application currently uses 'CTRL-X,a', 'CTRL-X,f' and 'CTRL-X,CTRL-F' // and the user wants to use 'CTRL-X'. 'CTRL-X' will only trigger as // 'activatedAmbiguously()' for obvious reasons. // // Example 2: // // Application currently uses 'CTRL-X'. User wants to use 'CTRL-X,CTRL-F'. // This will shadow 'CTRL-X' for the same reason as above. // // Example 3: // // Some weird combination of Example 1 and 2 with three shortcuts using // 1/2/3 key shortcuts. I think you can imagine. QList<QAction *> conflictingActions; //find conflicting shortcuts with existing actions foreach (QAction *qaction, allActions) { if (shortcutsConflictWith(qaction->shortcuts(), keySequence)) { // A conflict with a KAction. If that action is configurable // ask the user what to do. If not reject this keySequence. if (checkActionCollections.first()->isShortcutsConfigurable(qaction)) { conflictingActions.append(qaction); } else { wontStealShortcut(qaction, keySequence); return true; } } } if (conflictingActions.isEmpty()) { // No conflicting shortcuts found. return false; } if (stealShortcuts(conflictingActions, keySequence)) { stealActions = conflictingActions; // Announce that the user // agreed Q_FOREACH (QAction *stealAction, stealActions) { emit q->stealShortcut( keySequence, stealAction); } return false; } else { return true; } } bool KKeySequenceWidgetPrivate::conflictWithStandardShortcuts(const QKeySequence &keySequence) { if (!(checkAgainstShortcutTypes & KKeySequenceWidget::StandardShortcuts)) { return false; } KStandardShortcut::StandardShortcut ssc = KStandardShortcut::find(keySequence); if (ssc != KStandardShortcut::AccelNone && !stealStandardShortcut(ssc, keySequence)) { return true; } return false; } bool KKeySequenceWidgetPrivate::stealStandardShortcut(KStandardShortcut::StandardShortcut std, const QKeySequence &seq) { QString title = i18n("Conflict with Standard Application Shortcut"); QString message = i18n("The '%1' key combination is also used for the standard action " "\"%2\" that some applications use.\n" "Do you really want to use it as a global shortcut as well?", seq.toString(QKeySequence::NativeText), KStandardShortcut::label(std)); if (KMessageBox::warningContinueCancel(q, message, title, KGuiItem(i18n("Reassign"))) != KMessageBox::Continue) { return false; } return true; } void KKeySequenceWidgetPrivate::updateShortcutDisplay() { //empty string if no non-modifier was pressed QString s = keySequence.toString(QKeySequence::NativeText); s.replace(QLatin1Char('&'), QStringLiteral("&&")); if (isRecording) { if (modifierKeys) { if (!s.isEmpty()) { s.append(QLatin1Char(',')); } if (modifierKeys & Qt::META) { s += KKeyServer::modToStringUser(Qt::META) + QLatin1Char('+'); } #if defined(Q_OS_MAC) if (modifierKeys & Qt::ALT) { s += KKeyServer::modToStringUser(Qt::ALT) + QLatin1Char('+'); } if (modifierKeys & Qt::CTRL) { s += KKeyServer::modToStringUser(Qt::CTRL) + QLatin1Char('+'); } #else if (modifierKeys & Qt::CTRL) { s += KKeyServer::modToStringUser(Qt::CTRL) + QLatin1Char('+'); } if (modifierKeys & Qt::ALT) { s += KKeyServer::modToStringUser(Qt::ALT) + QLatin1Char('+'); } #endif if (modifierKeys & Qt::SHIFT) { s += KKeyServer::modToStringUser(Qt::SHIFT) + QLatin1Char('+'); } } else if (nKey == 0) { s = i18nc("What the user inputs now will be taken as the new shortcut", "Input"); } //make it clear that input is still going on s.append(QStringLiteral(" ...")); } if (s.isEmpty()) { s = i18nc("No shortcut defined", "None"); } s.prepend(QLatin1Char(' ')); s.append(QLatin1Char(' ')); keyButton->setText(s); } KKeySequenceButton::~KKeySequenceButton() { } //prevent Qt from special casing Tab and Backtab bool KKeySequenceButton::event(QEvent *e) { if (d->isRecording && e->type() == QEvent::KeyPress) { keyPressEvent(static_cast<QKeyEvent *>(e)); return true; } // The shortcut 'alt+c' ( or any other dialog local action shortcut ) // ended the recording and triggered the action associated with the // action. In case of 'alt+c' ending the dialog. It seems that those // ShortcutOverride events get sent even if grabKeyboard() is active. if (d->isRecording && e->type() == QEvent::ShortcutOverride) { e->accept(); return true; } if (d->isRecording && e->type() == QEvent::ContextMenu) { // is caused by Qt::Key_Menu e->accept(); return true; } return QPushButton::event(e); } void KKeySequenceButton::keyPressEvent(QKeyEvent *e) { int keyQt = e->key(); if (keyQt == -1) { // Qt sometimes returns garbage keycodes, I observed -1, if it doesn't know a key. // We cannot do anything useful with those (several keys have -1, indistinguishable) // and QKeySequence.toString() will also yield a garbage string. KMessageBox::sorry(this, i18n("The key you just pressed is not supported by Qt."), i18n("Unsupported Key")); return d->cancelRecording(); } uint newModifiers = e->modifiers() & (Qt::SHIFT | Qt::CTRL | Qt::ALT | Qt::META); //don't have the return or space key appear as first key of the sequence when they //were pressed to start editing - catch and them and imitate their effect if (!d->isRecording && ((keyQt == Qt::Key_Return || keyQt == Qt::Key_Space))) { d->startRecording(); d->modifierKeys = newModifiers; d->updateShortcutDisplay(); return; } // We get events even if recording isn't active. if (!d->isRecording) { return QPushButton::keyPressEvent(e); } e->accept(); d->modifierKeys = newModifiers; switch (keyQt) { case Qt::Key_AltGr: //or else we get unicode salad return; case Qt::Key_Shift: case Qt::Key_Control: case Qt::Key_Alt: case Qt::Key_Meta: case Qt::Key_Super_L: case Qt::Key_Super_R: d->controlModifierlessTimout(); d->updateShortcutDisplay(); break; default: if (d->nKey == 0 && !(d->modifierKeys & ~Qt::SHIFT)) { // It's the first key and no modifier pressed. Check if this is // allowed if (!(KKeySequenceWidgetPrivate::isOkWhenModifierless(keyQt) || d->allowModifierless)) { // No it's not return; } } // We now have a valid key press. if (keyQt) { if ((keyQt == Qt::Key_Backtab) && (d->modifierKeys & Qt::SHIFT)) { keyQt = Qt::Key_Tab | d->modifierKeys; } else if (KKeyServer::isShiftAsModifierAllowed(keyQt)) { keyQt |= d->modifierKeys; } else { keyQt |= (d->modifierKeys & ~Qt::SHIFT); } if (d->nKey == 0) { d->keySequence = QKeySequence(keyQt); } else { d->keySequence = KKeySequenceWidgetPrivate::appendToSequence(d->keySequence, keyQt); } d->nKey++; if ((!d->multiKeyShortcutsAllowed) || (d->nKey >= 4)) { d->doneRecording(); return; } d->controlModifierlessTimout(); d->updateShortcutDisplay(); } } } void KKeySequenceButton::keyReleaseEvent(QKeyEvent *e) { if (e->key() == -1) { // ignore garbage, see keyPressEvent() return; } if (!d->isRecording) { return QPushButton::keyReleaseEvent(e); } e->accept(); uint newModifiers = e->modifiers() & (Qt::SHIFT | Qt::CTRL | Qt::ALT | Qt::META); //if a modifier that belongs to the shortcut was released... if ((newModifiers & d->modifierKeys) < d->modifierKeys) { d->modifierKeys = newModifiers; d->controlModifierlessTimout(); d->updateShortcutDisplay(); } } //static QKeySequence KKeySequenceWidgetPrivate::appendToSequence(const QKeySequence &seq, int keyQt) { switch (seq.count()) { case 0: return QKeySequence(keyQt); case 1: return QKeySequence(seq[0], keyQt); case 2: return QKeySequence(seq[0], seq[1], keyQt); case 3: return QKeySequence(seq[0], seq[1], seq[2], keyQt); default: return seq; } } //static bool KKeySequenceWidgetPrivate::isOkWhenModifierless(int keyQt) { //this whole function is a hack, but especially the first line of code if (QKeySequence(keyQt).toString().length() == 1) { return false; } switch (keyQt) { case Qt::Key_Return: case Qt::Key_Space: case Qt::Key_Tab: case Qt::Key_Backtab: //does this ever happen? case Qt::Key_Backspace: case Qt::Key_Delete: return false; default: return true; } } #include "moc_kkeysequencewidget.cpp" #include "moc_kkeysequencewidget_p.cpp" diff --git a/src/kkeysequencewidget.h b/src/kkeysequencewidget.h index 4e6821d..c065dce 100644 --- a/src/kkeysequencewidget.h +++ b/src/kkeysequencewidget.h @@ -1,310 +1,310 @@ /* This file is part of the KDE libraries Copyright (C) 2001, 2002 Ellis Whitehead <ellis@kde.org> Copyright (C) 2007 Andreas Hartmetz <ahartmetz@gmail.com> 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 KKEYSEQUENCEWIDGET_H #define KKEYSEQUENCEWIDGET_H #include <kxmlgui_export.h> #include <QtCore/QList> #include <QPushButton> class KKeySequenceWidgetPrivate; class QAction; class KActionCollection; /** * @short A widget to input a QKeySequence. * * This widget lets the user choose a QKeySequence, which is usually used as a * shortcut key. The recording is initiated by calling captureKeySequence() or * the user clicking into the widget. * * The widgets provides support for conflict handling. See * setCheckForConflictsAgainst() for more information. * * \image html kkeysequencewidget.png "KDE Key Sequence Widget" * * @author Mark Donohoe <donohoe@kde.org> * @internal */ class KXMLGUI_EXPORT KKeySequenceWidget: public QWidget { Q_OBJECT Q_PROPERTY( bool multiKeyShortcutsAllowed READ multiKeyShortcutsAllowed WRITE setMultiKeyShortcutsAllowed) Q_PROPERTY( ShortcutTypes checkForConflictsAgainst READ checkForConflictsAgainst WRITE setCheckForConflictsAgainst) Q_PROPERTY( bool modifierlessAllowed READ isModifierlessAllowed WRITE setModifierlessAllowed) public: ///An enum about validation when setting a key sequence. ///@see setKeySequence() enum Validation { ///Validate key sequence Validate = 0, ///Use key sequence without validation NoValidate = 1 }; /** * Constructor. */ - explicit KKeySequenceWidget(QWidget *parent = 0); + explicit KKeySequenceWidget(QWidget *parent = nullptr); /** * Destructs the widget. */ virtual ~KKeySequenceWidget(); /** * \name Configuration * * Configuration options for the widget. */ //@{ enum ShortcutType { None = 0x00, //!< No checking for conflicts LocalShortcuts = 0x01, //!< Check with local shortcuts. @see setCheckActionCollections() StandardShortcuts = 0x02, //!< Check against standard shortcuts. @see KStandardShortcut GlobalShortcuts = 0x04 //!< Check against global shortcuts. @see KGlobalAccel }; Q_DECLARE_FLAGS(ShortcutTypes, ShortcutType) Q_FLAG(ShortcutTypes) /** * Configure if the widget should check for conflicts with existing * shortcuts. * * When capturing a key sequence for local shortcuts you should check * against GlobalShortcuts and your other local shortcuts. This is the * default. * * You have to provide the local actions to check against with * setCheckActionCollections(). * * When capturing a key sequence for a global shortcut you should * check against StandardShortcuts, GlobalShortcuts and your local * shortcuts. * * There are two ways to react to a user agreeing to steal a shortcut: * * 1. Listen to the stealShortcut() signal and steal the shortcuts * manually. It's your responsibility to save that change later when * you think it is appropriate. * * 2. Call applyStealShortcut and KKeySequenceWidget will steal the * shortcut. This will save the actionCollections the shortcut is part * of so make sure it doesn't inadvertly save some unwanted changes * too. Read its documentation for some limitation when handling * global shortcuts. * * If you want to do the conflict checking yourself here are some code * snippets for global ... * * \code * QStringList conflicting = KGlobalAccel::findActionNameSystemwide(keySequence); * if (!conflicting.isEmpty()) { * // Inform and ask the user about the conflict and reassigning * // the keys sequence * if (!KGlobalAccel::promptStealShortcutSystemwide(q, conflicting, keySequence)) { * return true; * } * KGlobalAccel::stealShortcutSystemwide(keySequence); * } * \endcode * * ... and standard shortcuts * * \code * KStandardShortcut::StandardShortcut ssc = KStandardShortcut::find(keySequence); * if (ssc != KStandardShortcut::AccelNone) { * // We have a conflict * } * \endcode * * * @since 4.2 */ void setCheckForConflictsAgainst(ShortcutTypes types); /** * The shortcut types we check for conflicts. * * @see setCheckForConflictsAgainst() * @since 4.2 */ ShortcutTypes checkForConflictsAgainst() const; /** * Allow multikey shortcuts? */ void setMultiKeyShortcutsAllowed(bool); bool multiKeyShortcutsAllowed() const; /** * This only applies to user input, not to setShortcut(). * Set whether to accept "plain" keys without modifiers (like Ctrl, Alt, Meta). * Plain keys by our definition include letter and symbol keys and * text editing keys (Return, Space, Tab, Backspace, Delete). * "Special" keys like F1, Cursor keys, Insert, PageDown will always work. */ void setModifierlessAllowed(bool allow); /** * @see setModifierlessAllowed() */ bool isModifierlessAllowed(); /** * Set whether a small button to set an empty key sequence should be displayed next to the * main input widget. The default is to show the clear button. */ void setClearButtonShown(bool show); //@} /** * Checks whether the key sequence @a seq is available to grab. * * The sequence is checked under the same rules as if it has been typed by * the user. This method is useful if you get key sequences from another * input source and want to check if it is save to set them. * * @since 4.2 */ bool isKeySequenceAvailable(const QKeySequence &seq) const; /** * Return the currently selected key sequence. */ QKeySequence keySequence() const; /** * Set a list of action collections to check against for conflictuous shortcut. * * @see setCheckForConflictsAgainst() * * If a KAction with a conflicting shortcut is found inside this list and * its shortcut can be configured (KAction::isShortcutConfigurable() * returns true) the user will be prompted whether to steal the shortcut * from this action. * * @since 4.1 */ void setCheckActionCollections(const QList<KActionCollection *> &actionCollections); /** * @deprecated since 4.1 * use setCheckActionCollections so that KKeySequenceWidget knows * in which action collection to call the writeSettings method after stealing * a shortcut from an action. */ #ifndef KXMLGUI_NO_DEPRECATED KXMLGUI_DEPRECATED void setCheckActionList(const QList<QAction *> &checkList); #endif /** * If the component using this widget supports shortcuts contexts, it has * to set its component name so we can check conflicts correctly. */ void setComponentName(const QString &componentName); Q_SIGNALS: /** * This signal is emitted when the current key sequence has changed, be it by user * input or programmatically. */ void keySequenceChanged(const QKeySequence &seq); /** * This signal is emitted after the user agreed to steal a shortcut from * an action. This is only done for local shortcuts. So you can be sure \a * action is one of the actions you provided with setCheckActionList() or * setCheckActionCollections(). * * If you listen to that signal and don't call applyStealShortcut() you * are supposed to steal the shortcut and save this change. */ void stealShortcut(const QKeySequence &seq, QAction *action); public Q_SLOTS: /** * Capture a shortcut from the keyboard. This call will only return once a key sequence * has been captured or input was aborted. * If a key sequence was input, keySequenceChanged() will be emitted. * * @see setModifierlessAllowed() */ void captureKeySequence(); /** * Set the key sequence. * * If @p val == Validate, and the call is actually changing the key sequence, * conflictuous shortcut will be checked. */ void setKeySequence(const QKeySequence &seq, Validation val = NoValidate); /** * Clear the key sequence. */ void clearKeySequence(); /** * Actually remove the shortcut that the user wanted to steal, from the * action that was using it. This only applies to actions provided to us * by setCheckActionCollections() and setCheckActionList(). * * Global and Standard Shortcuts have to be stolen immediately when the * user gives his consent (technical reasons). That means those changes * will be active even if you never call applyStealShortcut(). * * To be called before you apply your changes. No local shortcuts are * stolen until this function is called. */ void applyStealShortcut(); private: Q_PRIVATE_SLOT(d, void doneRecording()) private: friend class KKeySequenceWidgetPrivate; KKeySequenceWidgetPrivate *const d; Q_DISABLE_COPY(KKeySequenceWidget) }; Q_DECLARE_OPERATORS_FOR_FLAGS(KKeySequenceWidget::ShortcutTypes) #endif //KKEYSEQUENCEWIDGET_H diff --git a/src/kmainwindow.cpp b/src/kmainwindow.cpp index da2c922..3781535 100644 --- a/src/kmainwindow.cpp +++ b/src/kmainwindow.cpp @@ -1,934 +1,934 @@ /* This file is part of the KDE libraries Copyright (C) 2000 Reginald Stadlbauer (reggie@kde.org) (C) 1997 Stephan Kulow (coolo@kde.org) (C) 1997-2000 Sven Radej (radej@kde.org) (C) 1997-2000 Matthias Ettrich (ettrich@kde.org) (C) 1999 Chris Schlaeger (cs@kde.org) (C) 2002 Joseph Wenninger (jowenn@kde.org) (C) 2005-2006 Hamish Rodda (rodda@kde.org) (C) 2000-2008 David Faure (faure@kde.org) 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 "kmainwindow.h" #include "kmainwindow_p.h" #include "kmainwindowiface_p.h" #include "ktoolbarhandler_p.h" #include "khelpmenu.h" #include "ktoolbar.h" #include <QApplication> #include <QtCore/QList> #include <QtCore/QObject> #include <QtCore/QTimer> #include <QCloseEvent> #include <QDesktopWidget> #include <QDockWidget> #include <QLayout> #include <QMenuBar> #include <QSessionManager> #include <QStatusBar> #include <QStyle> #include <QWidget> #include <QWindow> #include <QDBusConnection> #include <ktoggleaction.h> #include <kaboutdata.h> #include <kconfig.h> #include <ksharedconfig.h> #include <klocalizedstring.h> #include <kwindowsystem.h> #include <kconfiggroup.h> #include <kwindowconfig.h> #include <kconfiggui.h> //#include <ctype.h> static const char WINDOW_PROPERTIES[]="WindowProperties"; static QMenuBar *internalMenuBar(KMainWindow *mw) { return mw->findChild<QMenuBar *>(QString(), Qt::FindDirectChildrenOnly); } static QStatusBar *internalStatusBar(KMainWindow *mw) { return mw->findChild<QStatusBar *>(QString(), Qt::FindDirectChildrenOnly); } /** * Listens to resize events from QDockWidgets. The KMainWindow * settings are set as dirty, as soon as at least one resize * event occurred. The listener is attached to the dock widgets * by dock->installEventFilter(dockResizeListener) inside * KMainWindow::event(). */ class DockResizeListener : public QObject { public: DockResizeListener(KMainWindow *win); virtual ~DockResizeListener(); bool eventFilter(QObject *watched, QEvent *event) Q_DECL_OVERRIDE; private: KMainWindow *m_win; }; DockResizeListener::DockResizeListener(KMainWindow *win) : QObject(win), m_win(win) { } DockResizeListener::~DockResizeListener() { } bool DockResizeListener::eventFilter(QObject *watched, QEvent *event) { switch (event->type()) { case QEvent::Resize: case QEvent::Move: case QEvent::Hide: m_win->k_ptr->setSettingsDirty(KMainWindowPrivate::CompressCalls); break; default: break; } return QObject::eventFilter(watched, event); } KMWSessionManager::KMWSessionManager() { connect(qApp, SIGNAL(saveStateRequest(QSessionManager&)), this, SLOT(saveState(QSessionManager&))); connect(qApp, SIGNAL(commitDataRequest(QSessionManager&)), this, SLOT(commitData(QSessionManager&))); } KMWSessionManager::~KMWSessionManager() { } void KMWSessionManager::saveState(QSessionManager &sm) { KConfigGui::setSessionConfig(sm.sessionId(), sm.sessionKey()); KConfig *config = KConfigGui::sessionConfig(); if (!KMainWindow::memberList().isEmpty()) { // According to Jochen Wilhelmy <digisnap@cs.tu-berlin.de>, this // hook is useful for better document orientation KMainWindow::memberList().at(0)->saveGlobalProperties(config); } int n = 0; foreach (KMainWindow *mw, KMainWindow::memberList()) { n++; mw->savePropertiesInternal(config, n); } KConfigGroup group(config, "Number"); group.writeEntry("NumberOfWindows", n); // store new status to disk config->sync(); // generate discard command for new file QString localFilePath = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation) + QLatin1Char('/') + config->name(); if (QFile::exists(localFilePath)) { QStringList discard; discard << QStringLiteral("rm"); discard << localFilePath; sm.setDiscardCommand(discard); } } void KMWSessionManager::commitData(QSessionManager &sm) { if (!sm.allowsInteraction()) { return; } /* Purpose of this exercise: invoke queryClose() without actually closing the windows, because - queryClose() may contain session management code, so it must be invoked - actually closing windows may quit the application - cf. QGuiApplication::quitOnLastWindowClosed() - quitting the application and thus closing the session manager connection violates the X11 XSMP protocol. The exact requirement of XSMP that would be broken is, in the description of the client's state machine: save-yourself-done: (changing state is forbidden) Closing the session manager connection causes a state change. Worst of all, that is a real problem with ksmserver - it will not save applications that quit on their own in state save-yourself-done. */ foreach (KMainWindow *window, KMainWindow::memberList()) { if (window->testAttribute(Qt::WA_WState_Hidden)) { continue; } QCloseEvent e; QApplication::sendEvent(window, &e); if (!e.isAccepted()) { sm.cancel(); return; } } } Q_GLOBAL_STATIC(KMWSessionManager, ksm) Q_GLOBAL_STATIC(QList<KMainWindow *>, sMemberList) KMainWindow::KMainWindow(QWidget *parent, Qt::WindowFlags f) : QMainWindow(parent, f), k_ptr(new KMainWindowPrivate) { k_ptr->init(this); } KMainWindow::KMainWindow(KMainWindowPrivate &dd, QWidget *parent, Qt::WindowFlags f) : QMainWindow(parent, f), k_ptr(&dd) { k_ptr->init(this); } void KMainWindowPrivate::init(KMainWindow *_q) { q = _q; #if QT_VERSION >= QT_VERSION_CHECK(5, 6, 0) QGuiApplication::setFallbackSessionManagementEnabled(false); #endif - q->setAnimated(q->style()->styleHint(QStyle::SH_Widget_Animate, 0, q)); + q->setAnimated(q->style()->styleHint(QStyle::SH_Widget_Animate, nullptr, q)); q->setAttribute(Qt::WA_DeleteOnClose); - helpMenu = 0; + helpMenu = nullptr; //actionCollection()->setWidget( this ); #if 0 QObject::connect(KGlobalSettings::self(), SIGNAL(settingsChanged(int)), q, SLOT(_k_slotSettingsChanged(int))); #endif // force KMWSessionManager creation ksm(); sMemberList()->append(q); // If application is translated, load translator information for use in // KAboutApplicationDialog or other getters. The context and messages below // both must be exactly as listed, and are forced to be loaded from the // application's own message catalog instead of kxmlgui's. KAboutData aboutData(KAboutData::applicationData()); if (aboutData.translators().isEmpty()) { aboutData.setTranslator( i18ndc(Q_NULLPTR, "NAME OF TRANSLATORS", "Your names"), i18ndc(Q_NULLPTR, "EMAIL OF TRANSLATORS", "Your emails")); KAboutData::setApplicationData(aboutData); } settingsDirty = false; autoSaveSettings = false; autoSaveWindowSize = true; // for compatibility //d->kaccel = actionCollection()->kaccel(); - settingsTimer = 0; - sizeTimer = 0; + settingsTimer = nullptr; + sizeTimer = nullptr; dockResizeListener = new DockResizeListener(_q); letDirtySettings = true; sizeApplied = false; } static bool endsWithHashNumber(const QString &s) { for (int i = s.length() - 1; i > 0; --i) { if (s[ i ] == QLatin1Char('#') && i != s.length() - 1) { return true; // ok } if (!s[ i ].isDigit()) { break; } } return false; } static inline bool isValidDBusObjectPathCharacter(const QChar &c) { ushort u = c.unicode(); return (u >= QLatin1Char('a') && u <= QLatin1Char('z')) || (u >= QLatin1Char('A') && u <= QLatin1Char('Z')) || (u >= QLatin1Char('0') && u <= QLatin1Char('9')) || (u == QLatin1Char('_')) || (u == QLatin1Char('/')); } void KMainWindowPrivate::polish(KMainWindow *q) { // Set a unique object name. Required by session management, window management, and for the dbus interface. QString objname; QString s; int unusedNumber = 1; const QString name = q->objectName(); bool startNumberingImmediately = true; bool tryReuse = false; if (name.isEmpty()) { // no name given objname = QStringLiteral("MainWindow#"); } else if (name.endsWith(QLatin1Char('#'))) { // trailing # - always add a number - KWin uses this for better grouping objname = name; } else if (endsWithHashNumber(name)) { // trailing # with a number - like above, try to use the given number first objname = name; tryReuse = true; startNumberingImmediately = false; } else { objname = name; startNumberingImmediately = false; } s = objname; if (startNumberingImmediately) { s += QLatin1Char('1'); } for (;;) { const QList<QWidget *> list = qApp->topLevelWidgets(); bool found = false; foreach (QWidget *w, list) { if (w != q && w->objectName() == s) { found = true; break; } } if (!found) { break; } if (tryReuse) { objname = name.left(name.length() - 1); // lose the hash unusedNumber = 0; // start from 1 below tryReuse = false; } s.setNum(++unusedNumber); s = objname + s; } q->setObjectName(s); q->winId(); // workaround for setWindowRole() crashing, and set also window role, just in case TT q->setWindowRole(s); // will keep insisting that object name suddenly should not be used for window role dbusName = QLatin1Char('/') + QCoreApplication::applicationName() + QLatin1Char('/'); dbusName += q->objectName().replace(QLatin1Char('/'), QLatin1Char('_')); // Clean up for dbus usage: any non-alphanumeric char should be turned into '_' const int len = dbusName.length(); for (int i = 0; i < len; ++i) { if (!isValidDBusObjectPathCharacter(dbusName[i])) { dbusName[i] = QLatin1Char('_'); } } QDBusConnection::sessionBus().registerObject(dbusName, q, QDBusConnection::ExportScriptableSlots | QDBusConnection::ExportScriptableProperties | QDBusConnection::ExportNonScriptableSlots | QDBusConnection::ExportNonScriptableProperties | QDBusConnection::ExportAdaptors); } void KMainWindowPrivate::setSettingsDirty(CallCompression callCompression) { if (!letDirtySettings) { return; } settingsDirty = true; if (autoSaveSettings) { if (callCompression == CompressCalls) { if (!settingsTimer) { settingsTimer = new QTimer(q); settingsTimer->setInterval(500); settingsTimer->setSingleShot(true); QObject::connect(settingsTimer, SIGNAL(timeout()), q, SLOT(saveAutoSaveSettings())); } settingsTimer->start(); } else { q->saveAutoSaveSettings(); } } } void KMainWindowPrivate::setSizeDirty() { if (autoSaveWindowSize) { if (!sizeTimer) { sizeTimer = new QTimer(q); sizeTimer->setInterval(500); sizeTimer->setSingleShot(true); QObject::connect(sizeTimer, SIGNAL(timeout()), q, SLOT(_k_slotSaveAutoSaveSize())); } sizeTimer->start(); } } KMainWindow::~KMainWindow() { sMemberList()->removeAll(this); delete static_cast<QObject *>(k_ptr->dockResizeListener); //so we don't get anymore events after k_ptr is destroyed delete k_ptr; } #ifndef KXMLGUI_NO_DEPRECATED QMenu *KMainWindow::helpMenu(const QString &aboutAppText, bool showWhatsThis) { K_D(KMainWindow); if (!d->helpMenu) { if (aboutAppText.isEmpty()) { d->helpMenu = new KHelpMenu(this, KAboutData::applicationData(), showWhatsThis); } else { d->helpMenu = new KHelpMenu(this, aboutAppText, showWhatsThis); } if (!d->helpMenu) { - return 0; + return nullptr; } } return d->helpMenu->menu(); } QMenu *KMainWindow::customHelpMenu(bool showWhatsThis) { K_D(KMainWindow); if (!d->helpMenu) { d->helpMenu = new KHelpMenu(this, QString(), showWhatsThis); connect(d->helpMenu, SIGNAL(showAboutApplication()), this, SLOT(showAboutApplication())); } return d->helpMenu->menu(); } #endif bool KMainWindow::canBeRestored(int number) { if (!qApp->isSessionRestored()) { return false; } KConfig *config = KConfigGui::sessionConfig(); if (!config) { return false; } KConfigGroup group(config, "Number"); const int n = group.readEntry("NumberOfWindows", 1); return number >= 1 && number <= n; } const QString KMainWindow::classNameOfToplevel(int number) { if (!qApp->isSessionRestored()) { return QString(); } KConfig *config = KConfigGui::sessionConfig(); if (!config) { return QString(); } KConfigGroup group(config, QByteArray(WINDOW_PROPERTIES).append(QByteArray::number(number)).constData()); if (!group.hasKey("ClassName")) { return QString(); } else { return group.readEntry("ClassName"); } } bool KMainWindow::restore(int number, bool show) { if (!canBeRestored(number)) { return false; } KConfig *config = KConfigGui::sessionConfig(); if (readPropertiesInternal(config, number)) { if (show) { KMainWindow::show(); } return false; } return false; } void KMainWindow::setCaption(const QString &caption) { setPlainCaption(caption); } void KMainWindow::setCaption(const QString &caption, bool modified) { QString title = caption; if (!title.contains(QStringLiteral("[*]")) && !title.isEmpty()) { // append the placeholder so that the modified mechanism works title.append(QStringLiteral(" [*]")); } setPlainCaption(title); setWindowModified(modified); } void KMainWindow::setPlainCaption(const QString &caption) { setWindowTitle(caption); } void KMainWindow::appHelpActivated(void) { K_D(KMainWindow); if (!d->helpMenu) { d->helpMenu = new KHelpMenu(this); if (!d->helpMenu) { return; } } d->helpMenu->appHelpActivated(); } void KMainWindow::closeEvent(QCloseEvent *e) { K_D(KMainWindow); // Save settings if auto-save is enabled, and settings have changed if (d->settingsTimer && d->settingsTimer->isActive()) { d->settingsTimer->stop(); saveAutoSaveSettings(); } if (d->sizeTimer && d->sizeTimer->isActive()) { d->sizeTimer->stop(); d->_k_slotSaveAutoSaveSize(); } if (queryClose()) { e->accept(); } else { e->ignore(); //if the window should not be closed, don't close it } } bool KMainWindow::queryClose() { return true; } void KMainWindow::saveGlobalProperties(KConfig *) { } void KMainWindow::readGlobalProperties(KConfig *) { } void KMainWindow::savePropertiesInternal(KConfig *config, int number) { K_D(KMainWindow); const bool oldASWS = d->autoSaveWindowSize; d->autoSaveWindowSize = true; // make saveMainWindowSettings save the window size KConfigGroup cg(config, QByteArray(WINDOW_PROPERTIES).append(QByteArray::number(number)).constData()); // store objectName, className, Width and Height for later restoring // (Only useful for session management) cg.writeEntry("ObjectName", objectName()); cg.writeEntry("ClassName", metaObject()->className()); saveMainWindowSettings(cg); // Menubar, statusbar and Toolbar settings. cg = KConfigGroup(config, QByteArray::number(number).constData()); saveProperties(cg); d->autoSaveWindowSize = oldASWS; } void KMainWindow::saveMainWindowSettings(KConfigGroup &cg) { K_D(KMainWindow); //qDebug(200) << "KMainWindow::saveMainWindowSettings " << cg.name(); // Called by session management - or if we want to save the window size anyway if (d->autoSaveWindowSize) { KWindowConfig::saveWindowSize(windowHandle(), cg); } // One day will need to save the version number, but for now, assume 0 // Utilise the QMainWindow::saveState() functionality. const QByteArray state = saveState(); cg.writeEntry("State", state.toBase64()); QStatusBar *sb = internalStatusBar(this); if (sb) { if (!cg.hasDefault("StatusBar") && !sb->isHidden()) { cg.revertToDefault("StatusBar"); } else { cg.writeEntry("StatusBar", sb->isHidden() ? "Disabled" : "Enabled"); } } QMenuBar *mb = internalMenuBar(this); if (mb) { if (!cg.hasDefault("MenuBar") && !mb->isHidden()) { cg.revertToDefault("MenuBar"); } else { cg.writeEntry("MenuBar", mb->isHidden() ? "Disabled" : "Enabled"); } } if (!autoSaveSettings() || cg.name() == autoSaveGroup()) { // TODO should be cg == d->autoSaveGroup, to compare both kconfig and group name if (!cg.hasDefault("ToolBarsMovable") && !KToolBar::toolBarsLocked()) { cg.revertToDefault("ToolBarsMovable"); } else { cg.writeEntry("ToolBarsMovable", KToolBar::toolBarsLocked() ? "Disabled" : "Enabled"); } } int n = 1; // Toolbar counter. toolbars are counted from 1, foreach (KToolBar *toolbar, toolBars()) { QByteArray groupName("Toolbar"); // Give a number to the toolbar, but prefer a name if there is one, // because there's no real guarantee on the ordering of toolbars groupName += (toolbar->objectName().isEmpty() ? QByteArray::number(n) : QByteArray(" ").append(toolbar->objectName().toUtf8())); KConfigGroup toolbarGroup(&cg, groupName.constData()); toolbar->saveSettings(toolbarGroup); n++; } } bool KMainWindow::readPropertiesInternal(KConfig *config, int number) { K_D(KMainWindow); const bool oldLetDirtySettings = d->letDirtySettings; d->letDirtySettings = false; if (number == 1) { readGlobalProperties(config); } // in order they are in toolbar list KConfigGroup cg(config, QByteArray(WINDOW_PROPERTIES).append(QByteArray::number(number)).constData()); // restore the object name (window role) if (cg.hasKey("ObjectName")) { setObjectName(cg.readEntry("ObjectName")); } d->sizeApplied = false; // since we are changing config file, reload the size of the window // if necessary. Do it before the call to applyMainWindowSettings. applyMainWindowSettings(cg); // Menubar, statusbar and toolbar settings. KConfigGroup grp(config, QByteArray::number(number).constData()); readProperties(grp); d->letDirtySettings = oldLetDirtySettings; return true; } void KMainWindow::applyMainWindowSettings(const KConfigGroup &cg) { K_D(KMainWindow); //qDebug(200) << "KMainWindow::applyMainWindowSettings " << cg.name(); QWidget *focusedWidget = QApplication::focusWidget(); const bool oldLetDirtySettings = d->letDirtySettings; d->letDirtySettings = false; if (!d->sizeApplied) { winId(); // ensure there's a window created KWindowConfig::restoreWindowSize(windowHandle(), cg); // NOTICE: QWindow::setGeometry() does NOT impact the backing QWidget geometry even if the platform // window was created -> QTBUG-40584. We therefore copy the size here. // TODO: remove once this was resolved in QWidget QPA resize(windowHandle()->size()); d->sizeApplied = true; } QStatusBar *sb = internalStatusBar(this); if (sb) { QString entry = cg.readEntry("StatusBar", "Enabled"); sb->setVisible( entry != QLatin1String("Disabled") ); } QMenuBar *mb = internalMenuBar(this); if (mb) { QString entry = cg.readEntry("MenuBar", "Enabled"); mb->setVisible( entry != QLatin1String("Disabled") ); } if (!autoSaveSettings() || cg.name() == autoSaveGroup()) { // TODO should be cg == d->autoSaveGroup, to compare both kconfig and group name QString entry = cg.readEntry("ToolBarsMovable", "Disabled"); KToolBar::setToolBarsLocked(entry == QLatin1String("Disabled")); } int n = 1; // Toolbar counter. toolbars are counted from 1, foreach (KToolBar *toolbar, toolBars()) { QByteArray groupName("Toolbar"); // Give a number to the toolbar, but prefer a name if there is one, // because there's no real guarantee on the ordering of toolbars groupName += (toolbar->objectName().isEmpty() ? QByteArray::number(n) : QByteArray(" ").append(toolbar->objectName().toUtf8())); KConfigGroup toolbarGroup(&cg, groupName.constData()); toolbar->applySettings(toolbarGroup); n++; } QByteArray state; if (cg.hasKey("State")) { state = cg.readEntry("State", state); state = QByteArray::fromBase64(state); // One day will need to load the version number, but for now, assume 0 restoreState(state); } if (focusedWidget) { focusedWidget->setFocus(); } d->settingsDirty = false; d->letDirtySettings = oldLetDirtySettings; } #ifndef KXMLGUI_NO_DEPRECATED void KMainWindow::restoreWindowSize(const KConfigGroup &cg) { KWindowConfig::restoreWindowSize(windowHandle(), cg); } #endif #ifndef KXMLGUI_NO_DEPRECATED void KMainWindow::saveWindowSize(KConfigGroup &cg) const { KWindowConfig::saveWindowSize(windowHandle(), cg); } #endif void KMainWindow::setSettingsDirty() { K_D(KMainWindow); d->setSettingsDirty(); } bool KMainWindow::settingsDirty() const { K_D(const KMainWindow); return d->settingsDirty; } void KMainWindow::setAutoSaveSettings(const QString &groupName, bool saveWindowSize) { setAutoSaveSettings(KConfigGroup(KSharedConfig::openConfig(), groupName), saveWindowSize); } void KMainWindow::setAutoSaveSettings(const KConfigGroup &group, bool saveWindowSize) { K_D(KMainWindow); d->autoSaveSettings = true; d->autoSaveGroup = group; d->autoSaveWindowSize = saveWindowSize; if (!saveWindowSize && d->sizeTimer) { d->sizeTimer->stop(); } // Now read the previously saved settings applyMainWindowSettings(d->autoSaveGroup); } void KMainWindow::resetAutoSaveSettings() { K_D(KMainWindow); d->autoSaveSettings = false; if (d->settingsTimer) { d->settingsTimer->stop(); } } bool KMainWindow::autoSaveSettings() const { K_D(const KMainWindow); return d->autoSaveSettings; } QString KMainWindow::autoSaveGroup() const { K_D(const KMainWindow); return d->autoSaveSettings ? d->autoSaveGroup.name() : QString(); } KConfigGroup KMainWindow::autoSaveConfigGroup() const { K_D(const KMainWindow); return d->autoSaveSettings ? d->autoSaveGroup : KConfigGroup(); } void KMainWindow::saveAutoSaveSettings() { K_D(KMainWindow); Q_ASSERT(d->autoSaveSettings); //qDebug(200) << "KMainWindow::saveAutoSaveSettings -> saving settings"; saveMainWindowSettings(d->autoSaveGroup); d->autoSaveGroup.sync(); d->settingsDirty = false; } bool KMainWindow::event(QEvent *ev) { K_D(KMainWindow); switch (ev->type()) { #if defined(Q_OS_WIN) || defined(Q_OS_OSX) case QEvent::Move: #endif case QEvent::Resize: d->setSizeDirty(); break; case QEvent::Polish: d->polish(this); break; case QEvent::ChildPolished: { QChildEvent *event = static_cast<QChildEvent *>(ev); QDockWidget *dock = qobject_cast<QDockWidget *>(event->child()); KToolBar *toolbar = qobject_cast<KToolBar *>(event->child()); QMenuBar *menubar = qobject_cast<QMenuBar *>(event->child()); if (dock) { connect(dock, SIGNAL(dockLocationChanged(Qt::DockWidgetArea)), this, SLOT(setSettingsDirty())); connect(dock, SIGNAL(visibilityChanged(bool)), this, SLOT(setSettingsDirty()), Qt::QueuedConnection); connect(dock, SIGNAL(topLevelChanged(bool)), this, SLOT(setSettingsDirty())); // there is no signal emitted if the size of the dock changes, // hence install an event filter instead dock->installEventFilter(k_ptr->dockResizeListener); } else if (toolbar) { // there is no signal emitted if the size of the toolbar changes, // hence install an event filter instead toolbar->installEventFilter(k_ptr->dockResizeListener); } else if (menubar) { // there is no signal emitted if the size of the menubar changes, // hence install an event filter instead menubar->installEventFilter(k_ptr->dockResizeListener); } } break; case QEvent::ChildRemoved: { QChildEvent *event = static_cast<QChildEvent *>(ev); QDockWidget *dock = qobject_cast<QDockWidget *>(event->child()); KToolBar *toolbar = qobject_cast<KToolBar *>(event->child()); QMenuBar *menubar = qobject_cast<QMenuBar *>(event->child()); if (dock) { disconnect(dock, SIGNAL(dockLocationChanged(Qt::DockWidgetArea)), this, SLOT(setSettingsDirty())); disconnect(dock, SIGNAL(visibilityChanged(bool)), this, SLOT(setSettingsDirty())); disconnect(dock, SIGNAL(topLevelChanged(bool)), this, SLOT(setSettingsDirty())); dock->removeEventFilter(k_ptr->dockResizeListener); } else if (toolbar) { toolbar->removeEventFilter(k_ptr->dockResizeListener); } else if (menubar) { menubar->removeEventFilter(k_ptr->dockResizeListener); } } break; default: break; } return QMainWindow::event(ev); } bool KMainWindow::hasMenuBar() { return internalMenuBar(this); } void KMainWindowPrivate::_k_slotSettingsChanged(int category) { Q_UNUSED(category); // This slot will be called when the style KCM changes settings that need // to be set on the already running applications. // At this level (KMainWindow) the only thing we need to restore is the // animations setting (whether the user wants builtin animations or not). - q->setAnimated(q->style()->styleHint(QStyle::SH_Widget_Animate, 0, q)); + q->setAnimated(q->style()->styleHint(QStyle::SH_Widget_Animate, nullptr, q)); } void KMainWindowPrivate::_k_slotSaveAutoSaveSize() { if (autoSaveGroup.isValid()) { KWindowConfig::saveWindowSize(q->windowHandle(), autoSaveGroup); } } KToolBar *KMainWindow::toolBar(const QString &name) { QString childName = name; if (childName.isEmpty()) { childName = QStringLiteral("mainToolBar"); } KToolBar *tb = findChild<KToolBar *>(childName); if (tb) { return tb; } KToolBar *toolbar = new KToolBar(childName, this); // non-XMLGUI toolbar return toolbar; } QList<KToolBar *> KMainWindow::toolBars() const { QList<KToolBar *> ret; foreach (QObject *child, children()) if (KToolBar *toolBar = qobject_cast<KToolBar *>(child)) { ret.append(toolBar); } return ret; } QList<KMainWindow *> KMainWindow::memberList() { return *sMemberList(); } QString KMainWindow::dbusName() const { return k_func()->dbusName; } #include "moc_kmainwindow.cpp" diff --git a/src/kmainwindow.h b/src/kmainwindow.h index 485f6cb..50b52ff 100644 --- a/src/kmainwindow.h +++ b/src/kmainwindow.h @@ -1,743 +1,743 @@ /* This file is part of the KDE libraries Copyright (C) 2000 Reginald Stadlbauer (reggie@kde.org) (C) 1997 Stephan Kulow (coolo@kde.org) (C) 1997-2000 Sven Radej (radej@kde.org) (C) 1997-2000 Matthias Ettrich (ettrich@kde.org) (C) 1999 Chris Schlaeger (cs@kde.org) (C) 2002 Joseph Wenninger (jowenn@kde.org) (C) 2005-2006 Hamish Rodda (rodda@kde.org) (C) 2000-2008 David Faure (faure@kde.org) 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 KMAINWINDOW_H #define KMAINWINDOW_H #include <kxmlgui_export.h> #include <QMainWindow> #include <QtCore/QMetaClassInfo> class QMenu; class KConfig; class KConfigGroup; class KMWSessionManager; class KMainWindowPrivate; class KToolBar; // internal, not public API, may change any time #define XMLGUI_DECLARE_PRIVATE(classname) \ inline classname ## Private *k_func() { return reinterpret_cast<classname ## Private *>(k_ptr); } \ inline const classname ## Private *k_func() const { return reinterpret_cast<classname ## Private *>(k_ptr); } \ friend class classname ## Private; // This is mostly from KDE3. TODO KDE5: remove the constructor parameter. #define KDE_DEFAULT_WINDOWFLAGS 0 /** * @short %KDE top level main window * * Top level widget that provides toolbars, a status line and a frame. * * It should be used as a top level (parent-less) widget. * It manages the geometry for all its children, including your * main widget. * * Normally, you will inherit from KMainWindow, * then construct (or use some existing) widget as * your main view. You can set only one main view. * * You can add as many toolbars as you like. There can be only one menubar * and only one statusbar. * * The toolbars, menubar, and statusbar can be created by the * KMainWindow and - unlike the old KMainWindow - may, but do not * have to, be deleted by you. KMainWindow will handle that internally. * * Height and width can be operated independently from each other. Simply * define the minimum/maximum height/width of your main widget and * KMainWindow will take this into account. For fixed size windows set * your main widget to a fixed size. * * Fixed aspect ratios (heightForWidth()) and fixed width widgets are * not supported. * * KMainWindow will set icon, mini icon and caption, which it gets * from KApplication. It provides full session management, and * will save its position, geometry and positions of toolbars and * menubar on logout. If you want to save additional data, reimplement * saveProperties() and (to read them again on next login) * readProperties(). To save special data about your data, reimplement * saveGlobalProperties(). To warn user that application or * windows have unsaved data on close or logout, reimplement * queryClose(). * * You have to implement session restoring also in your main() function. * There are also kRestoreMainWindows convenience functions which * can do this for you and restore all your windows on next login. * * Note that KMainWindow uses KGlobal::ref() and KGlobal::deref() so that closing * the last mainwindow will quit the application unless there is still something * that holds a ref in KGlobal - like a KIO job, or a systray icon. * * @see KApplication * @author Reginald Stadlbauer (reggie@kde.org) Stephan Kulow (coolo@kde.org), Matthias Ettrich (ettrich@kde.org), Chris Schlaeger (cs@kde.org), Sven Radej (radej@kde.org). Maintained by David Faure (faure@kde.org) */ class KXMLGUI_EXPORT KMainWindow : public QMainWindow { friend class KMWSessionManager; friend class DockResizeListener; XMLGUI_DECLARE_PRIVATE(KMainWindow) Q_OBJECT Q_PROPERTY(bool hasMenuBar READ hasMenuBar) Q_PROPERTY(bool autoSaveSettings READ autoSaveSettings) Q_PROPERTY(QString autoSaveGroup READ autoSaveGroup) public: /** * Construct a main window. * * @param parent The widget parent. This is usually 0 but it may also be the window * group leader. In that case, the KMainWindow becomes sort of a * secondary window. * * @param f Specify the window flags. The default is none. * * Note that a KMainWindow per-default is created with the * WA_DeleteOnClose attribute, i.e. it is automatically destroyed when the * window is closed. If you do not want this behavior, call * setAttribute(Qt::WA_DeleteOnClose, false); * * KMainWindows must be created on the heap with 'new', like: * \code * KMainWindow *kmw = new KMainWindow(...); * kmw->setObjectName(...); * \endcode * * Since the KDE Frameworks 5.16 release, KMainWindow will also enter information regarding * the application's translators by default, using KAboutData::setTranslator(). This only occurs * if no translators are already assigned in KAboutData (see KAboutData::setTranslator() for * details -- the auto-assignment here uses the same translated strings as specified for that * function). * * IMPORTANT: For session management and window management to work * properly, all main windows in the application should have a * different name. If you don't do it, KMainWindow will create * a unique name, but it's recommended to explicitly pass a window name that will * also describe the type of the window. If there can be several windows of the same * type, append '#' (hash) to the name, and KMainWindow will replace it with numbers to make * the names unique. For example, for a mail client which has one main window showing * the mails and folders, and which can also have one or more windows for composing * mails, the name for the folders window should be e.g. "mainwindow" and * for the composer windows "composer#". * */ - explicit KMainWindow(QWidget *parent = 0, Qt::WindowFlags f = KDE_DEFAULT_WINDOWFLAGS); + explicit KMainWindow(QWidget *parent = nullptr, Qt::WindowFlags f = KDE_DEFAULT_WINDOWFLAGS); /** * \brief Destructor. * * Will also destroy the toolbars, and menubar if * needed. */ virtual ~KMainWindow(); /** * Retrieve the standard help menu. * * It contains entries for the * help system (activated by F1), an optional "What's This?" entry * (activated by Shift F1), an application specific dialog box, * and an "About KDE" dialog box. * * Example (adding a standard help menu to your application): * \code * QMenu *help = helpMenu( <myTextString> ); * menuBar()->addMenu( help ); * \endcode * * @param aboutAppText The string that is used in the application * specific dialog box. If you leave this string empty the * information in the global KAboutData of the * application will be used to make a standard dialog box. * * @param showWhatsThis Set this to false if you do not want to include * the "What's This" menu entry. * * @return A standard help menu. * @deprecated use KHelpMenu directly */ #ifndef KXMLGUI_NO_DEPRECATED KXMLGUI_DEPRECATED QMenu *helpMenu(const QString &aboutAppText = QString(), bool showWhatsThis = true); #endif /** * Returns the help menu. Creates a standard help menu if none exists yet. * * It contains entries for the * help system (activated by F1), an optional "What's This?" entry * (activated by Shift F1), an application specific dialog box, * and an "About KDE" dialog box. You must create the application * specific dialog box yourself. When the "About application" * menu entry is activated, a signal will trigger the * showAboutApplication slot. See showAboutApplication for more * information. * * Example (adding a help menu to your application): * \code * menuBar()->addMenu( customHelpMenu() ); * \endcode * * @param showWhatsThis Set this to @p false if you do not want to include * the "What's This" menu entry. * * @return A standard help menu. * @deprecated use XMLGUI instead, or KHelpMenu directly */ #ifndef KXMLGUI_NO_DEPRECATED KXMLGUI_DEPRECATED QMenu *customHelpMenu(bool showWhatsThis = true); #endif /** * If the session did contain so high a number, @p true is returned, * else @p false. * @see restore() **/ static bool canBeRestored(int number); /** * Returns the className() of the @p number of the toplevel window which * should be restored. * * This is only useful if your application uses * different kinds of toplevel windows. */ static const QString classNameOfToplevel(int number); /** * Try to restore the toplevel widget as defined by @p number (1..X). * * You should call canBeRestored() first. * * If the session did not contain so high a number, the configuration * is not changed and @p false returned. * * That means clients could simply do the following: * \code * if (qApp->isSessionRestored()){ * int n = 1; * while (KMainWindow::canBeRestored(n)){ * (new childMW)->restore(n); * n++; * } * } else { * // create default application as usual * } * \endcode * Note that if @p show is true (default), QWidget::show() is called * implicitly in restore. * * With this you can easily restore all toplevel windows of your * application. * * If your application uses different kinds of toplevel * windows, then you can use KMainWindow::classNameOfToplevel(n) * to determine the exact type before calling the childMW * constructor in the example from above. * * <i>Note that you don't need to deal with this function. Use the * kRestoreMainWindows() convenience template function instead!</i> * @see kRestoreMainWindows() * @see readProperties() * @see canBeRestored() */ bool restore(int number, bool show = true); /** * Returns true, if there is a menubar */ bool hasMenuBar(); /** * List of members of KMainWindow class. */ static QList<KMainWindow *> memberList(); /** * Returns a pointer to the toolbar with the specified name. * This refers to toolbars created dynamically from the XML UI * framework. If the toolbar does not exist one will be created. * * @param name The internal name of the toolbar. If no name is * specified "mainToolBar" is assumed. * * @return A pointer to the toolbar **/ KToolBar *toolBar(const QString &name = QString()); /** * @return A list of all toolbars for this window */ QList<KToolBar *> toolBars() const; /** * Call this to enable "auto-save" of toolbar/menubar/statusbar settings * (and optionally window size). * If the *bars were moved around/shown/hidden when the window is closed, * saveMainWindowSettings( KConfigGroup(KSharedConfig::openConfig(), groupName) ) will be called. * * @param groupName a name that identifies this "type of window". * You can have several types of window in the same application. * * @param saveWindowSize set it to true to include the window size * when saving. * * Typically, you will call setAutoSaveSettings() in your * KMainWindow-inherited class constructor, and it will take care * of restoring and saving automatically. Make sure you call this * _after all_ your *bars have been created. * * To make sure that KMainWindow properly obtains the default * size of the window you should do the following: * - Remove hard coded resize() calls in the constructor or main, they * should be removed in favor of letting the automatic resizing * determine the default window size. Hard coded window sizes will * be wrong for users that have big fonts, use different styles, * long/small translations, large toolbars, and other factors. * - Put the setAutoSaveSettings ( or setupGUI() ) call after all widgets * have been created and placed inside the main window (i.e. for 99% of * apps setCentralWidget()) * - Widgets that inherit from QWidget (like game boards) should overload * "virtual QSize sizeHint() const;" to specify a default size rather * than letting QWidget::adjust use the default size of 0x0. */ void setAutoSaveSettings(const QString &groupName = QStringLiteral("MainWindow"), bool saveWindowSize = true); /** * Overload that lets you specify a KConfigGroup. * This allows the settings to be saved into another file than KSharedConfig::openConfig(). * @since 4.1 */ void setAutoSaveSettings(const KConfigGroup &group, bool saveWindowSize = true); /** * Disable the auto-save-settings feature. * You don't normally need to call this, ever. */ void resetAutoSaveSettings(); /** * @return the current autosave setting, i.e. true if setAutoSaveSettings() was called, * false by default or if resetAutoSaveSettings() was called. */ bool autoSaveSettings() const; /** * @return the group used for setting-autosaving. * Only meaningful if setAutoSaveSettings(QString) was called. * This can be useful for forcing a save or an apply, e.g. before and after * using KEditToolbar. * * NOTE: you should rather use saveAutoSaveSettings() for saving or autoSaveConfigGroup() for loading. * This method doesn't make sense if setAutoSaveSettings(KConfigGroup) was called. */ QString autoSaveGroup() const; /** * @return the group used for setting-autosaving. * Only meaningful if setAutoSaveSettings() was called. * This can be useful for forcing an apply, e.g. after using KEditToolbar. * @since 4.1 */ KConfigGroup autoSaveConfigGroup() const; /** * Read settings for statusbar, menubar and toolbar from their respective * groups in the config file and apply them. * * @param config Config group to read the settings from. * KF5 porting note: the unused bool argument was removed, make sure to remove it from your * reimplementations too! And add a Q_DECL_OVERRIDE for good measure. */ virtual void applyMainWindowSettings(const KConfigGroup &config); /** * Save settings for statusbar, menubar and toolbar to their respective * groups in the config group @p config. * * @param config Config group to save the settings to. */ void saveMainWindowSettings(KConfigGroup &config); /** * Returns the path under which this window's D-Bus object is exported. * @since 4.0.1 */ QString dbusName() const; /** * @returns Always @c false * @deprecated since 5.0, the functionality got removed **/ #ifndef KXMLGUI_NO_DEPRECATED KXMLGUI_DEPRECATED bool initialGeometrySet() const { return false; } #endif public Q_SLOTS: /** * Makes a KDE compliant caption (window title). * * @param caption Your caption. @em Do @em not include the application name * in this string. It will be added automatically according to the KDE * standard. */ virtual void setCaption(const QString &caption); /** * Makes a KDE compliant caption. * * @param caption Your caption. @em Do @em not include the application name * in this string. It will be added automatically according to the KDE * standard. * @param modified Specify whether the document is modified. This displays * an additional sign in the title bar, usually "**". */ virtual void setCaption(const QString &caption, bool modified); /** * Make a plain caption without any modifications. * * @param caption Your caption. This is the string that will be * displayed in the window title. */ virtual void setPlainCaption(const QString &caption); /** * Open the help page for the application. * * The application name is * used as a key to determine what to display and the system will attempt * to open \<appName\>/index.html. * * This method is intended for use by a help button in the toolbar or * components outside the regular help menu. Use helpMenu() when you * want to provide access to the help system from the help menu. * * Example (adding a help button to the first toolbar): * * \code * toolBar(0)->addAction(QIcon::fromTheme("help-contents"), i18n("Help"), * this, SLOT(appHelpActivated())); * \endcode * */ void appHelpActivated(void); /** * Tell the main window that it should save its settings when being closed. * This is part of the auto-save-settings feature. * For everything related to toolbars this happens automatically, * but you have to call setSettingsDirty() in the slot that toggles * the visibility of the statusbar. */ void setSettingsDirty(); protected: /** * Reimplemented to catch QEvent::Polish in order to adjust the object name * if needed, once all constructor code for the main window has run. * Also reimplemented to catch when a QDockWidget is added or removed. */ bool event(QEvent *event) Q_DECL_OVERRIDE; /** * Reimplemented to autosave settings and call queryClose(). * * We recommend that you reimplement queryClose() rather than closeEvent(). * If you do it anyway, ensure to call the base implementation to keep * the feature of auto-saving window settings working. */ void closeEvent(QCloseEvent *) Q_DECL_OVERRIDE; /** Called before the window is closed, either by the user or indirectly by the session manager. The purpose of this function is to prepare the window in a way that it is safe to close it, i.e. without the user losing some data. Default implementation returns true. Returning @p false will cancel the closing, and, if KApplication::sessionSaving() is true, it will also cancel KDE logout. Reimplement this function to prevent the user from losing data. Example: \code switch ( KMessageBox::warningYesNoCancel( this, i18n("Save changes to document foo?")) ) { case KMessageBox::Yes : // save document here. If saving fails, return false; return true; case KMessageBox::No : return true; default: // cancel return false; \endcode Note that you should probably @em not actually close the document from within this method, as it may be called by the session manager before the session is saved. If the document is closed before the session save occurs, its location might not be properly saved. In addition, the session shutdown may be canceled, in which case the document should remain open. @see KApplication::sessionSaving() */ virtual bool queryClose(); /** * Save your instance-specific properties. The function is * invoked when the session manager requests your application * to save its state. * * Please reimplement these function in childclasses. * * Note: No user interaction is allowed * in this function! * */ virtual void saveProperties(KConfigGroup &) {} /** * Read your instance-specific properties. * * Is called indirectly by restore(). */ virtual void readProperties(const KConfigGroup &) {} /** * Save your application-wide properties. The function is * invoked when the session manager requests your application * to save its state. * * This function is similar to saveProperties() but is only called for * the very first main window, regardless how many main window are open. * Override it if you need to save other data about your documents on * session end. sessionConfig is a config to which that data should be * saved. Normally, you don't need this function. But if you want to save * data about your documents that are not in opened windows you might need * it. * * Default implementation does nothing. */ virtual void saveGlobalProperties(KConfig *sessionConfig); /** * The counterpart of saveGlobalProperties(). * * Read the application-specific properties in again. */ virtual void readGlobalProperties(KConfig *sessionConfig); void savePropertiesInternal(KConfig *, int); bool readPropertiesInternal(KConfig *, int); /** * For inherited classes */ bool settingsDirty() const; /** * For inherited classes * @deprecated use KWindowConfig::saveWindowSize */ #ifndef KXMLGUI_NO_DEPRECATED KXMLGUI_DEPRECATED void saveWindowSize(KConfigGroup &config) const; #endif /** * For inherited classes * @deprecated use KWindowConfig::restoreWindowSize */ #ifndef KXMLGUI_NO_DEPRECATED KXMLGUI_DEPRECATED void restoreWindowSize(const KConfigGroup &config); #endif protected Q_SLOTS: /** * This slot does nothing. * * It must be reimplemented if you want * to use a custom About Application dialog box. This slot is * connected to the About Application entry in the menu returned * by customHelpMenu. * * Example: * \code * * void MyMainLevel::setupInterface() * { * .. * menuBar()->addMenu( customHelpMenu() ); * .. * } * * void MyMainLevel::showAboutApplication() * { * <activate your custom dialog> * } * \endcode * @deprecated use KHelpMenu */ #ifndef KXMLGUI_NO_DEPRECATED virtual KXMLGUI_DEPRECATED void showAboutApplication() {} #endif /** * This slot should only be called in case you reimplement closeEvent() and * if you are using the "auto-save" feature. In all other cases, * setSettingsDirty() should be called instead to benefit from the delayed * saving. * * @see setAutoSaveSettings * @see setSettingsDirty * * Example: * \code * * void MyMainWindow::closeEvent( QCloseEvent *e ) * { * // Save settings if auto-save is enabled, and settings have changed * if ( settingsDirty() && autoSaveSettings() ) * saveAutoSaveSettings(); * .. * } * \endcode */ void saveAutoSaveSettings(); protected: KMainWindow(KMainWindowPrivate &dd, QWidget *parent, Qt::WindowFlags f); KMainWindowPrivate *const k_ptr; private: Q_PRIVATE_SLOT(k_func(), void _k_slotSettingsChanged(int)) Q_PRIVATE_SLOT(k_func(), void _k_slotSaveAutoSaveSize()) }; /** * @def RESTORE * @ingroup XMLGUIMacros * Restores the last session. * * @deprecated since 5.0, use kRestoreMainWindows() instead **/ #ifndef KXMLGUI_NO_DEPRECATED #define RESTORE(type) { int n = 1;\ while (KMainWindow::canBeRestored(n)){\ (new type)->restore(n);\ n++;}} #endif /** * @def KDE_RESTORE_MAIN_WINDOWS_NUM_TEMPLATE_ARGS * @ingroup XMLGUIMacros * Returns the maximal number of arguments that are actually * supported by kRestoreMainWindows(). **/ #define KDE_RESTORE_MAIN_WINDOWS_NUM_TEMPLATE_ARGS 3 /** * Restores the last session. (To be used in your main function). * * These functions work also if you have more than one kind of toplevel * widget (each derived from KMainWindow, of course). * * Imagine you have three kinds of toplevel widgets: the classes childMW1, * childMW2 and childMW3. Than you can just do: * * \code * if (qApp->isSessionRestored()) * kRestoreMainWindows< childMW1, childMW2, childMW3 >(); * else { * // create default application as usual * } * \endcode * * kRestoreMainWindows<>() will create (on the heap) as many instances * of your main windows as have existed in the last session and * call KMainWindow::restore() with the correct arguments. Note that * also QWidget::show() is called implicitly. * * Currently, these functions are provided for up to three * template arguments. If you need more, tell us. To help you in * deciding whether or not you can use kRestoreMainWindows, a * define #KDE_RESTORE_MAIN_WINDOWS_NUM_TEMPLATE_ARGS is provided. * * @see KMainWindow::restore() * @see KMainWindow::classNameOfToplevel() **/ template <typename T> inline void kRestoreMainWindows() { for (int n = 1; KMainWindow::canBeRestored(n); ++n) { const QString className = KMainWindow::classNameOfToplevel(n); if (className == QLatin1String(T::staticMetaObject.className())) { (new T)->restore(n); } } } template <typename T0, typename T1> inline void kRestoreMainWindows() { const char *classNames[2]; classNames[0] = T0::staticMetaObject.className(); classNames[1] = T1::staticMetaObject.className(); for (int n = 1; KMainWindow::canBeRestored(n); ++n) { const QString className = KMainWindow::classNameOfToplevel(n); if (className == QLatin1String(classNames[0])) { (new T0)->restore(n); } else if (className == QLatin1String(classNames[1])) { (new T1)->restore(n); } } } template <typename T0, typename T1, typename T2> inline void kRestoreMainWindows() { const char *classNames[3]; classNames[0] = T0::staticMetaObject.className(); classNames[1] = T1::staticMetaObject.className(); classNames[2] = T2::staticMetaObject.className(); for (int n = 1; KMainWindow::canBeRestored(n); ++n) { const QString className = KMainWindow::classNameOfToplevel(n); if (className == QLatin1String(classNames[0])) { (new T0)->restore(n); } else if (className == QLatin1String(classNames[1])) { (new T1)->restore(n); } else if (className == QLatin1String(classNames[2])) { (new T2)->restore(n); } } } #endif diff --git a/src/kmenumenuhandler_p.cpp b/src/kmenumenuhandler_p.cpp index 2abb159..0ec63be 100644 --- a/src/kmenumenuhandler_p.cpp +++ b/src/kmenumenuhandler_p.cpp @@ -1,244 +1,244 @@ /* This file is part of the KDE project Copyright (C) 2006 Olivier Goffart <ogoffart@kde.org> 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 "kmenumenuhandler_p.h" #include "kxmlguibuilder.h" #include "kxmlguiclient.h" #include "kxmlguifactory.h" #include "kactioncollection.h" #include "kmainwindow.h" #include "ktoolbar.h" #include "kshortcutwidget.h" #include <QAction> #include <QContextMenuEvent> #include <QDialog> #include <QDialogButtonBox> #include <QWidget> #include <QDomDocument> #include <QDomNode> #include <QMenu> #include <QVBoxLayout> #include <QDebug> #include <kselectaction.h> #include <klocalizedstring.h> namespace KDEPrivate { KMenuMenuHandler::KMenuMenuHandler(KXMLGUIBuilder *builder) - : QObject(), m_builder(builder), m_popupMenu(0), m_popupAction(0), m_contextMenu(0) + : QObject(), m_builder(builder), m_popupMenu(nullptr), m_popupAction(nullptr), m_contextMenu(nullptr) { m_toolbarAction = new KSelectAction(i18n("Add to Toolbar"), this); connect(m_toolbarAction, SIGNAL(triggered(int)), this, SLOT(slotAddToToolBar(int))); } void KMenuMenuHandler::insertMenu(QMenu *popup) { popup->installEventFilter(this); } bool KMenuMenuHandler::eventFilter(QObject *watched, QEvent *event) { switch (event->type()) { case QEvent::MouseButtonPress: if (m_contextMenu && m_contextMenu->isVisible()) { m_contextMenu->hide(); return true; } break; case QEvent::MouseButtonRelease: if (m_contextMenu && m_contextMenu->isVisible()) { return true; } break; case QEvent::ContextMenu: { QContextMenuEvent *e = static_cast<QContextMenuEvent *>(event); QMenu *menu = static_cast<QMenu *>(watched); if (e->reason() == QContextMenuEvent::Mouse) { showContextMenu(menu, e->pos()); } else if (menu->activeAction()) { showContextMenu(menu, menu->actionGeometry(menu->activeAction()).center()); } } event->accept(); return true; default: break; } return false; } void KMenuMenuHandler::buildToolbarAction() { KMainWindow *window = qobject_cast<KMainWindow *>(m_builder->widget()); if (!window) { return; } QStringList toolbarlist; foreach (KToolBar *b, window->toolBars()) { toolbarlist << (b->windowTitle().isEmpty() ? b->objectName() : b->windowTitle()); } m_toolbarAction->setItems(toolbarlist); } static KActionCollection *findParentCollection(KXMLGUIFactory *factory, QAction *action) { foreach (KXMLGUIClient *client, factory->clients()) { KActionCollection *collection = client->actionCollection(); // if the call to actions() is too slow, add KActionCollection::contains(QAction*). if (collection->actions().contains(action)) { return collection; } } - return 0; + return nullptr; } void KMenuMenuHandler::slotSetShortcut() { if (!m_popupMenu || !m_popupAction) { return; } QDialog dialog(m_builder->widget()); dialog.setLayout(new QVBoxLayout); KShortcutWidget swidget(&dialog); swidget.setShortcut(m_popupAction->shortcuts()); dialog.layout()->addWidget(&swidget); QDialogButtonBox box(&dialog); box.setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); connect(&box, SIGNAL(accepted()), &dialog, SLOT(accept())); connect(&box, SIGNAL(rejected()), &dialog, SLOT(reject())); dialog.layout()->addWidget(&box); - KActionCollection *parentCollection = 0; + KActionCollection *parentCollection = nullptr; if (dynamic_cast<KXMLGUIClient *>(m_builder)) { QList<KActionCollection *> checkCollections; KXMLGUIFactory *factory = dynamic_cast<KXMLGUIClient *>(m_builder)->factory(); parentCollection = findParentCollection(factory, m_popupAction); foreach (KXMLGUIClient *client, factory->clients()) { checkCollections += client->actionCollection(); } swidget.setCheckActionCollections(checkCollections); } if (dialog.exec()) { m_popupAction->setShortcuts(swidget.shortcut()); swidget.applyStealShortcut(); if (parentCollection) { parentCollection->writeSettings(); } } } void KMenuMenuHandler::slotAddToToolBar(int tb) { KMainWindow *window = qobject_cast<KMainWindow *>(m_builder->widget()); if (!window) { return; } if (!m_popupMenu || !m_popupAction) { return; } KXMLGUIFactory *factory = dynamic_cast<KXMLGUIClient *>(m_builder)->factory(); QString actionName = m_popupAction->objectName(); // set by KActionCollection::addAction - KActionCollection *collection = 0; + KActionCollection *collection = nullptr; if (factory) { collection = findParentCollection(factory, m_popupAction); } if (!collection) { qWarning() << "Cannot find the action collection for action " << actionName; return; } KToolBar *toolbar = window->toolBars().at(tb); toolbar->addAction(m_popupAction); const KXMLGUIClient *client = collection->parentGUIClient(); QString xmlFile = client->localXMLFile(); QDomDocument document; document.setContent(KXMLGUIFactory::readConfigFile(client->xmlFile(), client->componentName())); QDomElement elem = document.documentElement().toElement(); const QLatin1String tagToolBar("ToolBar"); const QLatin1String attrNoEdit("noEdit"); const QLatin1String attrName("name"); QDomElement toolbarElem; QDomNode n = elem.firstChild(); for (; !n.isNull(); n = n.nextSibling()) { QDomElement elem = n.toElement(); if (!elem.isNull() && elem.tagName() == tagToolBar && elem.attribute(attrName) == toolbar->objectName()) { if (elem.attribute(attrNoEdit) == QStringLiteral("true")) { qWarning() << "The toolbar is not editable"; return; } toolbarElem = elem; break; } } if (toolbarElem.isNull()) { toolbarElem = document.createElement(tagToolBar); toolbarElem.setAttribute(attrName, toolbar->objectName()); elem.appendChild(toolbarElem); } KXMLGUIFactory::findActionByName(toolbarElem, actionName, true); KXMLGUIFactory::saveConfigFile(document, xmlFile); } void KMenuMenuHandler::showContextMenu(QMenu *menu, const QPoint &pos) { Q_ASSERT(!m_popupMenu); Q_ASSERT(!m_popupAction); Q_ASSERT(!m_contextMenu); m_popupMenu = menu; m_popupAction = menu->actionAt(pos); m_contextMenu = new QMenu; m_contextMenu->addAction(i18n("Configure Shortcut..."), this, SLOT(slotSetShortcut())); KMainWindow *window = qobject_cast<KMainWindow *>(m_builder->widget()); if (window) { m_contextMenu->addAction(m_toolbarAction); buildToolbarAction(); } m_contextMenu->exec(menu->mapToGlobal(pos)); delete m_contextMenu; - m_contextMenu = 0; + m_contextMenu = nullptr; - m_popupAction = 0; - m_popupMenu = 0; + m_popupAction = nullptr; + m_popupMenu = nullptr; } } //END namespace KDEPrivate diff --git a/src/ksendbugmail/main.h b/src/ksendbugmail/main.h index 58f01eb..6a8be82 100644 --- a/src/ksendbugmail/main.h +++ b/src/ksendbugmail/main.h @@ -1,43 +1,43 @@ /* Copyright (c) 2000 Bernd Johannes Wuebben <wuebben@math.cornell.edu> Copyright (c) 2000 Stephan Kulow <coolo@kde.org> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KSENDBUGMAIL_MAIN_H #define KSENDBUGMAIL_MAIN_H #include <QtCore/QObject> class SMTP; class BugMailer : public QObject { Q_OBJECT public: - BugMailer(SMTP *s) : QObject(0), sm(s) + BugMailer(SMTP *s) : QObject(nullptr), sm(s) { setObjectName(QStringLiteral("mailer")); } public Q_SLOTS: void slotError(int); void slotSend(); private: SMTP *sm; }; #endif diff --git a/src/ksendbugmail/smtp.cpp b/src/ksendbugmail/smtp.cpp index 5483bf7..d26ecf4 100644 --- a/src/ksendbugmail/smtp.cpp +++ b/src/ksendbugmail/smtp.cpp @@ -1,348 +1,348 @@ /* Copyright (c) 2000 Bernd Johannes Wuebben <wuebben@math.cornell.edu> Copyright (c) 2000 Stephan Kulow <coolo@kde.org> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "smtp.h" #include "../systeminformation_p.h" #include <stdio.h> #include <QDebug> #include <QSslSocket> #include <QHostInfo> SMTP::SMTP(char *serverhost, unsigned short int port, int timeout) { serverHost = serverhost; hostPort = port; timeOut = timeout * 1000; senderAddress = QStringLiteral("user@example.net"); recipientAddress = QStringLiteral("user@example.net"); messageSubject = QStringLiteral("(no subject)"); messageBody = QStringLiteral("empty"); messageHeader = QLatin1String(""); connected = false; finished = false; - sock = 0L; + sock = nullptr; state = Init; serverState = None; domainName = QHostInfo::localDomainName(); if (domainName.isEmpty()) { domainName = QStringLiteral("somemachine.example.net"); } // qDebug() << "SMTP object created"; connect(&connectTimer, SIGNAL(timeout()), this, SLOT(connectTimerTick())); connect(&timeOutTimer, SIGNAL(timeout()), this, SLOT(connectTimedOut())); connect(&interactTimer, SIGNAL(timeout()), this, SLOT(interactTimedOut())); // some sendmail will give 'duplicate helo' error, quick fix for now connect(this, SIGNAL(messageSent()), SLOT(closeConnection())); } SMTP::~SMTP() { delete sock; - sock = 0L; + sock = nullptr; connectTimer.stop(); timeOutTimer.stop(); } void SMTP::setServerHost(const QString &serverhost) { serverHost = serverhost; } void SMTP::setPort(unsigned short int port) { hostPort = port; } void SMTP::setTimeOut(int timeout) { timeOut = timeout; } void SMTP::setSenderAddress(const QString &sender) { senderAddress = sender; int index = senderAddress.indexOf('<'); if (index == -1) { return; } senderAddress = senderAddress.mid(index + 1); index = senderAddress.indexOf('>'); if (index != -1) { senderAddress = senderAddress.left(index); } senderAddress = senderAddress.simplified(); while (1) { index = senderAddress.indexOf(' '); if (index != -1) { senderAddress = senderAddress.mid(index + 1); // take one side } else { break; } } index = senderAddress.indexOf('@'); if (index == -1) { senderAddress.append("@localhost"); // won't go through without a local mail system } } void SMTP::setRecipientAddress(const QString &recipient) { recipientAddress = recipient; } void SMTP::setMessageSubject(const QString &subject) { messageSubject = subject; } void SMTP::setMessageBody(const QString &message) { messageBody = message; } void SMTP::setMessageHeader(const QString &header) { messageHeader = header; } void SMTP::openConnection(void) { // qDebug() << "started connect timer"; connectTimer.setSingleShot(true); connectTimer.start(100); } void SMTP::closeConnection(void) { socketClosed(); } void SMTP::sendMessage(void) { if (!connected) { connectTimerTick(); } if (state == Finished && connected) { // qDebug() << "state was == Finished\n"; finished = false; state = In; writeString = QStringLiteral("helo %1\r\n").arg(domainName); sock->write(writeString.toLatin1().constData(), writeString.length()); } if (connected) { // qDebug() << "enabling read on sock...\n"; interactTimer.setSingleShot(true); interactTimer.start(timeOut); } } void SMTP::connectTimerTick(void) { connectTimer.stop(); // timeOutTimer.start(timeOut, true); // qDebug() << "connectTimerTick called..."; delete sock; - sock = 0L; + sock = nullptr; // qDebug() << "connecting to " << serverHost << ":" << hostPort << " ..... "; sock = new QSslSocket(this); sock->connectToHost(serverHost, hostPort); connected = true; finished = false; state = Init; serverState = None; connect(sock, SIGNAL(readyRead()), this, SLOT(socketReadyToRead())); connect(sock, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(socketError(QAbstractSocket::SocketError))); connect(sock, SIGNAL(disconnected()), this, SLOT(socketClosed())); timeOutTimer.stop(); // qDebug() << "connected"; } void SMTP::connectTimedOut(void) { timeOutTimer.stop(); // qDebug() << "socket connection timed out"; socketClosed(); emit error(ConnectTimeout); } void SMTP::interactTimedOut(void) { interactTimer.stop(); // qDebug() << "time out waiting for server interaction"; socketClosed(); emit error(InteractTimeout); } void SMTP::socketReadyToRead() { int n, nl; // qDebug() << "socketRead() called..."; interactTimer.stop(); if (!sock) { return; } n = sock->read(readBuffer, SMTP_READ_BUFFER_SIZE - 1); if (n < 0) { return; } readBuffer[n] = 0; lineBuffer += readBuffer; nl = lineBuffer.indexOf('\n'); if (nl == -1) { return; } lastLine = lineBuffer.left(nl); lineBuffer = lineBuffer.right(lineBuffer.length() - nl - 1); processLine(&lastLine); if (connected) { interactTimer.setSingleShot(true); interactTimer.start(timeOut); } } void SMTP::socketError(QAbstractSocket::SocketError socketError) { // qDebug() << socketError << sock->errorString(); Q_UNUSED(socketError); emit error(ConnectError); socketClosed(); } void SMTP::socketClosed() { timeOutTimer.stop(); // qDebug() << "connection terminated"; connected = false; if (sock) { sock->deleteLater(); } - sock = 0; + sock = nullptr; emit connectionClosed(); } void SMTP::processLine(QString *line) { int i, stat; QString tmpstr; i = line->indexOf(' '); tmpstr = line->left(i); if (i > 3) { // qDebug() << "warning: SMTP status code longer than 3 digits: " << tmpstr; } stat = tmpstr.toInt(); serverState = static_cast<SMTPServerStatus>(stat); lastState = state; // qDebug() << "smtp state: [" << stat << "][" << *line << "]"; switch (stat) { case Greet: //220 state = In; writeString = QStringLiteral("helo %1\r\n").arg(domainName); // qDebug() << "out: " << writeString; sock->write(writeString.toLatin1().constData(), writeString.length()); break; case Goodbye: //221 state = Quit; break; case Successful://250 switch (state) { case In: state = Ready; writeString = QStringLiteral("mail from: %1\r\n").arg(senderAddress); // qDebug() << "out: " << writeString; sock->write(writeString.toLatin1().constData(), writeString.length()); break; case Ready: state = SentFrom; writeString = QStringLiteral("rcpt to: %1\r\n").arg(recipientAddress); // qDebug() << "out: " << writeString; sock->write(writeString.toLatin1().constData(), writeString.length()); break; case SentFrom: state = SentTo; writeString = QStringLiteral("data\r\n"); // qDebug() << "out: " << writeString; sock->write(writeString.toLatin1().constData(), writeString.length()); break; case Data: state = Finished; finished = true; emit messageSent(); break; default: state = CError; // qDebug() << "smtp error (state error): [" << lastState << "]:[" << stat << "][" << *line << "]"; socketClosed(); emit error(Command); break; } break; case ReadyData: //354 state = Data; writeString = QStringLiteral("Subject: %1\r\n").arg(messageSubject); writeString += messageHeader; writeString += QLatin1String("\r\n"); writeString += messageBody; writeString += QLatin1String(".\r\n"); // qDebug() << "out: " << writeString; sock->write(writeString.toLatin1().constData(), writeString.length()); break; case Error: //501 state = CError; // qDebug() << "smtp error (command error): [" << lastState << "]:[" << stat << "][" << *line << "]\n"; socketClosed(); emit error(Command); break; case Unknown: //550 state = CError; // qDebug() << "smtp error (unknown user): [" << lastState << "]:[" << stat << "][" << *line << "]"; socketClosed(); emit error(UnknownUser); break; default: state = CError; // qDebug() << "unknown response: [" << lastState << "]:[" << stat << "][" << *line << "]"; socketClosed(); emit error(UnknownResponse); } } diff --git a/src/ksendbugmail/smtp.h b/src/ksendbugmail/smtp.h index b6922ef..413c2e0 100644 --- a/src/ksendbugmail/smtp.h +++ b/src/ksendbugmail/smtp.h @@ -1,171 +1,171 @@ /* Copyright (c) 2000 Bernd Johannes Wuebben <wuebben@math.cornell.edu> Copyright (c) 2000 Stephan Kulow <coolo@kde.org> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef SMTP_H #define SMTP_H #include <QtCore/QObject> #include <QtCore/QTimer> #include <QtNetwork/QTcpSocket> /*int SMTPServerStatus[] = { 220, // greeting from server 221, // server acknolages goodbye 250, // command successful 354, // ready to receive data 501, // error 550, // user unknown 0 // null }; int SMTPClientStatus[] = { 50, // not logged in yet. 100, // logged in, got 220 150, // sent helo, got 250 200, // sent mail from, got 250 250, // sent rctp to, got 250 300, // data sent, got 354 350, // sent data/., got 250 400, // send quit, got 221 450, // finished, logged out 0 // null }; */ #define DEFAULT_SMTP_PORT 25 #define DEFAULT_SMTP_SERVER localhost #define DEFAULT_SMTP_TIMEOUT 60 #define SMTP_READ_BUFFER_SIZE 256 class SMTP: public QObject { Q_OBJECT public: - explicit SMTP(char *serverhost = 0, unsigned short int port = 0, + explicit SMTP(char *serverhost = nullptr, unsigned short int port = 0, int timeout = DEFAULT_SMTP_TIMEOUT); ~SMTP(); void setServerHost(const QString &serverhost); void setPort(unsigned short int port); void setTimeOut(int timeout); bool isConnected() { return connected; } bool isFinished() { return finished; } QString getLastLine() { return lastLine; } void setSenderAddress(const QString &sender); void setRecipientAddress(const QString &recipient); void setMessageSubject(const QString &subject); void setMessageBody(const QString &message); void setMessageHeader(const QString &header); typedef enum { None = 0, // null Greet = 220, // greeting from server Goodbye = 221, // server acknolages quit Successful = 250, // command successful ReadyData = 354, // server ready to receive data Error = 501, // error Unknown = 550 // user unknown } SMTPServerStatus; typedef enum { Init = 50, // not logged in yet In = 100, // logged in, got 220 Ready = 150, // sent HELO, got 250 SentFrom = 200, // sent MAIL FROM:, got 250 SentTo = 250, // sent RCTP TO:, got 250 Data = 300, // Data sent, got 354 Finished = 350, // finished sending data, got 250 Quit = 400, // sent Quit, got 221 Out = 450, // finished, logged out CError = 500 // didn't finish, had error or connection drop } SMTPClientStatus; typedef enum { NoError = 0, ConnectError = 10, NotConnected = 11, ConnectTimeout = 15, InteractTimeout = 16, UnknownResponse = 20, UnknownUser = 30, Command = 40 } SMTPError; protected: void processLine(QString *line); public Q_SLOTS: void openConnection(); void sendMessage(); void closeConnection(); void connectTimerTick(); void connectTimedOut(); void interactTimedOut(); void socketReadyToRead(); void socketClosed(); void socketError(QAbstractSocket::SocketError); Q_SIGNALS: void connectionClosed(); void messageSent(); void error(int); private: QString serverHost; unsigned short int hostPort; int timeOut; bool connected; bool finished; QString senderAddress; QString recipientAddress; QString messageSubject; QString messageBody, messageHeader; SMTPClientStatus state; SMTPClientStatus lastState; SMTPServerStatus serverState; QString domainName; QTcpSocket *sock; QTimer connectTimer; QTimer timeOutTimer; QTimer interactTimer; char readBuffer[SMTP_READ_BUFFER_SIZE]; QString lineBuffer; QString lastLine; QString writeString; }; #endif diff --git a/src/kshortcutsdialog.cpp b/src/kshortcutsdialog.cpp index 1ed7b91..9f889f7 100644 --- a/src/kshortcutsdialog.cpp +++ b/src/kshortcutsdialog.cpp @@ -1,263 +1,263 @@ /* This file is part of the KDE libraries Copyright (C) 1998 Mark Donohoe <donohoe@kde.org> Copyright (C) 1997 Nicolas Hadacek <hadacek@kde.org> Copyright (C) 1998 Matthias Ettrich <ettrich@kde.org> Copyright (C) 2001 Ellis Whitehead <ellis@kde.org> Copyright (C) 2006 Hamish Rodda <rodda@kde.org> Copyright (C) 2007 Roberto Raggi <roberto@kdevelop.org> Copyright (C) 2007 Andreas Hartmetz <ahartmetz@gmail.com> Copyright (C) 2008 Michael Jansen <kde@michael-jansen.biz> Copyright (C) 2008 Alexander Dymo <adymo@kdevelop.org> Copyright (C) 2009 Chani Armitage <chani@kde.org> 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 "kshortcutsdialog.h" #include "kshortcutsdialog_p.h" #include "kshortcutschemeshelper_p.h" #include <QApplication> #include <QDialogButtonBox> #include <QDomDocument> #include <klocalizedstring.h> #include <kconfiggroup.h> #include <kmessagebox.h> #include <ksharedconfig.h> #include "kxmlguiclient.h" #include "kxmlguifactory.h" #include "kactioncollection.h" /************************************************************************/ /* KShortcutsDialog */ /* */ /* Originally by Nicolas Hadacek <hadacek@via.ecp.fr> */ /* */ /* Substantially revised by Mark Donohoe <donohoe@kde.org> */ /* */ /* And by Espen Sand <espen@kde.org> 1999-10-19 */ /* (by using KDialog there is almost no code left ;) */ /* */ /************************************************************************/ QKeySequence primarySequence(const QList<QKeySequence> &sequences) { return sequences.isEmpty() ? QKeySequence() : sequences.at(0); } QKeySequence alternateSequence(const QList<QKeySequence> &sequences) { return sequences.size() <= 1 ? QKeySequence() : sequences.at(1); } class KShortcutsDialog::KShortcutsDialogPrivate { public: KShortcutsDialogPrivate(KShortcutsDialog *q) : q(q), - m_keyChooser(0), - m_schemeEditor(0), - m_detailsButton(0), + m_keyChooser(nullptr), + m_schemeEditor(nullptr), + m_detailsButton(nullptr), m_saveSettings(false) { } QList<KActionCollection *> m_collections; void changeShortcutScheme(const QString &scheme) { if (m_keyChooser->isModified() && KMessageBox::questionYesNo(q, i18n("The current shortcut scheme is modified. Save before switching to the new one?")) == KMessageBox::Yes) { m_keyChooser->save(); } else { m_keyChooser->undoChanges(); } QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); m_keyChooser->clearCollections(); foreach (KActionCollection *collection, m_collections) { // passing an empty stream forces the clients to reread the XML KXMLGUIClient *client = const_cast<KXMLGUIClient *>(collection->parentGUIClient()); if (client) { client->setXMLGUIBuildDocument(QDomDocument()); } } //get xmlguifactory if (!m_collections.isEmpty()) { const KXMLGUIClient *client = m_collections.first()->parentGUIClient(); if (client) { KXMLGUIFactory *factory = client->factory(); if (factory) { factory->changeShortcutScheme(scheme); } } } foreach (KActionCollection *collection, m_collections) { m_keyChooser->addCollection(collection); } QApplication::restoreOverrideCursor(); } void undoChanges() { m_keyChooser->undoChanges(); } void toggleDetails() { const bool isVisible = m_schemeEditor->isVisible(); m_schemeEditor->setVisible(!isVisible); m_detailsButton->setText(detailsButtonText() + (isVisible ? QStringLiteral(" >>") : QStringLiteral(" <<"))); } static QString detailsButtonText() { return i18n("Manage &Schemes"); } void save() { m_keyChooser->save(); emit q->saved(); } KShortcutsDialog *q; KShortcutsEditor *m_keyChooser; // ### move KShortcutSchemesEditor *m_schemeEditor; QPushButton *m_detailsButton; bool m_saveSettings; }; KShortcutsDialog::KShortcutsDialog(KShortcutsEditor::ActionTypes types, KShortcutsEditor::LetterShortcuts allowLetterShortcuts, QWidget *parent) : QDialog(parent), d(new KShortcutsDialogPrivate(this)) { setWindowTitle(i18n("Configure Shortcuts")); setModal(true); QVBoxLayout *layout = new QVBoxLayout; setLayout(layout); d->m_keyChooser = new KShortcutsEditor(this, types, allowLetterShortcuts); layout->addWidget(d->m_keyChooser); d->m_schemeEditor = new KShortcutSchemesEditor(this); connect(d->m_schemeEditor, SIGNAL(shortcutsSchemeChanged(QString)), this, SLOT(changeShortcutScheme(QString))); d->m_schemeEditor->hide(); layout->addWidget(d->m_schemeEditor); d->m_detailsButton = new QPushButton; d->m_detailsButton->setText(KShortcutsDialogPrivate::detailsButtonText() + QStringLiteral(" >>")); QPushButton *printButton = new QPushButton; KGuiItem::assign(printButton, KStandardGuiItem::print()); QDialogButtonBox *buttonBox = new QDialogButtonBox(this); buttonBox->addButton(d->m_detailsButton, QDialogButtonBox::ActionRole); buttonBox->addButton(printButton, QDialogButtonBox::ActionRole); buttonBox->setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel | QDialogButtonBox::RestoreDefaults); KGuiItem::assign(buttonBox->button(QDialogButtonBox::Ok), KStandardGuiItem::ok()); KGuiItem::assign(buttonBox->button(QDialogButtonBox::Cancel), KStandardGuiItem::cancel()); KGuiItem::assign(buttonBox->button(QDialogButtonBox::RestoreDefaults), KStandardGuiItem::defaults()); layout->addWidget(buttonBox); connect(buttonBox->button(QDialogButtonBox::RestoreDefaults), SIGNAL(clicked()), d->m_keyChooser, SLOT(allDefault())); connect(d->m_detailsButton, SIGNAL(clicked()), this, SLOT(toggleDetails())); connect(printButton, SIGNAL(clicked()), d->m_keyChooser, SLOT(printShortcuts())); connect(buttonBox, SIGNAL(rejected()), this, SLOT(undoChanges())); connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept())); connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); KConfigGroup group(KSharedConfig::openConfig(), "KShortcutsDialog Settings"); resize(group.readEntry("Dialog Size", sizeHint())); } KShortcutsDialog::~KShortcutsDialog() { KConfigGroup group(KSharedConfig::openConfig(), "KShortcutsDialog Settings"); group.writeEntry("Dialog Size", size(), KConfigGroup::Persistent | KConfigGroup::Global); delete d; } void KShortcutsDialog::addCollection(KActionCollection *collection, const QString &title) { d->m_keyChooser->addCollection(collection, title); d->m_collections << collection; } QList<KActionCollection *> KShortcutsDialog::actionCollections() const { return d->m_collections; } //FIXME should there be a setSaveSettings method? bool KShortcutsDialog::configure(bool saveSettings) { d->m_saveSettings = saveSettings; if (isModal()) { int retcode = exec(); return retcode; } else { show(); return false; } } void KShortcutsDialog::accept() { if (d->m_saveSettings) { d->save(); } QDialog::accept(); } QSize KShortcutsDialog::sizeHint() const { return QSize(600, 480); } int KShortcutsDialog::configure(KActionCollection *collection, KShortcutsEditor::LetterShortcuts allowLetterShortcuts, QWidget *parent, bool saveSettings) { //qDebug(125) << "KShortcutsDialog::configureKeys( KActionCollection*, " << saveSettings << " )"; KShortcutsDialog dlg(KShortcutsEditor::AllActions, allowLetterShortcuts, parent); dlg.d->m_keyChooser->addCollection(collection); return dlg.configure(saveSettings); } void KShortcutsDialog::importConfiguration(const QString &path) { KConfig config(path); d->m_keyChooser->importConfiguration(static_cast<KConfigBase *>(&config)); } void KShortcutsDialog::exportConfiguration(const QString &path) const { KConfig config(path); d->m_keyChooser->exportConfiguration(static_cast<KConfigBase *>(&config)); } #include "moc_kshortcutsdialog.cpp" #include "moc_kshortcutsdialog_p.cpp" diff --git a/src/kshortcutsdialog.h b/src/kshortcutsdialog.h index 56af501..65d9e0f 100644 --- a/src/kshortcutsdialog.h +++ b/src/kshortcutsdialog.h @@ -1,167 +1,167 @@ /* This file is part of the KDE libraries Copyright (C) 1997 Nicolas Hadacek <hadacek@kde.org> Copyright (C) 2001,2001 Ellis Whitehead <ellis@kde.org> Copyright (C) 2006 Hamish Rodda <rodda@kde.org> Copyright (C) 2007 Roberto Raggi <roberto@kdevelop.org> Copyright (C) 2007 Andreas Hartmetz <ahartmetz@gmail.com> Copyright (C) 2008 Michael Jansen <kde@michael-jansen.biz> 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 KSHORTCUTSDIALOG_H #define KSHORTCUTSDIALOG_H #include <kxmlgui_export.h> #include <QDialog> #include "kshortcutseditor.h" /** * @short Dialog for configuration of KActionCollection and KGlobalAccel. * * The KShortcutsDialog class is used for configuring dictionaries of key/action * associations for KActionCollection and KGlobalAccel. It uses the KShortcutsEditor widget * and offers buttons to set all keys to defaults and invoke on-line help. * * Several static methods are supplied which provide the most convenient interface * to the dialog. The most common and most encouraged use is with KActionCollection. * * \code * KShortcutsDialog::configure( actionCollection() ); * \endcode * * @since 4.3 * By default this dialog is modal. If you don't want that, setModal(false) and then the non-static * configure() will show the dialog. If you want to do anything extra when the dialog is done, * connect to okClicked() and/or cancelClicked(). However, if your extra stuff depends on the * changed settings already being saved, connect to saved() instead to be safe; if you connect to * okClicked() your function might be called before the save happens. * * example: * \code * KShortcutsDialog dlg; * dlg.addCollection(myActions); * dlg.setModal(false); * connect(&dlg, SIGNAL(saved()), this, SLOT(doExtraStuff())); * dlg.configure(); * \endcode * * \image html kshortcutsdialog.png "KDE Shortcuts Dialog" * * @author Nicolas Hadacek <hadacek@via.ecp.fr> * @author Hamish Rodda <rodda@kde.org> (KDE 4 porting) * @author Michael Jansen <kde@michael-jansen.biz> */ class KXMLGUI_EXPORT KShortcutsDialog : public QDialog { Q_OBJECT public: /** * Constructs a KShortcutsDialog as a child of @p parent. * Set @p allowLetterShortcuts to false if unmodified alphanumeric * keys ('A', '1', etc.) are not permissible shortcuts. */ explicit KShortcutsDialog(KShortcutsEditor::ActionTypes types = KShortcutsEditor::AllActions, KShortcutsEditor::LetterShortcuts allowLetterShortcuts = KShortcutsEditor::LetterShortcutsAllowed, - QWidget *parent = 0); + QWidget *parent = nullptr); /** * Destructor. Deletes all resources used by a KShortcutsDialog object. */ virtual ~KShortcutsDialog(); /** * Add all actions of the collection to the ones displayed and configured * by the dialog. * * @param title the title associated with the collection (if null, the * KAboutData::progName() of the collection's componentData is used) */ void addCollection(KActionCollection *, const QString &title = QString()); /** * @return the list of action collections that are available for configuration in the dialog. */ QList<KActionCollection *> actionCollections() const; /** * Run the dialog and call writeSettings() on the action collections * that were added if @p bSaveSettings is true. */ bool configure(bool saveSettings = true); /** @see QWidget::sizeHint() */ QSize sizeHint() const Q_DECL_OVERRIDE; /** * Pops up a modal dialog for configuring key settings. The new * shortcut settings will become active if the user presses OK. * * @param collection the KActionCollection to configure * @param allowLetterShortcuts set to KShortcutsEditor::LetterShortcutsDisallowed if unmodified alphanumeric * keys ('A', '1', etc.) are not permissible shortcuts. * @param parent the parent widget to attach to * @param bSaveSettings if true, the settings will also be saved back * by calling writeSettings() on the action collections that were added. * * @return Accept if the dialog was closed with OK, Reject otherwise. */ static int configure(KActionCollection *collection, KShortcutsEditor::LetterShortcuts allowLetterShortcuts = - KShortcutsEditor::LetterShortcutsAllowed, QWidget *parent = 0, bool bSaveSettings = true); + KShortcutsEditor::LetterShortcutsAllowed, QWidget *parent = nullptr, bool bSaveSettings = true); /** * Imports a shortcuts set up from @p path * * @since 5.15 */ void importConfiguration(const QString &path); /** * Exports a shortcuts set up from @p path * * @since 5.15 */ void exportConfiguration(const QString &path) const; public Q_SLOTS: /** * @reimp */ void accept() Q_DECL_OVERRIDE; Q_SIGNALS: /** * emitted after ok is clicked and settings are saved */ void saved(); private: Q_PRIVATE_SLOT(d, void changeShortcutScheme(const QString &)) Q_PRIVATE_SLOT(d, void undoChanges()) Q_PRIVATE_SLOT(d, void toggleDetails()) class KShortcutsDialogPrivate; friend class KShortcutsDialogPrivate; class KShortcutsDialogPrivate *const d; Q_DISABLE_COPY(KShortcutsDialog) }; #endif // KSHORTCUTSDIALOG_H diff --git a/src/kshortcutseditor.cpp b/src/kshortcutseditor.cpp index c32aed0..c2bf181 100644 --- a/src/kshortcutseditor.cpp +++ b/src/kshortcutseditor.cpp @@ -1,796 +1,796 @@ /* This file is part of the KDE libraries Copyright (C) 1998 Mark Donohoe <donohoe@kde.org> Copyright (C) 1997 Nicolas Hadacek <hadacek@kde.org> Copyright (C) 1998 Matthias Ettrich <ettrich@kde.org> Copyright (C) 2001 Ellis Whitehead <ellis@kde.org> Copyright (C) 2006 Hamish Rodda <rodda@kde.org> Copyright (C) 2007 Roberto Raggi <roberto@kdevelop.org> Copyright (C) 2007 Andreas Hartmetz <ahartmetz@gmail.com> Copyright (C) 2008 Michael Jansen <kde@michael-jansen.biz> 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 "config-xmlgui.h" #include "kshortcutseditor.h" // The following is needed for KShortcutsEditorPrivate and QTreeWidgetHack #include "kshortcutsdialog_p.h" #include <QAction> #include <QHeaderView> #include <QList> #include <QObject> #include <QTimer> #include <QTextDocument> #include <QTextTable> #include <QTextCursor> #include <QTextTableFormat> #include <QPrinter> #include <QPrintDialog> #include <QDebug> #include <kconfig.h> #include <kconfiggroup.h> #if HAVE_GLOBALACCEL # include <kglobalaccel.h> #endif #include <kmessagebox.h> #include "kactioncollection.h" #include "kactioncategory.h" #include <ktreewidgetsearchline.h> //--------------------------------------------------------------------- // KShortcutsEditor //--------------------------------------------------------------------- KShortcutsEditor::KShortcutsEditor(KActionCollection *collection, QWidget *parent, ActionTypes actionType, LetterShortcuts allowLetterShortcuts) : QWidget(parent) , d(new KShortcutsEditorPrivate(this)) { d->initGUI(actionType, allowLetterShortcuts); addCollection(collection); } KShortcutsEditor::KShortcutsEditor(QWidget *parent, ActionTypes actionType, LetterShortcuts allowLetterShortcuts) : QWidget(parent) , d(new KShortcutsEditorPrivate(this)) { d->initGUI(actionType, allowLetterShortcuts); } KShortcutsEditor::~KShortcutsEditor() { delete d; } bool KShortcutsEditor::isModified() const { // Iterate over all items QTreeWidgetItemIterator it(d->ui.list, QTreeWidgetItemIterator::NoChildren); for (; (*it); ++it) { KShortcutsEditorItem *item = dynamic_cast<KShortcutsEditorItem *>(*it); if (item && item->isModified()) { return true; } } return false; } void KShortcutsEditor::clearCollections() { d->delegate->contractAll(); d->ui.list->clear(); d->actionCollections.clear(); QTimer::singleShot(0, this, SLOT(resizeColumns())); } void KShortcutsEditor::addCollection(KActionCollection *collection, const QString &title) { // KXmlGui add action collections unconditionally. If some plugin doesn't // provide actions we don't want to create empty subgroups. if (collection->isEmpty()) { return; } // We add a bunch of items. Prevent the treewidget from permanently // updating. setUpdatesEnabled(false); d->actionCollections.append(collection); // Forward our actionCollections to the delegate which does the conflict // checking. d->delegate->setCheckActionCollections(d->actionCollections); QString displayTitle = title; if (displayTitle.isEmpty()) { // Use the programName (Translated). displayTitle = collection->componentDisplayName(); } QTreeWidgetItem *hier[3]; hier[KShortcutsEditorPrivate::Root] = d->ui.list->invisibleRootItem(); hier[KShortcutsEditorPrivate::Program] = d->findOrMakeItem(hier[KShortcutsEditorPrivate::Root], displayTitle); - hier[KShortcutsEditorPrivate::Action] = NULL; + hier[KShortcutsEditorPrivate::Action] = nullptr; // Set to remember which actions we have seen. QSet<QAction *> actionsSeen; // Add all categories in their own subtree below the collections root node QList<KActionCategory *> categories = collection->findChildren<KActionCategory *>(); foreach (KActionCategory *category, categories) { hier[KShortcutsEditorPrivate::Action] = d->findOrMakeItem(hier[KShortcutsEditorPrivate::Program], category->text()); foreach (QAction *action, category->actions()) { // Set a marker that we have seen this action actionsSeen.insert(action); d->addAction(action, hier, KShortcutsEditorPrivate::Action); } } // The rest of the shortcuts is added as a direct shild of the action // collections root node foreach (QAction *action, collection->actions()) { if (actionsSeen.contains(action)) { continue; } d->addAction(action, hier, KShortcutsEditorPrivate::Program); } // sort the list d->ui.list->sortItems(Name, Qt::AscendingOrder); // reenable updating setUpdatesEnabled(true); QTimer::singleShot(0, this, SLOT(resizeColumns())); } void KShortcutsEditor::clearConfiguration() { d->clearConfiguration(); } #ifndef KXMLGUI_NO_DEPRECATED void KShortcutsEditor::importConfiguration(KConfig *config) { d->importConfiguration(config); } #endif void KShortcutsEditor::importConfiguration(KConfigBase *config) { d->importConfiguration(config); } #ifndef KXMLGUI_NO_DEPRECATED void KShortcutsEditor::exportConfiguration(KConfig *config) const { exportConfiguration(static_cast<KConfigBase *>(config)); } #endif void KShortcutsEditor::exportConfiguration(KConfigBase *config) const { Q_ASSERT(config); if (!config) { return; } if (d->actionTypes & KShortcutsEditor::GlobalAction) { QString groupName(QStringLiteral("Global Shortcuts")); KConfigGroup group(config, groupName); foreach (KActionCollection *collection, d->actionCollections) { collection->exportGlobalShortcuts(&group, true); } } if (d->actionTypes & ~KShortcutsEditor::GlobalAction) { QString groupName(QStringLiteral("Shortcuts")); KConfigGroup group(config, groupName); foreach (KActionCollection *collection, d->actionCollections) { collection->writeSettings(&group, true); } } } void KShortcutsEditor::writeConfiguration(KConfigGroup *config) const { foreach (KActionCollection *collection, d->actionCollections) { collection->writeSettings(config); } } //slot void KShortcutsEditor::resizeColumns() { for (int i = 0; i < d->ui.list->columnCount(); i++) { d->ui.list->resizeColumnToContents(i); } } void KShortcutsEditor::commit() { for (QTreeWidgetItemIterator it(d->ui.list); (*it); ++it) { if (KShortcutsEditorItem *item = dynamic_cast<KShortcutsEditorItem *>(*it)) { item->commit(); } } } void KShortcutsEditor::save() { writeConfiguration(); // we have to call commit. If we wouldn't do that the changes would be // undone on deletion! That would lead to weird problems. Changes to // Global Shortcuts would vanish completely. Changes to local shortcuts // would vanish for this session. commit(); } // KDE5 : rename to undo() void KShortcutsEditor::undoChanges() { //This function used to crash sometimes when invoked by clicking on "cancel" //with Qt 4.2.something. Apparently items were deleted too early by Qt. //It seems to work with 4.3-ish Qt versions. Keep an eye on this. for (QTreeWidgetItemIterator it(d->ui.list); (*it); ++it) { if (KShortcutsEditorItem *item = dynamic_cast<KShortcutsEditorItem *>(*it)) { item->undo(); } } } //We ask the user here if there are any conflicts, as opposed to undoChanges(). //They don't do the same thing anyway, this just not to confuse any readers. //slot void KShortcutsEditor::allDefault() { d->allDefault(); } void KShortcutsEditor::printShortcuts() const { d->printShortcuts(); } KShortcutsEditor::ActionTypes KShortcutsEditor::actionTypes() const { return d->actionTypes; } void KShortcutsEditor::setActionTypes(ActionTypes actionTypes) { d->setActionTypes(actionTypes); } //--------------------------------------------------------------------- // KShortcutsEditorPrivate //--------------------------------------------------------------------- KShortcutsEditorPrivate::KShortcutsEditorPrivate(KShortcutsEditor *q) : q(q), - delegate(0) + delegate(nullptr) {} void KShortcutsEditorPrivate::initGUI(KShortcutsEditor::ActionTypes types, KShortcutsEditor::LetterShortcuts allowLetterShortcuts) { actionTypes = types; ui.setupUi(q); q->layout()->setMargin(0); ui.searchFilter->searchLine()->setTreeWidget(ui.list); // Plug into search line ui.list->header()->setSectionResizeMode(QHeaderView::ResizeToContents); ui.list->header()->hideSection(ShapeGesture); //mouse gestures didn't make it in time... ui.list->header()->hideSection(RockerGesture); #if HAVE_GLOBALACCEL bool hideGlobals = !(actionTypes & KShortcutsEditor::GlobalAction); #else bool hideGlobals = true; #endif if (hideGlobals) { ui.list->header()->hideSection(GlobalPrimary); ui.list->header()->hideSection(GlobalAlternate); } else if (!(actionTypes & ~KShortcutsEditor::GlobalAction)) { ui.list->header()->hideSection(LocalPrimary); ui.list->header()->hideSection(LocalAlternate); } // Create the Delegate. It is responsible for the KKeySeqeunceWidgets that // really change the shortcuts. delegate = new KShortcutsEditorDelegate( ui.list, allowLetterShortcuts == KShortcutsEditor::LetterShortcutsAllowed); ui.list->setItemDelegate(delegate); ui.list->setSelectionBehavior(QAbstractItemView::SelectItems); ui.list->setSelectionMode(QAbstractItemView::SingleSelection); //we have our own editing mechanism ui.list->setEditTriggers(QAbstractItemView::NoEditTriggers); ui.list->setAlternatingRowColors(true); //TODO listen to changes to global shortcuts QObject::connect(delegate, SIGNAL(shortcutChanged(QVariant,QModelIndex)), q, SLOT(capturedShortcut(QVariant,QModelIndex))); //hide the editor widget chen its item becomes hidden QObject::connect(ui.searchFilter->searchLine(), SIGNAL(hiddenChanged(QTreeWidgetItem*,bool)), delegate, SLOT(hiddenBySearchLine(QTreeWidgetItem*,bool))); ui.searchFilter->setFocus(); } void KShortcutsEditorPrivate::setActionTypes(KShortcutsEditor::ActionTypes types) { if (actionTypes == types) { return; } actionTypes = types; // show/hide the sections based on new selection QHeaderView *header = ui.list->header(); if (actionTypes & KShortcutsEditor::GlobalAction) { header->showSection(GlobalPrimary); header->showSection(GlobalAlternate); } else { header->hideSection(GlobalPrimary); header->hideSection(GlobalAlternate); } if (actionTypes & ~KShortcutsEditor::GlobalAction) { header->showSection(LocalPrimary); header->showSection(LocalAlternate); } else { header->hideSection(LocalPrimary); header->hideSection(LocalAlternate); } } bool KShortcutsEditorPrivate::addAction(QAction *action, QTreeWidgetItem *hier[], hierarchyLevel level) { // If the action name starts with unnamed- spit out a warning and ignore // it. That name will change at will and will break loading and writing QString actionName = action->objectName(); if (actionName.isEmpty() || actionName.startsWith(QStringLiteral("unnamed-"))) { qCritical() << "Skipping action without name " << action->text() << "," << actionName << "!"; return false; } // This code doesn't allow editing of QAction. It can not distinguish // between default and active shortcuts. This breaks many assumptions the // editor makes. const QVariant value = action->property("isShortcutConfigurable"); if (!value.isValid() || value.toBool()) { new KShortcutsEditorItem((hier[level]), action); return true; } return false; } void KShortcutsEditorPrivate::allDefault() { for (QTreeWidgetItemIterator it(ui.list); (*it); ++it) { if (!(*it)->parent() || (*it)->type() != ActionItem) { continue; } KShortcutsEditorItem *item = static_cast<KShortcutsEditorItem *>(*it); QAction *act = item->m_action; QList<QKeySequence> defaultShortcuts = act->property("defaultShortcuts").value<QList<QKeySequence> >(); if (act->shortcuts() != defaultShortcuts) { QKeySequence primary = defaultShortcuts.isEmpty() ? QKeySequence() : defaultShortcuts.at(0); QKeySequence alternate = defaultShortcuts.size() <= 1 ? QKeySequence() : defaultShortcuts.at(1); changeKeyShortcut(item, LocalPrimary, primary); changeKeyShortcut(item, LocalAlternate, alternate); } #if HAVE_GLOBALACCEL if (KGlobalAccel::self()->shortcut(act) != KGlobalAccel::self()->defaultShortcut(act)) { QList<QKeySequence> defaultShortcut = KGlobalAccel::self()->defaultShortcut(act); changeKeyShortcut(item, GlobalPrimary, primarySequence(defaultShortcut)); changeKeyShortcut(item, GlobalAlternate, alternateSequence(defaultShortcut)); } #endif #if 0 KShapeGesture actShapeGesture = KGestureMap::self()->shapeGesture(act); KShapeGesture actDefaultShapeGesture = KGestureMap::self()->defaultShapeGesture(act); if (actShapeGesture != actDefaultShapeGesture) { changeShapeGesture(item, actDefaultShapeGesture); } KRockerGesture actRockerGesture = KGestureMap::self()->rockerGesture(act); KRockerGesture actDefaultRockerGesture = KGestureMap::self()->defaultRockerGesture(act); if (actRockerGesture != actDefaultRockerGesture) { changeRockerGesture(item, actDefaultRockerGesture); } #endif } } //static KShortcutsEditorItem *KShortcutsEditorPrivate::itemFromIndex(QTreeWidget *const w, const QModelIndex &index) { QTreeWidgetItem *item = static_cast<QTreeWidgetHack *>(w)->itemFromIndex(index); if (item && item->type() == ActionItem) { return static_cast<KShortcutsEditorItem *>(item); } - return 0; + return nullptr; } QTreeWidgetItem *KShortcutsEditorPrivate::findOrMakeItem(QTreeWidgetItem *parent, const QString &name) { for (int i = 0; i < parent->childCount(); i++) { QTreeWidgetItem *child = parent->child(i); if (child->text(0) == name) { return child; } } QTreeWidgetItem *ret = new QTreeWidgetItem(parent, NonActionItem); ret->setText(0, name); ui.list->expandItem(ret); ret->setFlags(ret->flags() & ~Qt::ItemIsSelectable); return ret; } //private slot void KShortcutsEditorPrivate::capturedShortcut(const QVariant &newShortcut, const QModelIndex &index) { //dispatch to the right handler if (!index.isValid()) { return; } int column = index.column(); KShortcutsEditorItem *item = itemFromIndex(ui.list, index); Q_ASSERT(item); if (column >= LocalPrimary && column <= GlobalAlternate) { changeKeyShortcut(item, column, newShortcut.value<QKeySequence>()); } #if 0 else if (column == ShapeGesture) { changeShapeGesture(item, newShortcut.value<KShapeGesture>()); } else if (column == RockerGesture) { changeRockerGesture(item, newShortcut.value<KRockerGesture>()); } #endif } void KShortcutsEditorPrivate::changeKeyShortcut(KShortcutsEditorItem *item, uint column, const QKeySequence &capture) { // The keySequence we get is cleared by KKeySequenceWidget. No conflicts. if (capture == item->keySequence(column)) { return; } item->setKeySequence(column, capture); q->keyChange(); //force view update item->setText(column, capture.toString(QKeySequence::NativeText)); } #if 0 void KShortcutsEditorPrivate::changeShapeGesture(KShortcutsEditorItem *item, const KShapeGesture &capture) { if (capture == KGestureMap::self()->shapeGesture(item->m_action)) { return; } if (capture.isValid()) { bool conflict = false; KShortcutsEditorItem *otherItem; //search for conflicts for (QTreeWidgetItemIterator it(ui.list); (*it); ++it) { if (!(*it)->parent() || (*it == item)) { continue; } otherItem = static_cast<KShortcutsEditorItem *>(*it); //comparisons are possibly expensive KShapeGesture otherGesture = KGestureMap::self()->shapeGesture(otherItem->m_action); if (!otherGesture.isValid()) { continue; } if (capture == otherGesture) { conflict = true; break; } } if (conflict && !stealShapeGesture(otherItem, capture)) { return; } } item->setShapeGesture(capture); } #endif #if 0 void KShortcutsEditorPrivate::changeRockerGesture(KShortcutsEditorItem *item, const KRockerGesture &capture) { if (capture == KGestureMap::self()->rockerGesture(item->m_action)) { return; } if (capture.isValid()) { bool conflict = false; KShortcutsEditorItem *otherItem; for (QTreeWidgetItemIterator it(ui.list); (*it); ++it) { if (!(*it)->parent() || (*it == item)) { continue; } otherItem = static_cast<KShortcutsEditorItem *>(*it); KRockerGesture otherGesture = KGestureMap::self()->rockerGesture(otherItem->m_action); if (capture == otherGesture) { conflict = true; break; } } if (conflict && !stealRockerGesture(otherItem, capture)) { return; } } item->setRockerGesture(capture); } #endif void KShortcutsEditorPrivate::clearConfiguration() { for (QTreeWidgetItemIterator it(ui.list); (*it); ++it) { if (!(*it)->parent()) { continue; } KShortcutsEditorItem *item = static_cast<KShortcutsEditorItem *>(*it); changeKeyShortcut(item, LocalPrimary, QKeySequence()); changeKeyShortcut(item, LocalAlternate, QKeySequence()); changeKeyShortcut(item, GlobalPrimary, QKeySequence()); changeKeyShortcut(item, GlobalAlternate, QKeySequence()); #if 0 changeShapeGesture(item, KShapeGesture()); #endif } } void KShortcutsEditorPrivate::importConfiguration(KConfigBase *config) { Q_ASSERT(config); if (!config) { return; } KConfigGroup globalShortcutsGroup(config, QStringLiteral("Global Shortcuts")); if ((actionTypes & KShortcutsEditor::GlobalAction) && globalShortcutsGroup.exists()) { for (QTreeWidgetItemIterator it(ui.list); (*it); ++it) { if (!(*it)->parent()) { continue; } KShortcutsEditorItem *item = static_cast<KShortcutsEditorItem *>(*it); const QString actionId = item->data(Id).toString(); if (!globalShortcutsGroup.hasKey(actionId)) continue; QList<QKeySequence> sc = QKeySequence::listFromString(globalShortcutsGroup.readEntry(actionId, QString())); changeKeyShortcut(item, GlobalPrimary, primarySequence(sc)); changeKeyShortcut(item, GlobalAlternate, alternateSequence(sc)); } } if (actionTypes & ~KShortcutsEditor::GlobalAction) { const KConfigGroup localShortcutsGroup(config, QStringLiteral("Shortcuts")); for (QTreeWidgetItemIterator it(ui.list); (*it); ++it) { if (!(*it)->parent()) { continue; } KShortcutsEditorItem *item = static_cast<KShortcutsEditorItem *>(*it); const QString actionId = item->data(Id).toString(); if (!localShortcutsGroup.hasKey(actionId)) continue; QList<QKeySequence> sc = QKeySequence::listFromString(localShortcutsGroup.readEntry(actionId, QString())); changeKeyShortcut(item, LocalPrimary, primarySequence(sc)); changeKeyShortcut(item, LocalAlternate, alternateSequence(sc)); } } } #if 0 bool KShortcutsEditorPrivate::stealShapeGesture(KShortcutsEditorItem *item, const KShapeGesture &gst) { QString title = i18n("Key Conflict"); QString message = i18n("The '%1' shape gesture has already been allocated to the \"%2\" action.\n" "Do you want to reassign it from that action to the current one?", gst.shapeName(), item->m_action->text()); if (KMessageBox::warningContinueCancel(q, message, title, KGuiItem(i18n("Reassign"))) != KMessageBox::Continue) { return false; } item->setShapeGesture(KShapeGesture()); return true; } #endif #if 0 bool KShortcutsEditorPrivate::stealRockerGesture(KShortcutsEditorItem *item, const KRockerGesture &gst) { QString title = i18n("Key Conflict"); QString message = i18n("The '%1' rocker gesture has already been allocated to the \"%2\" action.\n" "Do you want to reassign it from that action to the current one?", gst.rockerName(), item->m_action->text()); if (KMessageBox::warningContinueCancel(q, message, title, KGuiItem(i18n("Reassign"))) != KMessageBox::Continue) { return false; } item->setRockerGesture(KRockerGesture()); return true; } #endif /*TODO for the printShortcuts function Nice to have features (which I'm not sure I can do before may due to more important things): - adjust the general page borders, IMHO they're too wide - add a custom printer options page that allows to filter out all actions that don't have a shortcut set to reduce this list. IMHO this should be optional as people might want to simply print all and when they find a new action that they assign a shortcut they can simply use a pen to fill out the empty space - find a way to align the Main/Alternate/Global entries in the shortcuts column without adding borders. I first did this without a nested table but instead simply added 3 rows and merged the 3 cells in the Action name and description column, but unfortunately I didn't find a way to remove the borders between the 6 shortcut cells. */ void KShortcutsEditorPrivate::printShortcuts() const { // One cant print on wince #ifndef _WIN32_WCE QTreeWidgetItem *root = ui.list->invisibleRootItem(); QTextDocument doc; doc.setDefaultFont(QFontDatabase::systemFont(QFontDatabase::GeneralFont)); QTextCursor cursor(&doc); cursor.beginEditBlock(); QTextCharFormat headerFormat; headerFormat.setProperty(QTextFormat::FontSizeAdjustment, 3); headerFormat.setFontWeight(QFont::Bold); cursor.insertText(i18nc("header for an applications shortcut list", "Shortcuts for %1", QGuiApplication::applicationDisplayName()), headerFormat); QTextCharFormat componentFormat; componentFormat.setProperty(QTextFormat::FontSizeAdjustment, 2); componentFormat.setFontWeight(QFont::Bold); QTextBlockFormat componentBlockFormat = cursor.blockFormat(); componentBlockFormat.setTopMargin(16); componentBlockFormat.setBottomMargin(16); QTextTableFormat tableformat; tableformat.setHeaderRowCount(1); tableformat.setCellPadding(4.0); tableformat.setCellSpacing(0); tableformat.setBorderStyle(QTextFrameFormat::BorderStyle_Solid); tableformat.setBorder(0.5); QList<QPair<QString, ColumnDesignation> > shortcutTitleToColumn; shortcutTitleToColumn << qMakePair(i18n("Main:"), LocalPrimary); shortcutTitleToColumn << qMakePair(i18n("Alternate:"), LocalAlternate); shortcutTitleToColumn << qMakePair(i18n("Global:"), GlobalPrimary); shortcutTitleToColumn << qMakePair(i18n("Global alternate:"), GlobalAlternate); for (int i = 0; i < root->childCount(); i++) { QTreeWidgetItem *item = root->child(i); cursor.insertBlock(componentBlockFormat, componentFormat); cursor.insertText(item->text(0)); QTextTable *table = cursor.insertTable(1, 3); table->setFormat(tableformat); int currow = 0; QTextTableCell cell = table->cellAt(currow, 0); QTextCharFormat format = cell.format(); format.setFontWeight(QFont::Bold); cell.setFormat(format); cell.firstCursorPosition().insertText(i18n("Action Name")); cell = table->cellAt(currow, 1); cell.setFormat(format); cell.firstCursorPosition().insertText(i18n("Shortcuts")); cell = table->cellAt(currow, 2); cell.setFormat(format); cell.firstCursorPosition().insertText(i18n("Description")); currow++; for (QTreeWidgetItemIterator it(item); *it; ++it) { if ((*it)->type() != ActionItem) { continue; } KShortcutsEditorItem *editoritem = static_cast<KShortcutsEditorItem *>(*it); table->insertRows(table->rows(), 1); QVariant data = editoritem->data(Name, Qt::DisplayRole); table->cellAt(currow, 0).firstCursorPosition().insertText(data.toString()); - QTextTable *shortcutTable = 0; + QTextTable *shortcutTable = nullptr; for (int k = 0; k < shortcutTitleToColumn.count(); k++) { data = editoritem->data(shortcutTitleToColumn.at(k).second, Qt::DisplayRole); QString key = data.value<QKeySequence>().toString(); if (!key.isEmpty()) { if (!shortcutTable) { shortcutTable = table->cellAt(currow, 1).firstCursorPosition().insertTable(1, 2); QTextTableFormat shortcutTableFormat = tableformat; shortcutTableFormat.setCellSpacing(0.0); shortcutTableFormat.setHeaderRowCount(0); shortcutTableFormat.setBorder(0.0); shortcutTable->setFormat(shortcutTableFormat); } else { shortcutTable->insertRows(shortcutTable->rows(), 1); } shortcutTable->cellAt(shortcutTable->rows() - 1, 0).firstCursorPosition().insertText(shortcutTitleToColumn.at(k).first); shortcutTable->cellAt(shortcutTable->rows() - 1, 1).firstCursorPosition().insertText(key); } } QAction *action = editoritem->m_action; cell = table->cellAt(currow, 2); format = cell.format(); format.setProperty(QTextFormat::FontSizeAdjustment, -1); cell.setFormat(format); cell.firstCursorPosition().insertHtml(action->whatsThis()); currow++; } cursor.movePosition(QTextCursor::End); } cursor.endEditBlock(); QPrinter printer; QPrintDialog *dlg = new QPrintDialog(&printer, q); if (dlg->exec() == QDialog::Accepted) { doc.print(&printer); } delete dlg; #endif } #include "moc_kshortcutseditor.cpp" diff --git a/src/kshortcutseditor.h b/src/kshortcutseditor.h index e66477b..c7c34c5 100644 --- a/src/kshortcutseditor.h +++ b/src/kshortcutseditor.h @@ -1,255 +1,255 @@ /* This file is part of the KDE libraries Copyright (C) 1997 Nicolas Hadacek <hadacek@kde.org> Copyright (C) 2001,2001 Ellis Whitehead <ellis@kde.org> Copyright (C) 2006 Hamish Rodda <rodda@kde.org> Copyright (C) 2007 Roberto Raggi <roberto@kdevelop.org> Copyright (C) 2007 Andreas Hartmetz <ahartmetz@gmail.com> Copyright (C) 2008 Michael Jansen <kde@michael-jansen.biz> 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 KSHORTCUTSEDITOR_H #define KSHORTCUTSEDITOR_H #include <kxmlgui_export.h> #include <QWidget> #if 0 #include <kgesture.h> #endif class KActionCollection; class KConfig; class KConfigBase; class KConfigGroup; class KGlobalAccel; class KShortcutsEditorPrivate; // KShortcutsEditor expects that the list of existing shortcuts is already // free of conflicts. If it is not, nothing will crash, but your users // won't like the resulting behavior. /** * @short Widget for configuration of KAccel and KGlobalAccel. * * Configure dictionaries of key/action associations for KActions, * including global shortcuts. * * The class takes care of all aspects of configuration, including * handling key conflicts internally. Connect to the allDefault() * slot if you want to set all configurable shortcuts to their * default values. * * @see KShortcutsDialog * @author Nicolas Hadacek <hadacek@via.ecp.fr> * @author Hamish Rodda <rodda@kde.org> (KDE 4 porting) * @author Michael Jansen <kde@michael-jansen.biz> */ class KXMLGUI_EXPORT KShortcutsEditor : public QWidget { Q_OBJECT Q_PROPERTY(ActionTypes actionTypes READ actionTypes WRITE setActionTypes) public: enum ActionType { /// Actions which are triggered by any keypress in a widget which has the action added to it WidgetAction = Qt::WidgetShortcut /*0*/, /// Actions which are triggered by any keypress in a window which has the action added to it or its child widget(s) WindowAction = Qt::WindowShortcut /*1*/, /// Actions which are triggered by any keypress in the application ApplicationAction = Qt::ApplicationShortcut /*2*/, /// Actions which are triggered by any keypress in the windowing system GlobalAction = 4, /// All actions AllActions = 0xffffffff }; Q_DECLARE_FLAGS(ActionTypes, ActionType) enum LetterShortcuts { /// Shortcuts without a modifier are not allowed, /// so 'A' would not be valid, whereas 'Ctrl+A' would be. /// This only applies to printable characters, however. /// 'F1', 'Insert' etc. could still be used. LetterShortcutsDisallowed = 0, /// Letter shortcuts are allowed LetterShortcutsAllowed }; /** * Constructor. * * @param collection the KActionCollection to configure * @param parent parent widget * @param actionTypes types of actions to display in this widget. * @param allowLetterShortcuts set to LetterShortcutsDisallowed if unmodified alphanumeric * keys ('A', '1', etc.) are not permissible shortcuts. */ KShortcutsEditor(KActionCollection *collection, QWidget *parent, ActionTypes actionTypes = AllActions, LetterShortcuts allowLetterShortcuts = LetterShortcutsAllowed); /** * \overload * * Creates a key chooser without a starting action collection. * * @param parent parent widget * @param actionTypes types of actions to display in this widget. * @param allowLetterShortcuts set to LetterShortcutsDisallowed if unmodified alphanumeric * keys ('A', '1', etc.) are not permissible shortcuts. */ explicit KShortcutsEditor(QWidget *parent, ActionTypes actionTypes = AllActions, LetterShortcuts allowLetterShortcuts = LetterShortcutsAllowed); /// Destructor virtual ~KShortcutsEditor(); /** * Are the unsaved changes? */ bool isModified() const; /** * Removes all action collections from the editor */ void clearCollections(); /** * Insert an action collection, i.e. add all its actions to the ones * already associated with the KShortcutsEditor object. * @param title subtree title of this collection of shortcut. */ void addCollection(KActionCollection *, const QString &title = QString()); /** * Undo all change made since the last commit(). */ void undoChanges(); /** * Save the changes. * * Before saving the changes are committed. This saves the actions to disk. * Any KActionCollection objects with the xmlFile() value set will be * written to an XML file. All other will be written to the application's * rc file. */ void save(); /** * Commit the changes without saving. * * This commits the changes without saving. * * @since 4.2 */ void commit(); /** * Removes all configured shortcuts. */ void clearConfiguration(); /** * Write the current settings to the \p config object. * * This does not initialize the \p config object. It adds the * configuration. * * @note this will not save the global configuration! globalaccel holds * that part of the configuration. * @see writeGlobalConfig() * * @param config Config object to save to or, or null to use the * applications config object * */ - void writeConfiguration(KConfigGroup *config = 0) const; + void writeConfiguration(KConfigGroup *config = nullptr) const; /** * Export the current setting to configuration @p config. * * This initializes the configuration object. This will export the global * configuration too. * * @param config Config object */ #ifndef KXMLGUI_NO_DEPRECATED KXMLGUI_DEPRECATED void exportConfiguration(KConfig *config) const; #endif void exportConfiguration(KConfigBase *config) const; /** * Import the settings from configuration @p config. * * This will remove all current setting before importing. All shortcuts * are set to QList<QKeySequence>() prior to importing from @p config! * * @param config Config object */ #ifndef KXMLGUI_NO_DEPRECATED KXMLGUI_DEPRECATED void importConfiguration(KConfig *config); #endif void importConfiguration(KConfigBase *config); /** * Sets the types of actions to display in this widget. * * @param actionTypes New types of actions * @since 5.0 */ void setActionTypes(ActionTypes actionTypes); /** * * @return The types of actions currently displayed in this widget. * @since 5.0 */ ActionTypes actionTypes() const; Q_SIGNALS: /** * Emitted when an action's shortcut has been changed. **/ void keyChange(); public Q_SLOTS: /** * Resize columns to width required */ void resizeColumns(); /** * Set all shortcuts to their default values (bindings). **/ void allDefault(); /** * Opens a printing dialog to print all the shortcuts */ void printShortcuts() const; private: Q_PRIVATE_SLOT(d, void capturedShortcut(QVariant, const QModelIndex &)) private: friend class KShortcutsDialog; friend class KShortcutsEditorPrivate; KShortcutsEditorPrivate *const d; Q_DISABLE_COPY(KShortcutsEditor) }; Q_DECLARE_OPERATORS_FOR_FLAGS(KShortcutsEditor::ActionTypes) #endif // KSHORTCUTSEDITOR_H diff --git a/src/kshortcutseditordelegate.cpp b/src/kshortcutseditordelegate.cpp index b96e5e4..1515220 100644 --- a/src/kshortcutseditordelegate.cpp +++ b/src/kshortcutseditordelegate.cpp @@ -1,334 +1,334 @@ /* This file is part of the KDE libraries Copyright (C) 1998 Mark Donohoe <donohoe@kde.org> Copyright (C) 1997 Nicolas Hadacek <hadacek@kde.org> Copyright (C) 1998 Matthias Ettrich <ettrich@kde.org> Copyright (C) 2001 Ellis Whitehead <ellis@kde.org> Copyright (C) 2006 Hamish Rodda <rodda@kde.org> Copyright (C) 2007 Roberto Raggi <roberto@kdevelop.org> Copyright (C) 2007 Andreas Hartmetz <ahartmetz@gmail.com> Copyright (C) 2008 Michael Jansen <kde@michael-jansen.biz> 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 "kshortcutsdialog_p.h" #include <QApplication> #include <QHeaderView> #include <QKeyEvent> #include <QLabel> #include <QPainter> #include <QTreeWidgetItemIterator> KShortcutsEditorDelegate::KShortcutsEditorDelegate(QTreeWidget *parent, bool allowLetterShortcuts) : KExtendableItemDelegate(parent), m_allowLetterShortcuts(allowLetterShortcuts), - m_editor(0) + m_editor(nullptr) { Q_ASSERT(qobject_cast<QAbstractItemView *>(parent)); QPixmap pixmap(16, 16); pixmap.fill(QColor(Qt::transparent)); QPainter p(&pixmap); QStyleOption option; option.rect = pixmap.rect(); bool isRtl = QApplication::isRightToLeft(); QApplication::style()->drawPrimitive(isRtl ? QStyle::PE_IndicatorArrowLeft : QStyle::PE_IndicatorArrowRight, &option, &p); p.end(); setExtendPixmap(pixmap); pixmap.fill(QColor(Qt::transparent)); p.begin(&pixmap); QApplication::style()->drawPrimitive(QStyle::PE_IndicatorArrowDown, &option, &p); p.end(); setContractPixmap(pixmap); parent->installEventFilter(this); // Listen to activation signals // connect(parent, SIGNAL(activated(QModelIndex)), this, SLOT(itemActivated(QModelIndex))); connect(parent, SIGNAL(clicked(QModelIndex)), this, SLOT(itemActivated(QModelIndex))); // Listen to collapse signals connect(parent, SIGNAL(collapsed(QModelIndex)), this, SLOT(itemCollapsed(QModelIndex))); } void KShortcutsEditorDelegate::stealShortcut( const QKeySequence &seq, QAction *action) { QTreeWidget *view = static_cast<QTreeWidget *>(parent()); // Iterate over all items QTreeWidgetItemIterator it(view, QTreeWidgetItemIterator::NoChildren); for (; (*it); ++it) { KShortcutsEditorItem *item = dynamic_cast<KShortcutsEditorItem *>(*it); if (item && item->data(0, ObjectRole).value<QObject *>() == action) { // We found the action, snapshot the current state. Steal the // shortcut. We will save the change later. const QList<QKeySequence> cut = action->shortcuts(); const QKeySequence primary = cut.isEmpty() ? QKeySequence() : cut.at(0); const QKeySequence alternate = cut.size() <= 1 ? QKeySequence() : cut.at(1); if (primary.matches(seq) != QKeySequence::NoMatch || seq.matches(primary) != QKeySequence::NoMatch) { item->setKeySequence(LocalPrimary, QKeySequence()); } if (alternate.matches(seq) != QKeySequence::NoMatch || seq.matches(alternate) != QKeySequence::NoMatch) { item->setKeySequence(LocalAlternate, QKeySequence()); } break; } } } QSize KShortcutsEditorDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const { QSize ret(KExtendableItemDelegate::sizeHint(option, index)); ret.rheight() += 4; return ret; } //slot void KShortcutsEditorDelegate::itemActivated(QModelIndex index) { //As per our constructor our parent *is* a QTreeWidget QTreeWidget *view = static_cast<QTreeWidget *>(parent()); KShortcutsEditorItem *item = KShortcutsEditorPrivate::itemFromIndex(view, index); if (!item) { //that probably was a non-leaf (type() !=ActionItem) item return; } int column = index.column(); if (column == Name) { // If user click in the name column activate the (Global|Local)Primary // column if possible. if (!view->header()->isSectionHidden(LocalPrimary)) { column = LocalPrimary; } else if (!view->header()->isSectionHidden(GlobalPrimary)) { column = GlobalPrimary; } else { // do nothing. } index = index.sibling(index.row(), column); view->selectionModel()->select(index, QItemSelectionModel::SelectCurrent); } // Check if the models wants us to edit the item at index if (!index.data(ShowExtensionIndicatorRole).toBool()) { return; } if (!isExtended(index)) { //we only want maximum ONE extender open at any time. if (m_editingIndex.isValid()) { KShortcutsEditorItem *oldItem = KShortcutsEditorPrivate::itemFromIndex(view, m_editingIndex); Q_ASSERT(oldItem); //here we really expect nothing but a real KShortcutsEditorItem oldItem->setNameBold(false); contractItem(m_editingIndex); } m_editingIndex = index; QWidget *viewport = static_cast<QAbstractItemView *>(parent())->viewport(); if (column >= LocalPrimary && column <= GlobalAlternate) { ShortcutEditWidget *editor = new ShortcutEditWidget(viewport, index.data(DefaultShortcutRole).value<QKeySequence>(), index.data(ShortcutRole).value<QKeySequence>(), m_allowLetterShortcuts); if (column == GlobalPrimary) { QObject *action = index.data(ObjectRole).value<QObject *>(); editor->setAction(action); editor->setMultiKeyShortcutsAllowed(false); QString componentName = action->property("componentName").toString(); if (componentName.isEmpty()) { componentName = QCoreApplication::applicationName(); } editor->setComponentName(componentName); } m_editor = editor; // For global shortcuts check against the kde standard shortcuts if (column == GlobalPrimary || column == GlobalAlternate) { editor->setCheckForConflictsAgainst( KKeySequenceWidget::LocalShortcuts | KKeySequenceWidget::GlobalShortcuts | KKeySequenceWidget::StandardShortcuts); } editor->setCheckActionCollections(m_checkActionCollections); connect(m_editor, SIGNAL(keySequenceChanged(QKeySequence)), this, SLOT(keySequenceChanged(QKeySequence))); connect(m_editor, SIGNAL(stealShortcut(QKeySequence,QAction*)), this, SLOT(stealShortcut(QKeySequence,QAction*))); } else if (column == RockerGesture) { m_editor = new QLabel(QStringLiteral("A lame placeholder"), viewport); } else if (column == ShapeGesture) { m_editor = new QLabel(QStringLiteral("<i>A towel</i>"), viewport); } else { return; } m_editor->installEventFilter(this); item->setNameBold(true); extendItem(m_editor, index); } else { //the item is extended, and clicking on it again closes it item->setNameBold(false); contractItem(index); view->selectionModel()->select(index, QItemSelectionModel::Clear); m_editingIndex = QModelIndex(); - m_editor = 0; + m_editor = nullptr; } } //slot void KShortcutsEditorDelegate::itemCollapsed(QModelIndex index) { if (!m_editingIndex.isValid()) { return; } const QAbstractItemModel *model = index.model(); for (int row = 0; row < model->rowCount(index); ++row) { for (int col = 0; col < index.model()->columnCount(index); ++col) { QModelIndex colIndex = model->index(row, col, index); if (colIndex == m_editingIndex) { itemActivated(m_editingIndex); //this will *close* the item's editor because it's already open } } } } //slot void KShortcutsEditorDelegate::hiddenBySearchLine(QTreeWidgetItem *item, bool hidden) { if (!hidden || !item) { return; } QTreeWidget *view = static_cast<QTreeWidget *>(parent()); QTreeWidgetItem *editingItem = KShortcutsEditorPrivate::itemFromIndex(view, m_editingIndex); if (editingItem == item) { itemActivated(m_editingIndex); //this will *close* the item's editor because it's already open } } bool KShortcutsEditorDelegate::eventFilter(QObject *o, QEvent *e) { if (o == m_editor) { //Prevent clicks in the empty part of the editor widget from closing the editor //because they would propagate to the itemview and be interpreted as a click in //an item's rect. That in turn would lead to an itemActivated() call, closing //the current editor. switch (e->type()) { case QEvent::MouseButtonPress: case QEvent::MouseButtonRelease: case QEvent::MouseButtonDblClick: return true; default: return false; } } else if (o == parent()) { // Make left/right cursor keys switch items instead of operate the scroll bar // (subclassing QtreeView/Widget would be cleaner but much more of a hassle) // Note that in our case we know that the selection mode is SingleSelection, // so we don't have to ask QAbstractItemView::selectionCommand() et al. if (e->type() != QEvent::KeyPress) { return false; } QKeyEvent *ke = static_cast<QKeyEvent *>(e); QTreeWidget *view = static_cast<QTreeWidget *>(parent()); QItemSelectionModel *selection = view->selectionModel(); QModelIndex index = selection->currentIndex(); switch (ke->key()) { case Qt::Key_Space: case Qt::Key_Select: // we are not using the standard "open editor" mechanism of QAbstractItemView, // so let's emulate that here. itemActivated(index); return true; case Qt::Key_Left: index = index.sibling(index.row(), index.column() - 1); break; case Qt::Key_Right: index = index.sibling(index.row(), index.column() + 1); break; default: return false; } // a cursor key was pressed if (index.isValid()) { selection->setCurrentIndex(index, QItemSelectionModel::ClearAndSelect); //### using PositionAtCenter for now; // EnsureVisible has no effect which seems to be a bug. view->scrollTo(index, QAbstractItemView::PositionAtCenter); } return true; } return false; } //slot void KShortcutsEditorDelegate::keySequenceChanged(const QKeySequence &seq) { QVariant ret = QVariant::fromValue(seq); emit shortcutChanged(ret, m_editingIndex); } void KShortcutsEditorDelegate::setCheckActionCollections( const QList<KActionCollection *> checkActionCollections) { m_checkActionCollections = checkActionCollections; } #if 0 //slot void KShortcutsEditorDelegate::shapeGestureChanged(const KShapeGesture &gest) { //this is somewhat verbose because the gesture types are not "built in" to QVariant QVariant ret = QVariant::fromValue(gest); emit shortcutChanged(ret, m_editingIndex); } #endif #if 0 //slot void KShortcutsEditorDelegate::rockerGestureChanged(const KRockerGesture &gest) { QVariant ret = QVariant::fromValue(gest); emit shortcutChanged(ret, m_editingIndex); } #endif diff --git a/src/kshortcutseditoritem.cpp b/src/kshortcutseditoritem.cpp index 933073b..125f34e 100644 --- a/src/kshortcutseditoritem.cpp +++ b/src/kshortcutseditoritem.cpp @@ -1,437 +1,437 @@ /* This file is part of the KDE libraries Copyright (C) 1998 Mark Donohoe <donohoe@kde.org> Copyright (C) 1997 Nicolas Hadacek <hadacek@kde.org> Copyright (C) 1998 Matthias Ettrich <ettrich@kde.org> Copyright (C) 2001 Ellis Whitehead <ellis@kde.org> Copyright (C) 2006 Hamish Rodda <rodda@kde.org> Copyright (C) 2007 Roberto Raggi <roberto@kdevelop.org> Copyright (C) 2007 Andreas Hartmetz <ahartmetz@gmail.com> Copyright (C) 2008 Michael Jansen <kde@michael-jansen.biz> 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 "config-xmlgui.h" #include "kshortcutsdialog_p.h" #include <QAction> #include <QTreeWidgetItem> #include <QDebug> #if 0 #include <kgesturemap.h> #endif #if HAVE_GLOBALACCEL # include <kglobalaccel.h> #endif KShortcutsEditorItem::KShortcutsEditorItem(QTreeWidgetItem *parent, QAction *action) : QTreeWidgetItem(parent, ActionItem) , m_action(action) , m_isNameBold(false) - , m_oldLocalShortcut(0) - , m_oldGlobalShortcut(0) + , m_oldLocalShortcut(nullptr) + , m_oldGlobalShortcut(nullptr) #if 0 , m_oldShapeGesture(0) , m_oldRockerGesture(0) #endif { // Filtering message requested by translators (scripting). m_id = m_action->objectName(); m_actionNameInTable = i18nc("@item:intable Action name in shortcuts configuration", "%1", KLocalizedString::removeAcceleratorMarker(m_action->text())); if (m_actionNameInTable.isEmpty()) { qWarning() << "Action without text!" << m_action->objectName(); m_actionNameInTable = m_id; } m_collator.setNumericMode(true); m_collator.setCaseSensitivity(Qt::CaseSensitive); } KShortcutsEditorItem::~KShortcutsEditorItem() { delete m_oldLocalShortcut; delete m_oldGlobalShortcut; #if 0 delete m_oldShapeGesture; delete m_oldRockerGesture; #endif } bool KShortcutsEditorItem::isModified() const { #if 0 return m_oldLocalShortcut || m_oldGlobalShortcut || m_oldShapeGesture || m_oldRockerGesture; #else return m_oldLocalShortcut || m_oldGlobalShortcut; #endif } QVariant KShortcutsEditorItem::data(int column, int role) const { switch (role) { case Qt::DisplayRole: switch (column) { case Name: return m_actionNameInTable; case Id: return m_id; case LocalPrimary: case LocalAlternate: case GlobalPrimary: case GlobalAlternate: return keySequence(column); #if 0 case ShapeGesture: return KGestureMap::self()->shapeGesture(m_action).shapeName(); case RockerGesture: return KGestureMap::self()->rockerGesture(m_action).rockerName(); #endif default: break; } break; case Qt::DecorationRole: if (column == Name) { return m_action->icon(); } else { return QIcon(); } case Qt::WhatsThisRole: return m_action->whatsThis(); case Qt::ToolTipRole: // There is no such thing as a QAction::description(). So we have // nothing to display here. return QVariant(); case Qt::FontRole: if (column == Name && m_isNameBold) { QFont modifiedFont = treeWidget()->font(); modifiedFont.setBold(true); return modifiedFont; } break; case KExtendableItemDelegate::ShowExtensionIndicatorRole: switch (column) { case Name: return false; case LocalPrimary: case LocalAlternate: return !m_action->property("isShortcutConfigurable").isValid() || m_action->property("isShortcutConfigurable").toBool(); #if HAVE_GLOBALACCEL case GlobalPrimary: case GlobalAlternate: if (!KGlobalAccel::self()->hasShortcut(m_action)) { return false; } return true; #endif default: return false; } //the following are custom roles, defined in this source file only case ShortcutRole: switch (column) { case LocalPrimary: case LocalAlternate: case GlobalPrimary: case GlobalAlternate: return keySequence(column); #if 0 case ShapeGesture: { //scoping for "ret" QVariant ret; ret.setValue(KGestureMap::self()->shapeGesture(m_action)); return ret; } case RockerGesture: { QVariant ret; ret.setValue(KGestureMap::self()->rockerGesture(m_action)); return ret; } #endif default: // Column not valid for this role Q_ASSERT(false); return QVariant(); } case DefaultShortcutRole: { QList<QKeySequence> defaultShortcuts = m_action->property("defaultShortcuts").value<QList<QKeySequence> >(); #if HAVE_GLOBALACCEL QList<QKeySequence> defaultGlobalShortcuts = KGlobalAccel::self()->defaultShortcut(m_action); #endif switch (column) { case LocalPrimary: return primarySequence(defaultShortcuts); case LocalAlternate: return alternateSequence(defaultShortcuts); #if HAVE_GLOBALACCEL case GlobalPrimary: return primarySequence(defaultGlobalShortcuts); case GlobalAlternate: return alternateSequence(defaultGlobalShortcuts); #endif #if 0 case ShapeGesture: { QVariant ret; ret.setValue(KGestureMap::self()->defaultShapeGesture(m_action)); return ret; } case RockerGesture: { QVariant ret; ret.setValue(KGestureMap::self()->defaultRockerGesture(m_action)); return ret; } #endif default: // Column not valid for this role Q_ASSERT(false); return QVariant(); } } case ObjectRole: return qVariantFromValue(static_cast<QObject *>(m_action)); default: break; } return QVariant(); } bool KShortcutsEditorItem::operator<(const QTreeWidgetItem &other) const { const int column = treeWidget() ? treeWidget()->sortColumn() : 0; return m_collator.compare(text(column), other.text(column)) < 0; } QKeySequence KShortcutsEditorItem::keySequence(uint column) const { QList<QKeySequence> shortcuts = m_action->shortcuts(); #if HAVE_GLOBALACCEL QList<QKeySequence> globalShortcuts = KGlobalAccel::self()->shortcut(m_action); #endif switch (column) { case LocalPrimary: return primarySequence(shortcuts); case LocalAlternate: return alternateSequence(shortcuts); #if HAVE_GLOBALACCEL case GlobalPrimary: return primarySequence(globalShortcuts); case GlobalAlternate: return alternateSequence(globalShortcuts); #endif default: return QKeySequence(); } } void KShortcutsEditorItem::setKeySequence(uint column, const QKeySequence &seq) { QList<QKeySequence> ks; #if HAVE_GLOBALACCEL if (column == GlobalPrimary || column == GlobalAlternate) { ks = KGlobalAccel::self()->shortcut(m_action); if (!m_oldGlobalShortcut) { m_oldGlobalShortcut = new QList<QKeySequence>(ks); } } else #endif { ks = m_action->shortcuts(); if (!m_oldLocalShortcut) { m_oldLocalShortcut = new QList<QKeySequence>(ks); } } if (column == LocalAlternate || column == GlobalAlternate) { if (ks.isEmpty()) { ks << QKeySequence(); } if (ks.size() <= 1) { ks << seq; } else { ks[1] = seq; } } else { if (ks.isEmpty()) { ks << seq; } else { ks[0] = seq; } } //avoid also setting the default shortcut - what we are setting here is custom by definition #if HAVE_GLOBALACCEL if (column == GlobalPrimary || column == GlobalAlternate) { KGlobalAccel::self()->setShortcut(m_action, ks, KGlobalAccel::NoAutoloading); } else #endif { m_action->setShortcuts(ks); } updateModified(); } #if 0 void KShortcutsEditorItem::setShapeGesture(const KShapeGesture &gst) { if (!m_oldShapeGesture) { m_oldShapeGesture = new KShapeGesture(gst); } KGestureMap::self()->setShapeGesture(m_action, gst); KGestureMap::self()->setDefaultShapeGesture(m_action, gst); updateModified(); } #endif #if 0 void KShortcutsEditorItem::setRockerGesture(const KRockerGesture &gst) { if (!m_oldRockerGesture) { m_oldRockerGesture = new KRockerGesture(gst); } KGestureMap::self()->setRockerGesture(m_action, gst); KGestureMap::self()->setDefaultRockerGesture(m_action, gst); updateModified(); } #endif //our definition of modified is "modified since the chooser was shown". void KShortcutsEditorItem::updateModified() { if (m_oldLocalShortcut && *m_oldLocalShortcut == m_action->shortcuts()) { delete m_oldLocalShortcut; - m_oldLocalShortcut = 0; + m_oldLocalShortcut = nullptr; } #if HAVE_GLOBALACCEL if (m_oldGlobalShortcut && *m_oldGlobalShortcut == KGlobalAccel::self()->shortcut(m_action)) { delete m_oldGlobalShortcut; - m_oldGlobalShortcut = 0; + m_oldGlobalShortcut = nullptr; } #endif #if 0 if (m_oldShapeGesture && *m_oldShapeGesture == KGestureMap::self()->shapeGesture(m_action)) { delete m_oldShapeGesture; m_oldShapeGesture = 0; } if (m_oldRockerGesture && *m_oldRockerGesture == KGestureMap::self()->rockerGesture(m_action)) { delete m_oldRockerGesture; m_oldRockerGesture = 0; } #endif } bool KShortcutsEditorItem::isModified(uint column) const { switch (column) { case Name: return false; case LocalPrimary: case LocalAlternate: if (!m_oldLocalShortcut) { return false; } if (column == LocalPrimary) { return primarySequence(*m_oldLocalShortcut) != primarySequence(m_action->shortcuts()); } else { return alternateSequence(*m_oldLocalShortcut) != alternateSequence(m_action->shortcuts()); } #if HAVE_GLOBALACCEL case GlobalPrimary: case GlobalAlternate: if (!m_oldGlobalShortcut) { return false; } if (column == GlobalPrimary) { return primarySequence(*m_oldGlobalShortcut) != primarySequence(KGlobalAccel::self()->shortcut(m_action)); } else { return alternateSequence(*m_oldGlobalShortcut) != alternateSequence(KGlobalAccel::self()->shortcut(m_action)); } #endif #if 0 case ShapeGesture: return static_cast<bool>(m_oldShapeGesture); case RockerGesture: return static_cast<bool>(m_oldRockerGesture); #endif default: return false; } } void KShortcutsEditorItem::undo() { #ifndef NDEBUG #if 0 if (m_oldLocalShortcut || m_oldGlobalShortcut || m_oldShapeGesture || m_oldRockerGesture) { //qDebug() << "Undoing changes for " << data(Name, Qt::DisplayRole).toString(); } #endif #endif if (m_oldLocalShortcut) { // We only ever reset the active Shortcut m_action->setShortcuts(*m_oldLocalShortcut); } #if HAVE_GLOBALACCEL if (m_oldGlobalShortcut) { KGlobalAccel::self()->setShortcut(m_action, *m_oldGlobalShortcut, KGlobalAccel::NoAutoloading); } #endif #if 0 if (m_oldShapeGesture) { KGestureMap::self()->setShapeGesture(m_action, *m_oldShapeGesture); KGestureMap::self()->setDefaultShapeGesture(m_action, *m_oldShapeGesture); } if (m_oldRockerGesture) { KGestureMap::self()->setRockerGesture(m_action, *m_oldRockerGesture); KGestureMap::self()->setDefaultRockerGesture(m_action, *m_oldRockerGesture); } #endif updateModified(); } void KShortcutsEditorItem::commit() { #ifndef NDEBUG #if 0 if (m_oldLocalShortcut || m_oldGlobalShortcut || m_oldShapeGesture || m_oldRockerGesture) { //qDebug() << "Committing changes for " << data(Name, Qt::DisplayRole).toString(); } #endif #endif delete m_oldLocalShortcut; - m_oldLocalShortcut = 0; + m_oldLocalShortcut = nullptr; delete m_oldGlobalShortcut; - m_oldGlobalShortcut = 0; + m_oldGlobalShortcut = nullptr; #if 0 delete m_oldShapeGesture; m_oldShapeGesture = 0; delete m_oldRockerGesture; m_oldRockerGesture = 0; #endif } diff --git a/src/kshortcutwidget.h b/src/kshortcutwidget.h index 7e2b5c6..aeef145 100644 --- a/src/kshortcutwidget.h +++ b/src/kshortcutwidget.h @@ -1,99 +1,99 @@ /* This file is part of the KDE libraries Copyright (C) 2007 Andreas Hartmetz <ahartmetz@gmail.com> 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 KSHORTCUTWIDGET_H #define KSHORTCUTWIDGET_H #include <kxmlgui_export.h> #include <QKeySequence> #include <QList> #include <QWidget> class KActionCollection; class KShortcutWidgetPrivate; /** * \image html kshortcutwidget.png "KDE Shortcut Widget" */ class KXMLGUI_EXPORT KShortcutWidget : public QWidget { Q_OBJECT Q_PROPERTY(bool modifierlessAllowed READ isModifierlessAllowed WRITE setModifierlessAllowed) public: - KShortcutWidget(QWidget *parent = 0); + KShortcutWidget(QWidget *parent = nullptr); ~KShortcutWidget(); void setModifierlessAllowed(bool allow); bool isModifierlessAllowed(); void setClearButtonsShown(bool show); QList<QKeySequence> shortcut() const; /** * Set a list of action collections to check against for conflictuous shortcut. * * If there is a conflictuous shortcut with a KAction, and that his shortcut can be configured * (KAction::isShortcutConfigurable() returns true) the user will be prompted for eventually steal * the shortcut from this action * * Global shortcuts are automatically checked for conflicts * * Don't forget to call applyStealShortcut to actually steal the shortcut. * * @since 4.1 */ void setCheckActionCollections(const QList<KActionCollection *> &actionCollections); /** * @deprecated since 4.1 * Use setCheckActionCollections so that KShortcutWidget knows * in which action collection to call the writeSettings method after stealing * a shortcut from an action. */ #ifndef KXMLGUI_NO_DEPRECATED KXMLGUI_DEPRECATED void setCheckActionList(const QList<QAction *> &checkList); #endif Q_SIGNALS: void shortcutChanged(const QList<QKeySequence> &cut); public Q_SLOTS: void setShortcut(const QList<QKeySequence> &cut); void clearShortcut(); /** * Actually remove the shortcut that the user wanted to steal, from the * action that was using it. * * To be called before you apply your changes. * No shortcuts are stolen until this function is called. */ void applyStealShortcut(); private: Q_PRIVATE_SLOT(d, void priKeySequenceChanged(const QKeySequence &)) Q_PRIVATE_SLOT(d, void altKeySequenceChanged(const QKeySequence &)) private: friend class KShortcutWidgetPrivate; KShortcutWidgetPrivate *const d; }; #endif //KSHORTCUTWIDGET_H diff --git a/src/kswitchlanguagedialog_p.cpp b/src/kswitchlanguagedialog_p.cpp index e9b2ebe..12a1659 100644 --- a/src/kswitchlanguagedialog_p.cpp +++ b/src/kswitchlanguagedialog_p.cpp @@ -1,453 +1,453 @@ /* * This file is part of the KDE Libraries * Copyright (C) 2007 Krzysztof Lichota (lichota@mimuw.edu.pl) * * 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 <QApplication> #include <QDialogButtonBox> #include <QDir> #include <QLayout> #include <QLabel> #include <QPushButton> #include <QtCore/QEvent> #include <QtCore/QMap> #include <QSettings> #include <QSharedPointer> #include <QStandardPaths> #include <QDebug> #include "kswitchlanguagedialog_p.h" #include <klanguagebutton.h> #include <klocalizedstring.h> #include <kmessagebox.h> // Believe it or not we can't use KConfig from here // (we need KConfig during QCoreApplication ctor which is too early for it) // So we cooked a QSettings based solution typedef QSharedPointer<QSettings> QSettingsPtr; static QSettingsPtr localeOverridesSettings() { const QString configPath = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation); const QDir configDir(configPath); if (!configDir.exists()) { configDir.mkpath(QStringLiteral(".")); } return QSettingsPtr(new QSettings(configPath + QStringLiteral("/klanguageoverridesrc"), QSettings::IniFormat)); } static QByteArray getApplicationSpecificLanguage(const QByteArray &defaultCode = QByteArray()) { QSettingsPtr settings = localeOverridesSettings(); settings->beginGroup(QStringLiteral("Language")); return settings->value(qAppName(), defaultCode).toByteArray(); } static void setApplicationSpecificLanguage(const QByteArray &languageCode) { QSettingsPtr settings = localeOverridesSettings(); settings->beginGroup(QStringLiteral("Language")); if (languageCode.isEmpty()) { settings->remove(qAppName()); } else { settings->setValue(qAppName(), languageCode); } } static void initializeLanguages() { const QByteArray languageCode = getApplicationSpecificLanguage(); if (!languageCode.isEmpty()) { QByteArray languages = qgetenv("LANGUAGE"); if (languages.isEmpty()) { qputenv("LANGUAGE", languageCode); } else { qputenv("LANGUAGE", languageCode + ":" + languages); } } } Q_COREAPP_STARTUP_FUNCTION(initializeLanguages) namespace KDEPrivate { struct LanguageRowData { LanguageRowData() { - label = 0; - languageButton = 0; - removeButton = 0; + label = nullptr; + languageButton = nullptr; + removeButton = nullptr; } QLabel *label; KLanguageButton *languageButton; QPushButton *removeButton; void setRowWidgets( QLabel *label, KLanguageButton *languageButton, QPushButton *removeButton ) { this->label = label; this->languageButton = languageButton; this->removeButton = removeButton; } }; class KSwitchLanguageDialogPrivate { public: KSwitchLanguageDialogPrivate(KSwitchLanguageDialog *parent); KSwitchLanguageDialog *p; //parent class /** Fills language button with names of languages for which given application has translation. */ void fillApplicationLanguages(KLanguageButton *button); /** Adds one button with language to widget. */ void addLanguageButton(const QString &languageCode, bool primaryLanguage); /** Returns list of languages chosen for application or default languages is they are not set. */ QStringList applicationLanguageList(); QMap<QPushButton *, LanguageRowData> languageRows; QList<KLanguageButton *> languageButtons; QGridLayout *languagesLayout; }; /*************************** KSwitchLanguageDialog **************************/ KSwitchLanguageDialog::KSwitchLanguageDialog(QWidget *parent) : QDialog(parent), d(new KSwitchLanguageDialogPrivate(this)) { setWindowTitle(i18n("Switch Application Language")); QVBoxLayout *topLayout = new QVBoxLayout; setLayout(topLayout); QLabel *label = new QLabel(i18n("Please choose the language which should be used for this application:"), this); topLayout->addWidget(label); QHBoxLayout *languageHorizontalLayout = new QHBoxLayout(); topLayout->addLayout(languageHorizontalLayout); d->languagesLayout = new QGridLayout(); languageHorizontalLayout->addLayout(d->languagesLayout); languageHorizontalLayout->addStretch(); const QStringList defaultLanguages = d->applicationLanguageList(); int count = defaultLanguages.count(); for (int i = 0; i < count; ++i) { QString language = defaultLanguages[i]; bool primaryLanguage = (i == 0); d->addLanguageButton(language, primaryLanguage); } if (!count) { QLocale l; d->addLanguageButton(l.name(), true); } QHBoxLayout *addButtonHorizontalLayout = new QHBoxLayout(); topLayout->addLayout(addButtonHorizontalLayout); QPushButton *addLangButton = new QPushButton(i18n("Add Fallback Language"), this); addLangButton->setToolTip(i18n("Adds one more language which will be used if other translations do not contain a proper translation.")); connect(addLangButton, SIGNAL(clicked()), this, SLOT(slotAddLanguageButton())); addButtonHorizontalLayout->addWidget(addLangButton); addButtonHorizontalLayout->addStretch(); topLayout->addStretch(10); QDialogButtonBox *buttonBox = new QDialogButtonBox(this); buttonBox->setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel | QDialogButtonBox::RestoreDefaults); KGuiItem::assign(buttonBox->button(QDialogButtonBox::Ok), KStandardGuiItem::ok()); KGuiItem::assign(buttonBox->button(QDialogButtonBox::Cancel), KStandardGuiItem::cancel()); KGuiItem::assign(buttonBox->button(QDialogButtonBox::RestoreDefaults), KStandardGuiItem::defaults()); topLayout->addWidget(buttonBox); connect(buttonBox, SIGNAL(accepted()), this, SLOT(slotOk())); connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept())); connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); connect(buttonBox->button(QDialogButtonBox::RestoreDefaults), SIGNAL(clicked()), this, SLOT(slotDefault())); } KSwitchLanguageDialog::~KSwitchLanguageDialog() { delete d; } void KSwitchLanguageDialog::slotAddLanguageButton() { //adding new button with en_US as it should always be present d->addLanguageButton(QStringLiteral("en_US"), d->languageButtons.isEmpty()); } void KSwitchLanguageDialog::removeButtonClicked() { QObject const *signalSender = sender(); if (!signalSender) { qCritical() << "KSwitchLanguageDialog::removeButtonClicked() called directly, not using signal" << endl; return; } QPushButton *removeButton = const_cast<QPushButton *>(::qobject_cast<const QPushButton *>(signalSender)); if (!removeButton) { qCritical() << "KSwitchLanguageDialog::removeButtonClicked() called from something else than QPushButton" << endl; return; } QMap<QPushButton *, LanguageRowData>::iterator it = d->languageRows.find(removeButton); if (it == d->languageRows.end()) { qCritical() << "KSwitchLanguageDialog::removeButtonClicked called from unknown QPushButton" << endl; return; } LanguageRowData languageRowData = it.value(); d->languageButtons.removeAll(languageRowData.languageButton); languageRowData.label->deleteLater(); languageRowData.languageButton->deleteLater(); languageRowData.removeButton->deleteLater(); d->languageRows.erase(it); } void KSwitchLanguageDialog::languageOnButtonChanged(const QString &languageCode) { Q_UNUSED(languageCode); #if 0 for (int i = 0, count = d->languageButtons.count(); i < count; ++i) { KLanguageButton *languageButton = d->languageButtons[i]; if (languageButton->current() == languageCode) { //update all buttons which have matching id //might update buttons which were not changed, but well... languageButton->setText(KLocale::global()->languageCodeToName(languageCode)); } } #endif } void KSwitchLanguageDialog::slotOk() { QStringList languages; for (int i = 0, count = d->languageButtons.count(); i < count; ++i) { KLanguageButton *languageButton = d->languageButtons[i]; languages << languageButton->current(); } if (d->applicationLanguageList() != languages) { QString languageString = languages.join(QLatin1Char(':')); //list is different from defaults or saved languages list setApplicationSpecificLanguage(languageString.toLatin1()); KMessageBox::information( this, i18n("The language for this application has been changed. The change will take effect the next time the application is started."), //text i18n("Application Language Changed"), //caption QStringLiteral("ApplicationLanguageChangedWarning") //dontShowAgainName ); } accept(); } void KSwitchLanguageDialog::slotDefault() { const QStringList defaultLanguages = d->applicationLanguageList(); setApplicationSpecificLanguage(QByteArray()); // read back the new default QString language = QString::fromLatin1(getApplicationSpecificLanguage("en_US")); if (defaultLanguages != (QStringList() << language)) { KMessageBox::information( this, i18n("The language for this application has been changed. The change will take effect the next time the application is started."), //text i18n("Application Language Changed"), //caption QStringLiteral("ApplicationLanguageChangedWarning") //dontShowAgainName ); } accept(); } /************************ KSwitchLanguageDialogPrivate ***********************/ KSwitchLanguageDialogPrivate::KSwitchLanguageDialogPrivate( KSwitchLanguageDialog *parent) : p(parent) { //NOTE: do NOT use "p" in constructor, it is not fully constructed } static bool stripCountryCode(QString *languageCode) { const int idx = languageCode->indexOf(QLatin1String("_")); if (idx != -1) { *languageCode = languageCode->left(idx); return true; } return false; } void KSwitchLanguageDialogPrivate::fillApplicationLanguages(KLanguageButton *button) { QLocale defaultLocale; QLocale cLocale(QLocale::C); QLocale::setDefault(cLocale); QSet<QString> insertedLanguges; const QList<QLocale> allLocales = QLocale::matchingLocales(QLocale::AnyLanguage, QLocale::AnyScript, QLocale::AnyCountry); foreach(const QLocale &l, allLocales) { QString languageCode = l.name(); if (l != cLocale) { const QString nativeName = l.nativeLanguageName(); // For some languages the native name might be empty. // In this case use the non native language name as fallback. // See: QTBUG-51323 const QString languageName = nativeName.isEmpty() ? QLocale::languageToString(l.language()) : nativeName; if (!insertedLanguges.contains(languageCode) && KLocalizedString::isApplicationTranslatedInto(languageCode)) { button->insertLanguage(languageCode, languageName); insertedLanguges << languageCode; } else if (stripCountryCode(&languageCode)) { if (!insertedLanguges.contains(languageCode) && KLocalizedString::isApplicationTranslatedInto(languageCode)) { button->insertLanguage(languageCode, languageName); insertedLanguges << languageCode; } } } } QLocale::setDefault(defaultLocale); } QStringList KSwitchLanguageDialogPrivate::applicationLanguageList() { QStringList languagesList; QByteArray languageCode = getApplicationSpecificLanguage(); if (!languageCode.isEmpty()) { languagesList = QString::fromLatin1(languageCode).split(QLatin1Char(':')); } if (languagesList.isEmpty()) { QLocale l; languagesList = l.uiLanguages(); // We get en-US here but we use en_US for (int i = 0; i < languagesList.count(); ++i) { languagesList[i].replace(QLatin1String("-"), QLatin1String("_")); } } for (int i = 0; i < languagesList.count();) { QString languageCode = languagesList[i]; if (!KLocalizedString::isApplicationTranslatedInto(languageCode)) { if (stripCountryCode(&languageCode)) { if (KLocalizedString::isApplicationTranslatedInto(languageCode)) { languagesList[i] = languageCode; ++i; continue; } } languagesList.removeAt(i); } else { ++i; } } return languagesList; } void KSwitchLanguageDialogPrivate::addLanguageButton(const QString &languageCode, bool primaryLanguage) { QString labelText = primaryLanguage ? i18n("Primary language:") : i18n("Fallback language:"); KLanguageButton *languageButton = new KLanguageButton(p); fillApplicationLanguages(languageButton); languageButton->setCurrentItem(languageCode); QObject::connect( languageButton, SIGNAL(activated(QString)), p, SLOT(languageOnButtonChanged(QString)) ); LanguageRowData languageRowData; - QPushButton *removeButton = 0; + QPushButton *removeButton = nullptr; if (!primaryLanguage) { removeButton = new QPushButton(i18n("Remove"), p); QObject::connect( removeButton, SIGNAL(clicked()), p, SLOT(removeButtonClicked()) ); } languageButton->setToolTip(primaryLanguage ? i18n("This is the main application language which will be used first, before any other languages.") : i18n("This is the language which will be used if any previous languages do not contain a proper translation.")); int numRows = languagesLayout->rowCount(); QLabel *languageLabel = new QLabel(labelText, p); languagesLayout->addWidget(languageLabel, numRows + 1, 1, Qt::AlignLeft); languagesLayout->addWidget(languageButton, numRows + 1, 2, Qt::AlignLeft); if (!primaryLanguage) { languagesLayout->addWidget(removeButton, numRows + 1, 3, Qt::AlignLeft); languageRowData.setRowWidgets(languageLabel, languageButton, removeButton); removeButton->show(); } languageRows.insert(removeButton, languageRowData); languageButtons.append(languageButton); languageButton->show(); languageLabel->show(); } } diff --git a/src/kswitchlanguagedialog_p.h b/src/kswitchlanguagedialog_p.h index 753c816..20744b6 100644 --- a/src/kswitchlanguagedialog_p.h +++ b/src/kswitchlanguagedialog_p.h @@ -1,94 +1,94 @@ /* * This file is part of the KDE Libraries * Copyright (C) 2007 Krzysztof Lichota (lichota@mimuw.edu.pl) * * 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 _KSWITCHLANGUAGEDIALOG_H_ #define _KSWITCHLANGUAGEDIALOG_H_ #include <QDialog> namespace KDEPrivate { class KSwitchLanguageDialogPrivate; /** * @short Standard "switch application language" dialog box. * * This class provides "switch application language" dialog box that is used * in KHelpMenu * * @author Krzysztof Lichota (lichota@mimuw.edu.pl) * @internal */ class KSwitchLanguageDialog : public QDialog { Q_OBJECT public: /** * Constructor. Creates a fully featured "Switch application language" dialog box. * Note that this dialog is made modeless in the KHelpMenu class so * the users may expect a modeless dialog. * * @param parent The parent of the dialog box. You should use the * toplevel window so that the dialog becomes centered. * @param name Internal name of the widget. This name in not used in the * caption. * @param modal If false, this widget will be modeless and must be * made visible using QWidget::show(). Otherwise it will be * modal and must be made visible using QWidget::exec() */ - KSwitchLanguageDialog(QWidget *parent = 0); + KSwitchLanguageDialog(QWidget *parent = nullptr); virtual ~KSwitchLanguageDialog(); protected Q_SLOTS: /** * Activated when the Ok button has been clicked. */ virtual void slotOk(); void slotDefault(); /** Called when one of language buttons changes state. */ virtual void languageOnButtonChanged(const QString &); /** Called to add one language button to dialog. */ virtual void slotAddLanguageButton(); /** Called when "Remove" language button is clicked. */ virtual void removeButtonClicked(); private: KSwitchLanguageDialogPrivate *const d; friend class KSwitchLanguageDialogPrivate; }; } #endif diff --git a/src/ktoggletoolbaraction.cpp b/src/ktoggletoolbaraction.cpp index abba065..73548cf 100644 --- a/src/ktoggletoolbaraction.cpp +++ b/src/ktoggletoolbaraction.cpp @@ -1,133 +1,133 @@ /* This file is part of the KDE libraries Copyright (C) 1999 Reginald Stadlbauer <reggie@kde.org> (C) 1999 Simon Hausmann <hausmann@kde.org> (C) 2000 Nicolas Hadacek <haadcek@kde.org> (C) 2000 Kurt Granroth <granroth@kde.org> (C) 2000 Michael Koch <koch@kde.org> (C) 2001 Holger Freyther <freyther@kde.org> (C) 2002 Ellis Whitehead <ellis@kde.org> (C) 2002 Joseph Wenninger <jowenn@kde.org> (C) 2003 Andras Mantia <amantia@kde.org> (C) 2005-2006 Hamish Rodda <rodda@kde.org> 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 "ktoggletoolbaraction.h" #include <QtCore/QByteArray> #include <QtCore/QEvent> #include <QtCore/QPointer> #include <KAuthorized> #include <klocalizedstring.h> #include "kmainwindow.h" #include "ktoolbar.h" class KToggleToolBarAction::Private { public: Private(KToggleToolBarAction *q) - : toolBarName(0), toolBar(0), beingToggled(false) + : toolBarName(nullptr), toolBar(nullptr), beingToggled(false) { const bool authorized = KAuthorized::authorizeAction(QStringLiteral("options_show_toolbar")); q->setEnabled(authorized); q->setVisible(authorized); } QByteArray toolBarName; QPointer<KToolBar> toolBar; bool beingToggled; }; KToggleToolBarAction::KToggleToolBarAction(const char *toolBarName, const QString &text, QObject *parent) : KToggleAction(text, parent), d(new Private(this)) { d->toolBarName = toolBarName; } KToggleToolBarAction::KToggleToolBarAction(KToolBar *toolBar, const QString &text, QObject *parent) : KToggleAction(text, parent), d(new Private(this)) { d->toolBar = toolBar; d->toolBar->installEventFilter(this); d->beingToggled = true; setChecked(d->toolBar->isVisible()); d->beingToggled = false; } KToggleToolBarAction::~KToggleToolBarAction() { delete d; } bool KToggleToolBarAction::eventFilter(QObject *watched, QEvent *event) { if (d->beingToggled) { return false; } d->beingToggled = true; if (watched == d->toolBar) { switch (event->type()) { case QEvent::Hide: if (isChecked()) { setChecked(false); } break; case QEvent::Show: if (!isChecked()) { setChecked(true); } break; default: break; } } d->beingToggled = false; return false; } KToolBar *KToggleToolBarAction::toolBar() { return d->toolBar; } void KToggleToolBarAction::slotToggled(bool checked) { if (!d->beingToggled && d->toolBar && checked != d->toolBar->isVisible()) { d->beingToggled = true; d->toolBar->setVisible(checked); d->beingToggled = false; QMainWindow *mw = d->toolBar->mainWindow(); if (mw && qobject_cast<KMainWindow *>(mw)) { static_cast<KMainWindow *>(mw)->setSettingsDirty(); } } KToggleAction::slotToggled(checked); } diff --git a/src/ktoolbar.cpp b/src/ktoolbar.cpp index 31be9b0..4569524 100644 --- a/src/ktoolbar.cpp +++ b/src/ktoolbar.cpp @@ -1,1474 +1,1474 @@ /* This file is part of the KDE libraries Copyright (C) 2000 Reginald Stadlbauer (reggie@kde.org) (C) 1997, 1998 Stephan Kulow (coolo@kde.org) (C) 1997, 1998 Mark Donohoe (donohoe@kde.org) (C) 1997, 1998 Sven Radej (radej@kde.org) (C) 1997, 1998 Matthias Ettrich (ettrich@kde.org) (C) 1999 Chris Schlaeger (cs@kde.org) (C) 1999 Kurt Granroth (granroth@kde.org) (C) 2005-2006 Hamish Rodda (rodda@kde.org) 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 "ktoolbar.h" #include <QPointer> #include <QAction> #include <QApplication> #include <QDesktopWidget> #include <QFrame> #include <QLayout> #include <QMenu> #include <QMimeData> #include <QDrag> #include <QMouseEvent> #include <QToolButton> #include <QtXml/QDomElement> #include <QDBusConnection> #include <QDBusMessage> #include <QDebug> #include <kauthorized.h> #include <kconfig.h> #include <ksharedconfig.h> #include <kicontheme.h> #include <klocalizedstring.h> #include <kstandardaction.h> #include <ktoggleaction.h> #include <kconfiggroup.h> #include "kactioncollection.h" #include "kedittoolbar.h" #include "kxmlguifactory.h" #include "kxmlguiwindow.h" /* Toolbar settings (e.g. icon size or toolButtonStyle) ===================================================== We have the following stack of settings (in order of priority) : - user-specified settings (loaded/saved in KConfig) - developer-specified settings in the XMLGUI file (if using xmlgui) (cannot change at runtime) - KDE-global default (user-configurable; can change at runtime) and when switching between kparts, they are saved as xml in memory, which, in the unlikely case of no-kmainwindow-autosaving, could be different from the user-specified settings saved in KConfig and would have priority over it. So, in summary, without XML: Global config / User settings (loaded/saved in kconfig) and with XML: Global config / App-XML attributes / User settings (loaded/saved in kconfig) And all those settings (except the KDE-global defaults) have to be stored in memory since we cannot retrieve them at random points in time, not knowing the xml document nor config file that holds these settings. Hence the iconSizeSettings and toolButtonStyleSettings arrays. For instance, if you change the KDE-global default, whether this makes a change on a given toolbar depends on whether there are settings at Level_AppXML or Level_UserSettings. Only if there are no settings at those levels, should the change of KDEDefault make a difference. */ enum SettingLevel { Level_KDEDefault, Level_AppXML, Level_UserSettings, NSettingLevels }; enum { Unset = -1 }; class KToolBar::Private { public: Private(KToolBar *qq) : q(qq), isMainToolBar(false), #ifndef KXMLGUI_NO_DEPRECATED enableContext(true), #endif unlockedMovable(true), - contextOrient(0), - contextMode(0), - contextSize(0), - contextButtonTitle(0), - contextShowText(0), - contextButtonAction(0), - contextTop(0), - contextLeft(0), - contextRight(0), - contextBottom(0), - contextIcons(0), - contextTextRight(0), - contextText(0), - contextTextUnder(0), - contextLockAction(0), - dropIndicatorAction(0), - context(0), - dragAction(0) + contextOrient(nullptr), + contextMode(nullptr), + contextSize(nullptr), + contextButtonTitle(nullptr), + contextShowText(nullptr), + contextButtonAction(nullptr), + contextTop(nullptr), + contextLeft(nullptr), + contextRight(nullptr), + contextBottom(nullptr), + contextIcons(nullptr), + contextTextRight(nullptr), + contextText(nullptr), + contextTextUnder(nullptr), + contextLockAction(nullptr), + dropIndicatorAction(nullptr), + context(nullptr), + dragAction(nullptr) { } void slotAppearanceChanged(); void slotContextAboutToShow(); void slotContextAboutToHide(); void slotContextLeft(); void slotContextRight(); void slotContextShowText(); void slotContextTop(); void slotContextBottom(); void slotContextIcons(); void slotContextText(); void slotContextTextRight(); void slotContextTextUnder(); void slotContextIconSize(); void slotLockToolBars(bool lock); void init(bool readConfig = true, bool isMainToolBar = false); QString getPositionAsString() const; QMenu *contextMenu(const QPoint &globalPos); void setLocked(bool locked); void adjustSeparatorVisibility(); void loadKDESettings(); void applyCurrentSettings(); - QAction *findAction(const QString &actionName, KXMLGUIClient **client = 0) const; + QAction *findAction(const QString &actionName, KXMLGUIClient **client = nullptr) const; static Qt::ToolButtonStyle toolButtonStyleFromString(const QString &style); static QString toolButtonStyleToString(Qt::ToolButtonStyle); static Qt::ToolBarArea positionFromString(const QString &position); static Qt::ToolButtonStyle toolButtonStyleSetting(); KToolBar *q; bool isMainToolBar : 1; #ifndef KXMLGUI_NO_DEPRECATED bool enableContext : 1; #endif bool unlockedMovable : 1; static bool s_editable; static bool s_locked; QSet<KXMLGUIClient *> xmlguiClients; QMenu *contextOrient; QMenu *contextMode; QMenu *contextSize; QAction *contextButtonTitle; QAction *contextShowText; QAction *contextButtonAction; QAction *contextTop; QAction *contextLeft; QAction *contextRight; QAction *contextBottom; QAction *contextIcons; QAction *contextTextRight; QAction *contextText; QAction *contextTextUnder; KToggleAction *contextLockAction; QMap<QAction *, int> contextIconSizes; class IntSetting { public: IntSetting() { for (int level = 0; level < NSettingLevels; ++level) { values[level] = Unset; } } int currentValue() const { int val = Unset; for (int level = 0; level < NSettingLevels; ++level) { if (values[level] != Unset) { val = values[level]; } } return val; } // Default value as far as the user is concerned is kde-global + app-xml. // If currentValue()==defaultValue() then nothing to write into kconfig. int defaultValue() const { int val = Unset; for (int level = 0; level < Level_UserSettings; ++level) { if (values[level] != Unset) { val = values[level]; } } return val; } QString toString() const { QString str; for (int level = 0; level < NSettingLevels; ++level) { str += QString::number(values[level]) + QLatin1Char(' '); } return str; } int &operator[](int index) { return values[index]; } private: int values[NSettingLevels]; }; IntSetting iconSizeSettings; IntSetting toolButtonStyleSettings; // either Qt::ToolButtonStyle or -1, hence "int". QList<QAction *> actionsBeingDragged; QAction *dropIndicatorAction; QMenu *context; QAction *dragAction; QPoint dragStartPosition; }; bool KToolBar::Private::s_editable = false; bool KToolBar::Private::s_locked = true; void KToolBar::Private::init(bool readConfig, bool _isMainToolBar) { isMainToolBar = _isMainToolBar; loadKDESettings(); // also read in our configurable settings (for non-xmlgui toolbars) if (readConfig) { KConfigGroup cg(KSharedConfig::openConfig(), QString()); q->applySettings(cg); } if (q->mainWindow()) { // Get notified when settings change connect(q, SIGNAL(allowedAreasChanged(Qt::ToolBarAreas)), q->mainWindow(), SLOT(setSettingsDirty())); connect(q, SIGNAL(iconSizeChanged(QSize)), q->mainWindow(), SLOT(setSettingsDirty())); connect(q, SIGNAL(toolButtonStyleChanged(Qt::ToolButtonStyle)), q->mainWindow(), SLOT(setSettingsDirty())); connect(q, SIGNAL(movableChanged(bool)), q->mainWindow(), SLOT(setSettingsDirty())); connect(q, SIGNAL(orientationChanged(Qt::Orientation)), q->mainWindow(), SLOT(setSettingsDirty())); } if (!KAuthorized::authorize(QStringLiteral("movable_toolbars"))) { q->setMovable(false); } else { q->setMovable(!KToolBar::toolBarsLocked()); } q->toggleViewAction()->setEnabled(KAuthorized::authorizeAction(QStringLiteral("options_show_toolbar"))); connect(q, SIGNAL(movableChanged(bool)), q, SLOT(slotMovableChanged(bool))); q->setAcceptDrops(true); QDBusConnection::sessionBus().connect(QString(), QStringLiteral("/KToolBar"), QStringLiteral("org.kde.KToolBar"), QStringLiteral("styleChanged"), q, SLOT(slotAppearanceChanged())); connect(KIconLoader::global(), SIGNAL(iconLoaderSettingsChanged()), q, SLOT(slotAppearanceChanged())); } QString KToolBar::Private::getPositionAsString() const { // get all of the stuff to save switch (q->mainWindow()->toolBarArea(const_cast<KToolBar *>(q))) { case Qt::BottomToolBarArea: return QStringLiteral("Bottom"); case Qt::LeftToolBarArea: return QStringLiteral("Left"); case Qt::RightToolBarArea: return QStringLiteral("Right"); case Qt::TopToolBarArea: default: return QStringLiteral("Top"); } } QMenu *KToolBar::Private::contextMenu(const QPoint &globalPos) { if (!context) { context = new QMenu(q); contextButtonTitle = context->addSection(i18nc("@title:menu", "Show Text")); contextShowText = context->addAction(QString(), q, SLOT(slotContextShowText())); context->addSection(i18nc("@title:menu", "Toolbar Settings")); contextOrient = new QMenu(i18nc("Toolbar orientation", "Orientation"), context); contextTop = contextOrient->addAction(i18nc("toolbar position string", "Top"), q, SLOT(slotContextTop())); contextTop->setChecked(true); contextLeft = contextOrient->addAction(i18nc("toolbar position string", "Left"), q, SLOT(slotContextLeft())); contextRight = contextOrient->addAction(i18nc("toolbar position string", "Right"), q, SLOT(slotContextRight())); contextBottom = contextOrient->addAction(i18nc("toolbar position string", "Bottom"), q, SLOT(slotContextBottom())); QActionGroup *positionGroup = new QActionGroup(contextOrient); Q_FOREACH (QAction *action, contextOrient->actions()) { action->setActionGroup(positionGroup); action->setCheckable(true); } contextMode = new QMenu(i18n("Text Position"), context); contextIcons = contextMode->addAction(i18n("Icons Only"), q, SLOT(slotContextIcons())); contextText = contextMode->addAction(i18n("Text Only"), q, SLOT(slotContextText())); contextTextRight = contextMode->addAction(i18n("Text Alongside Icons"), q, SLOT(slotContextTextRight())); contextTextUnder = contextMode->addAction(i18n("Text Under Icons"), q, SLOT(slotContextTextUnder())); QActionGroup *textGroup = new QActionGroup(contextMode); Q_FOREACH (QAction *action, contextMode->actions()) { action->setActionGroup(textGroup); action->setCheckable(true); } contextSize = new QMenu(i18n("Icon Size"), context); contextIconSizes.insert(contextSize->addAction(i18nc("@item:inmenu Icon size", "Default"), q, SLOT(slotContextIconSize())), iconSizeSettings.defaultValue()); // Query the current theme for available sizes KIconTheme *theme = KIconLoader::global()->theme(); QList<int> avSizes; if (theme) { avSizes = theme->querySizes(isMainToolBar ? KIconLoader::MainToolbar : KIconLoader::Toolbar); } qSort(avSizes); if (avSizes.count() < 10) { // Fixed or threshold type icons Q_FOREACH (int it, avSizes) { QString text; if (it < 19) { text = i18n("Small (%1x%2)", it, it); } else if (it < 25) { text = i18n("Medium (%1x%2)", it, it); } else if (it < 35) { text = i18n("Large (%1x%2)", it, it); } else { text = i18n("Huge (%1x%2)", it, it); } // save the size in the contextIconSizes map contextIconSizes.insert(contextSize->addAction(text, q, SLOT(slotContextIconSize())), it); } } else { // Scalable icons. const int progression[] = { 16, 22, 32, 48, 64, 96, 128, 192, 256 }; for (uint i = 0; i < 9; i++) { Q_FOREACH (int it, avSizes) { if (it >= progression[ i ]) { QString text; if (it < 19) { text = i18n("Small (%1x%2)", it, it); } else if (it < 25) { text = i18n("Medium (%1x%2)", it, it); } else if (it < 35) { text = i18n("Large (%1x%2)", it, it); } else { text = i18n("Huge (%1x%2)", it, it); } // save the size in the contextIconSizes map contextIconSizes.insert(contextSize->addAction(text, q, SLOT(slotContextIconSize())), it); break; } } } } QActionGroup *sizeGroup = new QActionGroup(contextSize); Q_FOREACH (QAction *action, contextSize->actions()) { action->setActionGroup(sizeGroup); action->setCheckable(true); } if (!q->toolBarsLocked() && !q->isMovable()) { unlockedMovable = false; } delete contextLockAction; contextLockAction = new KToggleAction(QIcon::fromTheme(QStringLiteral("system-lock-screen")), i18n("Lock Toolbar Positions"), q); contextLockAction->setChecked(q->toolBarsLocked()); connect(contextLockAction, SIGNAL(toggled(bool)), q, SLOT(slotLockToolBars(bool))); // Now add the actions to the menu context->addMenu(contextMode); context->addMenu(contextSize); context->addMenu(contextOrient); context->addSeparator(); connect(context, SIGNAL(aboutToShow()), q, SLOT(slotContextAboutToShow())); } contextButtonAction = q->actionAt(q->mapFromGlobal(globalPos)); if (contextButtonAction) { contextShowText->setText(contextButtonAction->text()); contextShowText->setIcon(contextButtonAction->icon()); contextShowText->setCheckable(true); } contextOrient->menuAction()->setVisible(!q->toolBarsLocked()); // Unplugging a submenu from abouttohide leads to the popupmenu floating around // So better simply call that code from after exec() returns (DF) //connect(context, SIGNAL(aboutToHide()), this, SLOT(slotContextAboutToHide())); return context; } void KToolBar::Private::setLocked(bool locked) { if (unlockedMovable) { q->setMovable(!locked); } } void KToolBar::Private::adjustSeparatorVisibility() { bool visibleNonSeparator = false; int separatorToShow = -1; for (int index = 0; index < q->actions().count(); ++index) { QAction *action = q->actions().at(index); if (action->isSeparator()) { if (visibleNonSeparator) { separatorToShow = index; visibleNonSeparator = false; } else { action->setVisible(false); } } else if (!visibleNonSeparator) { if (action->isVisible()) { visibleNonSeparator = true; if (separatorToShow != -1) { q->actions().at(separatorToShow)->setVisible(true); separatorToShow = -1; } } } } if (separatorToShow != -1) { q->actions().at(separatorToShow)->setVisible(false); } } Qt::ToolButtonStyle KToolBar::Private::toolButtonStyleFromString(const QString &_style) { QString style = _style.toLower(); if (style == QStringLiteral("textbesideicon") || style == QLatin1String("icontextright")) { return Qt::ToolButtonTextBesideIcon; } else if (style == QStringLiteral("textundericon") || style == QLatin1String("icontextbottom")) { return Qt::ToolButtonTextUnderIcon; } else if (style == QStringLiteral("textonly")) { return Qt::ToolButtonTextOnly; } else { return Qt::ToolButtonIconOnly; } } QString KToolBar::Private::toolButtonStyleToString(Qt::ToolButtonStyle style) { switch (style) { case Qt::ToolButtonIconOnly: default: return QStringLiteral("IconOnly"); case Qt::ToolButtonTextBesideIcon: return QStringLiteral("TextBesideIcon"); case Qt::ToolButtonTextOnly: return QStringLiteral("TextOnly"); case Qt::ToolButtonTextUnderIcon: return QStringLiteral("TextUnderIcon"); } } Qt::ToolBarArea KToolBar::Private::positionFromString(const QString &position) { Qt::ToolBarArea newposition = Qt::TopToolBarArea; if (position == QStringLiteral("left")) { newposition = Qt::LeftToolBarArea; } else if (position == QStringLiteral("bottom")) { newposition = Qt::BottomToolBarArea; } else if (position == QStringLiteral("right")) { newposition = Qt::RightToolBarArea; } return newposition; } // Global setting was changed void KToolBar::Private::slotAppearanceChanged() { loadKDESettings(); applyCurrentSettings(); } Qt::ToolButtonStyle KToolBar::Private::toolButtonStyleSetting() { KConfigGroup group(KSharedConfig::openConfig(), "Toolbar style"); const QString fallback = KToolBar::Private::toolButtonStyleToString(Qt::ToolButtonTextBesideIcon); return KToolBar::Private::toolButtonStyleFromString(group.readEntry("ToolButtonStyle", fallback)); } void KToolBar::Private::loadKDESettings() { iconSizeSettings[Level_KDEDefault] = q->iconSizeDefault(); if (isMainToolBar) { toolButtonStyleSettings[Level_KDEDefault] = toolButtonStyleSetting(); } else { const QString fallBack = toolButtonStyleToString(Qt::ToolButtonTextBesideIcon); /** TODO: if we get complaints about text beside icons on small screens, try the following code out on such systems - aseigo. // if we are on a small screen with a non-landscape ratio, then // we revert to text under icons since width is probably not our // friend in such cases QDesktopWidget *desktop = QApplication::desktop(); QRect screenGeom = desktop->screenGeometry(desktop->primaryScreen()); qreal ratio = screenGeom.width() / qreal(screenGeom.height()); if (screenGeom.width() < 1024 && ratio <= 1.4) { fallBack = "TextUnderIcon"; } **/ KConfigGroup group(KSharedConfig::openConfig(), "Toolbar style"); const QString value = group.readEntry("ToolButtonStyleOtherToolbars", fallBack); toolButtonStyleSettings[Level_KDEDefault] = KToolBar::Private::toolButtonStyleFromString(value); } } // Call this after changing something in d->iconSizeSettings or d->toolButtonStyleSettings void KToolBar::Private::applyCurrentSettings() { //qDebug() << q->objectName() << "iconSizeSettings:" << iconSizeSettings.toString() << "->" << iconSizeSettings.currentValue(); const int currentIconSize = iconSizeSettings.currentValue(); q->setIconSize(QSize(currentIconSize, currentIconSize)); //qDebug() << q->objectName() << "toolButtonStyleSettings:" << toolButtonStyleSettings.toString() << "->" << toolButtonStyleSettings.currentValue(); q->setToolButtonStyle(static_cast<Qt::ToolButtonStyle>(toolButtonStyleSettings.currentValue())); // And remember to save the new look later KMainWindow *kmw = q->mainWindow(); if (kmw) { kmw->setSettingsDirty(); } } QAction *KToolBar::Private::findAction(const QString &actionName, KXMLGUIClient **clientOut) const { Q_FOREACH (KXMLGUIClient *client, xmlguiClients) { QAction *action = client->actionCollection()->action(actionName); if (action) { if (clientOut) { *clientOut = client; } return action; } } - return 0; + return nullptr; } void KToolBar::Private::slotContextAboutToShow() { /** * The idea here is to reuse the "static" part of the menu to save time. * But the "Toolbars" action is dynamic (can be a single action or a submenu) * and ToolBarHandler::setupActions() deletes it, so better not keep it around. * So we currently plug/unplug the last two actions of the menu. * Another way would be to keep around the actions and plug them all into a (new each time) popupmenu. */ KXmlGuiWindow *kmw = qobject_cast<KXmlGuiWindow *>(q->mainWindow()); // try to find "configure toolbars" action - QAction *configureAction = 0; + QAction *configureAction = nullptr; const char *actionName = KStandardAction::name(KStandardAction::ConfigureToolbars); configureAction = findAction(QLatin1String(actionName)); if (!configureAction && kmw) { configureAction = kmw->actionCollection()->action(QLatin1String(actionName)); } if (configureAction) { context->addAction(configureAction); } context->addAction(contextLockAction); if (kmw) { kmw->setupToolbarMenuActions(); // Only allow hiding a toolbar if the action is also plugged somewhere else (e.g. menubar) QAction *tbAction = kmw->toolBarMenuAction(); if (!q->toolBarsLocked() && tbAction && tbAction->associatedWidgets().count() > 0) { context->addAction(tbAction); } } KEditToolBar::setGlobalDefaultToolBar(q->QObject::objectName().toLatin1().constData()); // Check the actions that should be checked switch (q->toolButtonStyle()) { case Qt::ToolButtonIconOnly: default: contextIcons->setChecked(true); break; case Qt::ToolButtonTextBesideIcon: contextTextRight->setChecked(true); break; case Qt::ToolButtonTextOnly: contextText->setChecked(true); break; case Qt::ToolButtonTextUnderIcon: contextTextUnder->setChecked(true); break; } QMapIterator< QAction *, int > it = contextIconSizes; while (it.hasNext()) { it.next(); if (it.value() == q->iconSize().width()) { it.key()->setChecked(true); break; } } switch (q->mainWindow()->toolBarArea(q)) { case Qt::BottomToolBarArea: contextBottom->setChecked(true); break; case Qt::LeftToolBarArea: contextLeft->setChecked(true); break; case Qt::RightToolBarArea: contextRight->setChecked(true); break; default: case Qt::TopToolBarArea: contextTop->setChecked(true); break; } const bool showButtonSettings = contextButtonAction && !contextShowText->text().isEmpty() && contextTextRight->isChecked(); contextButtonTitle->setVisible(showButtonSettings); contextShowText->setVisible(showButtonSettings); if (showButtonSettings) { contextShowText->setChecked(contextButtonAction->priority() >= QAction::NormalPriority); } } void KToolBar::Private::slotContextAboutToHide() { // We have to unplug whatever slotContextAboutToShow plugged into the menu. // Unplug the toolbar menu action KXmlGuiWindow *kmw = qobject_cast<KXmlGuiWindow *>(q->mainWindow()); if (kmw && kmw->toolBarMenuAction()) { if (kmw->toolBarMenuAction()->associatedWidgets().count() > 1) { context->removeAction(kmw->toolBarMenuAction()); } } // Unplug the configure toolbars action too, since it's afterwards anyway - QAction *configureAction = 0; + QAction *configureAction = nullptr; const char *actionName = KStandardAction::name(KStandardAction::ConfigureToolbars); configureAction = findAction(QLatin1String(actionName)); if (!configureAction && kmw) { configureAction = kmw->actionCollection()->action(QLatin1String(actionName)); } if (configureAction) { context->removeAction(configureAction); } context->removeAction(contextLockAction); } void KToolBar::Private::slotContextLeft() { q->mainWindow()->addToolBar(Qt::LeftToolBarArea, q); } void KToolBar::Private::slotContextRight() { q->mainWindow()->addToolBar(Qt::RightToolBarArea, q); } void KToolBar::Private::slotContextShowText() { Q_ASSERT(contextButtonAction); const QAction::Priority priority = contextShowText->isChecked() ? QAction::NormalPriority : QAction::LowPriority; contextButtonAction->setPriority(priority); // Find to which xml file and componentData the action belongs to QString componentName; QString filename; KXMLGUIClient *client; if (findAction(contextButtonAction->objectName(), &client)) { componentName = client->componentName(); filename = client->xmlFile(); } if (filename.isEmpty()) { componentName = QCoreApplication::applicationName(); filename = componentName + QStringLiteral("ui.rc"); } // Save the priority state of the action const QString configFile = KXMLGUIFactory::readConfigFile(filename, componentName); QDomDocument document; document.setContent(configFile); QDomElement elem = KXMLGUIFactory::actionPropertiesElement(document); QDomElement actionElem = KXMLGUIFactory::findActionByName(elem, contextButtonAction->objectName(), true); actionElem.setAttribute(QStringLiteral("priority"), priority); KXMLGUIFactory::saveConfigFile(document, filename, componentName); } void KToolBar::Private::slotContextTop() { q->mainWindow()->addToolBar(Qt::TopToolBarArea, q); } void KToolBar::Private::slotContextBottom() { q->mainWindow()->addToolBar(Qt::BottomToolBarArea, q); } void KToolBar::Private::slotContextIcons() { q->setToolButtonStyle(Qt::ToolButtonIconOnly); toolButtonStyleSettings[Level_UserSettings] = q->toolButtonStyle(); } void KToolBar::Private::slotContextText() { q->setToolButtonStyle(Qt::ToolButtonTextOnly); toolButtonStyleSettings[Level_UserSettings] = q->toolButtonStyle(); } void KToolBar::Private::slotContextTextUnder() { q->setToolButtonStyle(Qt::ToolButtonTextUnderIcon); toolButtonStyleSettings[Level_UserSettings] = q->toolButtonStyle(); } void KToolBar::Private::slotContextTextRight() { q->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); toolButtonStyleSettings[Level_UserSettings] = q->toolButtonStyle(); } void KToolBar::Private::slotContextIconSize() { QAction *action = qobject_cast<QAction *>(q->sender()); if (action && contextIconSizes.contains(action)) { const int iconSize = contextIconSizes.value(action); q->setIconDimensions(iconSize); } } void KToolBar::Private::slotLockToolBars(bool lock) { q->setToolBarsLocked(lock); } KToolBar::KToolBar(QWidget *parent, bool isMainToolBar, bool readConfig) : QToolBar(parent), d(new Private(this)) { d->init(readConfig, isMainToolBar); // KToolBar is auto-added to the top area of the main window if parent is a QMainWindow if (QMainWindow *mw = qobject_cast<QMainWindow *>(parent)) { mw->addToolBar(this); } } KToolBar::KToolBar(const QString &objectName, QWidget *parent, bool readConfig) : QToolBar(parent), d(new Private(this)) { setObjectName(objectName); // mainToolBar -> isMainToolBar = true -> buttonStyle is configurable // others -> isMainToolBar = false -> ### hardcoded default for buttonStyle !!! should be configurable? -> hidden key added d->init(readConfig, objectName == QStringLiteral("mainToolBar")); // KToolBar is auto-added to the top area of the main window if parent is a QMainWindow if (QMainWindow *mw = qobject_cast<QMainWindow *>(parent)) { mw->addToolBar(this); } } KToolBar::KToolBar(const QString &objectName, QMainWindow *parent, Qt::ToolBarArea area, bool newLine, bool isMainToolBar, bool readConfig) : QToolBar(parent), d(new Private(this)) { setObjectName(objectName); d->init(readConfig, isMainToolBar); if (newLine) { mainWindow()->addToolBarBreak(area); } mainWindow()->addToolBar(area, this); if (newLine) { mainWindow()->addToolBarBreak(area); } } KToolBar::~KToolBar() { delete d->contextLockAction; delete d; } #ifndef KXMLGUI_NO_DEPRECATED void KToolBar::setContextMenuEnabled(bool enable) { d->enableContext = enable; } #endif #ifndef KXMLGUI_NO_DEPRECATED bool KToolBar::contextMenuEnabled() const { return d->enableContext; } #endif void KToolBar::saveSettings(KConfigGroup &cg) { Q_ASSERT(!cg.name().isEmpty()); const int currentIconSize = iconSize().width(); //qDebug() << objectName() << currentIconSize << d->iconSizeSettings.toString() << "defaultValue=" << d->iconSizeSettings.defaultValue(); if (!cg.hasDefault("IconSize") && currentIconSize == d->iconSizeSettings.defaultValue()) { cg.revertToDefault("IconSize"); d->iconSizeSettings[Level_UserSettings] = Unset; } else { cg.writeEntry("IconSize", currentIconSize); d->iconSizeSettings[Level_UserSettings] = currentIconSize; } const Qt::ToolButtonStyle currentToolButtonStyle = toolButtonStyle(); if (!cg.hasDefault("ToolButtonStyle") && currentToolButtonStyle == d->toolButtonStyleSettings.defaultValue()) { cg.revertToDefault("ToolButtonStyle"); d->toolButtonStyleSettings[Level_UserSettings] = Unset; } else { cg.writeEntry("ToolButtonStyle", d->toolButtonStyleToString(currentToolButtonStyle)); d->toolButtonStyleSettings[Level_UserSettings] = currentToolButtonStyle; } } #ifndef KXMLGUI_NO_DEPRECATED void KToolBar::setXMLGUIClient(KXMLGUIClient *client) { d->xmlguiClients.clear(); d->xmlguiClients << client; } #endif void KToolBar::addXMLGUIClient(KXMLGUIClient *client) { d->xmlguiClients << client; } void KToolBar::removeXMLGUIClient(KXMLGUIClient *client) { d->xmlguiClients.remove(client); } void KToolBar::contextMenuEvent(QContextMenuEvent *event) { #ifndef KXMLGUI_NO_DEPRECATED if (mainWindow() && d->enableContext) { QPointer<KToolBar> guard(this); const QPoint globalPos = event->globalPos(); d->contextMenu(globalPos)->exec(globalPos); // "Configure Toolbars" recreates toolbars, so we might not exist anymore. if (guard) { d->slotContextAboutToHide(); } return; } #endif QToolBar::contextMenuEvent(event); } void KToolBar::loadState(const QDomElement &element) { QMainWindow *mw = mainWindow(); if (!mw) { return; } { QDomNode textNode = element.namedItem(QStringLiteral("text")); QByteArray domain; QByteArray text; QByteArray context; if (textNode.isElement()) { QDomElement textElement = textNode.toElement(); domain = textElement.attribute(QStringLiteral("translationDomain")).toUtf8(); text = textElement.text().toUtf8(); context = textElement.attribute(QStringLiteral("context")).toUtf8(); } else { textNode = element.namedItem(QStringLiteral("Text")); if (textNode.isElement()) { QDomElement textElement = textNode.toElement(); domain = textElement.attribute(QStringLiteral("translationDomain")).toUtf8(); text = textElement.text().toUtf8(); context = textElement.attribute(QStringLiteral("context")).toUtf8(); } } if (domain.isEmpty()) { domain = element.ownerDocument().documentElement().attribute(QStringLiteral("translationDomain")).toUtf8(); if (domain.isEmpty()) { domain = KLocalizedString::applicationDomain(); } } QString i18nText; if (!text.isEmpty() && !context.isEmpty()) { i18nText = i18ndc(domain.constData(), context.constData(), text.constData()); } else if (!text.isEmpty()) { i18nText = i18nd(domain.constData(), text.constData()); } if (!i18nText.isEmpty()) { setWindowTitle(i18nText); } } /* This method is called in order to load toolbar settings from XML. However this can be used in two rather different cases: - for the initial loading of the app's XML. In that case the settings are only the defaults (Level_AppXML), the user's KConfig settings will override them - for later re-loading when switching between parts in KXMLGUIFactory. In that case the XML contains the final settings, not the defaults. We do need the defaults, and the toolbar might have been completely deleted and recreated meanwhile. So we store the app-default settings into the XML. */ bool loadingAppDefaults = true; if (element.hasAttribute(QStringLiteral("tempXml"))) { // this isn't the first time, so the app-xml defaults have been saved into the (in-memory) XML loadingAppDefaults = false; const QString iconSizeDefault = element.attribute(QStringLiteral("iconSizeDefault")); if (!iconSizeDefault.isEmpty()) { d->iconSizeSettings[Level_AppXML] = iconSizeDefault.toInt(); } const QString toolButtonStyleDefault = element.attribute(QStringLiteral("toolButtonStyleDefault")); if (!toolButtonStyleDefault.isEmpty()) { d->toolButtonStyleSettings[Level_AppXML] = d->toolButtonStyleFromString(toolButtonStyleDefault); } } else { // loading app defaults bool newLine = false; QString attrNewLine = element.attribute(QStringLiteral("newline")).toLower(); if (!attrNewLine.isEmpty()) { newLine = attrNewLine == QStringLiteral("true"); } if (newLine && mw) { mw->insertToolBarBreak(this); } } int newIconSize = -1; if (element.hasAttribute(QStringLiteral("iconSize"))) { bool ok; newIconSize = element.attribute(QStringLiteral("iconSize")).trimmed().toInt(&ok); if (!ok) { newIconSize = -1; } } if (newIconSize != -1) { d->iconSizeSettings[loadingAppDefaults ? Level_AppXML : Level_UserSettings] = newIconSize; } const QString newToolButtonStyle = element.attribute(QStringLiteral("iconText")); if (!newToolButtonStyle.isEmpty()) { d->toolButtonStyleSettings[loadingAppDefaults ? Level_AppXML : Level_UserSettings] = d->toolButtonStyleFromString(newToolButtonStyle); } bool hidden = false; { QString attrHidden = element.attribute(QStringLiteral("hidden")).toLower(); if (!attrHidden.isEmpty()) { hidden = attrHidden == QStringLiteral("true"); } } Qt::ToolBarArea pos = Qt::NoToolBarArea; { QString attrPosition = element.attribute(QStringLiteral("position")).toLower(); if (!attrPosition.isEmpty()) { pos = KToolBar::Private::positionFromString(attrPosition); } } if (pos != Qt::NoToolBarArea) { mw->addToolBar(pos, this); } setVisible(!hidden); d->applyCurrentSettings(); } // Called when switching between xmlgui clients, in order to find any unsaved settings // again when switching back to the current xmlgui client. void KToolBar::saveState(QDomElement ¤t) const { Q_ASSERT(!current.isNull()); current.setAttribute(QStringLiteral("tempXml"), QStringLiteral("true")); current.setAttribute(QStringLiteral("noMerge"), QStringLiteral("1")); current.setAttribute(QStringLiteral("position"), d->getPositionAsString().toLower()); current.setAttribute(QStringLiteral("hidden"), isHidden() ? QStringLiteral("true") : QStringLiteral("false")); const int currentIconSize = iconSize().width(); if (currentIconSize == d->iconSizeSettings.defaultValue()) { current.removeAttribute(QStringLiteral("iconSize")); } else { current.setAttribute(QStringLiteral("iconSize"), iconSize().width()); } if (toolButtonStyle() == d->toolButtonStyleSettings.defaultValue()) { current.removeAttribute(QStringLiteral("iconText")); } else { current.setAttribute(QStringLiteral("iconText"), d->toolButtonStyleToString(toolButtonStyle())); } // Note: if this method is used by more than KXMLGUIBuilder, e.g. to save XML settings to *disk*, // then the stuff below shouldn't always be done. This is not the case currently though. if (d->iconSizeSettings[Level_AppXML] != Unset) { current.setAttribute(QStringLiteral("iconSizeDefault"), d->iconSizeSettings[Level_AppXML]); } if (d->toolButtonStyleSettings[Level_AppXML] != Unset) { const Qt::ToolButtonStyle bs = static_cast<Qt::ToolButtonStyle>(d->toolButtonStyleSettings[Level_AppXML]); current.setAttribute(QStringLiteral("toolButtonStyleDefault"), d->toolButtonStyleToString(bs)); } } // called by KMainWindow::applyMainWindowSettings to read from the user settings void KToolBar::applySettings(const KConfigGroup &cg) { Q_ASSERT(!cg.name().isEmpty()); if (cg.hasKey("IconSize")) { d->iconSizeSettings[Level_UserSettings] = cg.readEntry("IconSize", 0); } if (cg.hasKey("ToolButtonStyle")) { d->toolButtonStyleSettings[Level_UserSettings] = d->toolButtonStyleFromString(cg.readEntry("ToolButtonStyle", QString())); } d->applyCurrentSettings(); } KMainWindow *KToolBar::mainWindow() const { return qobject_cast<KMainWindow *>(const_cast<QObject *>(parent())); } void KToolBar::setIconDimensions(int size) { QToolBar::setIconSize(QSize(size, size)); d->iconSizeSettings[Level_UserSettings] = size; } int KToolBar::iconSizeDefault() const { return KIconLoader::global()->currentSize(d->isMainToolBar ? KIconLoader::MainToolbar : KIconLoader::Toolbar); } void KToolBar::slotMovableChanged(bool movable) { if (movable && !KAuthorized::authorize(QStringLiteral("movable_toolbars"))) { setMovable(false); } } void KToolBar::dragEnterEvent(QDragEnterEvent *event) { if (toolBarsEditable() && event->proposedAction() & (Qt::CopyAction | Qt::MoveAction) && event->mimeData()->hasFormat(QStringLiteral("application/x-kde-action-list"))) { QByteArray data = event->mimeData()->data(QStringLiteral("application/x-kde-action-list")); QDataStream stream(data); QStringList actionNames; stream >> actionNames; Q_FOREACH (const QString &actionName, actionNames) { Q_FOREACH (KActionCollection *ac, KActionCollection::allCollections()) { QAction *newAction = ac->action(actionName); if (newAction) { d->actionsBeingDragged.append(newAction); break; } } } if (!d->actionsBeingDragged.isEmpty()) { QAction *overAction = actionAt(event->pos()); QFrame *dropIndicatorWidget = new QFrame(this); dropIndicatorWidget->resize(8, height() - 4); dropIndicatorWidget->setFrameShape(QFrame::VLine); dropIndicatorWidget->setLineWidth(3); d->dropIndicatorAction = insertWidget(overAction, dropIndicatorWidget); insertAction(overAction, d->dropIndicatorAction); event->acceptProposedAction(); return; } } QToolBar::dragEnterEvent(event); } void KToolBar::dragMoveEvent(QDragMoveEvent *event) { if (toolBarsEditable()) Q_FOREVER { if (d->dropIndicatorAction) { - QAction *overAction = 0L; + QAction *overAction = nullptr; Q_FOREACH (QAction *action, actions()) { // want to make it feel that half way across an action you're dropping on the other side of it QWidget *widget = widgetForAction(action); if (event->pos().x() < widget->pos().x() + (widget->width() / 2)) { overAction = action; break; } } if (overAction != d->dropIndicatorAction) { // Check to see if the indicator is already in the right spot int dropIndicatorIndex = actions().indexOf(d->dropIndicatorAction); if (dropIndicatorIndex + 1 < actions().count()) { if (actions().at(dropIndicatorIndex + 1) == overAction) { break; } } else if (!overAction) { break; } insertAction(overAction, d->dropIndicatorAction); } event->accept(); return; } break; } QToolBar::dragMoveEvent(event); } void KToolBar::dragLeaveEvent(QDragLeaveEvent *event) { // Want to clear this even if toolBarsEditable was changed mid-drag (unlikey) delete d->dropIndicatorAction; - d->dropIndicatorAction = 0L; + d->dropIndicatorAction = nullptr; d->actionsBeingDragged.clear(); if (toolBarsEditable()) { event->accept(); return; } QToolBar::dragLeaveEvent(event); } void KToolBar::dropEvent(QDropEvent *event) { if (toolBarsEditable()) { Q_FOREACH (QAction *action, d->actionsBeingDragged) { if (actions().contains(action)) { removeAction(action); } insertAction(d->dropIndicatorAction, action); } } // Want to clear this even if toolBarsEditable was changed mid-drag (unlikey) delete d->dropIndicatorAction; - d->dropIndicatorAction = 0L; + d->dropIndicatorAction = nullptr; d->actionsBeingDragged.clear(); if (toolBarsEditable()) { event->accept(); return; } QToolBar::dropEvent(event); } void KToolBar::mousePressEvent(QMouseEvent *event) { if (toolBarsEditable() && event->button() == Qt::LeftButton) { if (QAction *action = actionAt(event->pos())) { d->dragAction = action; d->dragStartPosition = event->pos(); event->accept(); return; } } QToolBar::mousePressEvent(event); } void KToolBar::mouseMoveEvent(QMouseEvent *event) { if (!toolBarsEditable() || !d->dragAction) { return QToolBar::mouseMoveEvent(event); } if ((event->pos() - d->dragStartPosition).manhattanLength() < QApplication::startDragDistance()) { event->accept(); return; } QDrag *drag = new QDrag(this); QMimeData *mimeData = new QMimeData; QByteArray data; { QDataStream stream(&data, QIODevice::WriteOnly); QStringList actionNames; actionNames << d->dragAction->objectName(); stream << actionNames; } mimeData->setData(QStringLiteral("application/x-kde-action-list"), data); drag->setMimeData(mimeData); Qt::DropAction dropAction = drag->start(Qt::MoveAction); if (dropAction == Qt::MoveAction) // Only remove from this toolbar if it was moved to another toolbar // Otherwise the receiver moves it. if (drag->target() != this) { removeAction(d->dragAction); } - d->dragAction = 0L; + d->dragAction = nullptr; event->accept(); } void KToolBar::mouseReleaseEvent(QMouseEvent *event) { // Want to clear this even if toolBarsEditable was changed mid-drag (unlikey) if (d->dragAction) { - d->dragAction = 0L; + d->dragAction = nullptr; event->accept(); return; } QToolBar::mouseReleaseEvent(event); } bool KToolBar::eventFilter(QObject *watched, QEvent *event) { // Generate context menu events for disabled buttons too... if (event->type() == QEvent::MouseButtonPress) { QMouseEvent *me = static_cast<QMouseEvent *>(event); if (me->buttons() & Qt::RightButton) if (QWidget *ww = qobject_cast<QWidget *>(watched)) if (ww->parent() == this && !ww->isEnabled()) { QCoreApplication::postEvent(this, new QContextMenuEvent(QContextMenuEvent::Mouse, me->pos(), me->globalPos())); } } else if (event->type() == QEvent::ParentChange) { // Make sure we're not leaving stale event filters around, // when a child is reparented somewhere else if (QWidget *ww = qobject_cast<QWidget *>(watched)) { if (!this->isAncestorOf(ww)) { // New parent is not a subwidget - remove event filter ww->removeEventFilter(this); Q_FOREACH (QWidget *child, ww->findChildren<QWidget *>()) { child->removeEventFilter(this); } } } } QToolButton *tb; if ((tb = qobject_cast<QToolButton *>(watched))) { const QList<QAction *> tbActions = tb->actions(); if (!tbActions.isEmpty()) { // Handle MMB on toolbar buttons if (event->type() == QEvent::MouseButtonPress || event->type() == QEvent::MouseButtonRelease) { QMouseEvent *me = static_cast<QMouseEvent *>(event); if (me->button() == Qt::MidButton /*&& act->receivers(SIGNAL(triggered(Qt::MouseButtons,Qt::KeyboardModifiers)))*/) { QAction *act = tbActions.first(); if (me->type() == QEvent::MouseButtonPress) { tb->setDown(act->isEnabled()); } else { tb->setDown(false); if (act->isEnabled()) { QMetaObject::invokeMethod(act, "triggered", Qt::DirectConnection, Q_ARG(Qt::MouseButtons, me->button()), Q_ARG(Qt::KeyboardModifiers, QApplication::keyboardModifiers())); } } } } // CJK languages use more verbose accelerator marker: they add a Latin // letter in parenthesis, and put accelerator on that. Hence, the default // removal of ampersand only may not be enough there, instead the whole // parenthesis construct should be removed. Use KLocalizedString's method to do this. if (event->type() == QEvent::Show || event->type() == QEvent::Paint || event->type() == QEvent::EnabledChange) { QAction *act = tb->defaultAction(); if (act) { const QString text = KLocalizedString::removeAcceleratorMarker(act->iconText().isEmpty() ? act->text() : act->iconText()); const QString toolTip = KLocalizedString::removeAcceleratorMarker(act->toolTip()); // Filtering messages requested by translators (scripting). tb->setText(i18nc("@action:intoolbar Text label of toolbar button", "%1", text)); tb->setToolTip(i18nc("@info:tooltip Tooltip of toolbar button", "%1", toolTip)); } } } } // Redirect mouse events to the toolbar when drag + drop editing is enabled if (toolBarsEditable()) { if (QWidget *ww = qobject_cast<QWidget *>(watched)) { switch (event->type()) { case QEvent::MouseButtonPress: { QMouseEvent *me = static_cast<QMouseEvent *>(event); QMouseEvent newEvent(me->type(), mapFromGlobal(ww->mapToGlobal(me->pos())), me->globalPos(), me->button(), me->buttons(), me->modifiers()); mousePressEvent(&newEvent); return true; } case QEvent::MouseMove: { QMouseEvent *me = static_cast<QMouseEvent *>(event); QMouseEvent newEvent(me->type(), mapFromGlobal(ww->mapToGlobal(me->pos())), me->globalPos(), me->button(), me->buttons(), me->modifiers()); mouseMoveEvent(&newEvent); return true; } case QEvent::MouseButtonRelease: { QMouseEvent *me = static_cast<QMouseEvent *>(event); QMouseEvent newEvent(me->type(), mapFromGlobal(ww->mapToGlobal(me->pos())), me->globalPos(), me->button(), me->buttons(), me->modifiers()); mouseReleaseEvent(&newEvent); return true; } default: break; } } } return QToolBar::eventFilter(watched, event); } void KToolBar::actionEvent(QActionEvent *event) { if (event->type() == QEvent::ActionRemoved) { QWidget *widget = widgetForAction(event->action()); if (widget) { widget->removeEventFilter(this); Q_FOREACH (QWidget *child, widget->findChildren<QWidget *>()) { child->removeEventFilter(this); } } } QToolBar::actionEvent(event); if (event->type() == QEvent::ActionAdded) { QWidget *widget = widgetForAction(event->action()); if (widget) { widget->installEventFilter(this); Q_FOREACH (QWidget *child, widget->findChildren<QWidget *>()) { child->installEventFilter(this); } // Center widgets that do not have any use for more space. See bug 165274 if (!(widget->sizePolicy().horizontalPolicy() & QSizePolicy::GrowFlag) // ... but do not center when using text besides icon in vertical toolbar. See bug 243196 && !(orientation() == Qt::Vertical && toolButtonStyle() == Qt::ToolButtonTextBesideIcon)) { const int index = layout()->indexOf(widget); if (index != -1) { layout()->itemAt(index)->setAlignment(Qt::AlignJustify); } } } } d->adjustSeparatorVisibility(); } bool KToolBar::toolBarsEditable() { return KToolBar::Private::s_editable; } void KToolBar::setToolBarsEditable(bool editable) { if (KToolBar::Private::s_editable != editable) { KToolBar::Private::s_editable = editable; } } void KToolBar::setToolBarsLocked(bool locked) { if (KToolBar::Private::s_locked != locked) { KToolBar::Private::s_locked = locked; Q_FOREACH (KMainWindow *mw, KMainWindow::memberList()) { Q_FOREACH (KToolBar *toolbar, mw->findChildren<KToolBar *>()) { toolbar->d->setLocked(locked); } } } } bool KToolBar::toolBarsLocked() { return KToolBar::Private::s_locked; } void KToolBar::emitToolbarStyleChanged() { QDBusMessage message = QDBusMessage::createSignal(QStringLiteral("/KToolBar"), QStringLiteral("org.kde.KToolBar"), QStringLiteral("styleChanged")); QDBusConnection::sessionBus().send(message); } #include "moc_ktoolbar.cpp" diff --git a/src/kxmlguibuilder.cpp b/src/kxmlguibuilder.cpp index 01f9f03..d5d7447 100644 --- a/src/kxmlguibuilder.cpp +++ b/src/kxmlguibuilder.cpp @@ -1,412 +1,412 @@ /* This file is part of the KDE project Copyright (C) 2000 Simon Hausmann <hausmann@kde.org> David Faure <faure@kde.org> 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 "kxmlguibuilder.h" #include "kxmlguiclient.h" #include "ktoolbar.h" #include "kmainwindow.h" #include "kxmlguiwindow.h" #include "kmenumenuhandler_p.h" #include <kauthorized.h> #include <klocalizedstring.h> #include <QtXml/QDomElement> #include <QtCore/QObject> #include <QtCore/QMutableStringListIterator> #include <QAction> #include <QMenu> #include <QMenuBar> #include <QStatusBar> #include <QDebug> using namespace KDEPrivate; class KXMLGUIBuilderPrivate { public: - KXMLGUIBuilderPrivate() : m_client(0L) {} + KXMLGUIBuilderPrivate() : m_client(nullptr) {} ~KXMLGUIBuilderPrivate() { } QWidget *m_widget; QString tagMainWindow; QString tagMenuBar; QString tagMenu; QString tagToolBar; QString tagStatusBar; QString tagSeparator; QString tagTearOffHandle; QString tagMenuTitle; QString attrName; QString attrLineSeparator; QString attrDomain; QString attrText1; QString attrText2; QString attrContext; QString attrIcon; KXMLGUIClient *m_client; KMenuMenuHandler *m_menumenuhandler; }; KXMLGUIBuilder::KXMLGUIBuilder(QWidget *widget) : d(new KXMLGUIBuilderPrivate) { d->m_widget = widget; d->tagMainWindow = QStringLiteral("mainwindow"); d->tagMenuBar = QStringLiteral("menubar"); d->tagMenu = QStringLiteral("menu"); d->tagToolBar = QStringLiteral("toolbar"); d->tagStatusBar = QStringLiteral("statusbar"); d->tagSeparator = QStringLiteral("separator"); d->tagTearOffHandle = QStringLiteral("tearoffhandle"); d->tagMenuTitle = QStringLiteral("title"); d->attrName = QStringLiteral("name"); d->attrLineSeparator = QStringLiteral("lineseparator"); d->attrDomain = QStringLiteral("translationDomain"); d->attrText1 = QStringLiteral("text"); d->attrText2 = QStringLiteral("Text"); d->attrContext = QStringLiteral("context"); d->attrIcon = QStringLiteral("icon"); d->m_menumenuhandler = new KMenuMenuHandler(this); } KXMLGUIBuilder::~KXMLGUIBuilder() { delete d->m_menumenuhandler; delete d; } QWidget *KXMLGUIBuilder::widget() { return d->m_widget; } QStringList KXMLGUIBuilder::containerTags() const { QStringList res; res << d->tagMenu << d->tagToolBar << d->tagMainWindow << d->tagMenuBar << d->tagStatusBar; return res; } QWidget *KXMLGUIBuilder::createContainer(QWidget *parent, int index, const QDomElement &element, QAction *&containerAction) { - containerAction = 0; + containerAction = nullptr; if (element.attribute(QStringLiteral("deleted")).toLower() == QLatin1String("true")) { - return 0; + return nullptr; } const QString tagName = element.tagName().toLower(); if (tagName == d->tagMainWindow) { KMainWindow *mainwindow = qobject_cast<KMainWindow *>(d->m_widget); // could be 0 return mainwindow; } if (tagName == d->tagMenuBar) { KMainWindow *mainWin = qobject_cast<KMainWindow *>(d->m_widget); - QMenuBar *bar = 0; + QMenuBar *bar = nullptr; if (mainWin) { bar = mainWin->menuBar(); } if (!bar) { bar = new QMenuBar(d->m_widget); } bar->show(); return bar; } if (tagName == d->tagMenu) { // Look up to see if we are inside a mainwindow. If yes, then // use it as parent widget (to get kaction to plug itself into the // mainwindow). Don't use a popupmenu as parent widget, otherwise // the popup won't be hidden if it is used as a standalone menu as well. // And we don't want to set the parent for a standalone popupmenu, // otherwise its shortcuts appear. // // Note: menus with a parent of 0, coming from child clients, can be // leaked if the child client is deleted without a proper removeClient call, though. QWidget *p = parent; while (p && !qobject_cast<QMainWindow *>(p)) { p = p->parentWidget(); } QString name = element.attribute(d->attrName); if (!KAuthorized::authorizeAction(name)) { - return 0; + return nullptr; } QMenu *popup = new QMenu(p); popup->setObjectName(name); d->m_menumenuhandler->insertMenu(popup); QString i18nText; QDomElement textElem = element.namedItem(d->attrText1).toElement(); if (textElem.isNull()) { // try with capital T textElem = element.namedItem(d->attrText2).toElement(); } const QString text = textElem.text(); const QString context = textElem.attribute(d->attrContext); //qCDebug(DEBUG_KXMLGUI) << "DOMAIN" << KLocalizedString::applicationDomain(); //qCDebug(DEBUG_KXMLGUI) << "ELEMENT TEXT:" << text; if (text.isEmpty()) { // still no luck i18nText = i18n("No text"); } else { QByteArray domain = textElem.attribute(d->attrDomain).toUtf8(); if (domain.isEmpty()) { domain = element.ownerDocument().documentElement().attribute(d->attrDomain).toUtf8(); if (domain.isEmpty()) { domain = KLocalizedString::applicationDomain(); } } if (context.isEmpty()) { i18nText = i18nd(domain.constData(), text.toUtf8().constData()); } else { i18nText = i18ndc(domain.constData(), context.toUtf8().constData(), text.toUtf8().constData()); } } //qCDebug(DEBUG_KXMLGUI) << "ELEMENT i18n TEXT:" << i18nText; const QString icon = element.attribute(d->attrIcon); QIcon pix; if (!icon.isEmpty()) { pix = QIcon::fromTheme(icon); } if (parent) { QAction *act = popup->menuAction(); if (!icon.isEmpty()) { act->setIcon(pix); } act->setText(i18nText); if (index == -1 || index >= parent->actions().count()) { parent->addAction(act); } else { parent->insertAction(parent->actions().value(index), act); } containerAction = act; containerAction->setObjectName(name); } return popup; } if (tagName == d->tagToolBar) { QString name = element.attribute(d->attrName); KToolBar *bar = static_cast<KToolBar *>(d->m_widget->findChild<KToolBar *>(name)); if (!bar) { bar = new KToolBar(name, d->m_widget, false); } if (qobject_cast<KMainWindow *>(d->m_widget)) { if (d->m_client && !d->m_client->xmlFile().isEmpty()) { bar->addXMLGUIClient(d->m_client); } } bar->loadState(element); return bar; } if (tagName == d->tagStatusBar) { KMainWindow *mainWin = qobject_cast<KMainWindow *>(d->m_widget); if (mainWin) { mainWin->statusBar()->show(); return mainWin->statusBar(); } QStatusBar *bar = new QStatusBar(d->m_widget); return bar; } - return 0L; + return nullptr; } void KXMLGUIBuilder::removeContainer(QWidget *container, QWidget *parent, QDomElement &element, QAction *containerAction) { // Warning parent can be 0L if (qobject_cast<QMenu *>(container)) { if (parent) { parent->removeAction(containerAction); } delete container; } else if (qobject_cast<KToolBar *>(container)) { KToolBar *tb = static_cast<KToolBar *>(container); tb->saveState(element); delete tb; } else if (qobject_cast<QMenuBar *>(container)) { QMenuBar *mb = static_cast<QMenuBar *>(container); mb->hide(); // Don't delete menubar - it can be reused by createContainer. // If you decide that you do need to delete the menubar, make // sure that QMainWindow::d->mb does not point to a deleted // menubar object. } else if (qobject_cast<QStatusBar *>(container)) { if (qobject_cast<KMainWindow *>(d->m_widget)) { container->hide(); } else { delete static_cast<QStatusBar *>(container); } } else { qWarning() << "Unhandled container to remove : " << container->metaObject()->className(); } } QStringList KXMLGUIBuilder::customTags() const { QStringList res; res << d->tagSeparator << d->tagTearOffHandle << d->tagMenuTitle; return res; } QAction *KXMLGUIBuilder::createCustomElement(QWidget *parent, int index, const QDomElement &element) { - QAction *before = 0L; + QAction *before = nullptr; if (index > 0 && index < parent->actions().count()) { before = parent->actions().at(index); } const QString tagName = element.tagName().toLower(); if (tagName == d->tagSeparator) { if (QMenu *menu = qobject_cast<QMenu *>(parent)) { // QMenu already cares for leading/trailing/repeated separators // no need to check anything return menu->insertSeparator(before); } else if (QMenuBar *bar = qobject_cast<QMenuBar *>(parent)) { QAction *separatorAction = new QAction(bar); separatorAction->setSeparator(true); bar->insertAction(before, separatorAction); return separatorAction; } else if (KToolBar *bar = qobject_cast<KToolBar *>(parent)) { /* FIXME KAction port - any need to provide a replacement for lineSeparator/normal separator? bool isLineSep = true; QDomNamedNodeMap attributes = element.attributes(); unsigned int i = 0; for (; i < attributes.length(); i++ ) { QDomAttr attr = attributes.item( i ).toAttr(); if ( attr.name().toLower() == d->attrLineSeparator && attr.value().toLower() == QStringLiteral("false") ) { isLineSep = false; break; } } if ( isLineSep ) return bar->insertSeparator( index ? bar->actions()[index - 1] : 0L ); else*/ return bar->insertSeparator(before); } } else if (tagName == d->tagTearOffHandle) { static_cast<QMenu *>(parent)->setTearOffEnabled(true); } else if (tagName == d->tagMenuTitle) { if (QMenu *m = qobject_cast<QMenu *>(parent)) { QString i18nText; const QString text = element.text(); if (text.isEmpty()) { i18nText = i18n("No text"); } else { QByteArray domain = element.attribute(d->attrDomain).toUtf8(); if (domain.isEmpty()) { domain = element.ownerDocument().documentElement().attribute(d->attrDomain).toUtf8(); if (domain.isEmpty()) { domain = KLocalizedString::applicationDomain(); } } i18nText = i18nd(domain.constData(), qPrintable(text)); } QString icon = element.attribute(d->attrIcon); QIcon pix; if (!icon.isEmpty()) { pix = QIcon::fromTheme(icon); } if (!icon.isEmpty()) { return m->insertSection(before, pix, i18nText); } else { return m->insertSection(before, i18nText); } } } QAction *blank = new QAction(parent); blank->setVisible(false); parent->insertAction(before, blank); return blank; } void KXMLGUIBuilder::removeCustomElement(QWidget *parent, QAction *action) { parent->removeAction(action); } KXMLGUIClient *KXMLGUIBuilder::builderClient() const { return d->m_client; } void KXMLGUIBuilder::setBuilderClient(KXMLGUIClient *client) { d->m_client = client; } void KXMLGUIBuilder::finalizeGUI(KXMLGUIClient *) { KXmlGuiWindow *window = qobject_cast<KXmlGuiWindow *>(d->m_widget); if (window) { window->finalizeGUI(false); } } void KXMLGUIBuilder::virtual_hook(int, void *) { /*BASE::virtual_hook( id, data );*/ } diff --git a/src/kxmlguiclient.cpp b/src/kxmlguiclient.cpp index e5c2bb6..0a011b8 100644 --- a/src/kxmlguiclient.cpp +++ b/src/kxmlguiclient.cpp @@ -1,813 +1,813 @@ /* This file is part of the KDE libraries Copyright (C) 2000 Simon Hausmann <hausmann@kde.org> Copyright (C) 2000 Kurt Granroth <granroth@kde.org> 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 "kxmlguiclient.h" #include "kxmlguiversionhandler_p.h" #include "kxmlguifactory.h" #include "kxmlguibuilder.h" #include "kactioncollection.h" #include <QAction> #include <QDir> #include <QFile> #include <QDomDocument> #include <QTextStream> #include <QRegExp> #include <QPointer> #include <QCoreApplication> #include <QStandardPaths> #include <QDebug> #include <kauthorized.h> #include <klocalizedstring.h> #include <assert.h> class KXMLGUIClientPrivate { public: KXMLGUIClientPrivate() : m_componentName(QCoreApplication::applicationName()), - m_actionCollection(0), - m_parent(0L), - m_builder(0L) + m_actionCollection(nullptr), + m_parent(nullptr), + m_builder(nullptr) { m_textTagNames.append(QStringLiteral("text")); m_textTagNames.append(QStringLiteral("Text")); m_textTagNames.append(QStringLiteral("title")); } ~KXMLGUIClientPrivate() { } bool mergeXML(QDomElement &base, QDomElement &additive, KActionCollection *actionCollection); bool isEmptyContainer(const QDomElement &base, KActionCollection *actionCollection) const; QDomElement findMatchingElement(const QDomElement &base, const QDomElement &additive); QString m_componentName; QDomDocument m_doc; KActionCollection *m_actionCollection; QDomDocument m_buildDocument; QPointer<KXMLGUIFactory> m_factory; KXMLGUIClient *m_parent; //QPtrList<KXMLGUIClient> m_supers; QList<KXMLGUIClient *> m_children; KXMLGUIBuilder *m_builder; QString m_xmlFile; QString m_localXMLFile; QStringList m_textTagNames; // Actions to enable/disable on a state change QMap<QString, KXMLGUIClient::StateChange> m_actionsStateMap; }; KXMLGUIClient::KXMLGUIClient() : d(new KXMLGUIClientPrivate) { } KXMLGUIClient::KXMLGUIClient(KXMLGUIClient *parent) : d(new KXMLGUIClientPrivate) { parent->insertChildClient(this); } KXMLGUIClient::~KXMLGUIClient() { if (d->m_parent) { d->m_parent->removeChildClient(this); } if (d->m_factory) { qWarning() << this << "deleted without having been removed from the factory first. This will leak standalone popupmenus and could lead to crashes."; d->m_factory->forgetClient(this); } Q_FOREACH (KXMLGUIClient *client, d->m_children) { if (d->m_factory) { d->m_factory->forgetClient(client); } assert(client->d->m_parent == this); - client->d->m_parent = 0; + client->d->m_parent = nullptr; } delete d->m_actionCollection; delete d; } QAction *KXMLGUIClient::action(const char *name) const { QAction *act = actionCollection()->action(QLatin1String(name)); if (!act) { Q_FOREACH (KXMLGUIClient *client, d->m_children) { act = client->actionCollection()->action(QLatin1String(name)); if (act) { break; } } } return act; } KActionCollection *KXMLGUIClient::actionCollection() const { if (!d->m_actionCollection) { d->m_actionCollection = new KActionCollection(this); d->m_actionCollection->setObjectName(QStringLiteral("KXMLGUIClient-KActionCollection")); } return d->m_actionCollection; } QAction *KXMLGUIClient::action(const QDomElement &element) const { return actionCollection()->action(element.attribute(QStringLiteral("name"))); } QString KXMLGUIClient::componentName() const { return d->m_componentName; } QDomDocument KXMLGUIClient::domDocument() const { return d->m_doc; } QString KXMLGUIClient::xmlFile() const { return d->m_xmlFile; } QString KXMLGUIClient::localXMLFile() const { if (!d->m_localXMLFile.isEmpty()) { return d->m_localXMLFile; } if (!QDir::isRelativePath(d->m_xmlFile)) { return QString(); // can't save anything here } if (d->m_xmlFile.isEmpty()) { // setXMLFile not called at all, can't save. Use case: ToolBarHandler return QString(); } return QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QStringLiteral("/kxmlgui5/") + componentName() + QLatin1Char('/') + d->m_xmlFile; } void KXMLGUIClient::reloadXML() { // TODO: this method can't be used for the KXmlGuiWindow, since it doesn't merge in ui_standards.rc! // -> KDE5: load ui_standards_rc in setXMLFile using a flag, and remember that flag? // and then KEditToolBar can use reloadXML. QString file(xmlFile()); if (!file.isEmpty()) { setXMLFile(file); } } void KXMLGUIClient::setComponentName(const QString &componentName, const QString &componentDisplayName) { d->m_componentName = componentName; actionCollection()->setComponentName(componentName); actionCollection()->setComponentDisplayName(componentDisplayName); if (d->m_builder) { d->m_builder->setBuilderClient(this); } } QString KXMLGUIClient::standardsXmlFileLocation() { QString file = QStandardPaths::locate(QStandardPaths::GenericConfigLocation, QStringLiteral("ui/ui_standards.rc")); if (file.isEmpty()) { // fallback to resource, to allow to use the rc file compiled into this framework, must exist! file = QStringLiteral(":/kxmlgui5/ui_standards.rc"); Q_ASSERT(QFile::exists(file)); } return file; } void KXMLGUIClient::loadStandardsXmlFile() { setXML(KXMLGUIFactory::readConfigFile(standardsXmlFileLocation())); } void KXMLGUIClient::setXMLFile(const QString &_file, bool merge, bool setXMLDoc) { // store our xml file name if (!_file.isNull()) { d->m_xmlFile = _file; } if (!setXMLDoc) { return; } QString file = _file; QStringList allFiles; if (!QDir::isRelativePath(file)) { allFiles.append(file); } else { const QString filter = componentName() + QLatin1Char('/') + _file; // files on filesystem allFiles << QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QStringLiteral("kxmlgui5/") + filter); // KF >= 5.1 // KF >= 5.4 (resource file) const QString qrcFile(QStringLiteral(":/kxmlgui5/") + filter); if (QFile::exists(qrcFile)) { allFiles << qrcFile; } // then compat locations const QStringList compatFiles = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, filter) + // kdelibs4, KF 5.0 QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, _file); // kdelibs4, KF 5.0, caller passes component name if (allFiles.isEmpty() && !compatFiles.isEmpty()) { qWarning() << "KXMLGUI file found at deprecated location" << compatFiles << "-- please use ${KXMLGUI_INSTALL_DIR} to install this file instead."; } allFiles += compatFiles; } if (allFiles.isEmpty() && !_file.isEmpty()) { // if a non-empty file gets passed and we can't find it, // inform the developer using some debug output qWarning() << "cannot find .rc file" << _file << "for component" << componentName(); } // make sure to merge the settings from any file specified by setLocalXMLFile() if (!d->m_localXMLFile.isEmpty() && !file.endsWith(QStringLiteral("ui_standards.rc"))) { const bool exists = QDir::isRelativePath(d->m_localXMLFile) || QFile::exists(d->m_localXMLFile); if (exists && !allFiles.contains(d->m_localXMLFile)) { allFiles.prepend(d->m_localXMLFile); } } QString doc; if (!allFiles.isEmpty()) { file = findMostRecentXMLFile(allFiles, doc); } // Always call setXML, even on error, so that we don't keep all ui_standards.rc menus. setXML(doc, merge); } void KXMLGUIClient::setLocalXMLFile(const QString &file) { d->m_localXMLFile = file; } void KXMLGUIClient::replaceXMLFile(const QString &xmlfile, const QString &localxmlfile, bool merge) { if (!QDir::isAbsolutePath(xmlfile)) { qWarning() << "xml file" << xmlfile << "is not an absolute path"; } setLocalXMLFile(localxmlfile); setXMLFile(xmlfile, merge); } // The top document element may have translation domain attribute set, // or the translation domain may be implicitly the application domain. // This domain must be used to fetch translations for all text elements // in the document that do not have their own domain attribute. // In order to preserve this semantics through document mergings, // the top or application domain must be propagated to all text elements // lacking their own domain attribute. static void propagateTranslationDomain(QDomDocument &doc, const QStringList tagNames) { const QLatin1String attrDomain("translationDomain"); QDomElement base = doc.documentElement(); QString domain = base.attribute(attrDomain); if (domain.isEmpty()) { domain = QString::fromUtf8(KLocalizedString::applicationDomain()); if (domain.isEmpty()) { return; } } foreach (const QString &tagName, tagNames) { QDomNodeList textNodes = base.elementsByTagName(tagName); for (int i = 0; i < textNodes.length(); ++i) { QDomElement e = textNodes.item(i).toElement(); QString localDomain = e.attribute(attrDomain); if (localDomain.isEmpty()) { e.setAttribute(attrDomain, domain); } } } } void KXMLGUIClient::setXML(const QString &document, bool merge) { QDomDocument doc; QString errorMsg; int errorLine = 0, errorColumn = 0; // QDomDocument raises a parse error on empty document, but we accept no app-specific document, // in which case you only get ui_standards.rc layout. bool result = document.isEmpty() || doc.setContent(document, &errorMsg, &errorLine, &errorColumn); if (result) { propagateTranslationDomain(doc, d->m_textTagNames); setDOMDocument(doc, merge); } else { #ifdef NDEBUG qCritical() << "Error parsing XML document:" << errorMsg << "at line" << errorLine << "column" << errorColumn; setDOMDocument(QDomDocument(), merge); // otherwise empty menus from ui_standards.rc stay around #else qCritical() << "Error parsing XML document:" << errorMsg << "at line" << errorLine << "column" << errorColumn; abort(); #endif } } void KXMLGUIClient::setDOMDocument(const QDomDocument &document, bool merge) { if (merge && !d->m_doc.isNull()) { QDomElement base = d->m_doc.documentElement(); QDomElement e = document.documentElement(); // merge our original (global) xml with our new one d->mergeXML(base, e, actionCollection()); // reassign our pointer as mergeXML might have done something // strange to it base = d->m_doc.documentElement(); //qCDebug(DEBUG_KXMLGUI) << "Result of xmlgui merging:" << d->m_doc.toString(); // we want some sort of failsafe.. just in case if (base.isNull()) { d->m_doc = document; } } else { d->m_doc = document; } setXMLGUIBuildDocument(QDomDocument()); } // if (equals(a,b)) is more readable than if (a.compare(b, Qt::CaseInsensitive)==0) static inline bool equalstr(const QString &a, const QString &b) { return a.compare(b, Qt::CaseInsensitive) == 0; } static inline bool equalstr(const QString &a, const QLatin1String &b) { return a.compare(b, Qt::CaseInsensitive) == 0; } bool KXMLGUIClientPrivate::mergeXML(QDomElement &base, QDomElement &additive, KActionCollection *actionCollection) { const QLatin1String tagAction("Action"); const QLatin1String tagMerge("Merge"); const QLatin1String tagSeparator("Separator"); const QLatin1String tagMergeLocal("MergeLocal"); const QLatin1String tagText("text"); const QLatin1String attrAppend("append"); const QString attrName(QStringLiteral("name")); const QString attrWeakSeparator(QStringLiteral("weakSeparator")); const QString attrAlreadyVisited(QStringLiteral("alreadyVisited")); const QString attrNoMerge(QStringLiteral("noMerge")); const QLatin1String attrOne("1"); // there is a possibility that we don't want to merge in the // additive.. rather, we might want to *replace* the base with the // additive. this can be for any container.. either at a file wide // level or a simple container level. we look for the 'noMerge' // tag, in any event and just replace the old with the new if (additive.attribute(attrNoMerge) == attrOne) { // ### use toInt() instead? (Simon) base.parentNode().replaceChild(additive, base); return true; } else { // Merge attributes { const QDomNamedNodeMap attribs = additive.attributes(); const uint attribcount = attribs.count(); for (uint i = 0; i < attribcount; ++i) { const QDomNode node = attribs.item(i); base.setAttribute(node.nodeName(), node.nodeValue()); } } // iterate over all elements in the container (of the global DOM tree) QDomNode n = base.firstChild(); while (!n.isNull()) { QDomElement e = n.toElement(); n = n.nextSibling(); // Advance now so that we can safely delete e if (e.isNull()) { continue; } const QString tag = e.tagName(); // if there's an action tag in the global tree and the action is // not implemented, then we remove the element if (equalstr(tag, tagAction)) { const QString name = e.attribute(attrName); if (!actionCollection->action(name) || !KAuthorized::authorizeAction(name)) { // remove this child as we aren't using it base.removeChild(e); continue; } } // if there's a separator defined in the global tree, then add an // attribute, specifying that this is a "weak" separator else if (equalstr(tag, tagSeparator)) { e.setAttribute(attrWeakSeparator, uint(1)); // okay, hack time. if the last item was a weak separator OR // this is the first item in a container, then we nuke the // current one QDomElement prev = e.previousSibling().toElement(); if (prev.isNull() || (equalstr(prev.tagName(), tagSeparator) && !prev.attribute(attrWeakSeparator).isNull()) || (equalstr(prev.tagName(), tagText))) { // the previous element was a weak separator or didn't exist base.removeChild(e); continue; } } // the MergeLocal tag lets us specify where non-standard elements // of the local tree shall be merged in. After inserting the // elements we delete this element else if (equalstr(tag, tagMergeLocal)) { QDomNode it = additive.firstChild(); while (!it.isNull()) { QDomElement newChild = it.toElement(); it = it.nextSibling(); if (newChild.isNull()) { continue; } if (equalstr(newChild.tagName(), tagText)) { continue; } if (newChild.attribute(attrAlreadyVisited) == attrOne) { continue; } QString itAppend(newChild.attribute(attrAppend)); QString elemName(e.attribute(attrName)); if ((itAppend.isNull() && elemName.isEmpty()) || (itAppend == elemName)) { // first, see if this new element matches a standard one in // the global file. if it does, then we skip it as it will // be merged in, later QDomElement matchingElement = findMatchingElement(newChild, base); if (matchingElement.isNull() || equalstr(newChild.tagName(), tagSeparator)) { base.insertBefore(newChild, e); } } } base.removeChild(e); continue; } else if (equalstr(tag, tagText)) { continue; } else if (equalstr(tag, tagMerge)) { continue; } // in this last case we check for a separator tag and, if not, we // can be sure that it is a container --> proceed with child nodes // recursively and delete the just proceeded container item in // case it is empty (if the recursive call returns true) else { QDomElement matchingElement = findMatchingElement(e, additive); if (!matchingElement.isNull()) { matchingElement.setAttribute(attrAlreadyVisited, uint(1)); if (mergeXML(e, matchingElement, actionCollection)) { base.removeChild(e); additive.removeChild(matchingElement); // make sure we don't append it below continue; } continue; } else { // this is an important case here! We reach this point if the // "local" tree does not contain a container definition for // this container. However we have to call mergeXML recursively // and make it check if there are actions implemented for this // container. *If* none, then we can remove this container now QDomElement dummy; if (mergeXML(e, dummy, actionCollection)) { base.removeChild(e); } continue; } } } //here we append all child elements which were not inserted //previously via the LocalMerge tag n = additive.firstChild(); while (!n.isNull()) { QDomElement e = n.toElement(); n = n.nextSibling(); // Advance now so that we can safely delete e if (e.isNull()) { continue; } QDomElement matchingElement = findMatchingElement(e, base); if (matchingElement.isNull()) { base.appendChild(e); } } // do one quick check to make sure that the last element was not // a weak separator QDomElement last = base.lastChild().toElement(); if (equalstr(last.tagName(), tagSeparator) && (!last.attribute(attrWeakSeparator).isNull())) { base.removeChild(last); } } return isEmptyContainer(base, actionCollection); } bool KXMLGUIClientPrivate::isEmptyContainer(const QDomElement &base, KActionCollection *actionCollection) const { // now we check if we are empty (in which case we return "true", to // indicate the caller that it can delete "us" (the base element // argument of "this" call) QDomNode n = base.firstChild(); while (!n.isNull()) { const QDomElement e = n.toElement(); n = n.nextSibling(); // Advance now so that we can safely delete e if (e.isNull()) { continue; } const QString tag = e.tagName(); if (equalstr(tag, QLatin1String("Action"))) { // if base contains an implemented action, then we must not get // deleted (note that the actionCollection contains both, // "global" and "local" actions) if (actionCollection->action(e.attribute(QStringLiteral("name")))) { return false; } } else if (equalstr(tag, QLatin1String("Separator"))) { // if we have a separator which has *not* the weak attribute // set, then it must be owned by the "local" tree in which case // we must not get deleted either const QString weakAttr = e.attribute(QStringLiteral("weakSeparator")); if (weakAttr.isEmpty() || weakAttr.toInt() != 1) { return false; } } else if (equalstr(tag, QLatin1String("merge"))) { continue; } // a text tag is NOT enough to spare this container else if (equalstr(tag, QLatin1String("text"))) { continue; } // what's left are non-empty containers! *don't* delete us in this // case (at this position we can be *sure* that the container is // *not* empty, as the recursive call for it was in the first loop // which deleted the element in case the call returned "true" else { return false; } } return true; // I'm empty, please delete me. } QDomElement KXMLGUIClientPrivate::findMatchingElement(const QDomElement &base, const QDomElement &additive) { QDomNode n = additive.firstChild(); while (!n.isNull()) { QDomElement e = n.toElement(); n = n.nextSibling(); // Advance now so that we can safely delete e -- TODO we don't, so simplify this if (e.isNull()) { continue; } const QString tag = e.tagName(); // skip all action and merge tags as we will never use them if (equalstr(tag, QLatin1String("Action")) || equalstr(tag, QLatin1String("MergeLocal"))) { continue; } // now see if our tags are equivalent if (equalstr(tag, base.tagName()) && e.attribute(QStringLiteral("name")) == base.attribute(QStringLiteral("name"))) { return e; } } // nope, return a (now) null element return QDomElement(); } void KXMLGUIClient::setXMLGUIBuildDocument(const QDomDocument &doc) { d->m_buildDocument = doc; } QDomDocument KXMLGUIClient::xmlguiBuildDocument() const { return d->m_buildDocument; } void KXMLGUIClient::setFactory(KXMLGUIFactory *factory) { d->m_factory = factory; } KXMLGUIFactory *KXMLGUIClient::factory() const { return d->m_factory; } KXMLGUIClient *KXMLGUIClient::parentClient() const { return d->m_parent; } void KXMLGUIClient::insertChildClient(KXMLGUIClient *child) { if (child->d->m_parent) { child->d->m_parent->removeChildClient(child); } d->m_children.append(child); child->d->m_parent = this; } void KXMLGUIClient::removeChildClient(KXMLGUIClient *child) { assert(d->m_children.contains(child)); d->m_children.removeAll(child); - child->d->m_parent = 0; + child->d->m_parent = nullptr; } /*bool KXMLGUIClient::addSuperClient( KXMLGUIClient *super ) { if ( d->m_supers.contains( super ) ) return false; d->m_supers.append( super ); return true; }*/ QList<KXMLGUIClient *> KXMLGUIClient::childClients() { return d->m_children; } void KXMLGUIClient::setClientBuilder(KXMLGUIBuilder *builder) { d->m_builder = builder; } KXMLGUIBuilder *KXMLGUIClient::clientBuilder() const { return d->m_builder; } void KXMLGUIClient::plugActionList(const QString &name, const QList<QAction *> &actionList) { if (!d->m_factory) { return; } d->m_factory->plugActionList(this, name, actionList); } void KXMLGUIClient::unplugActionList(const QString &name) { if (!d->m_factory) { return; } d->m_factory->unplugActionList(this, name); } QString KXMLGUIClient::findMostRecentXMLFile(const QStringList &files, QString &doc) { KXmlGuiVersionHandler versionHandler(files); doc = versionHandler.finalDocument(); return versionHandler.finalFile(); } void KXMLGUIClient::addStateActionEnabled(const QString &state, const QString &action) { StateChange stateChange = getActionsToChangeForState(state); stateChange.actionsToEnable.append(action); //qCDebug(DEBUG_KXMLGUI) << "KXMLGUIClient::addStateActionEnabled( " << state << ", " << action << ")"; d->m_actionsStateMap.insert(state, stateChange); } void KXMLGUIClient::addStateActionDisabled(const QString &state, const QString &action) { StateChange stateChange = getActionsToChangeForState(state); stateChange.actionsToDisable.append(action); //qCDebug(DEBUG_KXMLGUI) << "KXMLGUIClient::addStateActionDisabled( " << state << ", " << action << ")"; d->m_actionsStateMap.insert(state, stateChange); } KXMLGUIClient::StateChange KXMLGUIClient::getActionsToChangeForState(const QString &state) { return d->m_actionsStateMap[state]; } void KXMLGUIClient::stateChanged(const QString &newstate, KXMLGUIClient::ReverseStateChange reverse) { StateChange stateChange = getActionsToChangeForState(newstate); bool setTrue = (reverse == StateNoReverse); bool setFalse = !setTrue; // Enable actions which need to be enabled... // for (QStringList::const_iterator it = stateChange.actionsToEnable.constBegin(); it != stateChange.actionsToEnable.constEnd(); ++it) { QAction *action = actionCollection()->action(*it); if (action) { action->setEnabled(setTrue); } } // and disable actions which need to be disabled... // for (QStringList::const_iterator it = stateChange.actionsToDisable.constBegin(); it != stateChange.actionsToDisable.constEnd(); ++it) { QAction *action = actionCollection()->action(*it); if (action) { action->setEnabled(setFalse); } } } void KXMLGUIClient::beginXMLPlug(QWidget *w) { actionCollection()->addAssociatedWidget(w); foreach (KXMLGUIClient *client, d->m_children) { client->beginXMLPlug(w); } } void KXMLGUIClient::endXMLPlug() { } void KXMLGUIClient::prepareXMLUnplug(QWidget *w) { actionCollection()->removeAssociatedWidget(w); foreach (KXMLGUIClient *client, d->m_children) { client->prepareXMLUnplug(w); } } void KXMLGUIClient::virtual_hook(int, void *) { /*BASE::virtual_hook( id, data );*/ } diff --git a/src/kxmlguifactory.cpp b/src/kxmlguifactory.cpp index 4727d0f..ef58094 100644 --- a/src/kxmlguifactory.cpp +++ b/src/kxmlguifactory.cpp @@ -1,771 +1,771 @@ /* This file is part of the KDE libraries Copyright (C) 1999,2000 Simon Hausmann <hausmann@kde.org> Copyright (C) 2000 Kurt Granroth <granroth@kde.org> 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 "config-xmlgui.h" #include "kxmlguifactory.h" #include "kxmlguifactory_p.h" #include "kshortcutschemeshelper_p.h" #include "kxmlguiclient.h" #include "kxmlguibuilder.h" #include "kshortcutsdialog.h" #include "kactioncollection.h" #include "debug.h" #include <QAction> #include <QtCore/QDir> #include <QtXml/QDomDocument> #include <QtCore/QFile> #include <QtCore/QCoreApplication> #include <QtCore/QTextStream> #include <QWidget> #include <QtCore/QDate> #include <QtCore/QVariant> #include <QTextCodec> #include <QStandardPaths> #include <ksharedconfig.h> #include <kconfiggroup.h> #if HAVE_GLOBALACCEL # include <kglobalaccel.h> #endif Q_DECLARE_METATYPE(QList<QKeySequence>) using namespace KXMLGUI; class KXMLGUIFactoryPrivate : public BuildState { public: enum ShortcutOption { SetActiveShortcut = 1, SetDefaultShortcut = 2}; KXMLGUIFactoryPrivate() { - m_rootNode = new ContainerNode(0L, QString(), QString()); + m_rootNode = new ContainerNode(nullptr, QString(), QString()); attrName = QStringLiteral("name"); } ~KXMLGUIFactoryPrivate() { delete m_rootNode; } void pushState() { m_stateStack.push(*this); } void popState() { BuildState::operator=(m_stateStack.pop()); } bool emptyState() const { return m_stateStack.isEmpty(); } QWidget *findRecursive(KXMLGUI::ContainerNode *node, bool tag); QList<QWidget *> findRecursive(KXMLGUI::ContainerNode *node, const QString &tagName); void applyActionProperties(const QDomElement &element, ShortcutOption shortcutOption = KXMLGUIFactoryPrivate::SetActiveShortcut); void configureAction(QAction *action, const QDomNamedNodeMap &attributes, ShortcutOption shortcutOption = KXMLGUIFactoryPrivate::SetActiveShortcut); void configureAction(QAction *action, const QDomAttr &attribute, ShortcutOption shortcutOption = KXMLGUIFactoryPrivate::SetActiveShortcut); void applyShortcutScheme(const QString &schemeName, KXMLGUIClient *client, const QList<QAction *> &actions); void refreshActionProperties(KXMLGUIClient *client, const QList<QAction *> &actions, const QDomDocument &doc); void saveDefaultActionProperties(const QList<QAction *> &actions); ContainerNode *m_rootNode; /* * Contains the container which is searched for in ::container . */ QString m_containerName; /* * List of all clients */ QList<KXMLGUIClient *> m_clients; QString attrName; BuildStateStack m_stateStack; }; QString KXMLGUIFactory::readConfigFile(const QString &filename, const QString &_componentName) { QString componentName = _componentName.isEmpty() ? QCoreApplication::applicationName() : _componentName; QString xml_file; if (!QDir::isRelativePath(filename)) { xml_file = filename; } else { // KF >= 5.1 (KXMLGUI_INSTALL_DIR) xml_file = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("kxmlgui5/") + componentName + QLatin1Char('/') + filename); if (!QFile::exists(xml_file)) { // KF >= 5.4 (resource file) xml_file = QStringLiteral(":/kxmlgui5/") + componentName + QLatin1Char('/') + filename; } bool warn = false; if (!QFile::exists(xml_file)) { // kdelibs4 / KF 5.0 solution xml_file = QStandardPaths::locate(QStandardPaths::GenericDataLocation, componentName + QLatin1Char('/') + filename); warn = true; } if (!QFile::exists(xml_file)) { // kdelibs4 / KF 5.0 solution, and the caller includes the component name // This was broken (lead to component/component/ in kdehome) and unnecessary // (they can specify it with setComponentName instead) xml_file = QStandardPaths::locate(QStandardPaths::GenericDataLocation, filename); warn = true; } if (warn && !xml_file.isEmpty()) { qCWarning(DEBUG_KXMLGUI) << "KXMLGUI file found at deprecated location" << xml_file << "-- please use ${KXMLGUI_INSTALL_DIR} to install these files instead."; } } QFile file(xml_file); if (xml_file.isEmpty() || !file.open(QIODevice::ReadOnly)) { qCritical() << "No such XML file" << filename; return QString(); } QByteArray buffer(file.readAll()); return QString::fromUtf8(buffer.constData(), buffer.size()); } bool KXMLGUIFactory::saveConfigFile(const QDomDocument &doc, const QString &filename, const QString &_componentName) { QString componentName = _componentName.isEmpty() ? QCoreApplication::applicationName() : _componentName; QString xml_file(filename); if (QDir::isRelativePath(xml_file)) xml_file = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QStringLiteral("/kxmlgui5/") + componentName + QLatin1Char('/') + filename; QFileInfo fileInfo(xml_file); QDir().mkpath(fileInfo.absolutePath()); QFile file(xml_file); if (xml_file.isEmpty() || !file.open(QIODevice::WriteOnly)) { qCritical() << "Could not write to" << filename; return false; } // write out our document QTextStream ts(&file); ts.setCodec(QTextCodec::codecForName("UTF-8")); ts << doc; file.close(); return true; } KXMLGUIFactory::KXMLGUIFactory(KXMLGUIBuilder *builder, QObject *parent) : QObject(parent), d(new KXMLGUIFactoryPrivate) { d->builder = builder; - d->guiClient = 0; + d->guiClient = nullptr; if (d->builder) { d->builderContainerTags = d->builder->containerTags(); d->builderCustomTags = d->builder->customTags(); } } KXMLGUIFactory::~KXMLGUIFactory() { Q_FOREACH (KXMLGUIClient *client, d->m_clients) { - client->setFactory(0L); + client->setFactory(nullptr); } delete d; } void KXMLGUIFactory::addClient(KXMLGUIClient *client) { //qCDebug(DEBUG_KXMLGUI) << client; if (client->factory()) { if (client->factory() == this) { return; } else { client->factory()->removeClient(client); //just in case someone does stupid things ;-) } } if (d->emptyState()) { emit makingChanges(true); } d->pushState(); // QTime dt; dt.start(); d->guiClient = client; // add this client to our client list if (!d->m_clients.contains(client)) { d->m_clients.append(client); } //else //qCDebug(DEBUG_KXMLGUI) << "XMLGUI client already added " << client; // Tell the client that plugging in is process and // let it know what builder widget its mainwindow shortcuts // should be attached to. client->beginXMLPlug(d->builder->widget()); // try to use the build document for building the client's GUI, as the build document // contains the correct container state information (like toolbar positions, sizes, etc.) . // if there is non available, then use the "real" document. QDomDocument doc = client->xmlguiBuildDocument(); if (doc.documentElement().isNull()) { doc = client->domDocument(); } QDomElement docElement = doc.documentElement(); d->m_rootNode->index = -1; // cache some variables d->clientName = docElement.attribute(d->attrName); d->clientBuilder = client->clientBuilder(); if (d->clientBuilder) { d->clientBuilderContainerTags = d->clientBuilder->containerTags(); d->clientBuilderCustomTags = d->clientBuilder->customTags(); } else { d->clientBuilderContainerTags.clear(); d->clientBuilderCustomTags.clear(); } // load shortcut schemes, user-defined shortcuts and other action properties d->saveDefaultActionProperties(client->actionCollection()->actions()); if (!doc.isNull()) { d->refreshActionProperties(client, client->actionCollection()->actions(), doc); } BuildHelper(*d, d->m_rootNode).build(docElement); // let the client know that we built its GUI. client->setFactory(this); // call the finalizeGUI method, to fix up the positions of toolbars for example. // Note: the client argument is ignored d->builder->finalizeGUI(d->guiClient); // reset some variables, for safety d->BuildState::reset(); client->endXMLPlug(); d->popState(); emit clientAdded(client); // build child clients Q_FOREACH (KXMLGUIClient *child, client->childClients()) { addClient(child); } if (d->emptyState()) { emit makingChanges(false); } /* QString unaddedActions; Q_FOREACH (KActionCollection* ac, KActionCollection::allCollections()) Q_FOREACH (QAction* action, ac->actions()) if (action->associatedWidgets().isEmpty()) unaddedActions += action->objectName() + ' '; if (!unaddedActions.isEmpty()) qCWarning(DEBUG_KXMLGUI) << "The following actions are not plugged into the gui (shortcuts will not work): " << unaddedActions; */ // qCDebug(DEBUG_KXMLGUI) << "addClient took " << dt.elapsed(); } void KXMLGUIFactory::refreshActionProperties() { Q_FOREACH (KXMLGUIClient *client, d->m_clients) { d->guiClient = client; QDomDocument doc = client->xmlguiBuildDocument(); if (doc.documentElement().isNull()) { client->reloadXML(); doc = client->domDocument(); } d->refreshActionProperties(client, client->actionCollection()->actions(), doc); } - d->guiClient = 0; + d->guiClient = nullptr; } static QString currentShortcutScheme() { const KConfigGroup cg = KSharedConfig::openConfig()->group("Shortcut Schemes"); return cg.readEntry("Current Scheme", "Default"); } // Find the right ActionProperties element, otherwise return null element static QDomElement findActionPropertiesElement(const QDomDocument &doc) { const QLatin1String tagActionProp("ActionProperties"); const QString schemeName = currentShortcutScheme(); QDomElement e = doc.documentElement().firstChildElement(); for (; !e.isNull(); e = e.nextSiblingElement()) { if (QString::compare(e.tagName(), tagActionProp, Qt::CaseInsensitive) == 0 && (e.attribute(QStringLiteral("scheme"), QStringLiteral("Default")) == schemeName)) { return e; } } return QDomElement(); } void KXMLGUIFactoryPrivate::refreshActionProperties(KXMLGUIClient *client, const QList<QAction *> &actions, const QDomDocument &doc) { // try to find and apply shortcuts schemes const QString schemeName = KShortcutSchemesHelper::currentShortcutSchemeName(); //qCDebug(DEBUG_KXMLGUI) << client->componentName() << ": applying shortcut scheme" << schemeName; if (schemeName != QLatin1String("Default")) { applyShortcutScheme(schemeName, client, actions); } else { // apply saved default shortcuts Q_FOREACH (QAction *action, actions) { QVariant savedDefaultShortcut = action->property("_k_DefaultShortcut"); if (savedDefaultShortcut.isValid()) { QList<QKeySequence> shortcut = savedDefaultShortcut.value<QList<QKeySequence> >(); action->setShortcuts(shortcut); action->setProperty("defaultShortcuts", QVariant::fromValue(shortcut)); //qCDebug(DEBUG_KXMLGUI) << "scheme said" << action->shortcut().toString() << "for action" << action->objectName(); } else { action->setShortcuts(QList<QKeySequence>()); } } } // try to find and apply user-defined shortcuts const QDomElement actionPropElement = findActionPropertiesElement(doc); if (!actionPropElement.isNull()) { applyActionProperties(actionPropElement); } } void KXMLGUIFactoryPrivate::saveDefaultActionProperties(const QList<QAction *> &actions) { // This method is called every time the user activated a new // kxmlguiclient. We only want to execute the following code only once in // the lifetime of an action. Q_FOREACH (QAction *action, actions) { // Skip NULL actions or those we have seen already. if (!action || action->property("_k_DefaultShortcut").isValid()) { continue; } // Check if the default shortcut is set QList<QKeySequence> defaultShortcut = action->property("defaultShortcuts").value<QList<QKeySequence> >(); QList<QKeySequence> activeShortcut = action->shortcuts(); //qCDebug(DEBUG_KXMLGUI) << action->objectName() << "default=" << defaultShortcut.toString() << "active=" << activeShortcut.toString(); // Check if we have an empty default shortcut and an non empty // custom shortcut. Print out a warning and correct the mistake. if ((!activeShortcut.isEmpty()) && defaultShortcut.isEmpty()) { qCritical() << "Shortcut for action " << action->objectName() << action->text() << "set with QAction::setShortcut()! Use KActionCollection::setDefaultShortcut(s) instead."; action->setProperty("_k_DefaultShortcut", QVariant::fromValue(activeShortcut)); } else { action->setProperty("_k_DefaultShortcut", QVariant::fromValue(defaultShortcut)); } } } void KXMLGUIFactory::changeShortcutScheme(const QString &scheme) { qCDebug(DEBUG_KXMLGUI) << "Changing shortcut scheme to" << scheme; KConfigGroup cg = KSharedConfig::openConfig()->group("Shortcut Schemes"); cg.writeEntry("Current Scheme", scheme); refreshActionProperties(); } void KXMLGUIFactory::forgetClient(KXMLGUIClient *client) { d->m_clients.removeAll(client); } void KXMLGUIFactory::removeClient(KXMLGUIClient *client) { //qCDebug(DEBUG_KXMLGUI) << client; // don't try to remove the client's GUI if we didn't build it if (!client || client->factory() != this) { return; } if (d->emptyState()) { emit makingChanges(true); } // remove this client from our client list d->m_clients.removeAll(client); // remove child clients first (create a copy of the list just in case the // original list is modified directly or indirectly in removeClient()) const QList<KXMLGUIClient *> childClients(client->childClients()); Q_FOREACH (KXMLGUIClient *child, childClients) { removeClient(child); } //qCDebug(DEBUG_KXMLGUI) << "calling removeRecursive"; d->pushState(); // cache some variables d->guiClient = client; d->clientName = client->domDocument().documentElement().attribute(d->attrName); d->clientBuilder = client->clientBuilder(); - client->setFactory(0L); + client->setFactory(nullptr); // if we don't have a build document for that client, yet, then create one by // cloning the original document, so that saving container information in the // DOM tree does not touch the original document. QDomDocument doc = client->xmlguiBuildDocument(); if (doc.documentElement().isNull()) { doc = client->domDocument().cloneNode(true).toDocument(); client->setXMLGUIBuildDocument(doc); } d->m_rootNode->destruct(doc.documentElement(), *d); // reset some variables d->BuildState::reset(); // This will destruct the KAccel object built around the given widget. client->prepareXMLUnplug(d->builder->widget()); d->popState(); if (d->emptyState()) { emit makingChanges(false); } emit clientRemoved(client); } QList<KXMLGUIClient *> KXMLGUIFactory::clients() const { return d->m_clients; } QWidget *KXMLGUIFactory::container(const QString &containerName, KXMLGUIClient *client, bool useTagName) { d->pushState(); d->m_containerName = containerName; d->guiClient = client; QWidget *result = d->findRecursive(d->m_rootNode, useTagName); - d->guiClient = 0L; + d->guiClient = nullptr; d->m_containerName.clear(); d->popState(); return result; } QList<QWidget *> KXMLGUIFactory::containers(const QString &tagName) { return d->findRecursive(d->m_rootNode, tagName); } void KXMLGUIFactory::reset() { d->m_rootNode->reset(); d->m_rootNode->clearChildren(); } void KXMLGUIFactory::resetContainer(const QString &containerName, bool useTagName) { if (containerName.isEmpty()) { return; } ContainerNode *container = d->m_rootNode->findContainer(containerName, useTagName); if (container && container->parent) { container->parent->removeChild(container); } } QWidget *KXMLGUIFactoryPrivate::findRecursive(KXMLGUI::ContainerNode *node, bool tag) { if (((!tag && node->name == m_containerName) || (tag && node->tagName == m_containerName)) && (!guiClient || node->client == guiClient)) { return node->container; } Q_FOREACH (ContainerNode *child, node->children) { QWidget *cont = findRecursive(child, tag); if (cont) { return cont; } } - return 0L; + return nullptr; } // Case insensitive equality without calling toLower which allocates a new string static inline bool equals(const QString &str1, const char *str2) { return str1.compare(QLatin1String(str2), Qt::CaseInsensitive) == 0; } static inline bool equals(const QString &str1, const QString &str2) { return str1.compare(str2, Qt::CaseInsensitive) == 0; } QList<QWidget *> KXMLGUIFactoryPrivate::findRecursive(KXMLGUI::ContainerNode *node, const QString &tagName) { QList<QWidget *> res; if (equals(node->tagName, tagName)) { res.append(node->container); } Q_FOREACH (KXMLGUI::ContainerNode *child, node->children) { res << findRecursive(child, tagName); } return res; } void KXMLGUIFactory::plugActionList(KXMLGUIClient *client, const QString &name, const QList<QAction *> &actionList) { d->pushState(); d->guiClient = client; d->actionListName = name; d->actionList = actionList; d->clientName = client->domDocument().documentElement().attribute(d->attrName); d->m_rootNode->plugActionList(*d); // Load shortcuts for these new actions d->saveDefaultActionProperties(actionList); d->refreshActionProperties(client, actionList, client->domDocument()); d->BuildState::reset(); d->popState(); } void KXMLGUIFactory::unplugActionList(KXMLGUIClient *client, const QString &name) { d->pushState(); d->guiClient = client; d->actionListName = name; d->clientName = client->domDocument().documentElement().attribute(d->attrName); d->m_rootNode->unplugActionList(*d); d->BuildState::reset(); d->popState(); } void KXMLGUIFactoryPrivate::applyActionProperties(const QDomElement &actionPropElement, ShortcutOption shortcutOption) { for (QDomElement e = actionPropElement.firstChildElement(); !e.isNull(); e = e.nextSiblingElement()) { if (!equals(e.tagName(), "action")) { continue; } QAction *action = guiClient->action(e); if (!action) { continue; } configureAction(action, e.attributes(), shortcutOption); } } void KXMLGUIFactoryPrivate::configureAction(QAction *action, const QDomNamedNodeMap &attributes, ShortcutOption shortcutOption) { for (int i = 0; i < attributes.length(); i++) { QDomAttr attr = attributes.item(i).toAttr(); if (attr.isNull()) { continue; } configureAction(action, attr, shortcutOption); } } void KXMLGUIFactoryPrivate::configureAction(QAction *action, const QDomAttr &attribute, ShortcutOption shortcutOption) { QString attrName = attribute.name(); // If the attribute is a deprecated "accel", change to "shortcut". if (equals(attrName, "accel")) { attrName = QStringLiteral("shortcut"); } // No need to re-set name, particularly since it's "objectName" in Qt4 if (equals(attrName, "name")) { return; } if (equals(attrName, "icon")) { action->setIcon(QIcon::fromTheme(attribute.value())); return; } QVariant propertyValue; QVariant::Type propertyType = action->property(attrName.toLatin1().constData()).type(); bool isShortcut = (propertyType == QVariant::KeySequence); if (propertyType == QVariant::Int) { propertyValue = QVariant(attribute.value().toInt()); } else if (propertyType == QVariant::UInt) { propertyValue = QVariant(attribute.value().toUInt()); } else if (isShortcut) { // Setting the shortcut by property also sets the default shortcut (which is incorrect), so we have to do it directly if (attrName == QStringLiteral("globalShortcut")) { #if HAVE_GLOBALACCEL KGlobalAccel::self()->setShortcut(action, QKeySequence::listFromString(attribute.value())); #endif } else { action->setShortcuts(QKeySequence::listFromString(attribute.value())); } if (shortcutOption & KXMLGUIFactoryPrivate::SetDefaultShortcut) { action->setProperty("defaultShortcuts", QVariant::fromValue(QKeySequence::listFromString(attribute.value()))); } } else { propertyValue = QVariant(attribute.value()); } if (!isShortcut && !action->setProperty(attrName.toLatin1().constData(), propertyValue)) { qCWarning(DEBUG_KXMLGUI) << "Error: Unknown action property " << attrName << " will be ignored!"; } } void KXMLGUIFactoryPrivate::applyShortcutScheme(const QString &schemeName, KXMLGUIClient *client, const QList<QAction *> &actions) { //First clear all existing shortcuts Q_FOREACH (QAction *action, actions) { action->setShortcuts(QList<QKeySequence>()); // We clear the default shortcut as well because the shortcut scheme will set its own defaults action->setProperty("defaultShortcuts", QVariant::fromValue(QList<QKeySequence>())); } // Find the document for the shortcut scheme using the current application path. // This allows to install a single XML file for a shortcut scheme for kdevelop // rather than 10. // Also look for the current xmlguiclient path. // Per component xml files make sense for making kmail shortcuts available in kontact. QString schemeFileName = KShortcutSchemesHelper::shortcutSchemeFileName(client->componentName(), schemeName); if (schemeFileName.isEmpty()) { schemeFileName = KShortcutSchemesHelper::applicationShortcutSchemeFileName(schemeName); } if (schemeFileName.isEmpty()) { qCWarning(DEBUG_KXMLGUI) << client->componentName() << ": shortcut scheme file not found:" << schemeName << "after trying" << QCoreApplication::applicationName() << "and" << client->componentName(); return; } QDomDocument scheme; QFile schemeFile(schemeFileName); if (schemeFile.open(QIODevice::ReadOnly)) { qCDebug(DEBUG_KXMLGUI) << client->componentName() << ": found shortcut scheme XML" << schemeFileName; scheme.setContent(&schemeFile); } if (scheme.isNull()) { return; } QDomElement docElement = scheme.documentElement(); QDomElement actionPropElement = docElement.namedItem(QStringLiteral("ActionProperties")).toElement(); //Check if we really have the shortcut configuration here if (!actionPropElement.isNull()) { //qCDebug(DEBUG_KXMLGUI) << "Applying shortcut scheme for XMLGUI client" << client->componentName(); //Apply all shortcuts we have applyActionProperties(actionPropElement, KXMLGUIFactoryPrivate::SetDefaultShortcut); //} else { //qCDebug(DEBUG_KXMLGUI) << "Invalid shortcut scheme file"; } } int KXMLGUIFactory::configureShortcuts(bool letterCutsOk, bool bSaveSettings) { KShortcutsDialog dlg(KShortcutsEditor::AllActions, letterCutsOk ? KShortcutsEditor::LetterShortcutsAllowed : KShortcutsEditor::LetterShortcutsDisallowed, qobject_cast<QWidget *>(parent())); Q_FOREACH (KXMLGUIClient *client, d->m_clients) { if (client) { qCDebug(DEBUG_KXMLGUI) << "Adding collection from client" << client->componentName() << "with" << client->actionCollection()->count() << "actions"; dlg.addCollection(client->actionCollection()); } } return dlg.configure(bSaveSettings); } // Find or create QDomElement KXMLGUIFactory::actionPropertiesElement(QDomDocument &doc) { // first, lets see if we have existing properties QDomElement elem = findActionPropertiesElement(doc); // if there was none, create one if (elem.isNull()) { elem = doc.createElement(QStringLiteral("ActionProperties")); elem.setAttribute(QStringLiteral("scheme"), currentShortcutScheme()); doc.documentElement().appendChild(elem); } return elem; } QDomElement KXMLGUIFactory::findActionByName(QDomElement &elem, const QString &sName, bool create) { const QLatin1String attrName("name"); for (QDomNode it = elem.firstChild(); !it.isNull(); it = it.nextSibling()) { QDomElement e = it.toElement(); if (e.attribute(attrName) == sName) { return e; } } if (create) { QDomElement act_elem = elem.ownerDocument().createElement(QStringLiteral("Action")); act_elem.setAttribute(attrName, sName); elem.appendChild(act_elem); return act_elem; } return QDomElement(); } diff --git a/src/kxmlguifactory.h b/src/kxmlguifactory.h index fa9842a..76809ab 100644 --- a/src/kxmlguifactory.h +++ b/src/kxmlguifactory.h @@ -1,225 +1,225 @@ /* This file is part of the KDE libraries Copyright (C) 1999 Simon Hausmann <hausmann@kde.org> Copyright (C) 2000 Kurt Granroth <granroth@kde.org> 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 kxmlguifactory_h #define kxmlguifactory_h #include <kxmlgui_export.h> #include <QtCore/QObject> class QAction; class KXMLGUIFactoryPrivate; class KXMLGUIClient; class KXMLGUIBuilder; class QDomAttr; class QDomDocument; class QDomElement; class QDomNode; class QDomNamedNodeMap; namespace KXMLGUI { struct MergingIndex; struct ContainerNode; struct ContainerClient; class BuildHelper; } /** * KXMLGUIFactory, together with KXMLGUIClient objects, can be used to create * a GUI of container widgets (like menus, toolbars, etc.) and container items * (menu items, toolbar buttons, etc.) from an XML document and action objects. * * Each KXMLGUIClient represents a part of the GUI, composed from containers and * actions. KXMLGUIFactory takes care of building (with the help of a KXMLGUIBuilder) * and merging the GUI from an unlimited number of clients. * * Each client provides XML through a QDomDocument and actions through a * KActionCollection . The XML document contains the rules for how to merge the * GUI. * * KXMLGUIFactory processes the DOM tree provided by a client and plugs in the client's actions, * according to the XML and the merging rules of previously inserted clients. Container widgets * are built via a KXMLGUIBuilder , which has to be provided with the KXMLGUIFactory constructor. */ class KXMLGUI_EXPORT KXMLGUIFactory : public QObject { friend class KXMLGUI::BuildHelper; Q_OBJECT public: /** * Constructs a KXMLGUIFactory. The provided @p builder KXMLGUIBuilder will be called * for creating and removing container widgets, when clients are added/removed from the GUI. * * Note that the ownership of the given KXMLGUIBuilder object won't be transferred to this * KXMLGUIFactory, so you have to take care of deleting it properly. */ - explicit KXMLGUIFactory(KXMLGUIBuilder *builder, QObject *parent = 0); + explicit KXMLGUIFactory(KXMLGUIBuilder *builder, QObject *parent = nullptr); /** * Destructor */ ~KXMLGUIFactory(); // XXX move to somewhere else? (Simon) /// @internal static QString readConfigFile(const QString &filename, const QString &componentName = QString()); /// @internal static bool saveConfigFile(const QDomDocument &doc, const QString &filename, const QString &componentName = QString()); /** * @internal * Find or create the ActionProperties element, used when saving custom action properties */ static QDomElement actionPropertiesElement(QDomDocument &doc); /** * @internal * Find or create the element for a given action, by name. * Used when saving custom action properties */ static QDomElement findActionByName(QDomElement &elem, const QString &sName, bool create); /** * Creates the GUI described by the QDomDocument of the client, * using the client's actions, and merges it with the previously * created GUI. * This also means that the order in which clients are added to the factory * is relevant; assuming that your application supports plugins, you should * first add your application to the factory and then the plugin, so that the * plugin's UI is merged into the UI of your application, and not the other * way round. */ void addClient(KXMLGUIClient *client); /** * Removes the GUI described by the client, by unplugging all * provided actions and removing all owned containers (and storing * container state information in the given client) */ void removeClient(KXMLGUIClient *client); void plugActionList(KXMLGUIClient *client, const QString &name, const QList<QAction *> &actionList); void unplugActionList(KXMLGUIClient *client, const QString &name); /** * Returns a list of all clients currently added to this factory */ QList<KXMLGUIClient *> clients() const; /** * Use this method to get access to a container widget with the name specified with @p containerName * and which is owned by the @p client. The container name is specified with a "name" attribute in the * XML document. * * This function is particularly useful for getting hold of a popupmenu defined in an XMLUI file. * For instance: * \code * QMenu *popup = static_cast<QMenu*>(guiFactory()->container("my_popup",this)); * \endcode * where @p "my_popup" is the name of the menu in the XMLUI file, and * @p "this" is XMLGUIClient which owns the popupmenu (e.g. the mainwindow, or the part, or the plugin...) * * @param containerName Name of the container widget * @param client Owner of the container widget * @param useTagName Specifies whether to compare the specified name with the name attribute or * the tag name. * * This method may return 0 if no container with the given name exists or is not owned by the client. */ QWidget *container(const QString &containerName, KXMLGUIClient *client, bool useTagName = false); QList<QWidget *> containers(const QString &tagName); /** * Use this method to free all memory allocated by the KXMLGUIFactory. This deletes the internal node * tree and therefore resets the internal state of the class. Please note that the actual GUI is * NOT touched at all, meaning no containers are deleted nor any actions unplugged. That is * something you have to do on your own. So use this method only if you know what you are doing :-) * * (also note that this will call KXMLGUIClient::setFactory( 0 ) for all inserted clients) */ void reset(); /** * Use this method to free all memory allocated by the KXMLGUIFactory for a specific container, * including all child containers and actions. This deletes the internal node subtree for the * specified container. The actual GUI is not touched, no containers are deleted or any actions * unplugged. Use this method only if you know what you are doing :-) * * (also note that this will call KXMLGUIClient::setFactory( 0 ) for all clients of the * container) */ void resetContainer(const QString &containerName, bool useTagName = false); /** * Use this method to reset and reread action properties (shortcuts, etc.) for all actions. * This is needed, for example, when you change shortcuts scheme at runtime. */ void refreshActionProperties(); public Q_SLOTS: /** * Show a standard configure shortcut for every action in this factory. * * This slot can be connected directly to the action to configure shortcuts. This is very simple to * do that by adding a single line * \code * KStandardAction::keyBindings( guiFactory(), SLOT( configureShortcuts() ), actionCollection() ); * \endcode * * @param bAllowLetterShortcuts Set to false if unmodified alphanumeric * keys ('A', '1', etc.) are not permissible shortcuts. * @param bSaveSettings if true, the settings will also be saved back to * the *uirc file which they were intially read from. */ int configureShortcuts(bool bAllowLetterShortcuts = true, bool bSaveSettings = true); void changeShortcutScheme(const QString &scheme); Q_SIGNALS: void clientAdded(KXMLGUIClient *client); void clientRemoved(KXMLGUIClient *client); /** * Emitted when the factory is currently making changes to the GUI, * i.e. adding or removing clients. * makingChanges(true) is emitted before any change happens, and * makingChanges(false) is emitted after the change is done. * This allows e.g. KMainWindow to know that the GUI is * being changed programmatically and not by the user (so there is no reason to * save toolbar settings afterwards). * @since 4.1.3 */ void makingChanges(bool); private: friend class KXMLGUIClient; /// Internal, called by KXMLGUIClient destructor void forgetClient(KXMLGUIClient *client); KXMLGUIFactoryPrivate *const d; }; #endif diff --git a/src/kxmlguifactory_p.cpp b/src/kxmlguifactory_p.cpp index 664259d..5481f93 100644 --- a/src/kxmlguifactory_p.cpp +++ b/src/kxmlguifactory_p.cpp @@ -1,837 +1,837 @@ /* This file is part of the KDE libraries Copyright (C) 2001 Simon Hausmann <hausmann@kde.org> 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 "kxmlguifactory_p.h" #include "kxmlguiclient.h" #include "kxmlguibuilder.h" #include "ktoolbar.h" #include <QWidget> #include <QDebug> #include "debug.h" #include <assert.h> using namespace KXMLGUI; void ActionList::plug(QWidget *container, int index) const { QAction *before = nullptr; // Insert after end of widget's current actions (default). if ((index < 0) || (index > container->actions().count())) { qWarning() << "Index " << index << " is not within range (0 - " << container->actions().count(); } else if (index != container->actions().count()) { before = container->actions().at(index); // Insert before indexed action. } Q_FOREACH (QAction *action, *this) { container->insertAction(before, action); // before = action; // BUG FIX: do not insert actions in reverse order. } } ContainerNode::ContainerNode(QWidget *_container, const QString &_tagName, const QString &_name, ContainerNode *_parent, KXMLGUIClient *_client, KXMLGUIBuilder *_builder, QAction *_containerAction, const QString &_mergingName, const QString &_groupName, const QStringList &customTags, const QStringList &containerTags) : parent(_parent), client(_client), builder(_builder), builderCustomTags(customTags), builderContainerTags(containerTags), container(_container), containerAction(_containerAction), tagName(_tagName), name(_name), groupName(_groupName), index(0), mergingName(_mergingName) { if (parent) { parent->children.append(this); } } ContainerNode::~ContainerNode() { qDeleteAll(children); qDeleteAll(clients); } void ContainerNode::removeChild(ContainerNode *child) { children.removeAll(child); deleteChild(child); } void ContainerNode::deleteChild(ContainerNode *child) { MergingIndexList::iterator mergingIt = findIndex(child->mergingName); adjustMergingIndices(-1, mergingIt, QString()); delete child; } /* * Find a merging index with the given name. Used to find an index defined by <Merge name="blah"/> * or by a <DefineGroup name="foo" /> tag. */ MergingIndexList::iterator ContainerNode::findIndex(const QString &name) { return std::find_if(mergingIndices.begin(), mergingIndices.end(), [&name](const MergingIndex &idx) { return idx.mergingName == name; }); } /* * Find a container recursively with the given name. Either compares _name with the * container's tag name or the value of the container's name attribute. Specified by * the tag bool . */ ContainerNode *ContainerNode::findContainer(const QString &_name, bool tag) { if ((tag && tagName == _name) || (!tag && name == _name)) { return this; } Q_FOREACH (ContainerNode *child, children) { ContainerNode *res = child->findContainer(_name, tag); if (res) { return res; } } - return 0; + return nullptr; } /* * Finds a child container node (not recursively) with the given name and tagname. Explicitly * leaves out container widgets specified in the exludeList . Also ensures that the containers * belongs to currClient. */ ContainerNode *ContainerNode::findContainer(const QString &name, const QString &tagName, const QList<QWidget *> *excludeList, KXMLGUIClient * /*currClient*/) { ContainerNode *res = nullptr; ContainerNodeList::ConstIterator nIt = children.constBegin(); if (!name.isEmpty()) { for (; nIt != children.constEnd(); ++nIt) if ((*nIt)->name == name && !excludeList->contains((*nIt)->container)) { res = *nIt; break; } return res; } if (!tagName.isEmpty()) for (; nIt != children.constEnd(); ++nIt) { if ((*nIt)->tagName == tagName && !excludeList->contains((*nIt)->container) /* * It is a bad idea to also compare the client, because * we don't want to do so in situations like these: * * <MenuBar> * <Menu> * ... * * other client: * <MenuBar> * <Menu> * ... * && (*nIt)->client == currClient ) */ ) { res = *nIt; break; } } return res; } ContainerClient *ContainerNode::findChildContainerClient(KXMLGUIClient *currentGUIClient, const QString &groupName, const MergingIndexList::iterator &mergingIdx) { if (!clients.isEmpty()) { Q_FOREACH (ContainerClient *client, clients) if (client->client == currentGUIClient) { if (groupName.isEmpty()) { return client; } if (groupName == client->groupName) { return client; } } } ContainerClient *client = new ContainerClient; client->client = currentGUIClient; client->groupName = groupName; if (mergingIdx != mergingIndices.end()) { client->mergingName = (*mergingIdx).mergingName; } clients.append(client); return client; } void ContainerNode::plugActionList(BuildState &state) { MergingIndexList::iterator mIt(mergingIndices.begin()); MergingIndexList::iterator mEnd(mergingIndices.end()); for (; mIt != mEnd; ++mIt) { plugActionList(state, mIt); } Q_FOREACH (ContainerNode *child, children) { child->plugActionList(state); } } void ContainerNode::plugActionList(BuildState &state, const MergingIndexList::iterator &mergingIdxIt) { static const QString &tagActionList = QLatin1String("actionlist"); const MergingIndex &mergingIdx = *mergingIdxIt; if (mergingIdx.clientName != state.clientName) { return; } if (!mergingIdx.mergingName.startsWith(tagActionList)) { return; } const QString k = mergingIdx.mergingName.mid(tagActionList.length()); if (k != state.actionListName) { return; } ContainerClient *client = findChildContainerClient(state.guiClient, QString(), mergingIndices.end()); client->actionLists.insert(k, state.actionList); state.actionList.plug(container, mergingIdx.value); adjustMergingIndices(state.actionList.count(), mergingIdxIt, QString()); } void ContainerNode::unplugActionList(BuildState &state) { MergingIndexList::iterator mIt(mergingIndices.begin()); MergingIndexList::iterator mEnd(mergingIndices.end()); for (; mIt != mEnd; ++mIt) { unplugActionList(state, mIt); } Q_FOREACH (ContainerNode *child, children) { child->unplugActionList(state); } } void ContainerNode::unplugActionList(BuildState &state, const MergingIndexList::iterator &mergingIdxIt) { static const QString &tagActionList = QStringLiteral("actionlist"); MergingIndex mergingIdx = *mergingIdxIt; QString k = mergingIdx.mergingName; if (k.indexOf(tagActionList) == -1) { return; } k = k.mid(tagActionList.length()); if (mergingIdx.clientName != state.clientName) { return; } if (k != state.actionListName) { return; } ContainerClient *client = findChildContainerClient(state.guiClient, QString(), mergingIndices.end()); ActionListMap::Iterator lIt(client->actionLists.find(k)); if (lIt == client->actionLists.end()) { return; } removeActions(lIt.value()); client->actionLists.erase(lIt); } void ContainerNode::adjustMergingIndices(int offset, const MergingIndexList::iterator &it, const QString ¤tClientName) { MergingIndexList::iterator mergingIt = it; MergingIndexList::iterator mergingEnd = mergingIndices.end(); for (; mergingIt != mergingEnd; ++mergingIt) { if ((*mergingIt).clientName != currentClientName) { (*mergingIt).value += offset; } } index += offset; } bool ContainerNode::destruct(QDomElement element, BuildState &state) //krazy:exclude=passbyvalue (this is correct QDom usage, and a ref wouldn't allow passing doc.documentElement() as argument) { destructChildren(element, state); unplugActions(state); // remove all merging indices the client defined QMutableVectorIterator<MergingIndex> cmIt = mergingIndices; while (cmIt.hasNext()) if (cmIt.next().clientName == state.clientName) { cmIt.remove(); } // ### check for merging index count, too? if (clients.isEmpty() && children.isEmpty() && container && client == state.guiClient) { QWidget *parentContainer = nullptr; if (parent && parent->container) { parentContainer = parent->container; } Q_ASSERT(builder); builder->removeContainer(container, parentContainer, element, containerAction); client = nullptr; return true; } if (client == state.guiClient) { client = nullptr; } return false; } void ContainerNode::destructChildren(const QDomElement &element, BuildState &state) { QMutableListIterator<ContainerNode *> childIt = children; while (childIt.hasNext()) { ContainerNode *childNode = childIt.next(); QDomElement childElement = findElementForChild(element, childNode); // destruct returns true in case the container really got deleted if (childNode->destruct(childElement, state)) { deleteChild(childNode); childIt.remove(); } } } QDomElement ContainerNode::findElementForChild(const QDomElement &baseElement, ContainerNode *childNode) { // ### slow for (QDomNode n = baseElement.firstChild(); !n.isNull(); n = n.nextSibling()) { QDomElement e = n.toElement(); if (e.tagName().toLower() == childNode->tagName && e.attribute(QStringLiteral("name")) == childNode->name) { return e; } } return QDomElement(); } void ContainerNode::unplugActions(BuildState &state) { if (!container) { return; } QMutableListIterator<ContainerClient *> clientIt(clients); while (clientIt.hasNext()) { //only unplug the actions of the client we want to remove, as the container might be owned //by a different client ContainerClient *cClient = clientIt.next(); if (cClient->client == state.guiClient) { unplugClient(cClient); delete cClient; clientIt.remove(); } } } void ContainerNode::removeActions(const QList<QAction *> &actions) { for (QAction *action : actions) { const int pos = container->actions().indexOf(action); if (pos != -1) { container->removeAction(action); for (MergingIndex &idx : mergingIndices) { if (idx.value > pos) { --idx.value; } } --index; } } } void ContainerNode::unplugClient(ContainerClient *client) { assert(builder); KToolBar *bar = qobject_cast<KToolBar *>(container); if (bar) { bar->removeXMLGUIClient(client->client); } // now quickly remove all custom elements (i.e. separators) and unplug all actions removeActions(client->customElements); removeActions(client->actions); // unplug all actionslists ActionListMap::ConstIterator alIt = client->actionLists.constBegin(); ActionListMap::ConstIterator alEnd = client->actionLists.constEnd(); for (; alIt != alEnd; ++alIt) { removeActions(alIt.value()); } } void ContainerNode::reset() { Q_FOREACH (ContainerNode *child, children) { child->reset(); } if (client) { client->setFactory(nullptr); } } int ContainerNode::calcMergingIndex(const QString &mergingName, MergingIndexList::iterator &it, BuildState &state, bool ignoreDefaultMergingIndex) { const MergingIndexList::iterator mergingIt = findIndex(mergingName.isEmpty() ? state.clientName : mergingName); const MergingIndexList::iterator mergingEnd = mergingIndices.end(); if (ignoreDefaultMergingIndex || (mergingIt == mergingEnd && state.currentDefaultMergingIt == mergingEnd)) { it = mergingEnd; return index; } if (mergingIt != mergingEnd) { it = mergingIt; } else { it = state.currentDefaultMergingIt; } return (*it).value; } void ContainerNode::dump(int offset) { QString indent; indent.fill(QLatin1Char(' '), offset); qDebug() << qPrintable(indent) << name << tagName << groupName << mergingName << mergingIndices; Q_FOREACH (ContainerNode *child, children) { child->dump(offset + 2); } } // TODO: return a struct with 3 members rather than 1 ret val + 2 out params int BuildHelper::calcMergingIndex(const QDomElement &element, MergingIndexList::iterator &it, QString &group) { const QLatin1String attrGroup("group"); bool haveGroup = false; group = element.attribute(attrGroup); if (!group.isEmpty()) { group.prepend(attrGroup); haveGroup = true; } int idx; if (haveGroup) { idx = parentNode->calcMergingIndex(group, it, m_state, ignoreDefaultMergingIndex); } else { it = m_state.currentClientMergingIt; if (it == parentNode->mergingIndices.end()) { idx = parentNode->index; } else { idx = (*it).value; } } return idx; } BuildHelper::BuildHelper(BuildState &state, ContainerNode *node) - : containerClient(0), ignoreDefaultMergingIndex(false), m_state(state), + : containerClient(nullptr), ignoreDefaultMergingIndex(false), m_state(state), parentNode(node) { // create a list of supported container and custom tags customTags = m_state.builderCustomTags; containerTags = m_state.builderContainerTags; if (parentNode->builder != m_state.builder) { customTags += parentNode->builderCustomTags; containerTags += parentNode->builderContainerTags; } if (m_state.clientBuilder) { customTags = m_state.clientBuilderCustomTags + customTags; containerTags = m_state.clientBuilderContainerTags + containerTags; } m_state.currentDefaultMergingIt = parentNode->findIndex(QStringLiteral("<default>")); parentNode->calcMergingIndex(QString(), m_state.currentClientMergingIt, m_state, /*ignoreDefaultMergingIndex*/ false); } void BuildHelper::build(const QDomElement &element) { for (QDomNode n = element.firstChild(); !n.isNull(); n = n.nextSibling()) { QDomElement e = n.toElement(); if (e.isNull()) { continue; } processElement(e); } } void BuildHelper::processElement(const QDomElement &e) { QString tag(e.tagName().toLower()); QString currName(e.attribute(QStringLiteral("name"))); bool isActionTag = (tag == QStringLiteral("action")); if (isActionTag || customTags.indexOf(tag) != -1) { processActionOrCustomElement(e, isActionTag); } else if (containerTags.indexOf(tag) != -1) { processContainerElement(e, tag, currName); } else if (tag == QStringLiteral("merge") || tag == QLatin1String("definegroup") || tag == QStringLiteral("actionlist")) { processMergeElement(tag, currName, e); } else if (tag == QStringLiteral("state")) { processStateElement(e); } } void BuildHelper::processActionOrCustomElement(const QDomElement &e, bool isActionTag) { if (!parentNode->container) { return; } MergingIndexList::iterator it(m_state.currentClientMergingIt); QString group; int idx = calcMergingIndex(e, it, group); containerClient = parentNode->findChildContainerClient(m_state.guiClient, group, it); bool guiElementCreated = false; if (isActionTag) { guiElementCreated = processActionElement(e, idx); } else { guiElementCreated = processCustomElement(e, idx); } if (guiElementCreated) { // adjust any following merging indices and the current running index for the container parentNode->adjustMergingIndices(1, it, m_state.clientName); } } bool BuildHelper::processActionElement(const QDomElement &e, int idx) { assert(m_state.guiClient); // look up the action and plug it in QAction *action = m_state.guiClient->action(e); if (!action) { return false; } //qCDebug(DEBUG_KXMLGUI) << e.attribute(QStringLiteral("name")) << "->" << action << "inserting at idx=" << idx; QAction *before = nullptr; if (idx >= 0 && idx < parentNode->container->actions().count()) { before = parentNode->container->actions().at(idx); } parentNode->container->insertAction(before, action); // save a reference to the plugged action, in order to properly unplug it afterwards. containerClient->actions.append(action); return true; } bool BuildHelper::processCustomElement(const QDomElement &e, int idx) { assert(parentNode->builder); QAction *action = parentNode->builder->createCustomElement(parentNode->container, idx, e); if (!action) { return false; } containerClient->customElements.append(action); return true; } void BuildHelper::processStateElement(const QDomElement &element) { QString stateName = element.attribute(QStringLiteral("name")); if (stateName.isEmpty()) { return; } for (QDomNode n = element.firstChild(); !n.isNull(); n = n.nextSibling()) { QDomElement e = n.toElement(); if (e.isNull()) { continue; } QString tagName = e.tagName().toLower(); if (tagName != QStringLiteral("enable") && tagName != QLatin1String("disable")) { continue; } bool processingActionsToEnable = (tagName == QStringLiteral("enable")); // process action names for (QDomNode n2 = n.firstChild(); !n2.isNull(); n2 = n2.nextSibling()) { QDomElement actionEl = n2.toElement(); if (actionEl.tagName().toLower() != QStringLiteral("action")) { continue; } QString actionName = actionEl.attribute(QStringLiteral("name")); if (actionName.isEmpty()) { return; } if (processingActionsToEnable) { m_state.guiClient->addStateActionEnabled(stateName, actionName); } else { m_state.guiClient->addStateActionDisabled(stateName, actionName); } } } } void BuildHelper::processMergeElement(const QString &tag, const QString &name, const QDomElement &e) { const QLatin1String tagDefineGroup("definegroup"); const QLatin1String tagActionList("actionlist"); const QLatin1String defaultMergingName("<default>"); const QLatin1String attrGroup("group"); QString mergingName(name); if (mergingName.isEmpty()) { if (tag == tagDefineGroup) { qCritical() << "cannot define group without name!" << endl; return; } if (tag == tagActionList) { qCritical() << "cannot define actionlist without name!" << endl; return; } mergingName = defaultMergingName; } if (tag == tagDefineGroup) { mergingName.prepend(attrGroup); //avoid possible name clashes by prepending // "group" to group definitions } else if (tag == tagActionList) { mergingName.prepend(tagActionList); } if (parentNode->findIndex(mergingName) != parentNode->mergingIndices.end()) { return; //do not allow the redefinition of merging indices! } MergingIndexList::iterator mIt(parentNode->mergingIndices.end()); QString group(e.attribute(attrGroup)); if (!group.isEmpty()) { group.prepend(attrGroup); } // calculate the index of the new merging index. Usually this does not need any calculation, // we just want the last available index (i.e. append) . But in case the <Merge> tag appears // "inside" another <Merge> tag from a previously build client, then we have to use the // "parent's" index. That's why we call calcMergingIndex here. MergingIndex newIdx; newIdx.value = parentNode->calcMergingIndex(group, mIt, m_state, ignoreDefaultMergingIndex); newIdx.mergingName = mergingName; newIdx.clientName = m_state.clientName; // if that merging index is "inside" another one, then append it right after the "parent". if (mIt != parentNode->mergingIndices.end()) { parentNode->mergingIndices.insert(++mIt, newIdx); } else { parentNode->mergingIndices.append(newIdx); } if (mergingName == defaultMergingName) { ignoreDefaultMergingIndex = true; } // re-calculate the running default and client merging indices // (especially important in case the QList data got reallocated due to growing!) m_state.currentDefaultMergingIt = parentNode->findIndex(defaultMergingName); parentNode->calcMergingIndex(QString(), m_state.currentClientMergingIt, m_state, ignoreDefaultMergingIndex); } void BuildHelper::processContainerElement(const QDomElement &e, const QString &tag, const QString &name) { ContainerNode *containerNode = parentNode->findContainer(name, tag, &containerList, m_state.guiClient); if (!containerNode) { MergingIndexList::iterator it(m_state.currentClientMergingIt); QString group; int idx = calcMergingIndex(e, it, group); QAction *containerAction; KXMLGUIBuilder *builder; QWidget *container = createContainer(parentNode->container, idx, e, containerAction, &builder); // no container? (probably some <text> tag or so ;-) if (!container) { return; } parentNode->adjustMergingIndices(1, it, m_state.clientName); // Check that the container widget is not already in parentNode. Q_ASSERT(std::find_if(parentNode->children.constBegin(), parentNode->children.constEnd(), [container](ContainerNode *child) { return child->container == container; }) == parentNode->children.constEnd()); containerList.append(container); QString mergingName; if (it != parentNode->mergingIndices.end()) { mergingName = (*it).mergingName; } QStringList cusTags = m_state.builderCustomTags; QStringList conTags = m_state.builderContainerTags; if (builder != m_state.builder) { cusTags = m_state.clientBuilderCustomTags; conTags = m_state.clientBuilderContainerTags; } containerNode = new ContainerNode(container, tag, name, parentNode, m_state.guiClient, builder, containerAction, mergingName, group, cusTags, conTags); } else { if (tag == QStringLiteral("toolbar")) { KToolBar *bar = qobject_cast<KToolBar *>(containerNode->container); if (bar) { if (m_state.guiClient && !m_state.guiClient->xmlFile().isEmpty()) { bar->addXMLGUIClient(m_state.guiClient); } } else { qWarning() << "toolbar container is not a KToolBar"; } } } BuildHelper(m_state, containerNode).build(e); // and re-calculate running values, for better performance m_state.currentDefaultMergingIt = parentNode->findIndex(QStringLiteral("<default>")); parentNode->calcMergingIndex(QString(), m_state.currentClientMergingIt, m_state, ignoreDefaultMergingIndex); } QWidget *BuildHelper::createContainer(QWidget *parent, int index, const QDomElement &element, QAction *&containerAction, KXMLGUIBuilder **builder) { QWidget *res = nullptr; if (m_state.clientBuilder) { res = m_state.clientBuilder->createContainer(parent, index, element, containerAction); if (res) { *builder = m_state.clientBuilder; return res; } } KXMLGUIClient *oldClient = m_state.builder->builderClient(); m_state.builder->setBuilderClient(m_state.guiClient); res = m_state.builder->createContainer(parent, index, element, containerAction); m_state.builder->setBuilderClient(oldClient); if (res) { *builder = m_state.builder; } return res; } void BuildState::reset() { clientName.clear(); actionListName.clear(); actionList.clear(); - guiClient = 0; - clientBuilder = 0; + guiClient = nullptr; + clientBuilder = nullptr; currentDefaultMergingIt = currentClientMergingIt = MergingIndexList::iterator(); } QDebug operator<<(QDebug stream, const MergingIndex &mi) { QDebugStateSaver saver(stream); stream.nospace() << "clientName=" << mi.clientName << " mergingName=" << mi.mergingName << " value=" << mi.value; return stream; } diff --git a/src/kxmlguifactory_p.h b/src/kxmlguifactory_p.h index bc63eb3..841b318 100644 --- a/src/kxmlguifactory_p.h +++ b/src/kxmlguifactory_p.h @@ -1,256 +1,256 @@ /* This file is part of the KDE libraries Copyright (C) 2001 Simon Hausmann <hausmann@kde.org> 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 kxmlguifactory_p_h #define kxmlguifactory_p_h #include <QtCore/QStringList> #include <QtCore/QMap> #include <QtXml/QDomElement> #include <QStack> #include <QAction> #include <QDebug> class QWidget; class KXMLGUIClient; class KXMLGUIBuilder; namespace KXMLGUI { struct BuildState; class ActionList : public QList<QAction *> { public: ActionList() {} ActionList(const QList<QAction *> &rhs) : QList<QAction *>(rhs) {} ActionList &operator=(const QList<QAction *> &rhs) { QList<QAction *>::operator=(rhs); return *this; } void plug(QWidget *container, int index) const; }; typedef QMap< QString, ActionList > ActionListMap; /* * This structure is used to know to which client certain actions and custom elements * (i.e. menu separators) belong. * We do not only use a ContainerClient per GUIClient but also per merging group. * * groupName : Used for grouped merging. Specifies the group name to which these actions/elements * belong to. * actionLists : maps from action list name to action list. * mergingName : The (named) merging point. * * A ContainerClient always belongs to a ContainerNode. */ struct ContainerClient { KXMLGUIClient *client; ActionList actions; QList<QAction *> customElements; QString groupName; //is empty if no group client ActionListMap actionLists; QString mergingName; }; typedef QList<ContainerClient *> ContainerClientList; struct ContainerNode; struct MergingIndex { int value; // the actual index value, used as index for plug() or createContainer() calls QString mergingName; // the name of the merging index (i.e. the name attribute of the // Merge or DefineGroup tag) QString clientName; // the name of the client that defined this index }; typedef QVector<MergingIndex> MergingIndexList; /* * Here we store detailed information about a container, its clients (client=a guiclient having actions * plugged into the container), child nodes, naming information (tagname and name attribute) and * merging index information, to plug/insert new actions/items a the correct position. * * The builder variable is needed for using the proper GUIBuilder for destruction ( to use the same for * con- and destruction ). The builderCustomTags and builderContainerTags variables are cached values * of what the corresponding methods of the GUIBuilder which built the container return. The stringlists * is shared all over the place, so there's no need to worry about memory consumption for these * variables :-) * * The mergingIndices list contains the merging indices ;-) , as defined by <Merge>, <DefineGroup> * or by <ActionList> tags. The order of these index structures within the mergingIndices list * is (and has to be) identical with the order in the DOM tree. * * Beside the merging indices we have the "real" index of the container. It points to the next free * position. * (used when no merging index is used for a certain action, custom element or sub-container) */ struct ContainerNode { ContainerNode(QWidget *_container, const QString &_tagName, const QString &_name, - ContainerNode *_parent = 0L, KXMLGUIClient *_client = 0L, - KXMLGUIBuilder *_builder = 0L, QAction *containerAction = 0, + ContainerNode *_parent = nullptr, KXMLGUIClient *_client = nullptr, + KXMLGUIBuilder *_builder = nullptr, QAction *containerAction = nullptr, const QString &_mergingName = QString(), const QString &groupName = QString(), const QStringList &customTags = QStringList(), const QStringList &containerTags = QStringList()); ~ContainerNode(); ContainerNode *parent; KXMLGUIClient *client; KXMLGUIBuilder *builder; QStringList builderCustomTags; QStringList builderContainerTags; QWidget *container; QAction *containerAction; QString tagName; QString name; QString groupName; //is empty if the container is in no group ContainerClientList clients; QList<ContainerNode *> children; int index; MergingIndexList mergingIndices; QString mergingName; void clearChildren() { qDeleteAll(children); children.clear(); } void removeChild(ContainerNode *child); void deleteChild(ContainerNode *child); void removeActions(const QList<QAction *> &actions); MergingIndexList::iterator findIndex(const QString &name); ContainerNode *findContainer(const QString &_name, bool tag); ContainerNode *findContainer(const QString &name, const QString &tagName, const QList<QWidget *> *excludeList, KXMLGUIClient *currClient); ContainerClient *findChildContainerClient(KXMLGUIClient *currentGUIClient, const QString &groupName, const MergingIndexList::iterator &mergingIdx); void plugActionList(BuildState &state); void plugActionList(BuildState &state, const MergingIndexList::iterator &mergingIdxIt); void unplugActionList(BuildState &state); void unplugActionList(BuildState &state, const MergingIndexList::iterator &mergingIdxIt); void adjustMergingIndices(int offset, const MergingIndexList::iterator &it, const QString ¤tClientName); bool destruct(QDomElement element, BuildState &state); void destructChildren(const QDomElement &element, BuildState &state); static QDomElement findElementForChild(const QDomElement &baseElement, ContainerNode *childNode); void unplugActions(BuildState &state); void unplugClient(ContainerClient *client); void reset(); int calcMergingIndex(const QString &mergingName, MergingIndexList::iterator &it, BuildState &state, bool ignoreDefaultMergingIndex); void dump(int offset = 0); }; typedef QList<ContainerNode *> ContainerNodeList; class BuildHelper { public: BuildHelper(BuildState &state, ContainerNode *node); void build(const QDomElement &element); private: void processElement(const QDomElement &element); void processActionOrCustomElement(const QDomElement &e, bool isActionTag); bool processActionElement(const QDomElement &e, int idx); bool processCustomElement(const QDomElement &e, int idx); void processStateElement(const QDomElement &element); void processMergeElement(const QString &tag, const QString &name, const QDomElement &e); void processContainerElement(const QDomElement &e, const QString &tag, const QString &name); QWidget *createContainer(QWidget *parent, int index, const QDomElement &element, QAction *&containerAction, KXMLGUIBuilder **builder); int calcMergingIndex(const QDomElement &element, MergingIndexList::iterator &it, QString &group); QStringList customTags; QStringList containerTags; QList<QWidget *> containerList; ContainerClient *containerClient; bool ignoreDefaultMergingIndex; BuildState &m_state; ContainerNode *parentNode; }; struct BuildState { - BuildState() : guiClient(0), builder(0), clientBuilder(0) {} + BuildState() : guiClient(nullptr), builder(nullptr), clientBuilder(nullptr) {} void reset(); QString clientName; QString actionListName; ActionList actionList; KXMLGUIClient *guiClient; MergingIndexList::iterator currentDefaultMergingIt; MergingIndexList::iterator currentClientMergingIt; KXMLGUIBuilder *builder; QStringList builderCustomTags; QStringList builderContainerTags; KXMLGUIBuilder *clientBuilder; QStringList clientBuilderCustomTags; QStringList clientBuilderContainerTags; }; typedef QStack<BuildState> BuildStateStack; } QDebug operator<<(QDebug stream, const KXMLGUI::MergingIndex &mi); #endif diff --git a/src/kxmlguiwindow.cpp b/src/kxmlguiwindow.cpp index 8e8f143..24d48f1 100644 --- a/src/kxmlguiwindow.cpp +++ b/src/kxmlguiwindow.cpp @@ -1,440 +1,440 @@ /* This file is part of the KDE libraries Copyright (C) 2000 Reginald Stadlbauer (reggie@kde.org) (C) 1997 Stephan Kulow (coolo@kde.org) (C) 1997-2000 Sven Radej (radej@kde.org) (C) 1997-2000 Matthias Ettrich (ettrich@kde.org) (C) 1999 Chris Schlaeger (cs@kde.org) (C) 2002 Joseph Wenninger (jowenn@kde.org) (C) 2005-2006 Hamish Rodda (rodda@kde.org) 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 "kxmlguiwindow.h" #include "kmainwindow_p.h" #include "kmessagebox.h" #include "kactioncollection.h" #include "kmainwindowiface_p.h" #include "ktoolbarhandler_p.h" #include "kxmlguifactory.h" #include "kedittoolbar.h" #include "khelpmenu.h" #include "ktoolbar.h" #include <QCloseEvent> #include <QDBusConnection> #include <QtXml/QDomDocument> #include <QLayout> #include <QDebug> #include <QMenuBar> #include <QObject> #include <QStatusBar> #include <QStyle> #include <QWidget> #include <QList> #include <ktoggleaction.h> #include <kstandardaction.h> #include <kconfig.h> #include <klocalizedstring.h> #include <kaboutdata.h> #include <kwindowsystem.h> #include <ksharedconfig.h> #include <kconfiggroup.h> #include <stdlib.h> #include <ctype.h> class KXmlGuiWindowPrivate : public KMainWindowPrivate { public: void _k_slotFactoryMakingChanges(bool b) { // While the GUI factory is adding/removing clients, // don't let KMainWindow think those are changes made by the user // #105525 letDirtySettings = !b; } bool showHelpMenu: 1; QSize defaultSize; KDEPrivate::ToolBarHandler *toolBarHandler; KToggleAction *showStatusBarAction; QPointer<KEditToolBar> toolBarEditor; KXMLGUIFactory *factory; }; KXmlGuiWindow::KXmlGuiWindow(QWidget *parent, Qt::WindowFlags f) : KMainWindow(*new KXmlGuiWindowPrivate, parent, f), KXMLGUIBuilder(this) { K_D(KXmlGuiWindow); d->showHelpMenu = true; - d->toolBarHandler = 0; - d->showStatusBarAction = 0; - d->factory = 0; + d->toolBarHandler = nullptr; + d->showStatusBarAction = nullptr; + d->factory = nullptr; new KMainWindowInterface(this); } QAction *KXmlGuiWindow::toolBarMenuAction() { K_D(KXmlGuiWindow); if (!d->toolBarHandler) { - return 0; + return nullptr; } return d->toolBarHandler->toolBarMenuAction(); } void KXmlGuiWindow::setupToolbarMenuActions() { K_D(KXmlGuiWindow); if (d->toolBarHandler) { d->toolBarHandler->setupActions(); } } KXmlGuiWindow::~KXmlGuiWindow() { K_D(KXmlGuiWindow); delete d->factory; } bool KXmlGuiWindow::event(QEvent *ev) { bool ret = KMainWindow::event(ev); if (ev->type() == QEvent::Polish) { QDBusConnection::sessionBus().registerObject(dbusName() + QStringLiteral("/actions"), actionCollection(), QDBusConnection::ExportScriptableSlots | QDBusConnection::ExportScriptableProperties | QDBusConnection::ExportNonScriptableSlots | QDBusConnection::ExportNonScriptableProperties | QDBusConnection::ExportChildObjects); } return ret; } void KXmlGuiWindow::setHelpMenuEnabled(bool showHelpMenu) { K_D(KXmlGuiWindow); d->showHelpMenu = showHelpMenu; } bool KXmlGuiWindow::isHelpMenuEnabled() const { K_D(const KXmlGuiWindow); return d->showHelpMenu; } KXMLGUIFactory *KXmlGuiWindow::guiFactory() { K_D(KXmlGuiWindow); if (!d->factory) { d->factory = new KXMLGUIFactory(this, this); connect(d->factory, SIGNAL(makingChanges(bool)), this, SLOT(_k_slotFactoryMakingChanges(bool))); } return d->factory; } void KXmlGuiWindow::configureToolbars() { K_D(KXmlGuiWindow); KConfigGroup cg(KSharedConfig::openConfig(), ""); saveMainWindowSettings(cg); if (!d->toolBarEditor) { d->toolBarEditor = new KEditToolBar(guiFactory(), this); d->toolBarEditor->setAttribute(Qt::WA_DeleteOnClose); connect(d->toolBarEditor, SIGNAL(newToolBarConfig()), SLOT(saveNewToolbarConfig())); } d->toolBarEditor->show(); } void KXmlGuiWindow::saveNewToolbarConfig() { // createGUI(xmlFile()); // this loses any plugged-in guiclients, so we use remove+add instead. guiFactory()->removeClient(this); guiFactory()->addClient(this); KConfigGroup cg(KSharedConfig::openConfig(), ""); applyMainWindowSettings(cg); } void KXmlGuiWindow::setupGUI(StandardWindowOptions options, const QString &xmlfile) { setupGUI(QSize(), options, xmlfile); } void KXmlGuiWindow::setupGUI(const QSize &defaultSize, StandardWindowOptions options, const QString &xmlfile) { K_D(KXmlGuiWindow); if (options & Keys) { KStandardAction::keyBindings(guiFactory(), SLOT(configureShortcuts()), actionCollection()); } if ((options & StatusBar) && statusBar()) { createStandardStatusBarAction(); } if (options & ToolBar) { setStandardToolBarMenuEnabled(true); KStandardAction::configureToolbars(this, SLOT(configureToolbars()), actionCollection()); } d->defaultSize = defaultSize; if (options & Create) { createGUI(xmlfile); } if (d->defaultSize.isValid()) { resize(d->defaultSize); } else if (isHidden()) { adjustSize(); } if (options & Save) { const KConfigGroup cg(autoSaveConfigGroup()); if (cg.isValid()) { setAutoSaveSettings(cg); } else { setAutoSaveSettings(); } } } void KXmlGuiWindow::createGUI(const QString &xmlfile) { K_D(KXmlGuiWindow); // disabling the updates prevents unnecessary redraws //setUpdatesEnabled( false ); // just in case we are rebuilding, let's remove our old client guiFactory()->removeClient(this); // make sure to have an empty GUI QMenuBar *mb = menuBar(); if (mb) { mb->clear(); } qDeleteAll(toolBars()); // delete all toolbars // don't build a help menu unless the user ask for it if (d->showHelpMenu) { delete d->helpMenu; // we always want a help menu d->helpMenu = new KHelpMenu(this, KAboutData::applicationData(), true); KActionCollection *actions = actionCollection(); QAction *helpContentsAction = d->helpMenu->action(KHelpMenu::menuHelpContents); QAction *whatsThisAction = d->helpMenu->action(KHelpMenu::menuWhatsThis); QAction *reportBugAction = d->helpMenu->action(KHelpMenu::menuReportBug); QAction *switchLanguageAction = d->helpMenu->action(KHelpMenu::menuSwitchLanguage); QAction *aboutAppAction = d->helpMenu->action(KHelpMenu::menuAboutApp); QAction *aboutKdeAction = d->helpMenu->action(KHelpMenu::menuAboutKDE); QAction *donateAction = d->helpMenu->action(KHelpMenu::menuDonate); if (helpContentsAction) { actions->addAction(helpContentsAction->objectName(), helpContentsAction); } if (whatsThisAction) { actions->addAction(whatsThisAction->objectName(), whatsThisAction); } if (reportBugAction) { actions->addAction(reportBugAction->objectName(), reportBugAction); } if (switchLanguageAction) { actions->addAction(switchLanguageAction->objectName(), switchLanguageAction); } if (aboutAppAction) { actions->addAction(aboutAppAction->objectName(), aboutAppAction); } if (aboutKdeAction) { actions->addAction(aboutKdeAction->objectName(), aboutKdeAction); } if (donateAction) { actions->addAction(donateAction->objectName(), donateAction); } } const QString windowXmlFile = xmlfile.isNull() ? componentName() + QStringLiteral("ui.rc") : xmlfile; // Help beginners who call setXMLFile and then setupGUI... if (!xmlFile().isEmpty() && xmlFile() != windowXmlFile) { qWarning() << "You called setXMLFile(" << xmlFile() << ") and then createGUI or setupGUI," << "which also calls setXMLFile and will overwrite the file you have previously set.\n" << "You should call createGUI(" << xmlFile() << ") or setupGUI(<options>," << xmlFile() << ") instead."; } // we always want to load in our global standards file loadStandardsXmlFile(); // now, merge in our local xml file. setXMLFile(windowXmlFile, true); // make sure we don't have any state saved already setXMLGUIBuildDocument(QDomDocument()); // do the actual GUI building guiFactory()->reset(); guiFactory()->addClient(this); checkAmbiguousShortcuts(); // setUpdatesEnabled( true ); } void KXmlGuiWindow::slotStateChanged(const QString &newstate) { stateChanged(newstate, KXMLGUIClient::StateNoReverse); } void KXmlGuiWindow::slotStateChanged(const QString &newstate, bool reverse) { stateChanged(newstate, reverse ? KXMLGUIClient::StateReverse : KXMLGUIClient::StateNoReverse); } void KXmlGuiWindow::setStandardToolBarMenuEnabled(bool enable) { K_D(KXmlGuiWindow); if (enable) { if (d->toolBarHandler) { return; } d->toolBarHandler = new KDEPrivate::ToolBarHandler(this); if (factory()) { factory()->addClient(d->toolBarHandler); } } else { if (!d->toolBarHandler) { return; } if (factory()) { factory()->removeClient(d->toolBarHandler); } delete d->toolBarHandler; - d->toolBarHandler = 0; + d->toolBarHandler = nullptr; } } bool KXmlGuiWindow::isStandardToolBarMenuEnabled() const { K_D(const KXmlGuiWindow); return (d->toolBarHandler); } void KXmlGuiWindow::createStandardStatusBarAction() { K_D(KXmlGuiWindow); if (!d->showStatusBarAction) { d->showStatusBarAction = KStandardAction::showStatusbar(this, SLOT(setSettingsDirty()), actionCollection()); QStatusBar *sb = statusBar(); // Creates statusbar if it doesn't exist already. connect(d->showStatusBarAction, SIGNAL(toggled(bool)), sb, SLOT(setVisible(bool))); d->showStatusBarAction->setChecked(sb->isHidden()); } else { // If the language has changed, we'll need to grab the new text and whatsThis - QAction *tmpStatusBar = KStandardAction::showStatusbar(NULL, NULL, NULL); + QAction *tmpStatusBar = KStandardAction::showStatusbar(nullptr, nullptr, nullptr); d->showStatusBarAction->setText(tmpStatusBar->text()); d->showStatusBarAction->setWhatsThis(tmpStatusBar->whatsThis()); delete tmpStatusBar; } } void KXmlGuiWindow::finalizeGUI(bool /*force*/) { // FIXME: this really needs to be removed with a code more like the one we had on KDE3. // what we need to do here is to position correctly toolbars so they don't overlap. // Also, take in count plugins could provide their own toolbars and those also need to // be restored. if (autoSaveSettings() && autoSaveConfigGroup().isValid()) { applyMainWindowSettings(autoSaveConfigGroup()); } } void KXmlGuiWindow::applyMainWindowSettings(const KConfigGroup &config) { K_D(KXmlGuiWindow); KMainWindow::applyMainWindowSettings(config); QStatusBar *sb = findChild<QStatusBar *>(); if (sb && d->showStatusBarAction) { d->showStatusBarAction->setChecked(!sb->isHidden()); } } // KDE5 TODO: change it to "using KXMLGUIBuilder::finalizeGUI;" in the header // and remove the reimplementation void KXmlGuiWindow::finalizeGUI(KXMLGUIClient *client) { KXMLGUIBuilder::finalizeGUI(client); } void KXmlGuiWindow::checkAmbiguousShortcuts() { QMap<QString, QAction*> shortcuts; QAction *editCutAction = actionCollection()->action(QStringLiteral("edit_cut")); QAction *deleteFileAction = actionCollection()->action(QStringLiteral("deletefile")); foreach (QAction *action, actionCollection()->actions()) { if (action->isEnabled()) { foreach (const QKeySequence &shortcut, action->shortcuts()) { if (shortcut.isEmpty()) { continue; } const QString portableShortcutText = shortcut.toString(); const QAction *existingShortcutAction = shortcuts.value(portableShortcutText); if (existingShortcutAction) { // If the shortcut is already in use we give a warning, so that hopefully the developer will find it // There is one exception, if the conflicting shortcut is a non primary shortcut of "edit_cut" // and "deleteFileAction" is the other action since Shift+Delete is used for both in our default code bool showWarning = true; if ((action == editCutAction && existingShortcutAction == deleteFileAction) || (action == deleteFileAction && existingShortcutAction == editCutAction)) { QList<QKeySequence> editCutActionShortcuts = editCutAction->shortcuts(); if (editCutActionShortcuts.indexOf(shortcut) > 0) // alternate shortcut { editCutActionShortcuts.removeAll(shortcut); editCutAction->setShortcuts(editCutActionShortcuts); showWarning = false; } } if (showWarning) { const QString actionName = KLocalizedString::removeAcceleratorMarker(action->text()); const QString existingShortcutActionName = KLocalizedString::removeAcceleratorMarker(existingShortcutAction->text()); const QString dontShowAgainString = existingShortcutActionName + actionName + shortcut.toString(); KMessageBox::information(this, i18n("There are two actions (%1, %2) that want to use the same shortcut (%3). This is most probably a bug. Please report it in <a href='https://bugs.kde.org'>bugs.kde.org</a>", existingShortcutActionName, actionName, shortcut.toString(QKeySequence::NativeText)), i18n("Ambiguous Shortcuts"), dontShowAgainString, KMessageBox::Notify | KMessageBox::AllowLink); } } else { shortcuts.insert(portableShortcutText, action); } } } } } #include "moc_kxmlguiwindow.cpp" diff --git a/src/kxmlguiwindow.h b/src/kxmlguiwindow.h index f19e9c2..54dad32 100644 --- a/src/kxmlguiwindow.h +++ b/src/kxmlguiwindow.h @@ -1,355 +1,355 @@ /* This file is part of the KDE libraries Copyright (C) 2000 Reginald Stadlbauer (reggie@kde.org) (C) 1997 Stephan Kulow (coolo@kde.org) (C) 1997-2000 Sven Radej (radej@kde.org) (C) 1997-2000 Matthias Ettrich (ettrich@kde.org) (C) 1999 Chris Schlaeger (cs@kde.org) (C) 2002 Joseph Wenninger (jowenn@kde.org) (C) 2005-2006 Hamish Rodda (rodda@kde.org) 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 KXMLGUIWINDOW_H #define KXMLGUIWINDOW_H #include "kxmlguiclient.h" #include "kxmlguibuilder.h" #include "kmainwindow.h" #include <QtCore/QMetaClassInfo> class KMenu; class KXMLGUIFactory; class KConfig; class KConfigGroup; class KToolBar; class KXmlGuiWindowPrivate; #define KDE_DEFAULT_WINDOWFLAGS 0 /** * @short %KDE top level main window with predefined action layout * * Instead of creating a KMainWindow manually and assigning menus, menu entries, * toolbar buttons and actions to it by hand, this class can be used to load an * rc file to manage the main window's actions. * * See http://techbase.kde.org/Development/Tutorials/Using_KActions#XMLGUI * for essential information on the XML file format and usage of this class. * * @see KMainWindow * @author Reginald Stadlbauer (reggie@kde.org) Stephan Kulow (coolo@kde.org), Matthias Ettrich (ettrich@kde.org), Chris Schlaeger (cs@kde.org), Sven Radej (radej@kde.org). Maintained by Sven Radej (radej@kde.org) */ class KXMLGUI_EXPORT KXmlGuiWindow : public KMainWindow, public KXMLGUIBuilder, virtual public KXMLGUIClient { XMLGUI_DECLARE_PRIVATE(KXmlGuiWindow) Q_OBJECT Q_PROPERTY(bool hasMenuBar READ hasMenuBar) Q_PROPERTY(bool autoSaveSettings READ autoSaveSettings) Q_PROPERTY(QString autoSaveGroup READ autoSaveGroup) Q_PROPERTY(bool standardToolBarMenuEnabled READ isStandardToolBarMenuEnabled WRITE setStandardToolBarMenuEnabled) public: /** * Construct a main window. * * @param parent The widget parent. This is usually 0 but it may also be the window * group leader. In that case, the KMainWindow becomes sort of a * secondary window. * * @param f Specify the widget flags. The default is * Qt::Window and Qt::WA_DeleteOnClose. Qt::Window indicates that a * main window is a toplevel window, regardless of whether it has a * parent or not. Qt::WA_DeleteOnClose indicates that a main window is * automatically destroyed when its window is closed. Pass 0 if * you do not want this behavior. * * @see http://doc.trolltech.com/qt.html#WindowType-enum * * KMainWindows must be created on the heap with 'new', like: * \code * KMainWindow *kmw = new KMainWindow(...); * kmw->setObjectName(...); * \endcode * * IMPORTANT: For session management and window management to work * properly, all main windows in the application should have a * different name. If you don't do it, KMainWindow will create * a unique name, but it's recommended to explicitly pass a window name that will * also describe the type of the window. If there can be several windows of the same * type, append '#' (hash) to the name, and KMainWindow will replace it with numbers to make * the names unique. For example, for a mail client which has one main window showing * the mails and folders, and which can also have one or more windows for composing * mails, the name for the folders window should be e.g. "mainwindow" and * for the composer windows "composer#". * */ - explicit KXmlGuiWindow(QWidget *parent = 0, Qt::WindowFlags f = KDE_DEFAULT_WINDOWFLAGS); + explicit KXmlGuiWindow(QWidget *parent = nullptr, Qt::WindowFlags f = KDE_DEFAULT_WINDOWFLAGS); /** * \brief Destructor. * * Will also destroy the toolbars, and menubar if * needed. */ virtual ~KXmlGuiWindow(); /** * Enables the build of a standard help menu when calling createGUI/setupGUI(). * * The default behavior is to build one, you must call this function * to disable it */ void setHelpMenuEnabled(bool showHelpMenu = true); /** * Return @p true when the help menu is enabled */ bool isHelpMenuEnabled() const; virtual KXMLGUIFactory *guiFactory(); /** * Create a GUI given a local XML file. In a regular app you usually want to use * setupGUI() instead of this one since it does more things for free * like setting up the toolbar/shortcut edit actions, etc. * * If @p xmlfile is NULL, * then it will try to construct a local XML filename like * appnameui.rc where 'appname' is your app's name. If that file * does not exist, then the XML UI code will only use the global * (standard) XML file for the layout purposes. * * @param xmlfile The local xmlfile (relative or absolute) */ void createGUI(const QString &xmlfile = QString()); /** * Sets whether KMainWindow should provide a menu that allows showing/hiding * the available toolbars ( using KToggleToolBarAction ) . In case there * is only one toolbar configured a simple 'Show \<toolbar name here\>' menu item * is shown. * * The menu / menu item is implemented using xmlgui. It will be inserted in your * menu structure in the 'Settings' menu. * * If your application uses a non-standard xmlgui resource file then you can * specify the exact position of the menu / menu item by adding a * <Merge name="StandardToolBarMenuHandler" /> * line to the settings menu section of your resource file ( usually appname.rc ). * * Note that you should enable this feature before calling createGUI() ( or similar ) . */ void setStandardToolBarMenuEnabled(bool enable); bool isStandardToolBarMenuEnabled() const; /** * Sets whether KMainWindow should provide a menu that allows showing/hiding * of the statusbar ( using KToggleStatusBarAction ). * * The menu / menu item is implemented using xmlgui. It will be inserted * in your menu structure in the 'Settings' menu. * * Note that you should enable this feature before calling createGUI() * ( or similar ). * * If an application maintains the action on its own (i.e. never calls * this function) a connection needs to be made to let KMainWindow * know when that status (hidden/shown) of the statusbar has changed. * For example: * connect(action, SIGNAL(activated()), * kmainwindow, SLOT(setSettingsDirty())); * Otherwise the status (hidden/show) of the statusbar might not be saved * by KMainWindow. */ void createStandardStatusBarAction(); /** * @see setupGUI() */ enum StandardWindowOption { /** * adds action to show/hide the toolbar(s) and adds * action to configure the toolbar(s). * @see setStandardToolBarMenuEnabled */ ToolBar = 1, /** * adds action to show the key configure action. */ Keys = 2, /** * adds action to show/hide the statusbar if the * statusbar exists. @see createStandardStatusBarAction */ StatusBar = 4, /** * auto-saves (and loads) the toolbar/menubar/statusbar settings and * window size using the default name. @see setAutoSaveSettings * * Typically you want to let the default window size be determined by * the widgets size hints. Make sure that setupGUI() is called after * all the widgets are created ( including setCentralWidget ) so the * default size's will be correct. @see setAutoSaveSettings for * more information on this topic. */ Save = 8, /** * calls createGUI() once ToolBar, Keys and Statusbar have been * taken care of. @see createGUI * * NOTE: when using KParts::MainWindow, remove this flag from the * setupGUI call, since you'll be using createGUI(part) instead. * @code * setupGUI(ToolBar | Keys | StatusBar | Save); * @endcode */ Create = 16, /** * All the above option * (this is the default) */ Default = ToolBar | Keys | StatusBar | Save | Create }; Q_FLAG(StandardWindowOption) Q_DECLARE_FLAGS(StandardWindowOptions, StandardWindowOption) /** * Configures the current windows and its actions in the typical KDE * fashion. The options are all enabled by default but can be turned * off if desired through the params or if the prereqs don't exists. * * Typically this function replaces createGUI(). * * @see StandardWindowOptions * @note Since this method will restore the state of the application (toolbar, dockwindows * positions...), you need to have added all your actions to your toolbars etc before * calling to this method. (This note is only applicable if you are using the Default or * Save flag). * @warning If you are calling createGUI yourself, remember to remove the Create flag from * the @p options parameter. * */ void setupGUI(StandardWindowOptions options = Default, const QString &xmlfile = QString()); /** * Configures the current windows and its actions in the typical KDE * fashion. The options are all enabled by default but can be turned * off if desired through the params or if the prereqs don't exists. * * @p defaultSize The default size of the window * * Typically this function replaces createGUI(). * * @see StandardWindowOptions * @note Since this method will restore the state of the application (toolbar, dockwindows * positions...), you need to have added all your actions to your toolbars etc before * calling to this method. (This note is only applicable if you are using the Default or * Save flag). * @warning If you are calling createGUI yourself, remember to remove the Create flag from * the @p options parameter. Also, call setupGUI always after you call createGUI. */ void setupGUI(const QSize &defaultSize, StandardWindowOptions options = Default, const QString &xmlfile = QString()); /** * Returns a pointer to the mainwindows action responsible for the toolbars menu */ QAction *toolBarMenuAction(); /** * @internal for KToolBar */ void setupToolbarMenuActions(); // KDE5 TODO: change it to "using KXMLGUIBuilder::finalizeGUI;" void finalizeGUI(KXMLGUIClient *client) Q_DECL_OVERRIDE; /** * @internal */ void finalizeGUI(bool force); // reimplemented for internal reasons void applyMainWindowSettings(const KConfigGroup &config) Q_DECL_OVERRIDE; public Q_SLOTS: /** * Show a standard configure toolbar dialog. * * This slot can be connected directly to the action to configure toolbar. * This is very simple to do that by adding a single line * \code * KStandardAction::configureToolbars( this, SLOT( configureToolbars() ), * actionCollection() ); * \endcode */ virtual void configureToolbars(); /** * Apply a state change * * Enable and disable actions as defined in the XML rc file */ virtual void slotStateChanged(const QString &newstate); /** * Apply a state change * * Enable and disable actions as defined in the XML rc file, * can "reverse" the state (disable the actions which should be * enabled, and vice-versa) if specified. */ void slotStateChanged(const QString &newstate, bool reverse); protected: /** * Reimplemented to catch QEvent::Polish in order to adjust the object name * if needed, once all constructor code for the main window has run. * Also reimplemented to catch when a QDockWidget is added or removed. */ bool event(QEvent *event) Q_DECL_OVERRIDE; /** * Checks if there are actions using the same shortcut. This is called * automatically from createGUI. * @since 5.30 */ void checkAmbiguousShortcuts(); protected Q_SLOTS: /** * Rebuilds the GUI after KEditToolbar changed the toolbar layout. * @see configureToolbars() */ virtual void saveNewToolbarConfig(); private: Q_PRIVATE_SLOT(k_func(), void _k_slotFactoryMakingChanges(bool)) }; Q_DECLARE_OPERATORS_FOR_FLAGS(KXmlGuiWindow::StandardWindowOptions) #endif diff --git a/tests/krulertest.cpp b/tests/krulertest.cpp index 57e547e..6348f4e 100644 --- a/tests/krulertest.cpp +++ b/tests/krulertest.cpp @@ -1,391 +1,391 @@ #include "krulertest.h" #include <kruler.h> #include <QApplication> #include <QLayout> #include <QLabel> #include <QKeyEvent> #include <QGroupBox> #include <QButtonGroup> #include <QSpinBox> #include <QDoubleSpinBox> /* void MyCheckBox::mouseReleaseEvent(QMouseEvent *e ) { QButton::mouseReleaseEvent(e); if (); } */ MouseWidget::MouseWidget(QWidget *parent) : QFrame(parent) { } void MouseWidget::mousePressEvent(QMouseEvent *e) { mouseButtonDown = true; emit newXPos(e->x()); emit newYPos(e->y()); } void MouseWidget::mouseReleaseEvent(QMouseEvent *) { mouseButtonDown = false; } void MouseWidget::mouseMoveEvent(QMouseEvent *e) { if (mouseButtonDown) { emit newXPos(e->x()); emit newYPos(e->y()); } } void MouseWidget::resizeEvent(QResizeEvent *r) { emit newWidth(r->size().width()); emit newHeight(r->size().height()); } KRulerTest::KRulerTest() - : QWidget(0) + : QWidget(nullptr) { QVBoxLayout *topLayout = new QVBoxLayout(this); mainframe = new QFrame(this); topLayout->addWidget(mainframe); layout = new QGridLayout(mainframe); miniwidget = new QFrame(mainframe); miniwidget->setFrameStyle(QFrame::WinPanel | QFrame::Raised); bigwidget = new MouseWidget(mainframe); bigwidget->setFrameStyle(QFrame::WinPanel | QFrame::Sunken); // QRect bwrect = bigwidget->frameRect(); // qDebug("big rect: top%i left%i bottom%i right%i", // bwrect.top(), bwrect.left(), bwrect.bottom(), bwrect.right()); hruler = new KRuler(Qt::Horizontal, mainframe); // hruler->setRange( bwrect.left(), bwrect.right() ); hruler->setRange(0, 1000); // hruler->setOffset( bwrect.left() - bigwidget->frameRect().left() ); hruler->setOffset(0); vruler = new KRuler(Qt::Vertical, mainframe); vruler->setFrameStyle(QFrame::WinPanel | QFrame::Sunken); vruler->setOffset(0); vruler->setRange(0, 1000); connect(bigwidget, SIGNAL(newXPos(int)), hruler, SLOT(slotNewValue(int))); connect(bigwidget, SIGNAL(newYPos(int)), vruler, SLOT(slotNewValue(int))); connect(bigwidget, SIGNAL(newWidth(int)), SLOT(slotNewWidth(int))); connect(bigwidget, SIGNAL(newHeight(int)), SLOT(slotNewHeight(int))); layout->addWidget(miniwidget, 0, 0); layout->addWidget(hruler, 0, 1); layout->addWidget(vruler, 1, 0); layout->addWidget(bigwidget, 1, 1); mouse_message = new QLabel(QStringLiteral("Press and hold mouse button\nfor pointer movement"), bigwidget); mouse_message->adjustSize(); mouse_message->move(4, 4); showMarks = new QGroupBox(QStringLiteral("Show which marks ?"), bigwidget); showMarks->setFixedSize(140, 160); showMarks->move(330, 4); showTM = new QCheckBox(QStringLiteral("show tiny marks"), showMarks); showTM->adjustSize(); showTM->move(5, 15); showTM->setChecked(true); connect(showTM, SIGNAL(toggled(bool)), SLOT(slotSetTinyMarks(bool))); showLM = new QCheckBox(QStringLiteral("show little marks"), showMarks); showLM->adjustSize(); showLM->move(5, 35); showLM->setChecked(true); connect(showLM, SIGNAL(toggled(bool)), SLOT(slotSetLittleMarks(bool))); showMM = new QCheckBox(QStringLiteral("show medium marks"), showMarks); showMM->adjustSize(); showMM->move(5, 55); showMM->setChecked(true); connect(showMM, SIGNAL(toggled(bool)), SLOT(slotSetMediumMarks(bool))); showBM = new QCheckBox(QStringLiteral("show big marks"), showMarks); showBM->adjustSize(); showBM->move(5, 75); showBM->setChecked(true); connect(showBM, SIGNAL(toggled(bool)), SLOT(slotSetBigMarks(bool))); showEM = new QCheckBox(QStringLiteral("show end marks"), showMarks); showEM->adjustSize(); showEM->move(5, 95); showEM->setChecked(true); connect(showEM, SIGNAL(toggled(bool)), SLOT(slotSetEndMarks(bool))); showPT = new QCheckBox(QStringLiteral("show ruler pointer"), showMarks); showPT->adjustSize(); showPT->move(5, 115); showPT->setChecked(true); connect(showPT, SIGNAL(toggled(bool)), SLOT(slotSetRulerPointer(bool))); fixLen = new QCheckBox(QStringLiteral("fix ruler length"), showMarks); fixLen->adjustSize(); fixLen->move(5, 135); fixLen->setChecked(true); connect(fixLen, SIGNAL(toggled(bool)), SLOT(slotFixRulerLength(bool))); connect(fixLen, SIGNAL(toggled(bool)), SLOT(slotCheckLength(bool))); lineEdit = new QGroupBox(QStringLiteral("Value of begin/end"), bigwidget); lineEdit->setFixedSize(140, 80); lineEdit->move(330, 4 + 160); beginMark = new QSpinBox(lineEdit); beginMark->setValue(0); beginMark->setRange(-1000, 1000); beginMark->move(5, 15); beginMark->setFixedSize(beginMark->sizeHint()); connect(beginMark, SIGNAL(valueChanged(int)), hruler, SLOT(slotNewOffset(int))); connect(beginMark, SIGNAL(valueChanged(int)), vruler, SLOT(slotNewOffset(int))); endMark = new QSpinBox(lineEdit); endMark->setValue(0); endMark->setRange(-1000, 1000); endMark->move(5, 35); endMark->setFixedSize(endMark->sizeHint()); connect(endMark, SIGNAL(valueChanged(int)), hruler, SLOT(slotEndOffset(int))); connect(endMark, SIGNAL(valueChanged(int)), vruler, SLOT(slotEndOffset(int))); lengthInput = new QSpinBox(lineEdit); lengthInput->setValue(0); lengthInput->setRange(-1000, 1000); lengthInput->move(5, 55); lengthInput->setFixedSize(lengthInput->sizeHint()); connect(lengthInput, SIGNAL(valueChanged(int)), hruler, SLOT(slotEndOffset(int))); connect(lengthInput, SIGNAL(valueChanged(int)), vruler, SLOT(slotEndOffset(int))); vertrot = new QGroupBox(QStringLiteral("Value of rotate translate for Vert."), bigwidget); vertrot->setFixedSize(140, 80); vertrot->move(330, 4 + 160 + 80 + 4); transX = new QDoubleSpinBox(vertrot); transX->setValue(0.0); transX->setRange(-1000, 1000); transX->setSingleStep(1); transX->move(5, 15); transX->setFixedSize(transX->sizeHint()); //transX->setLabel("transx", AlignLeft); connect(transX, SIGNAL(valueChanged(double)), SLOT(slotSetXTrans(double))); transY = new QDoubleSpinBox(vertrot); transY->setValue(-12.0); transY->setRange(-1000, 1000); transY->setSingleStep(1); transY->move(5, 35); transY->setFixedSize(transY->sizeHint()); //transY->setLabel("transy", AlignLeft); connect(transY, SIGNAL(valueChanged(double)), SLOT(slotSetYTrans(double))); rotV = new QDoubleSpinBox(vertrot); rotV->setValue(90.0); rotV->setRange(-1000, 1000); rotV->setSingleStep(1); rotV->move(5, 55); rotV->setFixedSize(rotV->sizeHint()); //rotV->setLabel("rot", AlignLeft); connect(rotV, SIGNAL(valueChanged(double)), SLOT(slotSetRotate(double))); metricstyle = new QGroupBox(QStringLiteral("metric styles"), bigwidget); QButtonGroup *metricstyleButtons = new QButtonGroup(bigwidget); metricstyle->setFixedSize(100, 120); metricstyle->move(330 - 110, 4); pixelmetric = new QRadioButton(QStringLiteral("pixel"), metricstyle); pixelmetric->adjustSize(); pixelmetric->move(5, 15); metricstyleButtons->addButton(pixelmetric, (int)KRuler::Pixel); inchmetric = new QRadioButton(QStringLiteral("inch"), metricstyle); inchmetric->adjustSize(); inchmetric->move(5, 35); metricstyleButtons->addButton(inchmetric, (int)KRuler::Inch); mmmetric = new QRadioButton(QStringLiteral("millimeter"), metricstyle); mmmetric->adjustSize(); mmmetric->move(5, 55); metricstyleButtons->addButton(mmmetric, (int)KRuler::Millimetres); cmmetric = new QRadioButton(QStringLiteral("centimeter"), metricstyle); cmmetric->adjustSize(); cmmetric->move(5, 75); metricstyleButtons->addButton(cmmetric, (int)KRuler::Centimetres); mmetric = new QRadioButton(QStringLiteral("meter"), metricstyle); mmetric->adjustSize(); mmetric->move(5, 95); metricstyleButtons->addButton(mmetric, (int)KRuler::Metres); connect(metricstyleButtons, SIGNAL(buttonClicked(int)), SLOT(slotSetMStyle(int))); slotUpdateShowMarks(); } KRulerTest::~KRulerTest() { delete layout; delete hruler; delete vruler; delete miniwidget; delete bigwidget; delete mainframe; } void KRulerTest::slotNewWidth(int width) { hruler->setMaximum(width); } void KRulerTest::slotNewHeight(int height) { vruler->setMaximum(height); } void KRulerTest::slotSetTinyMarks(bool set) { hruler->setShowTinyMarks(set); vruler->setShowTinyMarks(set); } void KRulerTest::slotSetLittleMarks(bool set) { hruler->setShowLittleMarks(set); vruler->setShowLittleMarks(set); } void KRulerTest::slotSetMediumMarks(bool set) { hruler->setShowMediumMarks(set); vruler->setShowMediumMarks(set); } void KRulerTest::slotSetBigMarks(bool set) { hruler->setShowBigMarks(set); vruler->setShowBigMarks(set); } void KRulerTest::slotSetEndMarks(bool set) { hruler->setShowEndMarks(set); vruler->setShowEndMarks(set); } void KRulerTest::slotSetRulerPointer(bool set) { hruler->setShowPointer(set); vruler->setShowPointer(set); } void KRulerTest::slotSetRulerLength(int len) { hruler->setLength(len); vruler->setLength(len); } void KRulerTest::slotFixRulerLength(bool fix) { hruler->setLengthFixed(fix); vruler->setLengthFixed(fix); } void KRulerTest::slotSetMStyle(int style) { hruler->setRulerMetricStyle((KRuler::MetricStyle)style); vruler->setRulerMetricStyle((KRuler::MetricStyle)style); slotUpdateShowMarks(); } void KRulerTest::slotUpdateShowMarks() { showTM->setChecked(hruler->showTinyMarks()); showLM->setChecked(hruler->showLittleMarks()); showMM->setChecked(hruler->showMediumMarks()); showBM->setChecked(hruler->showBigMarks()); showEM->setChecked(hruler->showEndMarks()); } void KRulerTest::slotCheckLength(bool fixlen) { Q_UNUSED(fixlen); beginMark->setValue(hruler->offset()); endMark->setValue(hruler->endOffset()); lengthInput->setValue(hruler->length()); } void KRulerTest::slotSetRotate(double d) { Q_UNUSED(d); #ifdef KRULER_ROTATE_TEST vruler->rotate = d; vruler->update(); //debug("rotate %.1f", d); #endif } void KRulerTest::slotSetXTrans(double d) { Q_UNUSED(d); #ifdef KRULER_ROTATE_TEST vruler->xtrans = d; vruler->update(); //debug("trans x %.1f", d); #endif } void KRulerTest::slotSetYTrans(double d) { Q_UNUSED(d); #ifdef KRULER_ROTATE_TEST vruler->ytrans = d; vruler->update(); //debug("trans y %.1f", d); #endif } /* --- MAIN -----------------------*/ int main(int argc, char **argv) { QApplication::setApplicationName(QStringLiteral("test")); QApplication *testapp; KRulerTest *window; testapp = new QApplication(argc, argv); testapp->setFont(QFont(QStringLiteral("Helvetica"), 12)); window = new KRulerTest(); window->setWindowTitle(QStringLiteral("KRulerTest")); window->resize(800, 600); window->show(); return testapp->exec(); } diff --git a/tests/krulertest.h b/tests/krulertest.h index 02e7743..8898167 100644 --- a/tests/krulertest.h +++ b/tests/krulertest.h @@ -1,85 +1,85 @@ /* -*- c++ -*- */ #ifndef krulertest_h #define krulertest_h #include <QCheckBox> #include <QRadioButton> #include <QFrame> class KRuler; class QWidget; class QGridLayout; class QCheckBox; class QGroupBox; class QLabel; class QSpinBox; class QDoubleSpinBox; class MouseWidget : public QFrame { Q_OBJECT public: - MouseWidget(QWidget *parent = 0); + MouseWidget(QWidget *parent = nullptr); Q_SIGNALS: void newXPos(int); void newYPos(int); void newWidth(int); void newHeight(int); protected: void mousePressEvent(QMouseEvent *) Q_DECL_OVERRIDE; void mouseReleaseEvent(QMouseEvent *) Q_DECL_OVERRIDE; void mouseMoveEvent(QMouseEvent *) Q_DECL_OVERRIDE; void resizeEvent(QResizeEvent *) Q_DECL_OVERRIDE; private: bool mouseButtonDown; }; class KRulerTest : public QWidget { Q_OBJECT public: KRulerTest(); ~KRulerTest(); private Q_SLOTS: void slotNewWidth(int); void slotNewHeight(int); void slotSetTinyMarks(bool); void slotSetLittleMarks(bool); void slotSetMediumMarks(bool); void slotSetBigMarks(bool); void slotSetEndMarks(bool); void slotSetRulerPointer(bool); void slotSetRulerLength(int); void slotFixRulerLength(bool); void slotSetMStyle(int); void slotUpdateShowMarks(); void slotCheckLength(bool); void slotSetRotate(double); void slotSetXTrans(double); void slotSetYTrans(double); private: KRuler *hruler, *vruler; QGridLayout *layout; QFrame *miniwidget, *bigwidget, *mainframe; QLabel *mouse_message; QGroupBox *showMarks, *lineEdit, *vertrot; QCheckBox *showTM, *showLM, *showMM, *showBM, *showEM, *showPT, *fixLen; QSpinBox *beginMark, *endMark, *lengthInput; QDoubleSpinBox *transX, *transY, *rotV; QGroupBox *metricstyle; QRadioButton *pixelmetric, *inchmetric, *mmmetric, *cmmetric, *mmetric; }; #endif diff --git a/tests/ktoolbartest.cpp b/tests/ktoolbartest.cpp index 7afe3e0..35d11cf 100644 --- a/tests/ktoolbartest.cpp +++ b/tests/ktoolbartest.cpp @@ -1,59 +1,59 @@ #include <QApplication> #include <QAction> #include <QMainWindow> #include <ktoolbar.h> #include <kactioncollection.h> // This is a test for "Automatically hide extra toolbar separators" // If several separators are next to each other, only one should show up. int main(int argc, char **argv) { QApplication::setApplicationName(QStringLiteral("kactiontest")); QApplication app(argc, argv); - KActionCollection coll(static_cast<QObject *>(0)); + KActionCollection coll(static_cast<QObject *>(nullptr)); QAction *action1 = coll.addAction(QStringLiteral("test1")); action1->setText(QStringLiteral("test1")); QAction *action2 = coll.addAction(QStringLiteral("test2")); action2->setText(QStringLiteral("test2")); QAction *action3 = coll.addAction(QStringLiteral("test3")); action3->setText(QStringLiteral("test3")); QAction *action4 = coll.addAction(QStringLiteral("test4")); action4->setText(QStringLiteral("test4")); QAction *action5 = coll.addAction(QStringLiteral("test5")); action5->setText(QStringLiteral("test5")); QAction *action6 = coll.addAction(QStringLiteral("test6")); action6->setText(QStringLiteral("test6")); QAction *action7 = coll.addAction(QStringLiteral("test7")); action7->setText(QStringLiteral("test7")); QMainWindow *mw = new QMainWindow(); KToolBar *tb = new KToolBar(mw); mw->addToolBar(tb); action2->setSeparator(true); action3->setSeparator(true); action7->setSeparator(true); coll.addAssociatedWidget(tb); mw->show(); app.exec(); mw->show(); action2->setVisible(false); app.exec(); mw->show(); action1->setVisible(false); return app.exec(); } diff --git a/tests/kwindowtest.cpp b/tests/kwindowtest.cpp index d676459..e30af35 100644 --- a/tests/kwindowtest.cpp +++ b/tests/kwindowtest.cpp @@ -1,439 +1,439 @@ #include <QMessageBox> #include <QTextEdit> #include <QtCore/QDir> #include <QTest> #include <stdlib.h> #include <QAction> #include <QApplication> #include <QStatusBar> #include <QComboBox> #include <khelpmenu.h> #include "kwindowtest.h" #include <QLineEdit> #include <kxmlguifactory.h> #include <kactionmenu.h> #include <kactioncollection.h> #include <ktoggleaction.h> TestWindow::TestWindow(QWidget *parent) : KXmlGuiWindow(parent) { ena = false; exitB = true; // exit button is shown lineL = true; // LineEdit is enabled greenF = false; // Frame not inserted - timer = 0; + timer = nullptr; setCaption(QStringLiteral("test window")); // The xmlgui file defines the layout of the menus and toolbars. // We only need to create actions with the right name here. // First four buttons fileNewAction = new QAction(QIcon::fromTheme(QStringLiteral("document-new")), QStringLiteral("Create.. (toggles upper button)"), this); actionCollection()->addAction(QStringLiteral("filenew"), fileNewAction); fileNewAction->setCheckable(true); connect(fileNewAction, SIGNAL(triggered(bool)), SLOT(slotNew())); QAction *fileOpenAction = new QAction(QIcon::fromTheme(QStringLiteral("document-open")), QStringLiteral("Open"), this); actionCollection()->addAction(QStringLiteral("fileopen"), fileOpenAction); connect(fileOpenAction, SIGNAL(triggered(bool)), SLOT(slotOpen())); KActionMenu *fileFloppyAction = new KActionMenu(QIcon::fromTheme(QStringLiteral("filefloppy")), QStringLiteral("Save (beep or delayed popup)"), this); actionCollection()->addAction(QStringLiteral("filefloppy"), fileFloppyAction); fileFloppyAction->setDelayed(true); connect(fileFloppyAction, SIGNAL(triggered(bool)), SLOT(slotSave())); QAction *filePrintAction = new QAction(QIcon::fromTheme(QStringLiteral("document-print")), QStringLiteral("Print (enables/disables open)"), this); actionCollection()->addAction(QStringLiteral("fileprint"), filePrintAction); filePrintAction->setToolTip(QStringLiteral("This tooltip does not work for menu items")); filePrintAction->setWhatsThis(QStringLiteral("This is the longer explanation of the action")); filePrintAction->setStatusTip(QStringLiteral("This action is supposed to print, but in fact enables/disables open")); connect(filePrintAction, SIGNAL(triggered(bool)), SLOT(slotPrint())); // And a combobox // arguments: text (or strList), ID, writable, signal, object, slot, enabled, // tooltiptext, size testComboBox = new QComboBox(toolBar()); //K3WidgetAction* comboAction = new K3WidgetAction(testComboBox, QString(), 0, 0, 0, actionCollection(), "combobox"); //connect(testComboBox, SIGNAL(activated(QString)), this, SLOT(slotList(QString))); // Then one line editor // arguments: text, id, signal, object (this), slot, enabled, tooltiptext, size testLineEdit = new QLineEdit(toolBar()); testLineEdit->setText(QStringLiteral("ftp://ftp.kde.org/pub/kde")); // K3WidgetAction* lineEditAction = new K3WidgetAction(testLineEdit, QString(), 0, 0, 0, actionCollection(), "location"); // connect(testLineEdit, SIGNAL(returnPressed()), this, SLOT(slotReturn())); // Now add another button and align it right exitAction = new QAction(QIcon::fromTheme(QStringLiteral("application-exit")), QStringLiteral("Exit"), this); actionCollection()->addAction(QStringLiteral("exit"), exitAction); connect(exitAction, SIGNAL(triggered(bool)), qApp, SLOT(quit())); // Another toolbar QAction *fileNewAction2 = new QAction(QIcon::fromTheme(QStringLiteral("document-new")), QStringLiteral("Create new file2 (Toggle)"), this); actionCollection()->addAction(QStringLiteral("filenew2"), fileNewAction2); connect(fileNewAction2, SIGNAL(toggled(bool)), this, SLOT(slotToggle(bool))); fileNewAction2->setToolTip(QStringLiteral("Tooltip")); fileNewAction2->setStatusTip(QStringLiteral("Statustip")); fileNewAction2->setWhatsThis(QStringLiteral("WhatsThis")); QAction *fileOpenAction2 = new QAction(QIcon::fromTheme(QStringLiteral("document-open")), QStringLiteral("Open (starts progress in sb)"), this); actionCollection()->addAction(QStringLiteral("fileopen2"), fileOpenAction2); connect(fileOpenAction2, SIGNAL(triggered(bool)), SLOT(slotOpen())); fileOpenAction2->setToolTip(QStringLiteral("This action starts a progressbar inside the statusbar")); QAction *fileFloppyAction2 = new QAction(QIcon::fromTheme(QStringLiteral("filefloppy")), QStringLiteral("Save file2 (autorepeat)"), this); actionCollection()->addAction(QStringLiteral("filefloppy2"), fileFloppyAction2); connect(fileFloppyAction2, SIGNAL(triggered(bool)), this, SLOT(slotSave())); itemsMenu = new QMenu; itemsMenu->addAction(QStringLiteral("delete/insert exit button"), this, SLOT(slotExit())); itemsMenu->addAction(QStringLiteral("enable/disable lineedit"), this, SLOT(slotLined())); itemsMenu->addAction(QStringLiteral("Toggle fileNew"), this, SLOT(slotNew())); itemsMenu->addAction(QStringLiteral("Combo: clear"), this, SLOT(slotClearCombo())); itemsMenu->addAction(QStringLiteral("Combo: insert list"), this, SLOT(slotInsertListInCombo())); itemsMenu->addAction(QStringLiteral("Combo: make item 3 current"), this, SLOT(slotMakeItem3Current())); itemsMenu->addAction(QStringLiteral("Important msg in statusbar"), this, SLOT(slotImportant())); QAction *filePrintAction2 = new QAction(QIcon::fromTheme(QStringLiteral("document-print")), QStringLiteral("Print (pops menu)"), this); actionCollection()->addAction(QStringLiteral("fileprint2"), filePrintAction2); filePrintAction2->setMenu(itemsMenu); // *** RADIO BUTTONS QActionGroup *radioGroup = new QActionGroup(this); radioGroup->setExclusive(true); KToggleAction *radioButton1 = new KToggleAction(QIcon::fromTheme(QStringLiteral("document-new")), QStringLiteral("Radiobutton1"), this); actionCollection()->addAction(QStringLiteral("radioButton1"), radioButton1); radioButton1->setActionGroup(radioGroup); KToggleAction *radioButton2 = new KToggleAction(QIcon::fromTheme(QStringLiteral("document-open")), QStringLiteral("Radiobutton2"), this); actionCollection()->addAction(QStringLiteral("radioButton2"), radioButton2); radioButton2->setActionGroup(radioGroup); KToggleAction *radioButton3 = new KToggleAction(QIcon::fromTheme(QStringLiteral("filefloppy")), QStringLiteral("Radiobutton3"), this); actionCollection()->addAction(QStringLiteral("radioButton3"), radioButton3); radioButton3->setActionGroup(radioGroup); KToggleAction *radioButton4 = new KToggleAction(QIcon::fromTheme(QStringLiteral("document-print")), QStringLiteral("Radiobutton4"), this); actionCollection()->addAction(QStringLiteral("radioButton4"), radioButton4); radioButton4->setActionGroup(radioGroup); connect(radioGroup, SIGNAL(triggered(QAction*)), this, SLOT(slotToggled(QAction*))); /**************************************************/ /*Now, we setup statusbar; order is not important. */ /**************************************************/ statusBar = new QStatusBar(this); //statusBar->insertItem("Hi there! ", 0); //statusBar->insertItem("Look for tooltips to see functions", 1); setStatusBar(statusBar); //DigitalClock *clk = new DigitalClock (statusBar); //clk->setFrameStyle(QFrame::NoFrame); //statusBar->insertWidget(clk, 70, 2); // Set main widget. In this example it is Qt's multiline text editor. widget = new QTextEdit(this); setCentralWidget(widget); // Setup is now complete setAutoSaveSettings(); // This is not strictly related to toolbars, menubars or KMainWindow. // Setup popup for completions completions = new QMenu; completions->addAction(QStringLiteral("/")); completions->addAction(QStringLiteral("/usr/")); completions->addAction(QStringLiteral("/lib/")); completions->addAction(QStringLiteral("/var/")); completions->addAction(QStringLiteral("/bin/")); completions->addAction(QStringLiteral("/kde/")); completions->addAction(QStringLiteral("/home/")); completions->addAction(QStringLiteral("/vmlinuz :-)")); connect(completions, SIGNAL(triggered(QAction*)), this, SLOT(slotCompletionsMenu(QAction*))); - pr = 0; + pr = nullptr; // KXMLGUIClient looks in the "data" resource for the .rc files // This line is for test programs only! qputenv("XDG_DATA_HOME", QFile::encodeName(QFileInfo(QFINDTESTDATA("kwindowtest.rc")).absolutePath())); setupGUI(QSize(400, 500), Default, QStringLiteral("kwindowtest.rc")); tb = toolBar(); tb1 = toolBar(QStringLiteral("AnotherToolBar")); } /***********************************/ /* Now slots for toolbar actions */ /***********************************/ void TestWindow::slotToggled(QAction *) { statusBar->showMessage(QStringLiteral("Button toggled"), 1500); } void TestWindow::slotInsertClock() { //DigitalClock *clock = new DigitalClock(tb1); //clock->setFrameStyle(QFrame::NoFrame); //tb1->insertWidget(8, 70, clock); } void TestWindow::slotNew() { //tb1->actions()[0]->toggle(); //toolBar()->removeAction( fileNewAction ); } void TestWindow::slotOpen() { - if (pr == 0) { + if (pr == nullptr) { pr = new QProgressBar(statusBar); pr->show(); } // statusBar->message(pr); if (!timer) { timer = new QTimer(this); connect(timer, SIGNAL(timeout()), this, SLOT(slotGoGoGoo())); } timer->start(100); } void TestWindow::slotGoGoGoo() { pr->setValue(pr->value() + 1); if (pr->value() == 100) { timer->stop(); statusBar->clearMessage(); delete pr; - pr = 0; + pr = nullptr; } } void TestWindow::slotSave() { qApp->beep(); statusBar->showMessage(QStringLiteral("Saving properties...")); } void TestWindow::slotPrint() { statusBar->showMessage(QStringLiteral("Print file pressed")); ena = !ena; qobject_cast<QAction *>(sender())->setEnabled(ena); } void TestWindow::slotReturn() { QString s = QStringLiteral("You entered "); s = s + testLineEdit->text(); statusBar->showMessage(s); } void TestWindow::slotList(const QString &str) { QString s = QStringLiteral("You chose "); s = s + str; statusBar->showMessage(s); } void TestWindow::slotCompletion() { // Now do a completion // Call your completing function and set that text in lineedit // QString s = tb->getLinedText(/* ID */ 4) // QString completed = complete (s); // tb->setLinedText(/* ID */ 4, completed.data()) // for now this: completions->popup(QCursor::pos()); // This popup should understunf keys up and down /* This is just an example. QLineEdit automatically sets cursor at end of string when ctrl-d or ctrl-s is pressed. KToolBar will also put cursor at end of text in LineEdit after inserting text with setLinedText (...). */ } void TestWindow::slotListCompletion() { /* Combo is not behaving good and it is ugly. I will see how it behaves in Qt-1.3, and then decide should I make a new combobox. */ QString s(testComboBox->currentText()); // get text in combo s += QStringLiteral("(completing)"); //tb->getCombo(4)->changeItem(s.data()); // setTextIncombo } void TestWindow::slotCompletionsMenu(QAction *action) { // Now set text in lined QString s = action->text(); testLineEdit->setText(s); // Cursor is automatically at the end of string after this } TestWindow::~TestWindow() { qDebug() << "kwindowtest finished"; } void TestWindow::beFixed() { widget->setFixedSize(400, 200); } void TestWindow::beYFixed() { widget->setMinimumSize(400, 200); widget->setMaximumSize(9999, 200); } void TestWindow::slotImportant() { statusBar->showMessage(QStringLiteral("This important message will go away in 15 seconds"), 15000); } void TestWindow::slotExit() { if (exitB == true) { tb->removeAction(exitAction); exitB = false; } else { if (tb->actions().count() >= 7) { tb->insertAction(tb->actions().at(6), exitAction); } else { tb->addAction(exitAction); } exitB = true; } } void TestWindow::slotLined() { lineL = !lineL; testLineEdit->setEnabled(lineL); // enable/disable lined } void TestWindow::slotToggle(bool on) { if (on == true) { statusBar->showMessage(QStringLiteral("Toggle is on")); } else { statusBar->showMessage(QStringLiteral("Toggle is off")); } } void TestWindow::slotFrame() { #if 0 if (greenF == false) { tb1->insertFrame(10, 100); tb1->alignItemRight(10); // this is pointless 'cause tb1 is not fullwidth QFrame *myFrame = tb1->getFrame(10); // get frame pointer if (myFrame == 0) { warning("bad frame ID"); return; } //paint it green // Or do whatever you want with it, just don't change its height (height = hardcoded = 24) // And don't move it // If you want to have something right from your toolbar you can reduce its // max_width with setMaxWidth() myFrame->setBackgroundColor(QColor("green")); greenF = true; } else { tb1->removeItem(10); greenF = false; } #endif } void TestWindow::slotMessage(int, bool boo) { if (boo) { statusBar->showMessage(QStringLiteral("This button does this and that"), 1500); } else { statusBar->clearMessage(); } } // Now few Combo slots, for Torben void TestWindow::slotClearCombo() { testComboBox->clear(); } void TestWindow::slotInsertListInCombo() { QStringList list; list.append(QStringLiteral("ListOne")); list.append(QStringLiteral("ListTwo")); list.append(QStringLiteral("ListThree")); list.append(QStringLiteral("ListFour")); list.append(QStringLiteral("ListFive")); list.append(QStringLiteral("ListSix")); list.append(QStringLiteral("ListSeven")); list.append(QStringLiteral("ListEight")); list.append(QStringLiteral("ListNine")); list.append(QStringLiteral("ListTen")); list.append(QStringLiteral("ListEleven")); list.append(QStringLiteral("ListAndSoOn")); testComboBox->addItems(list); } void TestWindow::slotMakeItem3Current() { testComboBox->setCurrentIndex(3); } int main(int argc, char *argv[]) { QApplication::setApplicationName(QStringLiteral("kwindowtest")); QApplication myApp(argc, argv); TestWindow *test = new TestWindow; myApp.setQuitOnLastWindowClosed(false); // don't quit after the messagebox! #if 0 int i = QMessageBox::information(0, "Select", "Select type of mainwidget", "Fixed", "Y-fixed", "Resizable"); if (i == 0) { test->beFixed(); } else if (i == 1) { test->beYFixed(); } #endif test->show(); myApp.setQuitOnLastWindowClosed(true); int ret = myApp.exec(); //delete test; return ret; } diff --git a/tests/kwindowtest.h b/tests/kwindowtest.h index c090c05..59ca8c6 100644 --- a/tests/kwindowtest.h +++ b/tests/kwindowtest.h @@ -1,68 +1,68 @@ #ifndef testwindow_h #define testwindow_h #include <QtCore/QTimer> #include <QProgressBar> #include <QStatusBar> #include <QMenuBar> #include <ktoolbar.h> #include <kxmlguiwindow.h> class QTextEdit; class QComboBox; class QLineEdit; class TestWindow : public KXmlGuiWindow { Q_OBJECT public: - TestWindow(QWidget *parent = 0); + TestWindow(QWidget *parent = nullptr); ~TestWindow(); public Q_SLOTS: void beFixed(); void beYFixed(); void slotNew(); void slotPrint(); void slotReturn(); void slotSave(); void slotList(const QString &str); void slotOpen(); void slotCompletion(); void slotCompletionsMenu(QAction *action); void slotInsertClock(); void slotLined(); void slotImportant(); void slotExit(); void slotFrame(); void slotListCompletion(); void slotMessage(int, bool); void slotToggle(bool); void slotClearCombo(); void slotGoGoGoo(); void slotInsertListInCombo(); void slotMakeItem3Current(); void slotToggled(QAction *action); protected: QMenu *itemsMenu; QMenu *completions; QStatusBar *statusBar; KToolBar *tb; KToolBar *tb1; class QLineEdit *testLineEdit; class QComboBox *testComboBox; QAction *fileNewAction; QAction *exitAction; bool lineL; bool exitB; bool greenF; bool ena; QTextEdit *widget; QTimer *timer; QProgressBar *pr; }; #endif diff --git a/tests/kxmlguiwindowtest.cpp b/tests/kxmlguiwindowtest.cpp index 54ceb28..cebee6b 100644 --- a/tests/kxmlguiwindowtest.cpp +++ b/tests/kxmlguiwindowtest.cpp @@ -1,120 +1,120 @@ /* This file is part of the KDE libraries Copyright (C) 2008 Rafael Fernández López <ereslibre@kde.org> 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 <QAction> #include <QTextEdit> #include <QTimer> #include <QApplication> #include <QStandardPaths> #include <QTest> #include <kxmlguiwindow.h> #include <kactioncollection.h> #include <kmessagebox.h> #include <kconfiggroup.h> // BUG: if this symbol is defined the problem consists on: // - main window is created. // - settings are saved (and applied), but in this case no toolbars exist yet, so they don't // apply to any toolbar. // - after 1 second the GUI is created. // // How to reproduce ? // - Move one toolbar to other place (bottom, left, right, or deattach it). // - Close the test (so settings are saved). // - Reopen the test. The toolbar you moved is not keeping the place you specified. #define REPRODUCE_TOOLBAR_BUG class MainWindow : public KXmlGuiWindow { Q_OBJECT public: - MainWindow(QWidget *parent = 0); + MainWindow(QWidget *parent = nullptr); public Q_SLOTS: void slotTest(); void slotCreate(); private: void setupActions(); }; void MainWindow::slotTest() { - KMessageBox::information(0, QStringLiteral("Test"), QStringLiteral("Test")); + KMessageBox::information(nullptr, QStringLiteral("Test"), QStringLiteral("Test")); } void MainWindow::slotCreate() { setupGUI(ToolBar|Keys); createGUI(xmlFile()); if (autoSaveConfigGroup().isValid()) { applyMainWindowSettings(autoSaveConfigGroup()); } } void MainWindow::setupActions() { QAction *testAction = new QAction(this); testAction->setText(QStringLiteral("Test")); testAction->setIcon(QIcon::fromTheme(QStringLiteral("kde"))); actionCollection()->addAction(QStringLiteral("test"), testAction); connect(testAction, SIGNAL(triggered(bool)), this, SLOT(slotTest())); KStandardAction::quit(qApp, SLOT(quit()), actionCollection()); setAutoSaveSettings(); // BUG: if the GUI is created after an amount of time (so settings have been saved), then toolbars // are shown misplaced. KMainWindow uses a 500 ms timer to save window settings. #ifdef REPRODUCE_TOOLBAR_BUG QTimer::singleShot(1000, this, SLOT(slotCreate())); // more than 500 ms so the main window has saved settings. // We can think of this case on natural applications when they // load plugins and change parts. It can take 1 second perfectly. #else QTimer::singleShot(0, this, SLOT(slotCreate())); #endif } MainWindow::MainWindow(QWidget *parent) : KXmlGuiWindow(parent) { setXMLFile(QFINDTESTDATA("kxmlguiwindowtestui.rc"), true); // Because we use a full path in setXMLFile, we need to call setLocalXMLFile too. // In your apps, just pass a relative filename to setXMLFile instead. setLocalXMLFile(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1Char('/') + QStringLiteral("kxmlguiwindowtest/kxmlguiwindowtestui.rc")); setCentralWidget(new QTextEdit(this)); setupActions(); } int main(int argc, char **argv) { QApplication app(argc, argv); MainWindow *mainWindow = new MainWindow; mainWindow->show(); return app.exec(); } #include "kxmlguiwindowtest.moc"