diff --git a/src/kconfigdialog.cpp b/src/kconfigdialog.cpp index 6cb0f51..32f3e41 100644 --- a/src/kconfigdialog.cpp +++ b/src/kconfigdialog.cpp @@ -1,381 +1,380 @@ /* * This file is part of the KDE libraries * Copyright (C) 2003 Benjamin C Meyer (ben+kdelibs at meyerhome dot net) * Copyright (C) 2003 Waldo Bastian * Copyright (C) 2004 Michael Brade * * 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 "kconfigdialog.h" #include #include #include #include #include #include #include #include #include #include #include class Q_DECL_HIDDEN KConfigDialog::KConfigDialogPrivate { public: KConfigDialogPrivate(KConfigDialog *q, const QString &name, KCoreConfigSkeleton *config) : q(q), shown(false), manager(nullptr) { q->setObjectName(name); q->setWindowTitle(i18nc("@title:window", "Configure")); q->setFaceType(List); if (!name.isEmpty()) { openDialogs.insert(name, q); } else { - QString genericName; - genericName.sprintf("SettingsDialog-%p", static_cast(q)); + const QString genericName = QString::asprintf("SettingsDialog-%p", static_cast(q)); openDialogs.insert(genericName, q); q->setObjectName(genericName); } QDialogButtonBox *buttonBox = q->buttonBox(); buttonBox->setStandardButtons(QDialogButtonBox::RestoreDefaults | QDialogButtonBox::Ok | QDialogButtonBox::Apply | QDialogButtonBox::Cancel | QDialogButtonBox::Help); connect(buttonBox->button(QDialogButtonBox::Ok), &QAbstractButton::clicked, q, &KConfigDialog::updateSettings); connect(buttonBox->button(QDialogButtonBox::Apply), &QAbstractButton::clicked, q, &KConfigDialog::updateSettings); connect(buttonBox->button(QDialogButtonBox::Apply), &QAbstractButton::clicked, q, [this]() { _k_updateButtons(); }); connect(buttonBox->button(QDialogButtonBox::Cancel), &QAbstractButton::clicked, q, &KConfigDialog::updateWidgets); connect(buttonBox->button(QDialogButtonBox::RestoreDefaults), &QAbstractButton::clicked, q, &KConfigDialog::updateWidgetsDefault); connect(buttonBox->button(QDialogButtonBox::RestoreDefaults), &QAbstractButton::clicked, q, [this]() { _k_updateButtons(); }); connect(buttonBox->button(QDialogButtonBox::Help), &QAbstractButton::clicked, q, &KConfigDialog::showHelp); connect(q, &KPageDialog::pageRemoved, q, &KConfigDialog::onPageRemoved); manager = new KConfigDialogManager(q, config); setupManagerConnections(manager); setApplyButtonEnabled(false); } KPageWidgetItem *addPageInternal(QWidget *page, const QString &itemName, const QString &pixmapName, const QString &header); void setupManagerConnections(KConfigDialogManager *manager); void setApplyButtonEnabled(bool enabled); void setRestoreDefaultsButtonEnabled(bool enabled); void _k_updateButtons(); void _k_settingsChangedSlot(); KConfigDialog * const q; QString mAnchor; QString mHelpApp; bool shown; KConfigDialogManager *manager; QMap managerForPage; /** * The list of existing dialogs. */ static QHash openDialogs; }; QHash KConfigDialog::KConfigDialogPrivate::openDialogs; KConfigDialog::KConfigDialog(QWidget *parent, const QString &name, KCoreConfigSkeleton *config) : KPageDialog(parent), d(new KConfigDialogPrivate(this, name, config)) { } KConfigDialog::~KConfigDialog() { KConfigDialogPrivate::openDialogs.remove(objectName()); delete d; } KPageWidgetItem *KConfigDialog::addPage(QWidget *page, const QString &itemName, const QString &pixmapName, const QString &header, bool manage) { Q_ASSERT(page); if (!page) { return nullptr; } KPageWidgetItem *item = d->addPageInternal(page, itemName, pixmapName, header); if (manage) { d->manager->addWidget(page); } if (d->shown && manage) { // update the default button if the dialog is shown QPushButton *defaultButton = buttonBox()->button(QDialogButtonBox::RestoreDefaults); if (defaultButton) { bool is_default = defaultButton->isEnabled() && d->manager->isDefault(); defaultButton->setEnabled(!is_default); } } return item; } KPageWidgetItem *KConfigDialog::addPage(QWidget *page, KCoreConfigSkeleton *config, const QString &itemName, const QString &pixmapName, const QString &header) { Q_ASSERT(page); if (!page) { return nullptr; } KPageWidgetItem *item = d->addPageInternal(page, itemName, pixmapName, header); d->managerForPage[page] = new KConfigDialogManager(page, config); d->setupManagerConnections(d->managerForPage[page]); if (d->shown) { // update the default button if the dialog is shown QPushButton *defaultButton = buttonBox()->button(QDialogButtonBox::RestoreDefaults); if (defaultButton) { bool is_default = defaultButton->isEnabled() && d->managerForPage[page]->isDefault(); defaultButton->setEnabled(!is_default); } } return item; } KPageWidgetItem *KConfigDialog::KConfigDialogPrivate::addPageInternal(QWidget *page, const QString &itemName, const QString &pixmapName, const QString &header) { QWidget *frame = new QWidget(q); QVBoxLayout *boxLayout = new QVBoxLayout(frame); boxLayout->setContentsMargins(0, 0, 0, 0); boxLayout->setContentsMargins(0, 0, 0, 0); QScrollArea *scroll = new QScrollArea(q); scroll->setFrameShape(QFrame::NoFrame); scroll->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); scroll->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); scroll->setWidget(page); scroll->setWidgetResizable(true); scroll->setSizePolicy( QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding ); boxLayout->addWidget(scroll); KPageWidgetItem *item = new KPageWidgetItem(frame, itemName); item->setHeader(header); if (!pixmapName.isEmpty()) { item->setIcon(QIcon::fromTheme(pixmapName)); } q->KPageDialog::addPage(item); return item; } void KConfigDialog::KConfigDialogPrivate::setupManagerConnections(KConfigDialogManager *manager) { q->connect(manager, SIGNAL(settingsChanged()), q, SLOT(_k_settingsChangedSlot())); q->connect(manager, SIGNAL(widgetModified()), q, SLOT(_k_updateButtons())); QDialogButtonBox *buttonBox = q->buttonBox(); q->connect(buttonBox->button(QDialogButtonBox::Ok), &QAbstractButton::clicked, manager, &KConfigDialogManager::updateSettings); q->connect(buttonBox->button(QDialogButtonBox::Apply), &QAbstractButton::clicked, manager, &KConfigDialogManager::updateSettings); q->connect(buttonBox->button(QDialogButtonBox::Cancel), &QAbstractButton::clicked, manager, &KConfigDialogManager::updateWidgets); q->connect(buttonBox->button(QDialogButtonBox::RestoreDefaults), &QAbstractButton::clicked, manager, &KConfigDialogManager::updateWidgetsDefault); } void KConfigDialog::KConfigDialogPrivate::setApplyButtonEnabled(bool enabled) { QPushButton *applyButton = q->buttonBox()->button(QDialogButtonBox::Apply); if (applyButton) { applyButton->setEnabled(enabled); } } void KConfigDialog::KConfigDialogPrivate::setRestoreDefaultsButtonEnabled(bool enabled) { QPushButton *restoreDefaultsButton = q->buttonBox()->button(QDialogButtonBox::RestoreDefaults); if (restoreDefaultsButton) { restoreDefaultsButton->setEnabled(enabled); } } void KConfigDialog::onPageRemoved(KPageWidgetItem *item) { QMap::iterator j = d->managerForPage.begin(); while (j != d->managerForPage.end()) { // there is a manager for this page, so remove it if (item->widget()->isAncestorOf(j.key())) { KConfigDialogManager *manager = j.value(); d->managerForPage.erase(j); delete manager; d->_k_updateButtons(); break; } ++j; } } KConfigDialog *KConfigDialog::exists(const QString &name) { QHash::const_iterator it = KConfigDialogPrivate::openDialogs.constFind(name); if (it != KConfigDialogPrivate::openDialogs.constEnd()) { return *it; } return nullptr; } bool KConfigDialog::showDialog(const QString &name) { KConfigDialog *dialog = exists(name); if (dialog) { dialog->show(); } return (dialog != nullptr); } void KConfigDialog::KConfigDialogPrivate::_k_updateButtons() { static bool only_once = false; if (only_once) { return; } only_once = true; QMap::iterator it; bool has_changed = manager->hasChanged() || q->hasChanged(); for (it = managerForPage.begin(); it != managerForPage.end() && !has_changed; ++it) { has_changed |= (*it)->hasChanged(); } setApplyButtonEnabled(has_changed); bool is_default = manager->isDefault() && q->isDefault(); for (it = managerForPage.begin(); it != managerForPage.end() && is_default; ++it) { is_default &= (*it)->isDefault(); } setRestoreDefaultsButtonEnabled(!is_default); emit q->widgetModified(); only_once = false; } void KConfigDialog::KConfigDialogPrivate::_k_settingsChangedSlot() { // Update the buttons _k_updateButtons(); emit q->settingsChanged(q->objectName()); } void KConfigDialog::showEvent(QShowEvent *e) { if (!d->shown) { QMap::iterator it; updateWidgets(); d->manager->updateWidgets(); for (it = d->managerForPage.begin(); it != d->managerForPage.end(); ++it) { (*it)->updateWidgets(); } bool has_changed = d->manager->hasChanged() || hasChanged(); for (it = d->managerForPage.begin(); it != d->managerForPage.end() && !has_changed; ++it) { has_changed |= (*it)->hasChanged(); } d->setApplyButtonEnabled(has_changed); bool is_default = d->manager->isDefault() && isDefault(); for (it = d->managerForPage.begin(); it != d->managerForPage.end() && is_default; ++it) { is_default &= (*it)->isDefault(); } d->setRestoreDefaultsButtonEnabled(!is_default); d->shown = true; } KPageDialog::showEvent(e); } void KConfigDialog::updateSettings() { } void KConfigDialog::updateWidgets() { } void KConfigDialog::updateWidgetsDefault() { } bool KConfigDialog::hasChanged() { return false; } bool KConfigDialog::isDefault() { return true; } void KConfigDialog::updateButtons() { d->_k_updateButtons(); } void KConfigDialog::settingsChangedSlot() { d->_k_settingsChangedSlot(); } void KConfigDialog::setHelp(const QString &anchor, const QString &appname) { d->mAnchor = anchor; d->mHelpApp = appname; } void KConfigDialog::showHelp() { KHelpClient::invokeHelp(d->mAnchor, d->mHelpApp); } #include "moc_kconfigdialog.cpp" diff --git a/src/krecentfilesaction.cpp b/src/krecentfilesaction.cpp index 4597329..8a990db 100644 --- a/src/krecentfilesaction.cpp +++ b/src/krecentfilesaction.cpp @@ -1,345 +1,347 @@ /* This file is part of the KDE libraries Copyright (C) 1999 Reginald Stadlbauer (C) 1999 Simon Hausmann (C) 2000 Nicolas Hadacek (C) 2000 Kurt Granroth (C) 2000 Michael Koch (C) 2001 Holger Freyther (C) 2002 Ellis Whitehead (C) 2002 Joseph Wenninger (C) 2003 Andras Mantia (C) 2005-2006 Hamish Rodda This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "krecentfilesaction.h" #include "krecentfilesaction_p.h" #include #include #include +#include #include +#include #include #include #include KRecentFilesAction::KRecentFilesAction(QObject *parent) : KSelectAction(parent), d_ptr(new KRecentFilesActionPrivate(this)) { Q_D(KRecentFilesAction); d->init(); } KRecentFilesAction::KRecentFilesAction(const QString &text, QObject *parent) : KSelectAction(parent), d_ptr(new KRecentFilesActionPrivate(this)) { Q_D(KRecentFilesAction); d->init(); // Want to keep the ampersands setText(text); } KRecentFilesAction::KRecentFilesAction(const QIcon &icon, const QString &text, QObject *parent) : KSelectAction(parent), d_ptr(new KRecentFilesActionPrivate(this)) { Q_D(KRecentFilesAction); d->init(); setIcon(icon); // Want to keep the ampersands setText(text); } void KRecentFilesActionPrivate::init() { Q_Q(KRecentFilesAction); delete q->menu(); q->setMenu(new QMenu()); q->setToolBarMode(KSelectAction::MenuMode); m_noEntriesAction = q->menu()->addAction(i18n("No Entries")); m_noEntriesAction->setObjectName(QStringLiteral("no_entries")); m_noEntriesAction->setEnabled(false); clearSeparator = q->menu()->addSeparator(); clearSeparator->setVisible(false); clearSeparator->setObjectName(QStringLiteral("separator")); clearAction = q->menu()->addAction(i18n("Clear List"), q, &KRecentFilesAction::clear); clearAction->setObjectName(QStringLiteral("clear_action")); clearAction->setVisible(false); q->setEnabled(false); q->connect(q, SIGNAL(triggered(QAction*)), SLOT(_k_urlSelected(QAction*))); } KRecentFilesAction::~KRecentFilesAction() { delete d_ptr; } void KRecentFilesActionPrivate::_k_urlSelected(QAction *action) { Q_Q(KRecentFilesAction); emit q->urlSelected(m_urls[action]); } int KRecentFilesAction::maxItems() const { Q_D(const KRecentFilesAction); return d->m_maxItems; } void KRecentFilesAction::setMaxItems(int maxItems) { Q_D(KRecentFilesAction); // set new maxItems d->m_maxItems = maxItems; // remove all excess items while (selectableActionGroup()->actions().count() > maxItems) { delete removeAction(selectableActionGroup()->actions().last()); } } static QString titleWithSensibleWidth(const QString &nameValue, const QString &value) { // Calculate 3/4 of screen geometry, we do not want // action titles to be bigger than that // Since we do not know in which screen we are going to show // we choose the min of all the screens - const QDesktopWidget desktopWidget; int maxWidthForTitles = INT_MAX; - for (int i = 0; i < desktopWidget.screenCount(); ++i) { - maxWidthForTitles = qMin(maxWidthForTitles, desktopWidget.availableGeometry(i).width() * 3 / 4); + const auto screens = QGuiApplication::screens(); + for (QScreen *screen : screens) { + maxWidthForTitles = qMin(maxWidthForTitles, screen->availableGeometry().width() * 3 / 4); } const QFontMetrics fontMetrics = QFontMetrics(QFont()); QString title = nameValue + QLatin1String(" [") + value + QLatin1Char(']'); - if (fontMetrics.width(title) > maxWidthForTitles) { + const int nameWidth = fontMetrics.boundingRect(title).width(); + if (nameWidth > maxWidthForTitles) { // If it does not fit, try to cut only the whole path, though if the // name is too long (more than 3/4 of the whole text) we cut it a bit too const int nameValueMaxWidth = maxWidthForTitles * 3 / 4; - const int nameWidth = fontMetrics.width(nameValue); QString cutNameValue, cutValue; if (nameWidth > nameValueMaxWidth) { cutNameValue = fontMetrics.elidedText(nameValue, Qt::ElideMiddle, nameValueMaxWidth); cutValue = fontMetrics.elidedText(value, Qt::ElideMiddle, maxWidthForTitles - nameValueMaxWidth); } else { cutNameValue = nameValue; cutValue = fontMetrics.elidedText(value, Qt::ElideMiddle, maxWidthForTitles - nameWidth); } title = cutNameValue + QLatin1String(" [") + cutValue + QLatin1Char(']'); } return title; } void KRecentFilesAction::addUrl(const QUrl &_url, const QString &name) { Q_D(KRecentFilesAction); /** * Create a deep copy here, because if _url is the parameter from * urlSelected() signal, we will delete it in the removeAction() call below. * but access it again in the addAction call... => crash */ const QUrl url(_url); if (url.isLocalFile() && url.toLocalFile().startsWith(QDir::tempPath())) { return; } const QString tmpName = name.isEmpty() ? url.fileName() : name; const QString pathOrUrl(url.toDisplayString(QUrl::PreferLocalFile)); #ifdef Q_OS_WIN const QString file = url.isLocalFile() ? QDir::toNativeSeparators(pathOrUrl) : pathOrUrl; #else const QString file = pathOrUrl; #endif // remove file if already in list const auto lstActions = selectableActionGroup()->actions(); for (QAction *action : lstActions) { const QString urlStr = d->m_urls[action].toDisplayString(QUrl::PreferLocalFile); #ifdef Q_OS_WIN const QString tmpFileName = url.isLocalFile() ? QDir::toNativeSeparators(urlStr) : urlStr; if (tmpFileName.endsWith(file, Qt::CaseInsensitive)) #else if (urlStr.endsWith(file)) #endif { removeAction(action)->deleteLater(); break; } } // remove oldest item if already maxitems in list if (d->m_maxItems && selectableActionGroup()->actions().count() == d->m_maxItems) { // remove oldest added item delete removeAction(selectableActionGroup()->actions().first()); } d->m_noEntriesAction->setVisible(false); d->clearSeparator->setVisible(true); d->clearAction->setVisible(true); setEnabled(true); // add file to list const QString title = titleWithSensibleWidth(tmpName, file); QAction *action = new QAction(title, selectableActionGroup()); addAction(action, url, tmpName); } void KRecentFilesAction::addAction(QAction *action, const QUrl &url, const QString &name) { Q_D(KRecentFilesAction); menu()->insertAction(menu()->actions().value(0), action); d->m_shortNames.insert(action, name); d->m_urls.insert(action, url); } QAction *KRecentFilesAction::removeAction(QAction *action) { Q_D(KRecentFilesAction); KSelectAction::removeAction(action); d->m_shortNames.remove(action); d->m_urls.remove(action); return action; } void KRecentFilesAction::removeUrl(const QUrl &url) { Q_D(KRecentFilesAction); for (QMap::ConstIterator it = d->m_urls.constBegin(); it != d->m_urls.constEnd(); ++it) if (it.value() == url) { delete removeAction(it.key()); return; } } QList KRecentFilesAction::urls() const { Q_D(const KRecentFilesAction); return d->m_urls.values(); } void KRecentFilesAction::clear() { clearEntries(); emit recentListCleared(); } void KRecentFilesAction::clearEntries() { Q_D(KRecentFilesAction); KSelectAction::clear(); d->m_shortNames.clear(); d->m_urls.clear(); d->m_noEntriesAction->setVisible(true); d->clearSeparator->setVisible(false); d->clearAction->setVisible(false); setEnabled(false); } void KRecentFilesAction::loadEntries(const KConfigGroup &_config) { Q_D(KRecentFilesAction); clearEntries(); QString key; QString value; QString nameKey; QString nameValue; QString title; QUrl url; KConfigGroup cg = _config; if (cg.name().isEmpty()) { cg = KConfigGroup(cg.config(), "RecentFiles"); } bool thereAreEntries = false; // read file list for (int i = 1; i <= d->m_maxItems; i++) { key = QStringLiteral("File%1").arg(i); value = cg.readPathEntry(key, QString()); if (value.isEmpty()) { continue; } url = QUrl::fromUserInput(value); // Don't restore if file doesn't exist anymore if (url.isLocalFile() && !QFile::exists(url.toLocalFile())) { continue; } // Don't restore where the url is already known (eg. broken config) if (d->m_urls.values().contains(url)) { continue; } #ifdef Q_OS_WIN // convert to backslashes if (url.isLocalFile()) { value = QDir::toNativeSeparators(value); } #endif nameKey = QStringLiteral("Name%1").arg(i); nameValue = cg.readPathEntry(nameKey, url.fileName()); title = titleWithSensibleWidth(nameValue, value); if (!value.isNull()) { thereAreEntries = true; addAction(new QAction(title, selectableActionGroup()), url, nameValue); } } if (thereAreEntries) { d->m_noEntriesAction->setVisible(false); d->clearSeparator->setVisible(true); d->clearAction->setVisible(true); setEnabled(true); } } void KRecentFilesAction::saveEntries(const KConfigGroup &_cg) { Q_D(KRecentFilesAction); QString key; QString value; QStringList lst = items(); KConfigGroup cg = _cg; if (cg.name().isEmpty()) { cg = KConfigGroup(cg.config(), "RecentFiles"); } cg.deleteGroup(); // write file list for (int i = 1; i <= selectableActionGroup()->actions().count(); i++) { key = QStringLiteral("File%1").arg(i); // i - 1 because we started from 1 value = d->m_urls[ selectableActionGroup()->actions()[ i - 1 ] ].toDisplayString(QUrl::PreferLocalFile); cg.writePathEntry(key, value); key = QStringLiteral("Name%1").arg(i); value = d->m_shortNames[ selectableActionGroup()->actions()[ i - 1 ] ]; cg.writePathEntry(key, value); } } #include "moc_krecentfilesaction.cpp" diff --git a/src/ktipdialog.cpp b/src/ktipdialog.cpp index 810aed3..67be01f 100644 --- a/src/ktipdialog.cpp +++ b/src/ktipdialog.cpp @@ -1,428 +1,431 @@ /***************************************************************** Copyright (c) 2000-2003 Matthias Hoelzer-Kluepfel Tobias Koenig Daniel Molkentin Copyright (c) 2008 Urs Wolfer Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ******************************************************************/ #include "ktipdialog.h" #include "kconfigwidgets_debug.h" #include #include #include #include #include #include #include +#include #include #include #include #include #include #include #include #include #include #include class Q_DECL_HIDDEN KTipDatabase::Private { public: void loadTips(const QString &tipFile); void addTips(const QString &tipFile); QStringList tips; int currentTip; }; void KTipDatabase::Private::loadTips(const QString &tipFile) { tips.clear(); addTips(tipFile); } /** * If you change something here, please update the script * preparetips5, which depends on extracting exactly the same * text as done here. */ void KTipDatabase::Private::addTips(const QString &tipFile) { const QString fileName = QStandardPaths::locate(QStandardPaths::GenericDataLocation, tipFile); if (fileName.isEmpty()) { qCDebug(KCONFIG_WIDGETS_LOG) << "KTipDatabase::addTips: can't find '" << tipFile << "' in standard dirs"; return; } QFile file(fileName); if (!file.open(QIODevice::ReadOnly)) { qCDebug(KCONFIG_WIDGETS_LOG) << "KTipDatabase::addTips: can't open '" << fileName << "' for reading"; return; } QByteArray data = file.readAll(); QString content = QString::fromUtf8(data.constData(), data.size()); const QRegExp rx(QStringLiteral("\\n+")); int pos = -1; while ((pos = content.indexOf(QLatin1String(""), pos + 1, Qt::CaseInsensitive)) != -1) { /** * To make translations work, tip extraction here must exactly * match what is done by the preparetips5 script. */ QString tip = content .mid(pos + 6, content.indexOf(QLatin1String(""), pos, Qt::CaseInsensitive) - pos - 6) .replace(rx, QStringLiteral("\n")); if (!tip.endsWith('\n')) { tip += '\n'; } if (tip.startsWith('\n')) { tip.remove(0, 1); } if (tip.isEmpty()) { qCDebug(KCONFIG_WIDGETS_LOG) << "Empty tip found! Skipping! " << pos; continue; } tips.append(tip); } file.close(); } KTipDatabase::KTipDatabase(const QString &_tipFile) : d(new Private) { QString tipFile = _tipFile; if (tipFile.isEmpty()) { tipFile = QCoreApplication::applicationName() + QStringLiteral("/tips"); } d->loadTips(tipFile); if (!d->tips.isEmpty()) { d->currentTip = KRandom::random() % d->tips.count(); } } KTipDatabase::KTipDatabase(const QStringList &tipsFiles) : d(new Private) { if (tipsFiles.isEmpty() || ((tipsFiles.count() == 1) && tipsFiles.first().isEmpty())) { d->addTips(QCoreApplication::applicationName() + QStringLiteral("/tips")); } else { for (QStringList::ConstIterator it = tipsFiles.begin(); it != tipsFiles.end(); ++it) { d->addTips(*it); } } if (!d->tips.isEmpty()) { d->currentTip = KRandom::random() % d->tips.count(); } } KTipDatabase::~KTipDatabase() { delete d; } void KTipDatabase::nextTip() { if (d->tips.isEmpty()) { return; } d->currentTip += 1; if (d->currentTip >= d->tips.count()) { d->currentTip = 0; } } void KTipDatabase::prevTip() { if (d->tips.isEmpty()) { return; } d->currentTip -= 1; if (d->currentTip < 0) { d->currentTip = d->tips.count() - 1; } } QString KTipDatabase::tip() const { if (d->tips.isEmpty()) { return QString(); } return d->tips[ d->currentTip ]; } class Q_DECL_HIDDEN KTipDialog::Private { public: Private(KTipDialog *_parent) : parent(_parent) { } ~Private() { delete database; } void _k_nextTip(); void _k_prevTip(); void _k_showOnStart(bool); KTipDialog *parent; KTipDatabase *database; QCheckBox *tipOnStart; QTextBrowser *tipText; static KTipDialog *mInstance; }; KTipDialog *KTipDialog::Private::mInstance = nullptr; void KTipDialog::Private::_k_prevTip() { database->prevTip(); tipText->setHtml(QStringLiteral("%1") .arg(i18nd(KLocalizedString::applicationDomain(), database->tip().toUtf8()))); } void KTipDialog::Private::_k_nextTip() { database->nextTip(); tipText->setHtml(QStringLiteral("%1") .arg(i18nd(KLocalizedString::applicationDomain(), database->tip().toUtf8()))); } void KTipDialog::Private::_k_showOnStart(bool on) { parent->setShowOnStart(on); } KTipDialog::KTipDialog(KTipDatabase *database, QWidget *parent) : QDialog(parent), d(new Private(this)) { setWindowTitle(i18nc("@title:window", "Tip of the Day")); /** * Parent is 0L when TipDialog is used as a mainWidget. This should * be the case only in ktip, so let's use the ktip layout. */ bool isTipDialog = (parent != nullptr); d->database = database; setWindowIcon(QIcon::fromTheme(QStringLiteral("ktip"))); QVBoxLayout *mainLayout = new QVBoxLayout(this); if (isTipDialog) { QLabel *titleLabel = new QLabel(this); titleLabel->setText(i18nc("@title", "Did you know...?\n")); titleLabel->setFont(QFont(qApp->font().family(), 20, QFont::Bold)); titleLabel->setAlignment(Qt::AlignCenter); mainLayout->addWidget(titleLabel); } QHBoxLayout *browserLayout = new QHBoxLayout(); mainLayout->addLayout(browserLayout); d->tipText = new QTextBrowser(this); d->tipText->setOpenExternalLinks(true); d->tipText->setWordWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere); QStringList paths; paths << QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QStringLiteral("icons"), QStandardPaths::LocateDirectory) << QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QStringLiteral("kdewizard/pics"), QStandardPaths::LocateDirectory); d->tipText->setSearchPaths(paths); d->tipText->setFrameStyle(QFrame::NoFrame); d->tipText->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); QPalette tipPal(d->tipText->palette()); tipPal.setColor(QPalette::Base, Qt::transparent); tipPal.setColor(QPalette::Text, tipPal.color(QPalette::WindowText)); d->tipText->setPalette(tipPal); browserLayout->addWidget(d->tipText); QLabel *label = new QLabel(this); label->setPixmap(QStringLiteral(":/kconfigwidgets/pics/ktip-bulb.png")); label->setAlignment(Qt::AlignRight | Qt::AlignVCenter); browserLayout->addWidget(label); if (!isTipDialog) { resize(520, 280); QSize sh = size(); - QRect rect = QApplication::desktop()->screenGeometry(QCursor::pos()); - - move(rect.x() + (rect.width() - sh.width()) / 2, - rect.y() + (rect.height() - sh.height()) / 2); + QScreen *screen = QGuiApplication::screenAt(QCursor::pos()); + if (screen) { + const QRect rect = screen->geometry(); + move(rect.x() + (rect.width() - sh.width()) / 2, + rect.y() + (rect.height() - sh.height()) / 2); + } } KSeparator *sep = new KSeparator(Qt::Horizontal); mainLayout->addWidget(sep); QHBoxLayout *buttonLayout = new QHBoxLayout(); mainLayout->addLayout(buttonLayout); d->tipOnStart = new QCheckBox(i18nc("@option:check", "&Show tips on startup")); buttonLayout->addWidget(d->tipOnStart, 1); QPushButton *prev = new QPushButton; KGuiItem::assign(prev, KStandardGuiItem::back(KStandardGuiItem::UseRTL)); prev->setText(i18nc("@action:button Goes to previous tip", "&Previous")); buttonLayout->addWidget(prev); QPushButton *next = new QPushButton; KGuiItem::assign(next, KStandardGuiItem::forward(KStandardGuiItem::UseRTL)); next->setText(i18nc("@action:button Goes to next tip, opposite to previous", "&Next")); buttonLayout->addWidget(next); QPushButton *ok = new QPushButton; KGuiItem::assign(ok, KStandardGuiItem::close()); ok->setDefault(true); buttonLayout->addWidget(ok); KConfigGroup config(KSharedConfig::openConfig(), "TipOfDay"); d->tipOnStart->setChecked(config.readEntry("RunOnStart", true)); connect(next, &QPushButton::clicked, this, [this]() { d->_k_nextTip(); }); connect(prev, &QPushButton::clicked, this, [this]() { d->_k_prevTip(); }); connect(ok, &QAbstractButton::clicked, this, &QDialog::accept); connect(d->tipOnStart, &QCheckBox::toggled, this, [this](bool state) { d->_k_showOnStart(state); }); ok->setFocus(); d->_k_nextTip(); } KTipDialog::~KTipDialog() { if (Private::mInstance == this) { Private::mInstance = nullptr; } delete d; } /** * use the one with a parent window as parameter instead of this one * or you will get focus problems */ void KTipDialog::showTip(const QString &tipFile, bool force) { showTip(nullptr, tipFile, force); } void KTipDialog::showTip(QWidget *parent, const QString &tipFile, bool force) { showMultiTip(parent, QStringList(tipFile), force); } void KTipDialog::showMultiTip(QWidget *parent, const QStringList &tipFiles, bool force) { KConfigGroup configGroup(KSharedConfig::openConfig(), "TipOfDay"); const bool runOnStart = configGroup.readEntry("RunOnStart", true); if (!force) { if (!runOnStart) { return; } // showing the tooltips on startup suggests the tooltip // will be shown *each time* on startup, not $random days later // TODO either remove or uncomment this code, but make the situation clear /*bool hasLastShown = configGroup.hasKey( "TipLastShown" ); if ( hasLastShown ) { const int oneDay = 24 * 60 * 60; QDateTime lastShown = configGroup.readEntry( "TipLastShown", QDateTime() ); // Show tip roughly once a week if ( lastShown.secsTo( QDateTime::currentDateTime() ) < (oneDay + (KRandom::random() % (10 * oneDay))) ) return; } configGroup.writeEntry( "TipLastShown", QDateTime::currentDateTime() ); if ( !hasLastShown ) return; // Don't show tip on first start*/ } if (!Private::mInstance) { Private::mInstance = new KTipDialog(new KTipDatabase(tipFiles), parent); } else // The application might have changed the RunOnStart option in its own // configuration dialog, so we should update the checkbox. { Private::mInstance->d->tipOnStart->setChecked(runOnStart); } Private::mInstance->show(); Private::mInstance->raise(); } void KTipDialog::setShowOnStart(bool on) { KConfigGroup config(KSharedConfig::openConfig(), "TipOfDay"); config.writeEntry("RunOnStart", on); } bool KTipDialog::eventFilter(QObject *object, QEvent *event) { if (object == d->tipText && event->type() == QEvent::KeyPress && (((QKeyEvent *)event)->key() == Qt::Key_Return || ((QKeyEvent *)event)->key() == Qt::Key_Space)) { accept(); } /** * If the user presses Return or Space, we close the dialog as if the * default button was pressed even if the QTextBrowser has the keyboard * focus. This could have the bad side-effect that the user cannot use the * keyboard to open urls in the QTextBrowser, so we just let it handle * the key event _additionally_. (Antonio) */ return QWidget::eventFilter(object, event); } #include "moc_ktipdialog.cpp"