diff --git a/src/kaboutapplicationdialog.cpp b/src/kaboutapplicationdialog.cpp index 2e6a410..fd96e25 100644 --- a/src/kaboutapplicationdialog.cpp +++ b/src/kaboutapplicationdialog.cpp @@ -1,155 +1,155 @@ /* This file is part of the KDE libraries Copyright (C) 2007 Urs Wolfer Copyright (C) 2008 Friedrich W. H. Kossebau Copyright (C) 2010 Teo Mrnjavac 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. */ #include "kaboutapplicationdialog.h" #include "kabstractaboutdialog_p.h" #include "../kxmlgui_version.h" // KF #include #include #include // Qt #include #include #include #include class Q_DECL_HIDDEN KAboutApplicationDialog::Private : public KAbstractAboutDialogPrivate { public: Private(const KAboutData &aboutData, KAboutApplicationDialog *parent) : q(parent) , aboutData(aboutData) {} void init(Options opt); private: KAboutApplicationDialog * const q; const KAboutData aboutData; }; KAboutApplicationDialog::KAboutApplicationDialog(const KAboutData &aboutData, QWidget *parent) : KAboutApplicationDialog(aboutData, NoOptions, parent) { } KAboutApplicationDialog::KAboutApplicationDialog(const KAboutData &aboutData, Options opt, QWidget *parent) : QDialog(parent) , d(new Private(aboutData, this)) { d->init(opt); } void KAboutApplicationDialog::Private::init(Options opt) { - q->setWindowTitle(i18n("About %1", aboutData.displayName())); + q->setWindowTitle(i18nc("@title:window", "About %1", aboutData.displayName())); //Set up the title widget... QPixmap titlePixmap; if (aboutData.programLogo().canConvert()) { titlePixmap = aboutData.programLogo().value(); } else if (aboutData.programLogo().canConvert()) { titlePixmap = QPixmap::fromImage(aboutData.programLogo().value()); } else { QIcon windowIcon = qApp->windowIcon(); // Legacy support for deprecated KAboutData::programIconName() QT_WARNING_PUSH QT_WARNING_DISABLE_CLANG("-Wdeprecated-declarations") QT_WARNING_DISABLE_GCC("-Wdeprecated-declarations") if (windowIcon.isNull() && !aboutData.programIconName().isEmpty()) { windowIcon = QIcon::fromTheme(aboutData.programIconName()); } QT_WARNING_POP titlePixmap = windowIcon.pixmap(48, 48); } QWidget *titleWidget = createTitleWidget(titlePixmap, aboutData.displayName(), aboutData.version(), q); //Then the tab bar... QTabWidget *tabWidget = new QTabWidget; tabWidget->setUsesScrollButtons(false); //Set up the first page... QWidget *aboutWidget = createAboutWidget(aboutData.shortDescription(), aboutData.otherText(), aboutData.copyrightStatement(), aboutData.homepage(), aboutData.licenses(), q); - tabWidget->addTab(aboutWidget, i18n("About")); + tabWidget->addTab(aboutWidget, i18nc("@title:tab", "About")); // Version QWidget *versionWidget = new QWidget(q); QVBoxLayout *versionLayout = new QVBoxLayout; if (!(opt & HideKdeVersion)) { QLabel *versionLabel = new QLabel( i18n("
  • KDE Frameworks %1
  • Qt %2 (built against %3)
  • The %4 windowing system
", QStringLiteral(KXMLGUI_VERSION_STRING), QString::fromLocal8Bit(qVersion()), QStringLiteral(QT_VERSION_STR), QGuiApplication::platformName())); versionLabel->setTextInteractionFlags(Qt::TextSelectableByMouse); versionLayout->addWidget(versionLabel); } versionLayout->addStretch(); versionWidget->setLayout(versionLayout); - tabWidget->addTab(versionWidget, i18n("Libraries")); + tabWidget->addTab(versionWidget, i18nc("@title:tab", "Libraries")); //And here we go, authors page... const int authorCount = aboutData.authors().count(); if (authorCount) { QWidget *authorWidget = createAuthorsWidget(aboutData.authors(), aboutData.ocsProviderUrl(), aboutData.customAuthorTextEnabled(), aboutData.customAuthorRichText(), aboutData.bugAddress(), q); - const QString authorPageTitle = i18np("Author", "Authors", authorCount); + const QString authorPageTitle = i18ncp("@title:tab", "Author", "Authors", authorCount); tabWidget->addTab(authorWidget, authorPageTitle); } //And credits page... if (!aboutData.credits().isEmpty()) { QWidget *creditWidget = createCreditWidget(aboutData.credits(), aboutData.ocsProviderUrl(), q); - tabWidget->addTab(creditWidget, i18n("Thanks To")); + tabWidget->addTab(creditWidget, i18nc("@title:tab", "Thanks To")); } //Finally, the optional translators page... if (!(opt & HideTranslators) && !aboutData.translators().isEmpty()) { QWidget *translatorWidget = createTranslatorsWidget(aboutData.translators(), aboutData.ocsProviderUrl(), q); tabWidget->addTab(translatorWidget, i18nc("@title:tab", "Translation")); } createForm(titleWidget, tabWidget, q); } KAboutApplicationDialog::~KAboutApplicationDialog() { delete d; // The delegate wants to be deleted before the items it created, otherwise // complains bitterly about it qDeleteAll(findChildren()); } diff --git a/src/kaboutapplicationpersonlistdelegate_p.cpp b/src/kaboutapplicationpersonlistdelegate_p.cpp index 144b8dc..6cd9a95 100644 --- a/src/kaboutapplicationpersonlistdelegate_p.cpp +++ b/src/kaboutapplicationpersonlistdelegate_p.cpp @@ -1,307 +1,307 @@ /* This file is part of the KDE libraries Copyright (C) 2010 Teo Mrnjavac 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 . */ #include "kaboutapplicationpersonlistdelegate_p.h" #include "kaboutapplicationpersonmodel_p.h" #include "kaboutapplicationpersonlistview_p.h" #include "ktoolbar.h" #include #include #include #include #include #include #include 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("mail-send")), 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("internet-services")), 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, &QToolBar::actionTriggered, this, &KAboutApplicationPersonListDelegate::launchUrl); connect(socialLinks, &QToolBar::actionTriggered, this, &KAboutApplicationPersonListDelegate::launchUrl); return list; } void KAboutApplicationPersonListDelegate::updateItemWidgets(const QList 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", + action->setToolTip(i18nc("@info:tooltip Action to send an email to a contributor", "Email contributor\n%1", profile.email())); action->setData(QString(QLatin1String("mailto:") + 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->setToolTip(i18nc("@info:tooltip", "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->setToolTip(i18nc("@info:tooltip", "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; const auto links = profile.ocsLinks(); for (const KAboutApplicationPersonProfileOcsLink &link : links) { 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())); + action->setToolTip(i18nc("@info:tooltip", "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())); + action->setToolTip(i18nc("@info:tooltip", "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())); + action->setToolTip(i18nc("@info:tooltip", "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->setToolTip(i18nc("@info:tooltip", "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, 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 = QLatin1String("") + i18nc("@item Contributor name in about dialog.", "%1", profile.name()) + QLatin1String(""); if (!profile.task().isEmpty()) { text += QLatin1String("\n
") + profile.task() + QLatin1String(""); } if (!profile.location().isEmpty()) { text += QLatin1String("\n
") + 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/kaboutkdedialog_p.cpp b/src/kaboutkdedialog_p.cpp index ce78059..706ecf4 100644 --- a/src/kaboutkdedialog_p.cpp +++ b/src/kaboutkdedialog_p.cpp @@ -1,162 +1,162 @@ /* This file is part of the KDE libraries Copyright (C) 2007 Urs Wolfer Parts of this class have been take from the KAboutKDE class, which was Copyright (C) 2000 Espen Sand 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 #include #include #include #include #include #include #include #include "../kxmlgui_version.h" namespace KDEPrivate { KAboutKdeDialog::KAboutKdeDialog(QWidget *parent) : QDialog(parent), d(nullptr) { - setWindowTitle(i18n("About KDE")); + setWindowTitle(i18nc("@title:window", "About KDE")); KTitleWidget *titleWidget = new KTitleWidget(this); titleWidget->setText(i18n("KDE - Be Free!")); 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("" "KDE is a world-wide community of software engineers, artists, writers, " "translators and creators who are committed to Free Software " "development. KDE produces the Plasma desktop environment, hundreds of applications, " "and the many software libraries that support them.

" "KDE is a cooperative enterprise: no single entity controls its direction or products. " "Instead, we work together to achieve the common goal of building the world's finest " "Free Software. Everyone is welcome to join and contribute to KDE, including you.

" "Visit %3 for " "more information about the KDE community and the software we produce.", QStringLiteral("https://www.gnu.org/philosophy/free-sw.html"), QStringLiteral("https://community.kde.org/Get_Involved"), QStringLiteral("https://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("" "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.

" "KDE has a bug tracking system. Visit " "%1 or " "use the \"Report Bug...\" dialog from the \"Help\" menu to report bugs.

" "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\".", 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("" "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!" "

" "Visit " "%1 " "for information on some projects in which you can participate." "

" "If you need more information or documentation, then a visit to " "%2 " "will provide you with what you need.", QStringLiteral("https://www.kde.org/community/getinvolved/"), QStringLiteral("https://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("" "KDE software is and will always be available free of charge, however creating it is not free.

" "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 %1" " for information on KDE e.V.

" "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.

" "We would like to encourage you to support our efforts with a " "financial donation, using one of the ways described at " "%2." "

Thank you very much in advance for your support.", QStringLiteral("https://ev.kde.org/"), QStringLiteral("https://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")); + tabWidget->addTab(about, i18nc("@title:tab About KDE", "&About")); + tabWidget->addTab(report, i18nc("@title:tab", "&Report Bugs or Wishes")); + tabWidget->addTab(join, i18nc("@title:tab", "&Join KDE")); + tabWidget->addTab(support, i18nc("@title:tab", "&Support KDE")); QLabel *image = new QLabel; QIcon icon(QStringLiteral(":/kxmlgui5/aboutkde.svg")); image->setPixmap(icon.pixmap(150, 250)); QHBoxLayout *midLayout = new QHBoxLayout; midLayout->addWidget(image); midLayout->addWidget(tabWidget); QDialogButtonBox *buttonBox = new QDialogButtonBox; buttonBox->setStandardButtons(QDialogButtonBox::Close); connect(buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept); connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject); QVBoxLayout *mainLayout = new QVBoxLayout; mainLayout->addWidget(titleWidget); mainLayout->addLayout(midLayout); mainLayout->addWidget(buttonBox); setLayout(mainLayout); } } diff --git a/src/kaboutplugindialog.cpp b/src/kaboutplugindialog.cpp index 2494041..c07f3bf 100644 --- a/src/kaboutplugindialog.cpp +++ b/src/kaboutplugindialog.cpp @@ -1,120 +1,120 @@ /* This file is part of the KDE libraries Copyright (C) 2007 Urs Wolfer Copyright (C) 2008, 2019 Friedrich W. H. Kossebau Copyright (C) 2010 Teo Mrnjavac 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. */ #include "kaboutplugindialog.h" #include "kabstractaboutdialog_p.h" // KF #include #include #include #include // Qt #include #include class KAboutPluginDialogPrivate : public KAbstractAboutDialogPrivate { public: KAboutPluginDialogPrivate(const KPluginMetaData &pluginMetaData, KAboutPluginDialog *parent) : q(parent) , pluginMetaData(pluginMetaData) , pluginLicense(KAboutLicense::byKeyword(pluginMetaData.license())) {} void init(KAboutPluginDialog::Options opt); public: KAboutPluginDialog * const q; const KPluginMetaData pluginMetaData; const KAboutLicense pluginLicense; }; KAboutPluginDialog::KAboutPluginDialog(const KPluginMetaData &pluginMetaData, QWidget *parent) : KAboutPluginDialog(pluginMetaData, NoOptions, parent) { } KAboutPluginDialog::KAboutPluginDialog(const KPluginMetaData &pluginMetaData, Options opt, QWidget *parent) : QDialog(parent) , d(new KAboutPluginDialogPrivate(pluginMetaData,this)) { d->init(opt); } KAboutPluginDialog::~KAboutPluginDialog() { // The delegates want to be deleted before the items it created qDeleteAll(findChildren()); } void KAboutPluginDialogPrivate::init(KAboutPluginDialog::Options opt) { - q->setWindowTitle(i18n("About %1", pluginMetaData.name())); + q->setWindowTitle(i18nc("@title:window", "About %1", pluginMetaData.name())); //Set up the title widget... const QIcon pluginIcon = !pluginMetaData.iconName().isEmpty() ? QIcon::fromTheme(pluginMetaData.iconName()) : qApp->windowIcon(); QWidget *titleWidget = createTitleWidget(pluginIcon.pixmap(48, 48), pluginMetaData.name(), pluginMetaData.version(), q); //Then the tab bar... QTabWidget *tabWidget = new QTabWidget; tabWidget->setUsesScrollButtons(false); //Set up the first page... QWidget *aboutWidget = createAboutWidget(pluginMetaData.description(), pluginMetaData.extraInformation(), pluginMetaData.copyrightText(), pluginMetaData.website(), {pluginLicense}, q); - tabWidget->addTab(aboutWidget, i18n("About")); + tabWidget->addTab(aboutWidget, i18nc("@title:tab", "About")); //And here we go, authors page... const int authorCount = pluginMetaData.authors().count(); if (authorCount) { // TODO: add bug report address to plugin metadata QWidget *authorWidget = createAuthorsWidget(pluginMetaData.authors(), QString(), false, QString(), QString(), q); - const QString authorPageTitle = i18np("Author", "Authors", authorCount); + const QString authorPageTitle = i18ncp("@title:tab", "Author", "Authors", authorCount); tabWidget->addTab(authorWidget, authorPageTitle); } //And credits page... if (!pluginMetaData.otherContributors().isEmpty()) { QWidget *creditWidget = createCreditWidget(pluginMetaData.otherContributors(), QString(), q); - tabWidget->addTab(creditWidget, i18n("Thanks To")); + tabWidget->addTab(creditWidget, i18nc("@title:tab", "Thanks To")); } //Finally, the optional translators page... if (!(opt & KAboutPluginDialog::HideTranslators) && !pluginMetaData.translators().isEmpty()) { QWidget *translatorWidget = createTranslatorsWidget(pluginMetaData.translators(), QString(), q); tabWidget->addTab(translatorWidget, i18nc("@title:tab", "Translation")); } createForm(titleWidget, tabWidget, q); } diff --git a/src/kactionconflictdetector.cpp b/src/kactionconflictdetector.cpp index 609de86..8bd44f3 100644 --- a/src/kactionconflictdetector.cpp +++ b/src/kactionconflictdetector.cpp @@ -1,71 +1,71 @@ /* This file is part of the KDE libraries Copyright (C) 1999 Reginald Stadlbauer (C) 1999 Simon Hausmann (C) 2000 Nicolas Hadacek (C) 2000 Kurt Granroth (C) 2000 Michael Koch (C) 2001 Holger Freyther (C) 2002 Ellis Whitehead (C) 2002 Joseph Wenninger (C) 2005-2006 Hamish Rodda This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include class KActionConflictDetector : public QObject { Q_OBJECT public: explicit KActionConflictDetector(QObject *parent = nullptr) : QObject(parent) { } bool eventFilter(QObject *watched, QEvent *event) override { if (qobject_cast(watched) && (event->type() == QEvent::Shortcut)) { QShortcutEvent *se = static_cast(event); if (se->isAmbiguous()) { KMessageBox::information( 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")); + i18nc("@title:window", "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) #include "kactionconflictdetector.moc" diff --git a/src/kbugreport.cpp b/src/kbugreport.cpp index 62dfd56..7c25eb7 100644 --- a/src/kbugreport.cpp +++ b/src/kbugreport.cpp @@ -1,592 +1,593 @@ /* This file is part of the KDE project Copyright (C) 1999 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 "kbugreport.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kdepackages.h" #include "../kxmlgui_version.h" #include "systeminformation_p.h" #include "config-xmlgui.h" #include class KBugReportPrivate { public: KBugReportPrivate(KBugReport *q): q(q), m_aboutData(KAboutData::applicationData()) {} enum BugDestination { BugsKdeOrg, CustomEmail, CustomUrl }; void _k_slotConfigureEmail(); void _k_slotSetFrom(); void _k_appChanged(int); void _k_updateUrl(); KBugReport *q; QProcess *m_process; KAboutData m_aboutData; QTextEdit *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 severityButtons; int currentSeverity() { for (int i = 0; i < severityButtons.count(); i++) if (severityButtons[i]->isChecked()) { return i; } return -1; } BugDestination bugDestination; }; KBugReport::KBugReport(const KAboutData &aboutData, QWidget *_parent) : QDialog(_parent), d(new KBugReportPrivate(this)) { - setWindowTitle(i18n("Submit Bug Report")); + setWindowTitle(i18nc("@title:window", "Submit Bug Report")); QDialogButtonBox *buttonBox = new QDialogButtonBox(this); buttonBox->setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); connect(buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept); connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject); d->m_aboutData = aboutData; d->m_process = nullptr; d->bugDestination = KBugReportPrivate::CustomEmail; const QString bugAddress = d->m_aboutData.bugAddress(); if (bugAddress == QLatin1String("submit@bugs.kde.org")) { // This is a core KDE application -> redirect to the web form d->bugDestination = KBugReportPrivate::BugsKdeOrg; } else if (!QUrl(bugAddress).scheme().isEmpty()) { // The bug reporting address is a URL -> redirect to that d->bugDestination = KBugReportPrivate::CustomUrl; } if (d->bugDestination != KBugReportPrivate::CustomEmail) { 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->bugDestination == KBugReportPrivate::CustomEmail) { // 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); + d->m_configureEmail = new QPushButton(i18nc("@action:button", "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()))); + KGuiItem(i18nc("@action:button", "&Send"), QStringLiteral("mail-send"), + i18nc("@info:tooltip", "Send bug report."), + i18nc("@info:whatsthis", "Send this bug report to %1.", d->m_aboutData.bugAddress()))); row++; } else { 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) + QLatin1String(", ") + QStringLiteral(XMLGUI_DISTRIBUTION_TEXT); if (d->bugDestination != KBugReportPrivate::BugsKdeOrg) { 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->bugDestination == KBugReportPrivate::CustomEmail) { // Severity - d->m_bgSeverity = new QGroupBox(i18n("Se&verity"), this); + d->m_bgSeverity = new QGroupBox(i18nc("@title:group", "Se&verity"), this); struct SeverityData { QString name; QString text; }; const std::array severityData = { { { QStringLiteral("critical"), i18nc("bug severity", "Critical") }, { QStringLiteral("grave"), i18nc("bug severity", "Grave") }, { QStringLiteral("normal"), i18nc("bug severity", "Normal") }, { QStringLiteral("wishlist"), i18nc("bug severity", "Wishlist") }, { QStringLiteral("i18n"), i18nc("bug severity", "Translation") }, } }; QHBoxLayout *severityLayout = new QHBoxLayout(d->m_bgSeverity); for (auto& severityDatum : severityData) { // Store the severity string as the name QRadioButton *rb = new QRadioButton(severityDatum.text, d->m_bgSeverity); rb->setObjectName(severityDatum.name); d->severityButtons.append(rb); severityLayout->addWidget(rb); } d->severityButtons[2]->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 QTextEdit(this); d->m_lineedit->setMinimumHeight(180); // make it big d->m_lineedit->setWordWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere); d->m_lineedit->setLineWrapMode(QTextEdit::WidgetWidth); lay->addWidget(d->m_lineedit, 10 /*stretch*/); d->_k_slotSetFrom(); } else { // Point to the web form QString text; if (d->bugDestination == KBugReportPrivate::BugsKdeOrg) { text = i18n("To submit a bug report, click on the button below. This will open a web browser " "window on https://bugs.kde.org where you will find " "a form to fill in. The information displayed above will be transferred to that server."); d->_k_updateUrl(); } else { text = i18n("To submit a bug report, click on the button below. This will open a web browser " "window on %2.", bugAddress, bugAddress); d->url = QUrl(bugAddress); } lay->addSpacing(10); 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(); QPushButton *okButton = buttonBox->button(QDialogButtonBox::Ok); if (d->bugDestination == KBugReportPrivate::BugsKdeOrg) { - okButton->setText(i18n("&Launch Bug Report Wizard")); + okButton->setText(i18nc("@action:button", "&Launch Bug Report Wizard")); } else { - okButton->setText(i18n("&Submit Bug Report")); + okButton->setText(i18nc("@action:button", "&Submit Bug Report")); } 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->bugDestination == KBugReportPrivate::CustomEmail) { return d->m_lineedit->toPlainText(); } else { return QString(); } } void KBugReport::setMessageBody(const QString &messageBody) { if (d->bugDestination == KBugReportPrivate::CustomEmail) { 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.truncate(index); } //qCDebug(DEBUG_KXMLGUI) << "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 (bugDestination != KBugReportPrivate::BugsKdeOrg) { m_strVersion += QLatin1Char(' ') + kde_version; strDisplayVersion += QLatin1Char(' ') + kde_version; } m_version->setText(strDisplayVersion); if (bugDestination == KBugReportPrivate::BugsKdeOrg) { _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("user_manager")); if (!m_process->waitForStarted()) { //qCDebug(DEBUG_KXMLGUI) << "Couldn't start kcmshell5.."; delete m_process; m_process = nullptr; return; } m_configureEmail->setEnabled(false); } void KBugReportPrivate::_k_slotSetFrom() { delete m_process; 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 + QLatin1Char('>'); } } m_from->setText(fromaddr); } void KBugReport::accept() { if (d->bugDestination != KBugReportPrivate::CustomEmail) { 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( "

You chose the severity Critical. " "Please note that this severity is intended only for bugs that:

" "
  • break unrelated software on the system (or the whole system)
  • " "
  • cause serious data loss
  • " "
  • introduce a security hole on the system where the affected package is installed
\n" "

Does the bug you are reporting cause any of the above damage? " "If it does not, please select a lower severity. Thank you.

"), QString(), KStandardGuiItem::cont(), KStandardGuiItem::cancel()) == KMessageBox::No) { return; } break; case 1: // grave if (KMessageBox::questionYesNo(this, i18n( "

You chose the severity Grave. " "Please note that this severity is intended only for bugs that:

" "
  • make the package in question unusable or mostly so
  • " "
  • cause data loss
  • " "
  • introduce a security hole allowing access to the accounts of users who use the affected package
\n" "

Does the bug you are reporting cause any of the above damage? " "If it does not, please select a lower severity. Thank you.

"), 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 https://bugs.kde.org/ for instructions."); KMessageBox::error(this, msg + QLatin1String("\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->bugDestination == KBugReportPrivate::CustomEmail && ((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()); + i18nc("@title:window", "Close Message"), + KStandardGuiItem::discard(), KStandardGuiItem::cont()); if (rc == KMessageBox::No) { e->ignore(); return; } } QDialog::closeEvent(e); } QString KBugReport::text() const { //qCDebug(DEBUG_KXMLGUI) << 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 == QLatin1String("i18n") && QLocale().language() != QLocale::system().language()) { // Case 1 : i18n bug QString package = QLatin1String("i18n_") + QLocale::languageToString(QLocale().language()); package.replace(QLatin1Char('_'), QLatin1Char('-')); return QLatin1String("Package: ") + package + QLatin1String("\nApplication: ") + appname + QLatin1String("\nVersion: ") + d->m_strVersion + // not really i18n's version, so better here IMHO QLatin1Char('\n') + os + QLatin1Char('\n') + bodyText; } else { appname.replace(QLatin1Char('_'), QLatin1Char('-')); // Case 2 : normal bug return QLatin1String("Package: ") + appname + QLatin1String("\nVersion: ") + d->m_strVersion + QLatin1String("\nSeverity: ") + severity + QLatin1String("\nCompiler: ") + QStringLiteral(XMLGUI_COMPILER_VERSION) + QLatin1Char('\n') + os + QLatin1Char('\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); //qCDebug(DEBUG_KXMLGUI) << command << args; if (!proc.waitForStarted()) { qCritical() << "Unable to open a pipe to " << command; return false; } proc.write(text().toUtf8()); proc.closeWriteChannel(); proc.waitForFinished(); //qCDebug(DEBUG_KXMLGUI) << "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/kedittoolbar.cpp b/src/kedittoolbar.cpp index 41cb36d..2e2b94c 100644 --- a/src/kedittoolbar.cpp +++ b/src/kedittoolbar.cpp @@ -1,1743 +1,1743 @@ /* This file is part of the KDE libraries Copyright (C) 2000 Kurt Granroth Copyright (C) 2006 Hamish Rodda Copyright 2007 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 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 "debug.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kactioncollection.h" #include "kxmlguifactory.h" #include "ktoolbar.h" #include "ktoolbarhelper_p.h" #include "../kxmlgui_version.h" static const char separatorstring[] = I18N_NOOP("--- separator ---"); static const char spacerstring[] = I18N_NOOP("--- expanding spacer ---"); #define SEPARATORSTRING i18n(separatorstring) #define SPACERSTRING i18n(spacerstring) //static const char *const s_XmlTypeToString[] = { "Shell", "Part", "Local", "Merged" }; typedef QList 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() == QLatin1String("ToolBar")) { if (elem.attribute(QStringLiteral("noEdit")) != QLatin1String("true")) { list.append(elem); } } else { if (elem.tagName() != QLatin1String("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 { QString name = KToolbarHelper::i18nToolBarName(it); // 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(QStringLiteral("name"))); name += QLatin1String(" <") + doc_name + QLatin1Char('>'); } return name; } typedef QList 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_isSpacer(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 setSpacer(bool spacer) { m_isSpacer = spacer; } 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 isSpacer() const { return m_isSpacer; } bool isTextAlongsideIconHidden() const { return m_isTextAlongsideIconHidden; } int index() const { return listWidget()->row(const_cast(this)); } private: QString m_internalTag; QString m_internalName; QString m_statusText; bool m_isSeparator; bool m_isSpacer; 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.isSpacer(); 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 spacer; s >> spacer; item.setSpacer(spacer); 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 items) const { if (items.isEmpty()) { return nullptr; } QMimeData *mimedata = new QMimeData(); QByteArray data; { QDataStream stream(&data, QIODevice::WriteOnly); // we only support single selection ToolBarItem *item = static_cast(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(QListWidget::currentItem()); } IconTextEditDialog::IconTextEditDialog(QWidget *parent) : QDialog(parent) { - setWindowTitle(i18n("Change Text")); + setWindowTitle(i18nc("@title:window", "Change Text")); setModal(true); QVBoxLayout *layout = new QVBoxLayout; setLayout(layout); QGridLayout *grid = new QGridLayout; grid->setContentsMargins(0, 0, 0, 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); + m_cbHidden = new QCheckBox(i18nc("@option:check", "&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, &QDialogButtonBox::accepted, this, &QDialog::accept); connect(m_buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject); layout->addWidget(m_buttonBox); connect(m_lineEdit, &QLineEdit::textChanged, this, &IconTextEditDialog::slotTextChanged); 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(nullptr), m_loadedOnce(false) { m_componentName = cName; m_isPart = false; 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 + QLatin1String("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); //qCDebug(DEBUG_KXMLGUI) << "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 { for (const auto &xmlFile : m_xmlFiles) { xmlFile.dump(); } } #endif QComboBox *m_toolbarCombo; QToolButton *m_upAction; QToolButton *m_removeAction; QToolButton *m_insertAction; QToolButton *m_downAction; //QValueList m_actionList; KActionCollection *m_collection; KEditToolBarWidget * const 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(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 = nullptr; q->setDefaultToolBar(QString()); - q->setWindowTitle(i18n("Configure Toolbars")); + q->setWindowTitle(i18nc("@title:window", "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*))); QObject::connect(m_buttonBox, &QDialogButtonBox::rejected, q, &QDialog::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 = nullptr; m_accept = false; if (m_factory) { const auto clients = m_factory->clients(); for (KXMLGUIClient *client : 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)) { qCWarning(DEBUG_KXMLGUI) << "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.remove(0, slash); } const QString xml_file = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1String("/kxmlgui5/") + QCoreApplication::instance()->applicationName() + QLatin1Char('/') + m_file; if (QFile::exists(xml_file)) if (!QFile::remove(xml_file)) { qCWarning(DEBUG_KXMLGUI) << "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(); #if KXMLGUI_BUILD_DEPRECATED_SINCE(4, 0) emit q->newToolbarConfig(); // compat #endif } 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(); #if KXMLGUI_BUILD_DEPRECATED_SINCE(4, 0) emit q->newToolbarConfig(); // compat #endif } q->accept(); } void KEditToolBarPrivate::applyClicked() { (void)m_widget->save(); _k_enableApply(false); emit q->newToolBarConfig(); #if KXMLGUI_BUILD_DEPRECATED_SINCE(4, 0) emit q->newToolbarConfig(); // compat #endif } 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; const auto clients = factory->clients(); for (KXMLGUIClient *client : 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); const auto widgetActions = m_widget->actionCollection()->actions(); for (QAction *action : widgetActions) { action->setShortcutContext(Qt::WidgetWithChildrenShortcut); } } void KEditToolBarWidget::save() { //qDebug(240) << "KEditToolBarWidget::save"; for (const auto &xmlFile : qAsConst(d->m_xmlFiles)) { // let's not save non-modified files if (!xmlFile.m_isModified) { continue; } // let's also skip (non-existent) merged files if (xmlFile.type() == XmlData::Merged) { continue; } // Add noMerge="1" to all the menus since we are saving the merged data QDomNodeList menuNodes = xmlFile.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")); } //qCDebug(DEBUG_KXMLGUI) << (*it).domDocument().toString(); //qDebug(240) << "Saving " << (*it).xmlFile(); // if we got this far, we might as well just save it KXMLGUIFactory::saveConfigFile(xmlFile.domDocument(), xmlFile.xmlFile()); } if (!d->m_factory) { return; } rebuildKXMLGUIClients(); } void KEditToolBarWidget::rebuildKXMLGUIClients() { if (!d->m_factory) { return; } const QList 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 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"; for (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. for (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 = new QPushButton(i18nc("@action:button", "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 = new QPushButton(i18nc("@action:button", "Change Te&xt..."), m_widget); m_changeIconText->setIcon(QIcon::fromTheme(QStringLiteral("edit-rename"))); 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->setContentsMargins(0, 0, 0, 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 for (const auto &xmlFile : qAsConst(m_xmlFiles)) { // 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 (xmlFile.type() == XmlData::Merged) { continue; } // each xml file may have any number of toolbars for (const auto &bar : qAsConst(xmlFile.barList())) { const QString text = xmlFile.toolBarText(bar); m_toolbarCombo->addItem(text); const QString name = bar.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 tagSpacer("Spacer"); 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")); int spacer_num = 0; QString spacer_name(QStringLiteral("spacer_%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 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() == tagSpacer) { ToolBarItem *act = new ToolBarItem(m_activeList, tagSpacer, spacer_name.arg(spacer_num++), QString()); act->setSpacer(true); act->setText(SPACERSTRING); 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("")); } else { act->setText(i18n("", 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... const auto actions = actionCollection->actions(); for (QAction *action : 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 const auto actions = actionCollection->actions(); for (QAction *action : 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 and spacers to the inactive list ToolBarItem *sep = new ToolBarItem(nullptr, tagSeparator, sep_name.arg(sep_num++), QString()); sep->setSeparator(true); sep->setText(SEPARATORSTRING); m_inactiveList->insertItem(0, sep); ToolBarItem *spacer = new ToolBarItem(nullptr, tagSpacer, spacer_name.arg(spacer_num++), QString()); spacer->setSpacer(true); spacer->setText(SPACERSTRING); m_inactiveList->insertItem(1, spacer); } KActionCollection *KEditToolBarWidget::actionCollection() const { return d->m_collection; } void KEditToolBarWidgetPrivate::slotToolBarSelected(int index) { // 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; for (auto &xmlFile : m_xmlFiles) { // skip the merged one in favor of the local one, // so that we can change icons if (xmlFile.type() == XmlData::Merged) { continue; } // each xml file may have any number of toolbars for (const auto &bar : xmlFile.barList()) { // is this our toolbar? if (toolbarNumber == index) { // save our current settings m_currentXmlData = &xmlFile; m_currentToolBarElem = bar; //qCDebug(DEBUG_KXMLGUI) << "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 (xmlFile.type() == XmlData::Part || xmlFile.type() == XmlData::Shell) { m_widget->setDOMDocument(xmlFile.domDocument()); } return; } ++toolbarNumber; } } } void KEditToolBarWidgetPrivate::slotInactiveSelectionChanged() { if (!m_inactiveList->selectedItems().isEmpty()) { m_insertAction->setEnabled(true); QString statusText = static_cast(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 = nullptr; if (!m_activeList->selectedItems().isEmpty()) { toolitem = static_cast(m_activeList->selectedItems().first()); } m_removeAction->setEnabled(toolitem); m_changeIcon->setEnabled(toolitem && toolitem->internalTag() == QLatin1String("Action")); m_changeIconText->setEnabled(toolitem && toolitem->internalTag() == QLatin1String("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(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(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 and spacer specially if (item->isSeparator()) { new_item = m_widget->domDocument().createElement(QStringLiteral("Separator")); } else if (item->isSpacer()) { new_item = m_widget->domDocument().createElement(QStringLiteral("Spacer")); } 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(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(item->listWidget()->item(newRow))); } void KEditToolBarWidgetPrivate::updateLocal(QDomElement &elem) { for (auto &xmlFile : m_xmlFiles) { if (xmlFile.type() == XmlData::Merged) { continue; } if (xmlFile.type() == XmlData::Shell || xmlFile.type() == XmlData::Part) { if (m_currentXmlData->xmlFile() == xmlFile.xmlFile()) { xmlFile.m_isModified = true; return; } continue; } xmlFile.m_isModified = true; const QLatin1String attrName("name"); for (const auto &bar : qAsConst(xmlFile.barList())) { const QString name(bar.attribute(attrName)); const QString tag(bar.tagName()); if ((tag != elem.tagName()) || (name != elem.attribute(attrName))) { continue; } QDomElement toolbar = xmlFile.domDocument().documentElement().toElement(); toolbar.replaceChild(elem, bar); return; } // just append it QDomElement toolbar = xmlFile.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(); //qCDebug(DEBUG_KXMLGUI) << 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) { //qCDebug(DEBUG_KXMLGUI) << "slotDropped list=" << (list==m_activeList?"activeList":"inactiveList") // << "index=" << index << "sourceIsActiveList=" << sourceIsActiveList; if (list == m_activeList) { ToolBarItem *after = index > 0 ? static_cast(list->item(index - 1)) : nullptr; //qCDebug(DEBUG_KXMLGUI) << "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/kkeysequencewidget.cpp b/src/kkeysequencewidget.cpp index 7524f4d..ed27fb8 100644 --- a/src/kkeysequencewidget.cpp +++ b/src/kkeysequencewidget.cpp @@ -1,861 +1,861 @@ /* This file is part of the KDE libraries Copyright (C) 1998 Mark Donohoe Copyright (C) 2001 Ellis Whitehead Copyright (C) 2007 Andreas Hartmetz 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 "debug.h" #include #include #include #include #include #include #include #include #include #include #if HAVE_GLOBALACCEL # include #endif #include "kactioncollection.h" 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 @p seq with a current standard * shortcut? */ bool conflictWithStandardShortcuts(const QKeySequence &seq); /** * Conflicts the key sequence @p seq with a current local * shortcut? */ bool conflictWithLocalShortcuts(const QKeySequence &seq); /** * Conflicts the key sequence @p seq with a current global * shortcut? */ bool conflictWithGlobalShortcuts(const QKeySequence &seq); /** * Get permission to steal the shortcut @seq from the standard shortcut @p 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 > &shortcuts, const QKeySequence &sequence) { if (shortcuts.isEmpty()) { // Usage error. Just say no return false; } QString clashingKeys; for (auto it = shortcuts.begin(), end = shortcuts.end(); it != end; ++it) { const auto seqAsString = it.key().toString(); for (const KGlobalShortcutInfo &info : it.value()) { clashingKeys += i18n("Shortcut '%1' in Application %2 for action %3\n", seqAsString, 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"))) + return KMessageBox::warningContinueCancel(parent, message, title, KGuiItem(i18nc("@action:button", "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 checkList; // deprecated /** * The list of action collections to check against for conflict shortcut */ QList checkActionCollections; /** * The action to steal the shortcut from. */ QList stealActions; bool stealShortcuts(const QList &actions, const QKeySequence &seq); void wontStealShortcut(QAction *item, const QKeySequence &seq); }; KKeySequenceWidgetPrivate::KKeySequenceWidgetPrivate(KKeySequenceWidget *q) : q(q) , 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 &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; for (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) { + if (KMessageBox::warningContinueCancel(q, message, title, KGuiItem(i18nc("@action:button", "Reassign"))) != KMessageBox::Continue) { return false; } return true; } void KKeySequenceWidgetPrivate::wontStealShortcut(QAction *item, const QKeySequence &seq) { - QString title(i18n("Shortcut conflict")); + QString title(i18nc("@title:window", "Shortcut conflict")); QString msg(i18n("The '%1' key combination is already used by the %2 action.
" "Please select a different one.
", 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, &KKeySequenceButton::clicked, this, &KKeySequenceWidget::captureKeySequence); connect(d->clearButton, &QToolButton::clicked, this, &KKeySequenceWidget::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->setContentsMargins(0, 0, 0, 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.")); + keyButton->setToolTip(i18nc("@info:tooltip", "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); } #if KXMLGUI_BUILD_DEPRECATED_SINCE(4, 1) void KKeySequenceWidget::setCheckActionList(const QList &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 &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 changedCollections; for (QAction *stealAction : qAsConst(d->stealActions)) { // Stealing a shortcut means setting it to an empty one. stealAction->setShortcuts(QList()); // The following code will find the action we are about to // steal from and save it's actioncollection. KActionCollection *parentCollection = nullptr; for (KActionCollection *collection : qAsConst(d->checkActionCollections)) { if (collection->actions().contains(stealAction)) { parentCollection = collection; break; } } // Remember the changed collection if (parentCollection) { changedCollections.insert(parentCollection); } } for (KActionCollection *col : qAsConst(changedCollections)) { col->writeSettings(); } d->stealActions.clear(); } void KKeySequenceWidgetPrivate::startRecording() { nKey = 0; modifierKeys = 0; oldKeySequence = keySequence; keySequence = QKeySequence(); isRecording = true; keyButton->grabKeyboard(); if (!QWidget::keyboardGrabber()) { qCWarning(DEBUG_KXMLGUI) << "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(QLatin1String("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 > 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 &shortcuts, const QKeySequence &needle) { if (needle.isEmpty()) { return false; } for (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 allActions; allActions += checkList; for (KActionCollection *collection : qAsConst(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 conflictingActions; //find conflicting shortcuts with existing actions for (QAction *qaction : qAsConst(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 for (QAction *stealAction : qAsConst(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 title = i18nc("@title:window", "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) { + if (KMessageBox::warningContinueCancel(q, message, title, KGuiItem(i18nc("@action:button", "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('&'), QLatin1String("&&")); if (isRecording) { if (modifierKeys) { if (!s.isEmpty()) { s.append(QLatin1Char(',')); } if (modifierKeys & Qt::MetaModifier) { s += QKeySequence(Qt::MetaModifier).toString(QKeySequence::NativeText); } #if defined(Q_OS_MAC) if (modifierKeys & Qt::AltModifier) { s += QKeySequence(Qt::AltModifier).toString(QKeySequence::NativeText); } if (modifierKeys & Qt::ControlModifier) { s += QKeySequence(Qt::ControlModifier).toString(QKeySequence::NativeText); } #else if (modifierKeys & Qt::ControlModifier) { s += QKeySequence(Qt::ControlModifier).toString(QKeySequence::NativeText); } if (modifierKeys & Qt::AltModifier) { s += QKeySequence(Qt::AltModifier).toString(QKeySequence::NativeText); } #endif if (modifierKeys & Qt::ShiftModifier) { s += QKeySequence(Qt::ShiftModifier).toString(QKeySequence::NativeText); } if (modifierKeys & Qt::KeypadModifier) { s += QKeySequence(Qt::KeypadModifier).toString(QKeySequence::NativeText); } } 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(QLatin1String(" ...")); } if (s.isEmpty()) { s = i18nc("No shortcut defined", "None"); } s = QLatin1Char(' ') + s + 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(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")); + i18nc("@title:window", "Unsupported Key")); d->cancelRecording(); return; } uint newModifiers = e->modifiers() & (Qt::ShiftModifier | Qt::ControlModifier | Qt::AltModifier | Qt::MetaModifier | Qt::KeypadModifier); //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) { QPushButton::keyPressEvent(e); return; } 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) { QPushButton::keyReleaseEvent(e); return; } e->accept(); uint newModifiers = e->modifiers() & (Qt::ShiftModifier | Qt::ControlModifier | Qt::AltModifier | Qt::MetaModifier | Qt::KeypadModifier); //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" diff --git a/src/klicensedialog_p.cpp b/src/klicensedialog_p.cpp index 60d73f8..f976afd 100644 --- a/src/klicensedialog_p.cpp +++ b/src/klicensedialog_p.cpp @@ -1,75 +1,75 @@ /* This file is part of the KDE libraries Copyright (C) 2007 Urs Wolfer Copyright (C) 2008 Friedrich W. H. Kossebau Copyright (C) 2010 Teo Mrnjavac 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. */ #include "klicensedialog_p.h" // KF #include #include // Qt #include #include #include #include #include #include KLicenseDialog::KLicenseDialog(const KAboutLicense &license, QWidget *parent) : QDialog(parent) { setAttribute(Qt::WA_DeleteOnClose); QVBoxLayout *layout = new QVBoxLayout; setLayout(layout); - setWindowTitle(i18n("License Agreement")); + setWindowTitle(i18nc("@title:window", "License Agreement")); const QFont font = QFontDatabase::systemFont(QFontDatabase::FixedFont); const QString licenseText = license.text(); QTextBrowser *licenseBrowser = new QTextBrowser(this); licenseBrowser->setFont(font); licenseBrowser->setLineWrapMode(QTextEdit::NoWrap); licenseBrowser->setText(licenseText); layout->addWidget(licenseBrowser); QDialogButtonBox *buttonBox = new QDialogButtonBox(this); buttonBox->setStandardButtons(QDialogButtonBox::Close); connect(buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept); connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject); layout->addWidget(buttonBox); // try to set up the dialog such that the full width of the // document is visible without horizontal scroll-bars being required const int marginHint = style()->pixelMetric(QStyle::PM_DefaultChildMargin); const qreal idealWidth = licenseBrowser->document()->idealWidth() + (2 * marginHint) + licenseBrowser->verticalScrollBar()->width() * 2; // try to allow enough height for a reasonable number of lines to be shown QFontMetrics metrics(font); const int idealHeight = metrics.height() * 30; resize(sizeHint().expandedTo(QSize(qRound(idealWidth), idealHeight))); } KLicenseDialog::~KLicenseDialog() = default; diff --git a/src/kmenumenuhandler_p.cpp b/src/kmenumenuhandler_p.cpp index 722afa2..1058fc6 100644 --- a/src/kmenumenuhandler_p.cpp +++ b/src/kmenumenuhandler_p.cpp @@ -1,252 +1,252 @@ /* This file is part of the KDE project Copyright (C) 2006 Olivier Goffart 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 "debug.h" #include #include #include #include #include #include #include #include #include #include namespace KDEPrivate { KMenuMenuHandler::KMenuMenuHandler(KXMLGUIBuilder *builder) : 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, &QAction::triggered, this, &KMenuMenuHandler::slotAddToToolBar); } 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(event); QMenu *menu = static_cast(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(m_builder->widget()); if (!window) { return; } QStringList toolbarlist; const auto toolbars = window->toolBars(); for (KToolBar *b : toolbars) { toolbarlist << (b->windowTitle().isEmpty() ? b->objectName() : b->windowTitle()); } m_toolbarAction->setItems(toolbarlist); } static KActionCollection *findParentCollection(KXMLGUIFactory *factory, QAction *action) { const auto clients = factory->clients(); for (KXMLGUIClient *client : 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 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, &QDialogButtonBox::accepted, &dialog, &QDialog::accept); connect(&box, &QDialogButtonBox::rejected, &dialog, &QDialog::reject); dialog.layout()->addWidget(&box); KActionCollection *parentCollection = nullptr; if (dynamic_cast(m_builder)) { QList checkCollections; KXMLGUIFactory *factory = dynamic_cast(m_builder)->factory(); parentCollection = findParentCollection(factory, m_popupAction); const auto clients = factory->clients(); for (KXMLGUIClient *client : 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(m_builder->widget()); if (!window) { return; } if (!m_popupMenu || !m_popupAction) { return; } KXMLGUIFactory *factory = dynamic_cast(m_builder)->factory(); QString actionName = m_popupAction->objectName(); // set by KActionCollection::addAction KActionCollection *collection = nullptr; if (factory) { collection = findParentCollection(factory, m_popupAction); } if (!collection) { qCWarning(DEBUG_KXMLGUI) << "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) == QLatin1String("true")) { qCWarning(DEBUG_KXMLGUI) << "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); auto *action = menu->actionAt(pos); if (!action || action->isSeparator()) { return; } m_popupMenu = menu; m_popupAction = action; m_contextMenu = new QMenu; - m_contextMenu->addAction(i18n("Configure Shortcut..."), this, &KMenuMenuHandler::slotSetShortcut); + m_contextMenu->addAction(i18nc("@action:inmenu", "Configure Shortcut..."), this, &KMenuMenuHandler::slotSetShortcut); KMainWindow *window = qobject_cast(m_builder->widget()); if (window) { m_contextMenu->addAction(m_toolbarAction); buildToolbarAction(); } m_contextMenu->exec(menu->mapToGlobal(pos)); delete m_contextMenu; m_contextMenu = nullptr; m_popupAction = nullptr; m_popupMenu = nullptr; } } //END namespace KDEPrivate diff --git a/src/kshortcuteditwidget.cpp b/src/kshortcuteditwidget.cpp index 62397c9..7cc7e4e 100644 --- a/src/kshortcuteditwidget.cpp +++ b/src/kshortcuteditwidget.cpp @@ -1,207 +1,207 @@ /* This file is part of the KDE libraries Copyright (C) 1998 Mark Donohoe Copyright (C) 1997 Nicolas Hadacek Copyright (C) 1998 Matthias Ettrich Copyright (C) 2001 Ellis Whitehead Copyright (C) 2006 Hamish Rodda Copyright (C) 2007 Roberto Raggi Copyright (C) 2007 Andreas Hartmetz 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 #include #include #include #include #include #include #if HAVE_GLOBALACCEL # include #endif #include "kkeysequencewidget.h" void TabConnectedWidget::paintEvent(QPaintEvent *e) { QWidget::paintEvent(e); QPainter p(this); QPen pen(QPalette().highlight().color()); pen.setWidth(6); p.setPen(pen); p.drawLine(0, 0, width(), 0); if (qApp->isLeftToRight()) { p.drawLine(0, 0, 0, height()); } else { p.drawLine(width(), 0, width(), height()); } } ShortcutEditWidget::ShortcutEditWidget(QWidget *viewport, const QKeySequence &defaultSeq, const QKeySequence &activeSeq, bool allowLetterShortcuts) : TabConnectedWidget(viewport), m_defaultKeySequence(defaultSeq), m_isUpdating(false), m_action(nullptr), m_noneText(i18nc("No shortcut defined", "None")) { QGridLayout *layout = new QGridLayout(this); - m_defaultRadio = new QRadioButton(i18n("Default:"), this); + m_defaultRadio = new QRadioButton(i18nc("@option:radio", "Default:"), this); m_defaultLabel = new QLabel(m_noneText, this); const QString defaultText = defaultSeq.toString(QKeySequence::NativeText); if (!defaultText.isEmpty()) { m_defaultLabel->setText(defaultText); } - m_customRadio = new QRadioButton(i18n("Custom:"), this); + m_customRadio = new QRadioButton(i18nc("@option:radio", "Custom:"), this); m_customEditor = new KKeySequenceWidget(this); m_customEditor->setModifierlessAllowed(allowLetterShortcuts); layout->addWidget(m_defaultRadio, 0, 0); layout->addWidget(m_defaultLabel, 0, 1); layout->addWidget(m_customRadio, 1, 0); layout->addWidget(m_customEditor, 1, 1); layout->setColumnStretch(2, 1); setKeySequence(activeSeq); connect(m_defaultRadio, &QRadioButton::toggled, this, &ShortcutEditWidget::defaultToggled); connect(m_customEditor, &KKeySequenceWidget::keySequenceChanged, this, &ShortcutEditWidget::setCustom); connect(m_customEditor, &KKeySequenceWidget::stealShortcut, this, &ShortcutEditWidget::stealShortcut); #if HAVE_GLOBALACCEL connect(KGlobalAccel::self(), &KGlobalAccel::globalShortcutChanged, this, [this](QAction *action, const QKeySequence &seq) { if (action != m_action) { return; } setKeySequence(seq); } ); #endif } KKeySequenceWidget::ShortcutTypes ShortcutEditWidget::checkForConflictsAgainst() const { return m_customEditor->checkForConflictsAgainst(); } //slot void ShortcutEditWidget::defaultToggled(bool checked) { if (m_isUpdating) { return; } m_isUpdating = true; if (checked) { // The default key sequence should be activated. We check first if this is // possible. if (m_customEditor->isKeySequenceAvailable(m_defaultKeySequence)) { // Clear the customs widget m_customEditor->clearKeySequence(); emit keySequenceChanged(m_defaultKeySequence); } else { // We tried to switch to the default key sequence and failed. Go // back. m_customRadio->setChecked(true); } } else { // The empty key sequence is always valid emit keySequenceChanged(QKeySequence()); } m_isUpdating = false; } void ShortcutEditWidget::setCheckActionCollections( const QList &checkActionCollections) { // We just forward them to out KKeySequenceWidget. m_customEditor->setCheckActionCollections(checkActionCollections); } void ShortcutEditWidget::setCheckForConflictsAgainst(KKeySequenceWidget::ShortcutTypes types) { m_customEditor->setCheckForConflictsAgainst(types); } void ShortcutEditWidget::setComponentName(const QString &componentName) { m_customEditor->setComponentName(componentName); } void ShortcutEditWidget::setMultiKeyShortcutsAllowed(bool allowed) { // We just forward them to out KKeySequenceWidget. m_customEditor->setMultiKeyShortcutsAllowed(allowed); } bool ShortcutEditWidget::multiKeyShortcutsAllowed() const { return m_customEditor->multiKeyShortcutsAllowed(); } void ShortcutEditWidget::setAction(QObject *action) { m_action = action; } //slot void ShortcutEditWidget::setCustom(const QKeySequence &seq) { if (m_isUpdating) { return; } // seq is a const reference to a private variable of KKeySequenceWidget. // Somewhere below we possible change that one. But we want to emit seq // whatever happens. So we make a copy. QKeySequence original = seq; m_isUpdating = true; // Check if the user typed in the default sequence into the custom field. // We do this by calling setKeySequence which will do the right thing. setKeySequence(original); emit keySequenceChanged(original); m_isUpdating = false; } void ShortcutEditWidget::setKeySequence(const QKeySequence &activeSeq) { const QString seqString = activeSeq.isEmpty() ? m_noneText : activeSeq.toString(QKeySequence::NativeText); if (seqString == m_defaultLabel->text()) { m_defaultRadio->setChecked(true); m_customEditor->clearKeySequence(); } else { m_customRadio->setChecked(true); // m_customEditor->setKeySequence does some stuff we only want to // execute when the sequence really changes. if (activeSeq != m_customEditor->keySequence()) { m_customEditor->setKeySequence(activeSeq); } } } diff --git a/src/kshortcutschemeseditor.cpp b/src/kshortcutschemeseditor.cpp index 2330831..315a955 100644 --- a/src/kshortcutschemeseditor.cpp +++ b/src/kshortcutschemeseditor.cpp @@ -1,217 +1,219 @@ /* This file is part of the KDE libraries Copyright (C) 2008 Alexander Dymo 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kshortcutsdialog.h" #include "kshortcutschemeshelper_p.h" #include "kactioncollection.h" #include "kxmlguiclient.h" #include KShortcutSchemesEditor::KShortcutSchemesEditor(KShortcutsDialog *parent) - : QGroupBox(i18n("Shortcut Schemes"), parent), m_dialog(parent) + : QGroupBox(i18nc("@title:group", "Shortcut Schemes"), parent), m_dialog(parent) { KConfigGroup group(KSharedConfig::openConfig(), "Shortcut Schemes"); QStringList schemes; schemes << QStringLiteral("Default"); // List files in the shortcuts subdir, each one is a scheme. See KShortcutSchemesHelper::{shortcutSchemeFileName,exportActionCollection} const QStringList shortcutsDirs = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QCoreApplication::applicationName() + QLatin1String("/shortcuts"), QStandardPaths::LocateDirectory); qCDebug(DEBUG_KXMLGUI) << "shortcut scheme dirs:" << shortcutsDirs; for (const QString &dir : shortcutsDirs) { const auto files = QDir(dir).entryList(QDir::Files | QDir::NoDotAndDotDot); for (const QString &file : files) { qCDebug(DEBUG_KXMLGUI) << "shortcut scheme file:" << file; schemes << file; } } const QString currentScheme = group.readEntry("Current Scheme", "Default"); qCDebug(DEBUG_KXMLGUI) << "Current Scheme" << currentScheme; QHBoxLayout *l = new QHBoxLayout(this); QLabel *schemesLabel = new QLabel(i18n("Current scheme:"), this); l->addWidget(schemesLabel); m_schemesList = new QComboBox(this); m_schemesList->setEditable(false); m_schemesList->addItems(schemes); m_schemesList->setSizeAdjustPolicy(QComboBox::AdjustToContents); const int schemeIdx = m_schemesList->findText(currentScheme); if (schemeIdx > -1) { m_schemesList->setCurrentIndex(schemeIdx); } else { qCWarning(DEBUG_KXMLGUI) << "Current scheme" << currentScheme << "not found in" << shortcutsDirs; } schemesLabel->setBuddy(m_schemesList); l->addWidget(m_schemesList); - m_newScheme = new QPushButton(i18n("New...")); + m_newScheme = new QPushButton(i18nc("@action:button", "New...")); l->addWidget(m_newScheme); - m_deleteScheme = new QPushButton(i18n("Delete")); + m_deleteScheme = new QPushButton(i18nc("@action:button", "Delete")); l->addWidget(m_deleteScheme); - QPushButton *moreActions = new QPushButton(i18n("More Actions")); + QPushButton *moreActions = new QPushButton(i18nc("@action:button", "More Actions")); l->addWidget(moreActions); QMenu *moreActionsMenu = new QMenu(this); - moreActionsMenu->addAction(i18n("Save shortcuts to scheme"), + moreActionsMenu->addAction(i18nc("@action:inmenu", "Save shortcuts to scheme"), this, &KShortcutSchemesEditor::saveAsDefaultsForScheme); - moreActionsMenu->addAction(i18n("Export Scheme..."), + moreActionsMenu->addAction(i18nc("@action:inmenu", "Export Scheme..."), this, &KShortcutSchemesEditor::exportShortcutsScheme); - moreActionsMenu->addAction(i18n("Import Scheme..."), + moreActionsMenu->addAction(i18nc("@action:inmenu", "Import Scheme..."), this, &KShortcutSchemesEditor::importShortcutsScheme); moreActions->setMenu(moreActionsMenu); l->addStretch(1); #if QT_VERSION < QT_VERSION_CHECK(5, 14, 0) connect(m_schemesList, QOverload::of(&QComboBox::activated), this, &KShortcutSchemesEditor::shortcutsSchemeChanged); #else connect(m_schemesList, &QComboBox::textActivated, this, &KShortcutSchemesEditor::shortcutsSchemeChanged); #endif connect(m_newScheme, &QPushButton::clicked, this, &KShortcutSchemesEditor::newScheme); connect(m_deleteScheme, &QPushButton::clicked, this, &KShortcutSchemesEditor::deleteScheme); updateDeleteButton(); } void KShortcutSchemesEditor::newScheme() { bool ok; - const QString newName = QInputDialog::getText(this, i18n("Name for New Scheme"), + const QString newName = QInputDialog::getText(this, i18nc("@title:window", "Name for New Scheme"), i18n("Name for new scheme:"), QLineEdit::Normal, i18n("New Scheme"), &ok); if (!ok) { return; } if (m_schemesList->findText(newName) != -1) { KMessageBox::sorry(this, i18n("A scheme with this name already exists.")); return; } const QString newSchemeFileName = KShortcutSchemesHelper::writableApplicationShortcutSchemeFileName(newName); QDir().mkpath(QFileInfo(newSchemeFileName).absolutePath()); QFile schemeFile(newSchemeFileName); if (!schemeFile.open(QFile::WriteOnly | QFile::Truncate)) { qCWarning(DEBUG_KXMLGUI) << "Couldn't write to" << newSchemeFileName; return; } QDomDocument doc; QDomElement docElem = doc.createElement(QStringLiteral("gui")); doc.appendChild(docElem); QDomElement elem = doc.createElement(QStringLiteral("ActionProperties")); docElem.appendChild(elem); QTextStream out(&schemeFile); out << doc.toString(4); m_schemesList->addItem(newName); m_schemesList->setCurrentIndex(m_schemesList->findText(newName)); updateDeleteButton(); emit shortcutsSchemeChanged(newName); } void KShortcutSchemesEditor::deleteScheme() { if (KMessageBox::questionYesNo(this, i18n("Do you really want to delete the scheme %1?\n\ Note that this will not remove any system wide shortcut schemes.", currentScheme())) == KMessageBox::No) { return; } //delete the scheme for the app itself QFile::remove(KShortcutSchemesHelper::writableApplicationShortcutSchemeFileName(currentScheme())); //delete all scheme files we can find for xmlguiclients in the user directories const auto dialogCollections = m_dialog->actionCollections(); for (KActionCollection *collection : dialogCollections) { const KXMLGUIClient *client = collection->parentGUIClient(); if (!client) { continue; } QFile::remove(KShortcutSchemesHelper::writableShortcutSchemeFileName(client->componentName(), currentScheme())); } m_schemesList->removeItem(m_schemesList->findText(currentScheme())); updateDeleteButton(); emit shortcutsSchemeChanged(currentScheme()); } QString KShortcutSchemesEditor::currentScheme() { return m_schemesList->currentText(); } void KShortcutSchemesEditor::exportShortcutsScheme() { //ask user about dir - QString path = QFileDialog::getSaveFileName(this, i18n("Export Shortcuts"), QDir::currentPath(), i18n("Shortcuts (*.shortcuts)")); + QString path = QFileDialog::getSaveFileName(this, i18nc("@title:window", "Export Shortcuts"), + QDir::currentPath(), i18n("Shortcuts (*.shortcuts)")); if (path.isEmpty()) { return; } m_dialog->exportConfiguration(path); } void KShortcutSchemesEditor::importShortcutsScheme() { //ask user about dir - QString path = QFileDialog::getOpenFileName(this, i18n("Import Shortcuts"), QDir::currentPath(), i18n("Shortcuts (*.shortcuts)")); + QString path = QFileDialog::getOpenFileName(this, i18nc("@title:window", "Import Shortcuts"), + QDir::currentPath(), i18n("Shortcuts (*.shortcuts)")); if (path.isEmpty()) { return; } m_dialog->importConfiguration(path); } void KShortcutSchemesEditor::saveAsDefaultsForScheme() { if (KShortcutSchemesHelper::saveShortcutScheme(m_dialog->actionCollections(), currentScheme())) { KMessageBox::information(this, i18n("Shortcut scheme successfully saved.")); } else { // We'd need to return to return more than a bool, to show more details here. KMessageBox::sorry(this, i18n("Error saving the shortcut scheme.")); } } void KShortcutSchemesEditor::updateDeleteButton() { m_deleteScheme->setEnabled(m_schemesList->count() >= 1); } diff --git a/src/kshortcutsdialog.cpp b/src/kshortcutsdialog.cpp index c193474..9da657a 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 Copyright (C) 1997 Nicolas Hadacek Copyright (C) 1998 Matthias Ettrich Copyright (C) 2001 Ellis Whitehead Copyright (C) 2006 Hamish Rodda Copyright (C) 2007 Roberto Raggi Copyright (C) 2007 Andreas Hartmetz Copyright (C) 2008 Michael Jansen Copyright (C) 2008 Alexander Dymo Copyright (C) 2009 Chani Armitage 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 #include #include #include #include #include #include #include "kxmlguiclient.h" #include "kxmlguifactory.h" #include "kactioncollection.h" /************************************************************************/ /* KShortcutsDialog */ /* */ /* Originally by Nicolas Hadacek */ /* */ /* Substantially revised by Mark Donohoe */ /* */ /* And by Espen Sand 1999-10-19 */ /* (by using KDialog there is almost no code left ;) */ /* */ /************************************************************************/ QKeySequence primarySequence(const QList &sequences) { return sequences.isEmpty() ? QKeySequence() : sequences.at(0); } QKeySequence alternateSequence(const QList &sequences) { return sequences.size() <= 1 ? QKeySequence() : sequences.at(1); } class Q_DECL_HIDDEN KShortcutsDialog::KShortcutsDialogPrivate { public: KShortcutsDialogPrivate(KShortcutsDialog *q) : q(q), m_keyChooser(nullptr), m_schemeEditor(nullptr), m_detailsButton(nullptr), m_saveSettings(false) { } QList 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(); for (KActionCollection *collection : qAsConst(m_collections)) { // passing an empty stream forces the clients to reread the XML KXMLGUIClient *client = const_cast(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); } } } for (KActionCollection *collection : qAsConst(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 Keyboard Shortcuts")); + setWindowTitle(i18nc("@title:window", "Configure Keyboard 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() + QLatin1String(" >>")); 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), &QAbstractButton::clicked, d->m_keyChooser, &KShortcutsEditor::allDefault); connect(d->m_detailsButton, SIGNAL(clicked()), this, SLOT(toggleDetails())); connect(printButton, &QPushButton::clicked, d->m_keyChooser, &KShortcutsEditor::printShortcuts); connect(buttonBox, SIGNAL(rejected()), this, SLOT(undoChanges())); connect(buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept); connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::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 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(&config)); } void KShortcutsDialog::exportConfiguration(const QString &path) const { KConfig config(path); d->m_keyChooser->exportConfiguration(static_cast(&config)); } #include "moc_kshortcutsdialog.cpp" diff --git a/src/kswitchlanguagedialog_p.cpp b/src/kswitchlanguagedialog_p.cpp index 62471f3..ba02060 100644 --- a/src/kswitchlanguagedialog_p.cpp +++ b/src/kswitchlanguagedialog_p.cpp @@ -1,443 +1,443 @@ /* * 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 "kswitchlanguagedialog_p.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // 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 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 + QLatin1String("/klanguageoverridesrc"), QSettings::IniFormat)); } static QByteArray getApplicationSpecificLanguage(const QByteArray &defaultCode = QByteArray()) { QSettingsPtr settings = localeOverridesSettings(); settings->beginGroup(QStringLiteral("Language")); return settings->value(qAppName(), defaultCode).toByteArray(); } namespace KDEPrivate { Q_COREAPP_STARTUP_FUNCTION(initializeLanguages) void setApplicationSpecificLanguage(const QByteArray &languageCode) { QSettingsPtr settings = localeOverridesSettings(); settings->beginGroup(QStringLiteral("Language")); if (languageCode.isEmpty()) { settings->remove(qAppName()); } else { settings->setValue(qAppName(), languageCode); } } void initializeLanguages() { const QByteArray languageCode = getApplicationSpecificLanguage(); if (!languageCode.isEmpty()) { QByteArray languages = qgetenv("LANGUAGE"); if (languages.isEmpty()) { qputenv("LANGUAGE", languageCode); } else { qputenv("LANGUAGE", languageCode + ':' + languages); } // Ideally setting the LANGUAGE would change the default QLocale too // but unfortunately this is too late since the QCoreApplication constructor // already created a QLocale at this stage so we need to set the reset it // by triggering the creation and destruction of a QSystemLocale // this is highly dependant on Qt internals, so may break, but oh well QSystemLocale *dummy = new QSystemLocale(); delete dummy; } } struct LanguageRowData { LanguageRowData() { 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 languageRows; QList languageButtons; QGridLayout *languagesLayout; }; /*************************** KSwitchLanguageDialog **************************/ KSwitchLanguageDialog::KSwitchLanguageDialog(QWidget *parent) : QDialog(parent), d(new KSwitchLanguageDialogPrivate(this)) { - setWindowTitle(i18n("Switch Application Language")); + setWindowTitle(i18nc("@title:window", "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.")); + QPushButton *addLangButton = new QPushButton(i18nc("@action:button", "Add Fallback Language"), this); + addLangButton->setToolTip(i18nc("@info:tooltip", "Adds one more language which will be used if other translations do not contain a proper translation.")); connect(addLangButton, &QPushButton::clicked, this, &KSwitchLanguageDialog::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, &QDialogButtonBox::accepted, this, &KSwitchLanguageDialog::slotOk); connect(buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept); connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject); connect(buttonBox->button(QDialogButtonBox::RestoreDefaults), &QPushButton::clicked, this, &KSwitchLanguageDialog::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"; return; } QPushButton *removeButton = const_cast(::qobject_cast(signalSender)); if (!removeButton) { qCritical() << "KSwitchLanguageDialog::removeButtonClicked() called from something else than QPushButton"; return; } QMap::iterator it = d->languageRows.find(removeButton); if (it == d->languageRows.end()) { qCritical() << "KSwitchLanguageDialog::removeButtonClicked called from unknown QPushButton"; 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 (auto *languageButton : qAsConst(d->languageButtons)) { 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 + i18nc("@title:window", "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(QLatin1Char('_')); if (idx != -1) { *languageCode = languageCode->left(idx); return true; } return false; } void KSwitchLanguageDialogPrivate::fillApplicationLanguages(KLanguageButton *button) { const QLocale cLocale(QLocale::C); QSet insertedLanguges; const QList allLocales = QLocale::matchingLocales(QLocale::AnyLanguage, QLocale::AnyScript, QLocale::AnyCountry); for (const QLocale &l : allLocales) { if (l != cLocale) { QString languageCode = l.name(); if (!insertedLanguges.contains(languageCode) && KLocalizedString::isApplicationTranslatedInto(languageCode)) { button->insertLanguage(languageCode); insertedLanguges << languageCode; } else if (stripCountryCode(&languageCode)) { if (!insertedLanguges.contains(languageCode) && KLocalizedString::isApplicationTranslatedInto(languageCode)) { button->insertLanguage(languageCode); insertedLanguges << languageCode; } } } } } 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 (auto &language : languagesList) { language.replace(QLatin1Char('-'), QLatin1Char('_')); } } 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, &KLanguageButton::activated, p, &KSwitchLanguageDialog::languageOnButtonChanged); LanguageRowData languageRowData; QPushButton *removeButton = nullptr; if (!primaryLanguage) { - removeButton = new QPushButton(i18n("Remove"), p); + removeButton = new QPushButton(i18nc("@action:button", "Remove"), p); QObject::connect(removeButton, &QPushButton::clicked, p, &KSwitchLanguageDialog::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.")); + ? i18nc("@info:tooltip", "This is the main application language which will be used first, before any other languages.") + : i18nc("@info:tooltip", "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/ktoolbar.cpp b/src/ktoolbar.cpp index 8414699..eedae8c 100644 --- a/src/ktoolbar.cpp +++ b/src/ktoolbar.cpp @@ -1,1420 +1,1420 @@ /* 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 #include #include #include #include #include #include #include #include #include #include #ifdef QT_DBUS_LIB #include #include #endif #include #include #include #include #include #include #include #include #include "kactioncollection.h" #include "kedittoolbar.h" #include "kxmlguifactory.h" #include "kxmlguiwindow.h" #include "ktoolbarhelper_p.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 Q_DECL_HIDDEN KToolBar::Private { public: Private(KToolBar *qq) : q(qq), isMainToolBar(false), #if KXMLGUI_BUILD_DEPRECATED_SINCE(5, 0) enableContext(true), #endif unlockedMovable(true), 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 = 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; #if KXMLGUI_BUILD_DEPRECATED_SINCE(5, 0) bool enableContext : 1; #endif bool unlockedMovable : 1; static bool s_editable; static bool s_locked; QSet 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 contextIconSizes; class IntSetting { public: IntSetting() { for (int &value : values) { value = Unset; } } int currentValue() const { int val = Unset; for (int value : values) { if (value != Unset) { val = value; } } 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 value : values) { str += QString::number(value) + 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 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, &QToolBar::allowedAreasChanged, q->mainWindow(), &KMainWindow::setSettingsDirty); connect(q, &QToolBar::iconSizeChanged, q->mainWindow(), &KMainWindow::setSettingsDirty); connect(q, &QToolBar::toolButtonStyleChanged, q->mainWindow(), &KMainWindow::setSettingsDirty); connect(q, &QToolBar::movableChanged, q->mainWindow(), &KMainWindow::setSettingsDirty); connect(q, &QToolBar::orientationChanged, q->mainWindow(), &KMainWindow::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, &QToolBar::movableChanged, q, &KToolBar::slotMovableChanged); q->setAcceptDrops(true); #ifdef QT_DBUS_LIB QDBusConnection::sessionBus().connect(QString(), QStringLiteral("/KToolBar"), QStringLiteral("org.kde.KToolBar"), QStringLiteral("styleChanged"), q, SLOT(slotAppearanceChanged())); #endif 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(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); const auto orientActions = contextOrient->actions(); for (QAction *action : orientActions) { 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())); + contextIcons = contextMode->addAction(i18nc("@item:inmenu", "Icons Only"), q, SLOT(slotContextIcons())); + contextText = contextMode->addAction(i18nc("@item:inmenu", "Text Only"), q, SLOT(slotContextText())); + contextTextRight = contextMode->addAction(i18nc("@item:inmenu", "Text Alongside Icons"), q, SLOT(slotContextTextRight())); + contextTextUnder = contextMode->addAction(i18nc("@item:inmenu", "Text Under Icons"), q, SLOT(slotContextTextUnder())); QActionGroup *textGroup = new QActionGroup(contextMode); const auto modeActions = contextMode->actions(); for (QAction *action : modeActions) { 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 avSizes; if (theme) { avSizes = theme->querySizes(isMainToolBar ? KIconLoader::MainToolbar : KIconLoader::Toolbar); } std::sort(avSizes.begin(), avSizes.end()); if (avSizes.count() < 10) { // Fixed or threshold type icons for (int it : qAsConst(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 (int p : progression) { for (int it : qAsConst(avSizes)) { if (it >= p) { 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); const auto sizeActions = contextSize->actions(); for (QAction *action : sizeActions) { 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 == QLatin1String("textbesideicon") || style == QLatin1String("icontextright")) { return Qt::ToolButtonTextBesideIcon; } else if (style == QLatin1String("textundericon") || style == QLatin1String("icontextbottom")) { return Qt::ToolButtonTextUnderIcon; } else if (style == QLatin1String("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 == QLatin1String("left")) { newposition = Qt::LeftToolBarArea; } else if (position == QLatin1String("bottom")) { newposition = Qt::BottomToolBarArea; } else if (position == QLatin1String("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() { //qCDebug(DEBUG_KXMLGUI) << q->objectName() << "iconSizeSettings:" << iconSizeSettings.toString() << "->" << iconSizeSettings.currentValue(); const int currentIconSize = iconSizeSettings.currentValue(); q->setIconSize(QSize(currentIconSize, currentIconSize)); //qCDebug(DEBUG_KXMLGUI) << q->objectName() << "toolButtonStyleSettings:" << toolButtonStyleSettings.toString() << "->" << toolButtonStyleSettings.currentValue(); q->setToolButtonStyle(static_cast(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 { for (KXMLGUIClient *client : xmlguiClients) { QAction *action = client->actionCollection()->action(actionName); if (action) { if (clientOut) { *clientOut = client; } return action; } } 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(q->mainWindow()); // try to find "configure toolbars" action 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().isEmpty()) { 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(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 = 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 + QLatin1String("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(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(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 == QLatin1String("mainToolBar"))); // KToolBar is auto-added to the top area of the main window if parent is a QMainWindow if (QMainWindow *mw = qobject_cast(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; } #if KXMLGUI_BUILD_DEPRECATED_SINCE(5, 0) void KToolBar::setContextMenuEnabled(bool enable) { d->enableContext = enable; } #endif #if KXMLGUI_BUILD_DEPRECATED_SINCE(5, 0) bool KToolBar::contextMenuEnabled() const { return d->enableContext; } #endif void KToolBar::saveSettings(KConfigGroup &cg) { Q_ASSERT(!cg.name().isEmpty()); const int currentIconSize = iconSize().width(); //qCDebug(DEBUG_KXMLGUI) << 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; } } #if KXMLGUI_BUILD_DEPRECATED_SINCE(5, 0) 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) { #if KXMLGUI_BUILD_DEPRECATED_SINCE(5, 0) if (mainWindow() && d->enableContext) { QPointer 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; } { const QString& i18nText = KToolbarHelper::i18nToolBarName(element); 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 == QLatin1String("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 == QLatin1String("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(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(const_cast(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; const auto allCollections = KActionCollection::allCollections(); for (const QString &actionName : qAsConst(actionNames)) { for (KActionCollection *ac : 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 = nullptr; const auto actions = this->actions(); for (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 = nullptr; d->actionsBeingDragged.clear(); if (toolBarsEditable()) { event->accept(); return; } QToolBar::dragLeaveEvent(event); } void KToolBar::dropEvent(QDropEvent *event) { if (toolBarsEditable()) { for (QAction *action : qAsConst(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 = 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) { QToolBar::mouseMoveEvent(event); return; } 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->exec(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 = 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 = 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(event); if (me->buttons() & Qt::RightButton) if (QWidget *ww = qobject_cast(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(watched)) { if (!this->isAncestorOf(ww)) { // New parent is not a subwidget - remove event filter ww->removeEventFilter(this); const auto children = ww->findChildren(); for (QWidget *child : children) { child->removeEventFilter(this); } } } } // Redirect mouse events to the toolbar when drag + drop editing is enabled if (toolBarsEditable()) { if (QWidget *ww = qobject_cast(watched)) { switch (event->type()) { case QEvent::MouseButtonPress: { QMouseEvent *me = static_cast(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(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(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); const auto children = widget->findChildren(); for (QWidget *child : children) { child->removeEventFilter(this); } } } QToolBar::actionEvent(event); if (event->type() == QEvent::ActionAdded) { QWidget *widget = widgetForAction(event->action()); if (widget) { widget->installEventFilter(this); const auto children = widget->findChildren(); for (QWidget *child : children) { 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; const auto windows = KMainWindow::memberList(); for (KMainWindow *mw : windows) { const auto toolbars = mw->findChildren(); for (KToolBar *toolbar : toolbars) { toolbar->d->setLocked(locked); } } } } bool KToolBar::toolBarsLocked() { return KToolBar::Private::s_locked; } void KToolBar::emitToolbarStyleChanged() { #ifdef QT_DBUS_LIB QDBusMessage message = QDBusMessage::createSignal(QStringLiteral("/KToolBar"), QStringLiteral("org.kde.KToolBar"), QStringLiteral("styleChanged")); QDBusConnection::sessionBus().send(message); #endif } #include "moc_ktoolbar.cpp"