diff --git a/src/libkdepim/ldap/addhostdialog.cpp b/src/libkdepim/ldap/addhostdialog.cpp index 0c9d78c..e3a5e45 100644 --- a/src/libkdepim/ldap/addhostdialog.cpp +++ b/src/libkdepim/ldap/addhostdialog.cpp @@ -1,201 +1,201 @@ /* This file is part of libkldap. Copyright (c) 2002-2010 Tobias Koenig 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 "addhostdialog.h" #include #include #include #include #include #include #include #include #include #include using namespace KLDAP; class KLDAP::AddHostDialogPrivate { public: AddHostDialogPrivate(AddHostDialog *qq) : q(qq) { } ~AddHostDialogPrivate() { writeConfig(); } void readConfig(); void writeConfig(); KLDAP::LdapConfigWidget *mCfg = nullptr; KLDAP::LdapServer *mServer = nullptr; QPushButton *mOkButton = nullptr; AddHostDialog *q = nullptr; }; void AddHostDialogPrivate::readConfig() { KConfigGroup group(KSharedConfig::openConfig(), "AddHostDialog"); const QSize size = group.readEntry("Size", QSize(600, 400)); if (size.isValid()) { q->resize(size); } } void AddHostDialogPrivate::writeConfig() { KConfigGroup group(KSharedConfig::openConfig(), "AddHostDialog"); group.writeEntry("Size", q->size()); group.sync(); } AddHostDialog::AddHostDialog(KLDAP::LdapServer *server, QWidget *parent) : QDialog(parent) , d(new KLDAP::AddHostDialogPrivate(this)) { setWindowTitle(i18n("Add Host")); QVBoxLayout *mainLayout = new QVBoxLayout(this); QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this); d->mOkButton = buttonBox->button(QDialogButtonBox::Ok); d->mOkButton->setDefault(true); d->mOkButton->setShortcut(Qt::CTRL | Qt::Key_Return); connect(buttonBox, &QDialogButtonBox::rejected, this, &AddHostDialog::reject); setModal(true); d->mServer = server; QWidget *page = new QWidget(this); mainLayout->addWidget(page); mainLayout->addWidget(buttonBox); QHBoxLayout *layout = new QHBoxLayout(page); - layout->setMargin(0); + layout->setContentsMargins(0, 0, 0, 0); d->mCfg = new KLDAP::LdapConfigWidget( KLDAP::LdapConfigWidget::W_USER |KLDAP::LdapConfigWidget::W_PASS |KLDAP::LdapConfigWidget::W_BINDDN |KLDAP::LdapConfigWidget::W_REALM |KLDAP::LdapConfigWidget::W_HOST |KLDAP::LdapConfigWidget::W_PORT |KLDAP::LdapConfigWidget::W_VER |KLDAP::LdapConfigWidget::W_TIMELIMIT |KLDAP::LdapConfigWidget::W_SIZELIMIT |KLDAP::LdapConfigWidget::W_PAGESIZE |KLDAP::LdapConfigWidget::W_DN |KLDAP::LdapConfigWidget::W_FILTER |KLDAP::LdapConfigWidget::W_SECBOX |KLDAP::LdapConfigWidget::W_AUTHBOX, page); layout->addWidget(d->mCfg); d->mCfg->setHost(d->mServer->host()); d->mCfg->setPort(d->mServer->port()); d->mCfg->setDn(d->mServer->baseDn()); d->mCfg->setUser(d->mServer->user()); d->mCfg->setBindDn(d->mServer->bindDn()); d->mCfg->setPassword(d->mServer->password()); d->mCfg->setTimeLimit(d->mServer->timeLimit()); d->mCfg->setSizeLimit(d->mServer->sizeLimit()); d->mCfg->setPageSize(d->mServer->pageSize()); d->mCfg->setVersion(d->mServer->version()); d->mCfg->setFilter(d->mServer->filter()); switch (d->mServer->security()) { case KLDAP::LdapServer::TLS: d->mCfg->setSecurity(KLDAP::LdapConfigWidget::TLS); break; case KLDAP::LdapServer::SSL: d->mCfg->setSecurity(KLDAP::LdapConfigWidget::SSL); break; default: d->mCfg->setSecurity(KLDAP::LdapConfigWidget::None); } switch (d->mServer->auth()) { case KLDAP::LdapServer::Simple: d->mCfg->setAuth(KLDAP::LdapConfigWidget::Simple); break; case KLDAP::LdapServer::SASL: d->mCfg->setAuth(KLDAP::LdapConfigWidget::SASL); break; default: d->mCfg->setAuth(KLDAP::LdapConfigWidget::Anonymous); } d->mCfg->setMech(d->mServer->mech()); KAcceleratorManager::manage(this); connect(d->mCfg, &KLDAP::LdapConfigWidget::hostNameChanged, this, &AddHostDialog::slotHostEditChanged); connect(d->mOkButton, &QPushButton::clicked, this, &AddHostDialog::slotOk); d->mOkButton->setEnabled(!d->mServer->host().isEmpty()); d->readConfig(); } AddHostDialog::~AddHostDialog() { delete d; } void AddHostDialog::slotHostEditChanged(const QString &text) { d->mOkButton->setEnabled(!text.isEmpty()); } void AddHostDialog::slotOk() { d->mServer->setHost(d->mCfg->host()); d->mServer->setPort(d->mCfg->port()); d->mServer->setBaseDn(d->mCfg->dn()); d->mServer->setUser(d->mCfg->user()); d->mServer->setBindDn(d->mCfg->bindDn()); d->mServer->setPassword(d->mCfg->password()); d->mServer->setTimeLimit(d->mCfg->timeLimit()); d->mServer->setSizeLimit(d->mCfg->sizeLimit()); d->mServer->setPageSize(d->mCfg->pageSize()); d->mServer->setVersion(d->mCfg->version()); d->mServer->setFilter(d->mCfg->filter()); switch (d->mCfg->security()) { case KLDAP::LdapConfigWidget::TLS: d->mServer->setSecurity(KLDAP::LdapServer::TLS); break; case KLDAP::LdapConfigWidget::SSL: d->mServer->setSecurity(KLDAP::LdapServer::SSL); break; default: d->mServer->setSecurity(KLDAP::LdapServer::None); } switch (d->mCfg->auth()) { case KLDAP::LdapConfigWidget::Simple: d->mServer->setAuth(KLDAP::LdapServer::Simple); break; case KLDAP::LdapConfigWidget::SASL: d->mServer->setAuth(KLDAP::LdapServer::SASL); break; default: d->mServer->setAuth(KLDAP::LdapServer::Anonymous); } d->mServer->setMech(d->mCfg->mech()); QDialog::accept(); } #include "moc_addhostdialog.cpp" diff --git a/src/libkdepim/ldap/kcmldap.cpp b/src/libkdepim/ldap/kcmldap.cpp index 627404a..55517b6 100644 --- a/src/libkdepim/ldap/kcmldap.cpp +++ b/src/libkdepim/ldap/kcmldap.cpp @@ -1,72 +1,72 @@ /* This file is part of libkldap. Copyright (c) 2002-2009 Tobias Koenig Copyright (C) 2013-2019 Laurent Montel 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 "kcmldap_p.h" #include #include #include #include #include #include #include "ldapconfigurewidget.h" K_PLUGIN_CLASS_WITH_JSON(KCMLdap, "kcmldap.json") KCMLdap::KCMLdap(QWidget *parent, const QVariantList &) : KCModule(parent) { setButtons(KCModule::Apply); KAboutData *about = new KAboutData(QStringLiteral("kcmldap"), i18n("kcmldap"), QString(), i18n("LDAP Server Settings"), KAboutLicense::LGPL, i18n("(c) 2009 - 2010 Tobias Koenig")); about->addAuthor(i18n("Tobias Koenig"), QString(), QStringLiteral("tokoe@kde.org")); setAboutData(about); QVBoxLayout *layout = new QVBoxLayout(this); - layout->setMargin(0); + layout->setContentsMargins(0, 0, 0, 0); mLdapConfigureWidget = new KLDAP::LdapConfigureWidget(this); layout->addWidget(mLdapConfigureWidget); connect(mLdapConfigureWidget, &KLDAP::LdapConfigureWidget::changed, this, QOverload::of(&KCMLdap::changed)); } KCMLdap::~KCMLdap() { } void KCMLdap::load() { mLdapConfigureWidget->load(); } void KCMLdap::save() { mLdapConfigureWidget->save(); } #include "kcmldap.moc" diff --git a/src/libkdepim/ldap/ldapconfigurewidget.cpp b/src/libkdepim/ldap/ldapconfigurewidget.cpp index 0b79652..37aa453 100644 --- a/src/libkdepim/ldap/ldapconfigurewidget.cpp +++ b/src/libkdepim/ldap/ldapconfigurewidget.cpp @@ -1,356 +1,356 @@ /* * Copyright (C) 2019 Laurent Montel * * 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 "ldapconfigurewidget.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ldapclientsearch.h" #include "ldapclientsearchconfig.h" #include #include "addhostdialog.h" using namespace KLDAP; class LDAPItem : public QListWidgetItem { public: LDAPItem(QListWidget *parent, const KLDAP::LdapServer &server, bool isActive = false) : QListWidgetItem(parent, QListWidgetItem::UserType) , mIsActive(isActive) { setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsUserCheckable); setCheckState(isActive ? Qt::Checked : Qt::Unchecked); setServer(server); } void setServer(const KLDAP::LdapServer &server) { mServer = server; setText(mServer.host()); } const KLDAP::LdapServer &server() const { return mServer; } void setIsActive(bool isActive) { mIsActive = isActive; } bool isActive() const { return mIsActive; } private: KLDAP::LdapServer mServer; bool mIsActive = false; }; LdapConfigureWidget::LdapConfigureWidget(QWidget *parent) : QWidget(parent) { mClientSearchConfig = new KLDAP::LdapClientSearchConfig; initGUI(); connect(mHostListView, &QListWidget::currentItemChanged, this, &LdapConfigureWidget::slotSelectionChanged); connect(mHostListView, &QListWidget::itemDoubleClicked, this, &LdapConfigureWidget::slotEditHost); connect(mHostListView, &QListWidget::itemClicked, this, &LdapConfigureWidget::slotItemClicked); connect(mUpButton, &QToolButton::clicked, this, &LdapConfigureWidget::slotMoveUp); connect(mDownButton, &QToolButton::clicked, this, &LdapConfigureWidget::slotMoveDown); } LdapConfigureWidget::~LdapConfigureWidget() { delete mClientSearchConfig; } void LdapConfigureWidget::slotSelectionChanged(QListWidgetItem *item) { bool state = (item != nullptr); mEditButton->setEnabled(state); mRemoveButton->setEnabled(state); mDownButton->setEnabled(item && (mHostListView->row(item) != (mHostListView->count() - 1))); mUpButton->setEnabled(item && (mHostListView->row(item) != 0)); } void LdapConfigureWidget::slotItemClicked(QListWidgetItem *item) { LDAPItem *ldapItem = dynamic_cast(item); if (!ldapItem) { return; } if ((ldapItem->checkState() == Qt::Checked) != ldapItem->isActive()) { Q_EMIT changed(true); ldapItem->setIsActive(ldapItem->checkState() == Qt::Checked); } } void LdapConfigureWidget::slotAddHost() { KLDAP::LdapServer server; KLDAP::AddHostDialog dlg(&server, this); if (dlg.exec() && !server.host().trimmed().isEmpty()) { //krazy:exclude=crashy new LDAPItem(mHostListView, server); Q_EMIT changed(true); } } void LdapConfigureWidget::slotEditHost() { LDAPItem *item = dynamic_cast(mHostListView->currentItem()); if (!item) { return; } KLDAP::LdapServer server = item->server(); KLDAP::AddHostDialog dlg(&server, this); dlg.setWindowTitle(i18n("Edit Host")); if (dlg.exec() && !server.host().isEmpty()) { //krazy:exclude=crashy item->setServer(server); Q_EMIT changed(true); } } void LdapConfigureWidget::slotRemoveHost() { QListWidgetItem *item = mHostListView->currentItem(); if (!item) { return; } LDAPItem *ldapItem = dynamic_cast(item); if (KMessageBox::No == KMessageBox::questionYesNo(this, i18n("Do you want to remove setting for host \"%1\"?", ldapItem->server().host()), i18n("Remove Host"))) { return; } delete mHostListView->takeItem(mHostListView->currentRow()); slotSelectionChanged(mHostListView->currentItem()); Q_EMIT changed(true); } static void swapItems(LDAPItem *item, LDAPItem *other) { KLDAP::LdapServer server = item->server(); bool isActive = item->isActive(); item->setServer(other->server()); item->setIsActive(other->isActive()); item->setCheckState(other->isActive() ? Qt::Checked : Qt::Unchecked); other->setServer(server); other->setIsActive(isActive); other->setCheckState(isActive ? Qt::Checked : Qt::Unchecked); } void LdapConfigureWidget::slotMoveUp() { const QList selectedItems = mHostListView->selectedItems(); if (selectedItems.isEmpty()) { return; } LDAPItem *item = static_cast(mHostListView->selectedItems().first()); if (!item) { return; } LDAPItem *above = static_cast(mHostListView->item(mHostListView->row(item) - 1)); if (!above) { return; } swapItems(item, above); mHostListView->setCurrentItem(above); above->setSelected(true); Q_EMIT changed(true); } void LdapConfigureWidget::slotMoveDown() { const QList selectedItems = mHostListView->selectedItems(); if (selectedItems.isEmpty()) { return; } LDAPItem *item = static_cast(mHostListView->selectedItems().first()); if (!item) { return; } LDAPItem *below = static_cast(mHostListView->item(mHostListView->row(item) + 1)); if (!below) { return; } swapItems(item, below); mHostListView->setCurrentItem(below); below->setSelected(true); Q_EMIT changed(true); } void LdapConfigureWidget::load() { mHostListView->clear(); KConfig *config = KLDAP::LdapClientSearchConfig::config(); KConfigGroup group(config, "LDAP"); int count = group.readEntry("NumSelectedHosts", 0); for (int i = 0; i < count; ++i) { KLDAP::LdapServer server; mClientSearchConfig->readConfig(server, group, i, true); LDAPItem *item = new LDAPItem(mHostListView, server, true); item->setCheckState(Qt::Checked); } count = group.readEntry("NumHosts", 0); for (int i = 0; i < count; ++i) { KLDAP::LdapServer server; mClientSearchConfig->readConfig(server, group, i, false); new LDAPItem(mHostListView, server); } Q_EMIT changed(false); } void LdapConfigureWidget::save() { mClientSearchConfig->clearWalletPassword(); KConfig *config = KLDAP::LdapClientSearchConfig::config(); config->deleteGroup("LDAP"); KConfigGroup group(config, "LDAP"); int selected = 0; int unselected = 0; for (int i = 0; i < mHostListView->count(); ++i) { LDAPItem *item = dynamic_cast(mHostListView->item(i)); if (!item) { continue; } KLDAP::LdapServer server = item->server(); if (item->checkState() == Qt::Checked) { mClientSearchConfig->writeConfig(server, group, selected, true); selected++; } else { mClientSearchConfig->writeConfig(server, group, unselected, false); unselected++; } } group.writeEntry("NumSelectedHosts", selected); group.writeEntry("NumHosts", unselected); config->sync(); Q_EMIT changed(false); } void LdapConfigureWidget::initGUI() { QVBoxLayout *layout = new QVBoxLayout(this); layout->setObjectName(QStringLiteral("layout")); - layout->setMargin(0); + layout->setContentsMargins(0, 0, 0, 0); QGroupBox *groupBox = new QGroupBox(i18n("LDAP Servers")); layout->addWidget(groupBox); QVBoxLayout *mainLayout = new QVBoxLayout; mainLayout->setObjectName(QStringLiteral("mainlayout")); groupBox->setLayout(mainLayout); // Contents of the QVGroupBox: label and hbox QLabel *label = new QLabel(i18n("Check all servers that should be used:")); mainLayout->addWidget(label); QWidget *hBox = new QWidget(this); mainLayout->addWidget(hBox); QHBoxLayout *hBoxHBoxLayout = new QHBoxLayout(hBox); - hBoxHBoxLayout->setMargin(0); + hBoxHBoxLayout->setContentsMargins(0, 0, 0, 0); hBoxHBoxLayout->setSpacing(6); // Contents of the hbox: listview and up/down buttons on the right (vbox) mHostListView = new QListWidget(hBox); hBoxHBoxLayout->addWidget(mHostListView); mHostListView->setSortingEnabled(false); QWidget *upDownBox = new QWidget(hBox); QVBoxLayout *upDownBoxVBoxLayout = new QVBoxLayout(upDownBox); - upDownBoxVBoxLayout->setMargin(0); + upDownBoxVBoxLayout->setContentsMargins(0, 0, 0, 0); hBoxHBoxLayout->addWidget(upDownBox); upDownBoxVBoxLayout->setSpacing(6); mUpButton = new QToolButton(upDownBox); upDownBoxVBoxLayout->addWidget(mUpButton); mUpButton->setIcon(QIcon::fromTheme(QStringLiteral("go-up"))); mUpButton->setIconSize(QSize(KIconLoader::SizeSmall, KIconLoader::SizeSmall)); mUpButton->setEnabled(false); // b/c no item is selected yet mDownButton = new QToolButton(upDownBox); upDownBoxVBoxLayout->addWidget(mDownButton); mDownButton->setIcon(QIcon::fromTheme(QStringLiteral("go-down"))); mDownButton->setIconSize(QSize(KIconLoader::SizeSmall, KIconLoader::SizeSmall)); mDownButton->setEnabled(false); // b/c no item is selected yet QWidget *spacer = new QWidget(upDownBox); upDownBoxVBoxLayout->addWidget(spacer); upDownBoxVBoxLayout->setStretchFactor(spacer, 100); QDialogButtonBox *buttons = new QDialogButtonBox(this); QPushButton *add = buttons->addButton(i18n("&Add Host..."), QDialogButtonBox::ActionRole); connect(add, &QPushButton::clicked, this, &LdapConfigureWidget::slotAddHost); mEditButton = buttons->addButton(i18n("&Edit Host..."), QDialogButtonBox::ActionRole); connect(mEditButton, &QPushButton::clicked, this, &LdapConfigureWidget::slotEditHost); mEditButton->setEnabled(false); mRemoveButton = buttons->addButton(i18n("&Remove Host"), QDialogButtonBox::ActionRole); connect(mRemoveButton, &QPushButton::clicked, this, &LdapConfigureWidget::slotRemoveHost); mRemoveButton->setEnabled(false); buttons->layout(); layout->addWidget(buttons); } diff --git a/src/libkdepim/ldap/ldapsearchdialog.cpp b/src/libkdepim/ldap/ldapsearchdialog.cpp index c59d54f..e2cced7 100644 --- a/src/libkdepim/ldap/ldapsearchdialog.cpp +++ b/src/libkdepim/ldap/ldapsearchdialog.cpp @@ -1,895 +1,895 @@ /* * This file is part of libkldap. * * Copyright (C) 2002 Klarälvdalens Datakonsult AB * * Author: Steffen Hansen * * 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 "ldapsearchdialog.h" #include "ldapclient.h" #include "ldapclientsearchconfig.h" #include "widgets/progressindicatorlabel.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 #include #include #include #include using namespace KLDAP; static QString asUtf8(const QByteArray &val) { if (val.isEmpty()) { return QString(); } const char *data = val.data(); //QString::fromUtf8() bug workaround if (data[ val.size() - 1 ] == '\0') { return QString::fromUtf8(data, val.size() - 1); } else { return QString::fromUtf8(data, val.size()); } } static QString join(const KLDAP::LdapAttrValue &lst, const QString &sep) { QString res; bool alredy = false; KLDAP::LdapAttrValue::ConstIterator end(lst.constEnd()); for (KLDAP::LdapAttrValue::ConstIterator it = lst.constBegin(); it != end; ++it) { if (alredy) { res += sep; } alredy = true; res += asUtf8(*it); } return res; } static QMap &adrbookattr2ldap() { static QMap keys; if (keys.isEmpty()) { keys[ i18nc("@item LDAP search key", "Title") ] = QStringLiteral("title"); keys[ i18n("Full Name") ] = QStringLiteral("cn"); keys[ i18nc("@item LDAP search key", "Email") ] = QStringLiteral("mail"); keys[ i18n("Home Number") ] = QStringLiteral("homePhone"); keys[ i18n("Work Number") ] = QStringLiteral("telephoneNumber"); keys[ i18n("Mobile Number") ] = QStringLiteral("mobile"); keys[ i18n("Fax Number") ] = QStringLiteral("facsimileTelephoneNumber"); keys[ i18n("Pager") ] = QStringLiteral("pager"); keys[ i18n("Street") ] = QStringLiteral("street"); keys[ i18nc("@item LDAP search key", "State") ] = QStringLiteral("st"); keys[ i18n("Country") ] = QStringLiteral("co"); keys[ i18n("City") ] = QStringLiteral("l"); //krazy:exclude=doublequote_chars keys[ i18n("Organization") ] = QStringLiteral("o"); //krazy:exclude=doublequote_chars keys[ i18n("Company") ] = QStringLiteral("Company"); keys[ i18n("Department") ] = QStringLiteral("department"); keys[ i18n("Zip Code") ] = QStringLiteral("postalCode"); keys[ i18n("Postal Address") ] = QStringLiteral("postalAddress"); keys[ i18n("Description") ] = QStringLiteral("description"); keys[ i18n("User ID") ] = QStringLiteral("uid"); } return keys; } static QString makeFilter(const QString &query, LdapSearchDialog::FilterType attr, bool startsWith) { /* The reasoning behind this filter is: * If it's a person, or a distlist, show it, even if it doesn't have an email address. * If it's not a person, or a distlist, only show it if it has an email attribute. * This allows both resource accounts with an email address which are not a person and * person entries without an email address to show up, while still not showing things * like structural entries in the ldap tree. */ QString result(QStringLiteral("&(|(objectclass=person)(objectclass=groupofnames)(mail=*))(")); if (query.isEmpty()) { // Return a filter that matches everything return result + QStringLiteral("|(cn=*)(sn=*)") + QLatin1Char(')'); } if (attr == LdapSearchDialog::Name) { result += startsWith ? QStringLiteral("|(cn=%1*)(sn=%2*)") : QStringLiteral("|(cn=*%1*)(sn=*%2*)"); result = result.arg(query, query); } else { result += startsWith ? QStringLiteral("%1=%2*") : QStringLiteral("%1=*%2*"); if (attr == LdapSearchDialog::Email) { result = result.arg(QStringLiteral("mail"), query); } else if (attr == LdapSearchDialog::HomeNumber) { result = result.arg(QStringLiteral("homePhone"), query); } else if (attr == LdapSearchDialog::WorkNumber) { result = result.arg(QStringLiteral("telephoneNumber"), query); } else { // Error? result.clear(); return result; } } result += QLatin1Char(')'); return result; } static KContacts::Addressee convertLdapAttributesToAddressee(const KLDAP::LdapAttrMap &attrs) { KContacts::Addressee addr; // name if (!attrs.value(QStringLiteral("cn")).isEmpty()) { addr.setNameFromString(asUtf8(attrs[QStringLiteral("cn")].first())); } // email KLDAP::LdapAttrValue lst = attrs[QStringLiteral("mail")]; KLDAP::LdapAttrValue::ConstIterator it = lst.constBegin(); bool pref = true; while (it != lst.constEnd()) { addr.insertEmail(asUtf8(*it), pref); pref = false; ++it; } if (!attrs.value(QStringLiteral("o")).isEmpty()) { addr.setOrganization(asUtf8(attrs[ QStringLiteral("o") ].first())); } if (addr.organization().isEmpty() && !attrs.value(QStringLiteral("Company")).isEmpty()) { addr.setOrganization(asUtf8(attrs[ QStringLiteral("Company") ].first())); } // Address KContacts::Address workAddr(KContacts::Address::Work); if (!attrs.value(QStringLiteral("department")).isEmpty()) { addr.setDepartment(asUtf8(attrs[ QStringLiteral("department") ].first())); } if (!workAddr.isEmpty()) { addr.insertAddress(workAddr); } // phone if (!attrs.value(QStringLiteral("homePhone")).isEmpty()) { KContacts::PhoneNumber homeNr = asUtf8(attrs[ QStringLiteral("homePhone") ].first()); homeNr.setType(KContacts::PhoneNumber::Home); addr.insertPhoneNumber(homeNr); } if (!attrs.value(QStringLiteral("telephoneNumber")).isEmpty()) { KContacts::PhoneNumber workNr = asUtf8(attrs[ QStringLiteral("telephoneNumber") ].first()); workNr.setType(KContacts::PhoneNumber::Work); addr.insertPhoneNumber(workNr); } if (!attrs.value(QStringLiteral("facsimileTelephoneNumber")).isEmpty()) { KContacts::PhoneNumber faxNr = asUtf8(attrs[ QStringLiteral("facsimileTelephoneNumber") ].first()); faxNr.setType(KContacts::PhoneNumber::Fax); addr.insertPhoneNumber(faxNr); } if (!attrs.value(QStringLiteral("mobile")).isEmpty()) { KContacts::PhoneNumber cellNr = asUtf8(attrs[ QStringLiteral("mobile") ].first()); cellNr.setType(KContacts::PhoneNumber::Cell); addr.insertPhoneNumber(cellNr); } if (!attrs.value(QStringLiteral("pager")).isEmpty()) { KContacts::PhoneNumber pagerNr = asUtf8(attrs[ QStringLiteral("pager") ].first()); pagerNr.setType(KContacts::PhoneNumber::Pager); addr.insertPhoneNumber(pagerNr); } return addr; } class ContactListModel : public QAbstractTableModel { public: enum Role { ServerRole = Qt::UserRole + 1 }; explicit ContactListModel(QObject *parent) : QAbstractTableModel(parent) { } void addContact(const KLDAP::LdapAttrMap &contact, const QString &server) { beginResetModel(); mContactList.append(contact); mServerList.append(server); endResetModel(); } QPair contact(const QModelIndex &index) const { if (!index.isValid() || index.row() < 0 || index.row() >= mContactList.count()) { return qMakePair(KLDAP::LdapAttrMap(), QString()); } return qMakePair(mContactList.at(index.row()), mServerList.at(index.row())); } QString email(const QModelIndex &index) const { if (!index.isValid() || index.row() < 0 || index.row() >= mContactList.count()) { return QString(); } return asUtf8(mContactList.at(index.row()).value(QStringLiteral("mail")).first()).trimmed(); } QString fullName(const QModelIndex &index) const { if (!index.isValid() || index.row() < 0 || index.row() >= mContactList.count()) { return QString(); } return asUtf8(mContactList.at(index.row()).value(QStringLiteral("cn")).first()).trimmed(); } void clear() { beginResetModel(); mContactList.clear(); mServerList.clear(); endResetModel(); } int rowCount(const QModelIndex &parent = QModelIndex()) const override { if (!parent.isValid()) { return mContactList.count(); } else { return 0; } } int columnCount(const QModelIndex &parent = QModelIndex()) const override { if (!parent.isValid()) { return 18; } else { return 0; } } QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override { if (orientation == Qt::Vertical || role != Qt::DisplayRole || section < 0 || section > 17) { return QVariant(); } switch (section) { case 0: return i18n("Full Name"); case 1: return i18nc("@title:column Column containing email addresses", "Email"); case 2: return i18n("Home Number"); case 3: return i18n("Work Number"); case 4: return i18n("Mobile Number"); case 5: return i18n("Fax Number"); case 6: return i18n("Company"); case 7: return i18n("Organization"); case 8: return i18n("Street"); case 9: return i18nc("@title:column Column containing the residential state of the address", "State"); case 10: return i18n("Country"); case 11: return i18n("Zip Code"); case 12: return i18n("Postal Address"); case 13: return i18n("City"); case 14: return i18n("Department"); case 15: return i18n("Description"); case 16: return i18n("User ID"); case 17: return i18nc("@title:column Column containing title of the person", "Title"); default: return QVariant(); } } QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override { if (!index.isValid()) { return QVariant(); } if (index.row() < 0 || index.row() >= mContactList.count() || index.column() < 0 || index.column() > 17) { return QVariant(); } if (role == ServerRole) { return mServerList.at(index.row()); } if ((role != Qt::DisplayRole) && (role != Qt::ToolTipRole)) { return QVariant(); } const KLDAP::LdapAttrMap map = mContactList.at(index.row()); switch (index.column()) { case 0: return join(map.value(QStringLiteral("cn")), QStringLiteral(", ")); case 1: return join(map.value(QStringLiteral("mail")), QStringLiteral(", ")); case 2: return join(map.value(QStringLiteral("homePhone")), QStringLiteral(", ")); case 3: return join(map.value(QStringLiteral("telephoneNumber")), QStringLiteral(", ")); case 4: return join(map.value(QStringLiteral("mobile")), QStringLiteral(", ")); case 5: return join(map.value(QStringLiteral("facsimileTelephoneNumber")), QStringLiteral(", ")); case 6: return join(map.value(QStringLiteral("Company")), QStringLiteral(", ")); case 7: return join(map.value(QStringLiteral("o")), QStringLiteral(", ")); case 8: return join(map.value(QStringLiteral("street")), QStringLiteral(", ")); case 9: return join(map.value(QStringLiteral("st")), QStringLiteral(", ")); case 10: return join(map.value(QStringLiteral("co")), QStringLiteral(", ")); case 11: return join(map.value(QStringLiteral("postalCode")), QStringLiteral(", ")); case 12: return join(map.value(QStringLiteral("postalAddress")), QStringLiteral(", ")); case 13: return join(map.value(QStringLiteral("l")), QStringLiteral(", ")); case 14: return join(map.value(QStringLiteral("department")), QStringLiteral(", ")); case 15: return join(map.value(QStringLiteral("description")), QStringLiteral(", ")); case 16: return join(map.value(QStringLiteral("uid")), QStringLiteral(", ")); case 17: return join(map.value(QStringLiteral("title")), QStringLiteral(", ")); default: return QVariant(); } } private: QVector mContactList; QStringList mServerList; }; class Q_DECL_HIDDEN LdapSearchDialog::Private { public: Private(LdapSearchDialog *qq) : q(qq) { } QList< QPair > selectedItems() { QList< QPair > contacts; const QModelIndexList selected = mResultView->selectionModel()->selectedRows(); const int numberOfSelectedElement(selected.count()); contacts.reserve(numberOfSelectedElement); for (int i = 0; i < numberOfSelectedElement; ++i) { contacts.append(mModel->contact(sortproxy->mapToSource(selected.at(i)))); } return contacts; } void saveSettings(); void restoreSettings(); void cancelQuery(); void slotAddResult(const KLDAP::LdapClient &, const KLDAP::LdapObject &); void slotSetScope(bool); void slotStartSearch(); void slotStopSearch(); void slotSearchDone(); void slotError(const QString &); void slotSelectAll(); void slotUnselectAll(); void slotSelectionChanged(); LdapSearchDialog *q; KGuiItem startSearchGuiItem; KGuiItem stopSearchGuiItem; int mNumHosts = 0; QList mLdapClientList; bool mIsConfigured = false; KContacts::Addressee::List mSelectedContacts; KComboBox *mFilterCombo = nullptr; KComboBox *mSearchType = nullptr; QLineEdit *mSearchEdit = nullptr; QCheckBox *mRecursiveCheckbox = nullptr; QTableView *mResultView = nullptr; QPushButton *mSearchButton = nullptr; ContactListModel *mModel = nullptr; KPIM::ProgressIndicatorLabel *progressIndication = nullptr; QSortFilterProxyModel *sortproxy = nullptr; KLineEdit *searchLine = nullptr; QPushButton *user1Button = nullptr; }; LdapSearchDialog::LdapSearchDialog(QWidget *parent) : QDialog(parent) , d(new Private(this)) { setWindowTitle(i18n("Import Contacts from LDAP")); QVBoxLayout *mainLayout = new QVBoxLayout(this); QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Cancel, this); d->user1Button = new QPushButton; buttonBox->addButton(d->user1Button, QDialogButtonBox::ActionRole); QPushButton *user2Button = new QPushButton; buttonBox->addButton(user2Button, QDialogButtonBox::ActionRole); connect(d->user1Button, &QPushButton::clicked, this, &LdapSearchDialog::slotUser1); connect(user2Button, &QPushButton::clicked, this, &LdapSearchDialog::slotUser2); connect(buttonBox, &QDialogButtonBox::rejected, this, &LdapSearchDialog::slotCancelClicked); d->user1Button->setDefault(true); setModal(false); KGuiItem::assign(buttonBox->button(QDialogButtonBox::Cancel), KStandardGuiItem::close()); QFrame *page = new QFrame(this); mainLayout->addWidget(page); mainLayout->addWidget(buttonBox); QVBoxLayout *topLayout = new QVBoxLayout(page); - topLayout->setMargin(0); + topLayout->setContentsMargins(0, 0, 0, 0); QGroupBox *groupBox = new QGroupBox(i18n("Search for Addresses in Directory"), page); QGridLayout *boxLayout = new QGridLayout(); groupBox->setLayout(boxLayout); boxLayout->setColumnStretch(1, 1); QLabel *label = new QLabel(i18n("Search for:"), groupBox); boxLayout->addWidget(label, 0, 0); d->mSearchEdit = new QLineEdit(groupBox); d->mSearchEdit->setClearButtonEnabled(true); boxLayout->addWidget(d->mSearchEdit, 0, 1); label->setBuddy(d->mSearchEdit); label = new QLabel(i18nc("In LDAP attribute", "in"), groupBox); boxLayout->addWidget(label, 0, 2); d->mFilterCombo = new KComboBox(groupBox); d->mFilterCombo->addItem(i18nc("@item:inlistbox Name of the contact", "Name"), QVariant::fromValue(Name)); d->mFilterCombo->addItem(i18nc("@item:inlistbox email address of the contact", "Email"), QVariant::fromValue(Email)); d->mFilterCombo->addItem(i18nc("@item:inlistbox", "Home Number"), QVariant::fromValue(HomeNumber)); d->mFilterCombo->addItem(i18nc("@item:inlistbox", "Work Number"), QVariant::fromValue(WorkNumber)); boxLayout->addWidget(d->mFilterCombo, 0, 3); d->startSearchGuiItem = KGuiItem(i18nc("@action:button Start searching", "&Search"), QStringLiteral("edit-find")); d->stopSearchGuiItem = KStandardGuiItem::stop(); QSize buttonSize; d->mSearchButton = new QPushButton(groupBox); KGuiItem::assign(d->mSearchButton, d->startSearchGuiItem); buttonSize = d->mSearchButton->sizeHint(); if (buttonSize.width() < d->mSearchButton->sizeHint().width()) { buttonSize = d->mSearchButton->sizeHint(); } d->mSearchButton->setFixedWidth(buttonSize.width()); d->mSearchButton->setDefault(true); boxLayout->addWidget(d->mSearchButton, 0, 4); d->mRecursiveCheckbox = new QCheckBox(i18n("Recursive search"), groupBox); d->mRecursiveCheckbox->setChecked(true); boxLayout->addWidget(d->mRecursiveCheckbox, 1, 0, 1, 5); d->mSearchType = new KComboBox(groupBox); d->mSearchType->addItem(i18n("Contains")); d->mSearchType->addItem(i18n("Starts With")); boxLayout->addWidget(d->mSearchType, 1, 3, 1, 2); topLayout->addWidget(groupBox); QHBoxLayout *quickSearchLineLayout = new QHBoxLayout; quickSearchLineLayout->addStretch(); d->searchLine = new KLineEdit; d->searchLine->setTrapReturnKey(true); d->searchLine->setClearButtonEnabled(true); d->searchLine->setPlaceholderText(i18n("Search in result")); quickSearchLineLayout->addWidget(d->searchLine); topLayout->addLayout(quickSearchLineLayout); d->mResultView = new QTableView(page); d->mResultView->setSelectionMode(QTableView::MultiSelection); d->mResultView->setSelectionBehavior(QTableView::SelectRows); d->mModel = new ContactListModel(d->mResultView); d->sortproxy = new QSortFilterProxyModel(this); d->sortproxy->setFilterKeyColumn(-1); //Search in all column d->sortproxy->setSourceModel(d->mModel); d->sortproxy->setFilterCaseSensitivity(Qt::CaseInsensitive); connect(d->searchLine, &QLineEdit::textChanged, d->sortproxy, &QSortFilterProxyModel::setFilterFixedString); d->mResultView->setModel(d->sortproxy); d->mResultView->verticalHeader()->hide(); d->mResultView->setSortingEnabled(true); d->mResultView->horizontalHeader()->setSortIndicatorShown(true); connect(d->mResultView, QOverload::of(&QTableView::clicked), this, [this]() { d->slotSelectionChanged(); }); topLayout->addWidget(d->mResultView); d->mResultView->setContextMenuPolicy(Qt::CustomContextMenu); connect(d->mResultView, &QTableView::customContextMenuRequested, this, &LdapSearchDialog::slotCustomContextMenuRequested); QHBoxLayout *buttonLayout = new QHBoxLayout; - buttonLayout->setMargin(0); + buttonLayout->setContentsMargins(0, 0, 0, 0); topLayout->addLayout(buttonLayout); d->progressIndication = new KPIM::ProgressIndicatorLabel(i18n("Searching...")); buttonLayout->addWidget(d->progressIndication); QDialogButtonBox *buttons = new QDialogButtonBox(page); QPushButton *button = buttons->addButton(i18n("Select All"), QDialogButtonBox::ActionRole); connect(button, &QPushButton::clicked, this, [this]() { d->slotSelectAll(); }); button = buttons->addButton(i18n("Unselect All"), QDialogButtonBox::ActionRole); connect(button, &QPushButton::clicked, this, [this]() { d->slotUnselectAll(); }); buttonLayout->addWidget(buttons); d->user1Button->setText(i18n("Add Selected")); user2Button->setText(i18n("Configure LDAP Servers...")); connect(d->mRecursiveCheckbox, &QCheckBox::toggled, this, [this](bool state) { d->slotSetScope(state); }); connect(d->mSearchButton, SIGNAL(clicked()), this, SLOT(slotStartSearch())); setTabOrder(d->mSearchEdit, d->mFilterCombo); setTabOrder(d->mFilterCombo, d->mSearchButton); d->mSearchEdit->setFocus(); d->slotSelectionChanged(); d->restoreSettings(); } LdapSearchDialog::~LdapSearchDialog() { d->saveSettings(); delete d; } void LdapSearchDialog::setSearchText(const QString &text) { d->mSearchEdit->setText(text); } KContacts::Addressee::List LdapSearchDialog::selectedContacts() const { return d->mSelectedContacts; } void LdapSearchDialog::slotCustomContextMenuRequested(const QPoint &pos) { const QModelIndex index = d->mResultView->indexAt(pos); if (index.isValid()) { QMenu menu(this); QAction *act = menu.addAction(i18n("Copy")); if (menu.exec(QCursor::pos()) == act) { QClipboard *cb = QApplication::clipboard(); cb->setText(index.data().toString(), QClipboard::Clipboard); } } } void LdapSearchDialog::Private::slotSelectionChanged() { user1Button->setEnabled(mResultView->selectionModel()->hasSelection()); } void LdapSearchDialog::Private::restoreSettings() { // Create one KLDAP::LdapClient per selected server and configure it. // First clean the list to make sure it is empty at // the beginning of the process qDeleteAll(mLdapClientList); mLdapClientList.clear(); KConfig *config = KLDAP::LdapClientSearchConfig::config(); KConfigGroup searchGroup(config, "LDAPSearch"); mSearchType->setCurrentIndex(searchGroup.readEntry("SearchType", 0)); // then read the config file and register all selected // server in the list KConfigGroup group(config, "LDAP"); mNumHosts = group.readEntry("NumSelectedHosts", 0); if (!mNumHosts) { mIsConfigured = false; } else { mIsConfigured = true; KLDAP::LdapClientSearchConfig *clientSearchConfig = new KLDAP::LdapClientSearchConfig; for (int j = 0; j < mNumHosts; ++j) { KLDAP::LdapServer ldapServer; KLDAP::LdapClient *ldapClient = new KLDAP::LdapClient(0, q); clientSearchConfig->readConfig(ldapServer, group, j, true); ldapClient->setServer(ldapServer); QStringList attrs; QMap::ConstIterator end(adrbookattr2ldap().constEnd()); for (QMap::ConstIterator it = adrbookattr2ldap().constBegin(); it != end; ++it) { attrs << *it; } ldapClient->setAttributes(attrs); q->connect(ldapClient, SIGNAL(result(KLDAP::LdapClient,KLDAP::LdapObject)), q, SLOT(slotAddResult(KLDAP::LdapClient,KLDAP::LdapObject))); q->connect(ldapClient, SIGNAL(done()), q, SLOT(slotSearchDone())); q->connect(ldapClient, &LdapClient::error, q, [this](const QString &err) {slotError(err);}); mLdapClientList.append(ldapClient); } delete clientSearchConfig; mModel->clear(); } KConfigGroup groupHeader(config, "Headers"); mResultView->horizontalHeader()->restoreState(groupHeader.readEntry("HeaderState", QByteArray())); KConfigGroup groupSize(config, "Size"); const QSize dialogSize = groupSize.readEntry("Size", QSize()); if (dialogSize.isValid()) { q->resize(dialogSize); } else { q->resize(QSize(600, 400).expandedTo(q->minimumSizeHint())); } } void LdapSearchDialog::Private::saveSettings() { KConfig *config = KLDAP::LdapClientSearchConfig::config(); KConfigGroup group(config, "LDAPSearch"); group.writeEntry("SearchType", mSearchType->currentIndex()); KConfigGroup groupHeader(config, "Headers"); groupHeader.writeEntry("HeaderState", mResultView->horizontalHeader()->saveState()); groupHeader.sync(); KConfigGroup size(config, "Size"); size.writeEntry("Size", q->size()); size.sync(); group.sync(); } void LdapSearchDialog::Private::cancelQuery() { for (KLDAP::LdapClient *client : qAsConst(mLdapClientList)) { client->cancelQuery(); } } void LdapSearchDialog::Private::slotAddResult(const KLDAP::LdapClient &client, const KLDAP::LdapObject &obj) { mModel->addContact(obj.attributes(), client.server().host()); } void LdapSearchDialog::Private::slotSetScope(bool rec) { for (KLDAP::LdapClient *client : qAsConst(mLdapClientList)) { if (rec) { client->setScope(QStringLiteral("sub")); } else { client->setScope(QStringLiteral("one")); } } } void LdapSearchDialog::Private::slotStartSearch() { cancelQuery(); if (!mIsConfigured) { KMessageBox::error(q, i18n("You must select an LDAP server before searching.")); q->slotUser2(); return; } #ifndef QT_NO_CURSOR QApplication::setOverrideCursor(Qt::WaitCursor); #endif KGuiItem::assign(mSearchButton, stopSearchGuiItem); progressIndication->start(); q->disconnect(mSearchButton, SIGNAL(clicked()), q, SLOT(slotStartSearch())); q->connect(mSearchButton, SIGNAL(clicked()), q, SLOT(slotStopSearch())); const bool startsWith = (mSearchType->currentIndex() == 1); const QString filter = makeFilter(mSearchEdit->text().trimmed(), mFilterCombo->currentData().value(), startsWith); // loop in the list and run the KLDAP::LdapClients mModel->clear(); for (KLDAP::LdapClient *client : qAsConst(mLdapClientList)) { client->startQuery(filter); } saveSettings(); } void LdapSearchDialog::Private::slotStopSearch() { cancelQuery(); slotSearchDone(); } void LdapSearchDialog::Private::slotSearchDone() { // If there are no more active clients, we are done. for (KLDAP::LdapClient *client : qAsConst(mLdapClientList)) { if (client->isActive()) { return; } } q->disconnect(mSearchButton, SIGNAL(clicked()), q, SLOT(slotStopSearch())); q->connect(mSearchButton, SIGNAL(clicked()), q, SLOT(slotStartSearch())); KGuiItem::assign(mSearchButton, startSearchGuiItem); progressIndication->stop(); #ifndef QT_NO_CURSOR QApplication::restoreOverrideCursor(); #endif } void LdapSearchDialog::Private::slotError(const QString &error) { #ifndef QT_NO_CURSOR QApplication::restoreOverrideCursor(); #endif KMessageBox::error(q, error); } void LdapSearchDialog::closeEvent(QCloseEvent *e) { d->slotStopSearch(); e->accept(); } void LdapSearchDialog::Private::slotUnselectAll() { mResultView->clearSelection(); slotSelectionChanged(); } void LdapSearchDialog::Private::slotSelectAll() { mResultView->selectAll(); slotSelectionChanged(); } void LdapSearchDialog::slotUser1() { // Import selected items d->mSelectedContacts.clear(); const QList< QPair > &items = d->selectedItems(); if (!items.isEmpty()) { const QDateTime now = QDateTime::currentDateTime(); for (int i = 0; i < items.count(); ++i) { KContacts::Addressee contact = convertLdapAttributesToAddressee(items.at(i).first); // set a comment where the contact came from contact.setNote(i18nc("arguments are host name, datetime", "Imported from LDAP directory %1 on %2", items.at(i).second, QLocale().toString(now, QLocale::ShortFormat))); d->mSelectedContacts.append(contact); } } d->slotStopSearch(); Q_EMIT contactsAdded(); accept(); } void LdapSearchDialog::slotUser2() { // Configure LDAP servers QPointer dialog = new KCMultiDialog(this); dialog->setWindowTitle(i18n("Configure the Address Book LDAP Settings")); dialog->addModule(QStringLiteral("kcmldap.desktop")); if (dialog->exec()) { //krazy:exclude=crashy d->restoreSettings(); } delete dialog; } void LdapSearchDialog::slotCancelClicked() { d->slotStopSearch(); reject(); } #include "moc_ldapsearchdialog.cpp" diff --git a/src/libkdepim/multiplyingline/multiplyinglineeditor.cpp b/src/libkdepim/multiplyingline/multiplyinglineeditor.cpp index d7db0e9..581f285 100644 --- a/src/libkdepim/multiplyingline/multiplyinglineeditor.cpp +++ b/src/libkdepim/multiplyingline/multiplyinglineeditor.cpp @@ -1,168 +1,168 @@ /* Copyright (C) 2010 Casey Link Copyright (C) 2009-2010 Klaralvdalens Datakonsult AB, a KDAB Group company Refactored from earlier code by: Copyright (c) 2010 Volker Krause Copyright (c) 2004 Cornelius Schumacher 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 "multiplyinglineeditor.h" #include "multiplyinglineview_p.h" #include using namespace KPIM; MultiplyingLineEditor::MultiplyingLineEditor(MultiplyingLineFactory *factory, QWidget *parent) : QWidget(parent) , mModified(false) , mMultiplyingLineFactory(factory) { QBoxLayout *topLayout = new QHBoxLayout(this); - topLayout->setMargin(0); + topLayout->setContentsMargins(0, 0, 0, 0); mView = new MultiplyingLineView(mMultiplyingLineFactory, this); topLayout->addWidget(mView); connect(mView, &MultiplyingLineView::focusUp, this, &MultiplyingLineEditor::focusUp); connect(mView, &MultiplyingLineView::focusDown, this, &MultiplyingLineEditor::focusDown); connect(mView, &MultiplyingLineView::completionModeChanged, this, &MultiplyingLineEditor::completionModeChanged); connect(mView, &MultiplyingLineView::lineDeleted, this, &MultiplyingLineEditor::lineDeleted); connect(mView, &MultiplyingLineView::lineAdded, this, &MultiplyingLineEditor::lineAdded); connect(mView, &MultiplyingLineView::sizeHintChanged, this, &MultiplyingLineEditor::sizeHintChanged); } MultiplyingLineEditor::~MultiplyingLineEditor() { delete mMultiplyingLineFactory; } bool MultiplyingLineEditor::addData(const MultiplyingLineData::Ptr &data) { MultiplyingLine *line = mView->emptyLine(); bool tooManyAddress = false; if (!line) { line = mView->addLine(); } if (!line) { tooManyAddress = true; } if (line && data) { line->setData(data); } return tooManyAddress; } void MultiplyingLineEditor::removeData(const MultiplyingLineData::Ptr &data) { mView->removeData(data); } void MultiplyingLineEditor::clear() { const auto lstLines = mView->lines(); for (MultiplyingLine *line : lstLines) { line->slotPropagateDeletion(); } } bool MultiplyingLineEditor::isModified() { return mModified || mView->isModified(); } void MultiplyingLineEditor::clearModified() { mModified = false; mView->clearModified(); } void MultiplyingLineEditor::setFocus() { mView->setFocus(); } void MultiplyingLineEditor::setFocusTop() { mView->setFocusTop(); } void MultiplyingLineEditor::setFocusBottom() { mView->setFocusBottom(); } int MultiplyingLineEditor::setFirstColumnWidth(int w) { return mView->setFirstColumnWidth(w); } void MultiplyingLineEditor::setCompletionMode(KCompletion::CompletionMode mode) { mView->setCompletionMode(mode); } MultiplyingLineFactory *MultiplyingLineEditor::factory() const { return mMultiplyingLineFactory; } QList< MultiplyingLineData::Ptr > MultiplyingLineEditor::allData() const { return mView->allData(); } MultiplyingLineData::Ptr MultiplyingLineEditor::activeData() const { return mView->activeLine()->data(); } QList< MultiplyingLine * > MultiplyingLineEditor::lines() const { return mView->lines(); } MultiplyingLine *MultiplyingLineEditor::activeLine() const { return mView->activeLine(); } void MultiplyingLineEditor::setFrameStyle(int shape) { mView->setFrameStyle(shape); } void MultiplyingLineEditor::setAutoResizeView(bool resize) { mView->setAutoResize(resize); } bool MultiplyingLineEditor::autoResizeView() { return mView->autoResize(); } void MultiplyingLineEditor::setDynamicSizeHint(bool dynamic) { mView->setDynamicSizeHint(dynamic); } bool MultiplyingLineEditor::dynamicSizeHint() const { return mView->dynamicSizeHint(); } diff --git a/src/libkdepim/multiplyingline/multiplyinglineview_p.cpp b/src/libkdepim/multiplyingline/multiplyinglineview_p.cpp index 29f4c58..4c4ca09 100644 --- a/src/libkdepim/multiplyingline/multiplyinglineview_p.cpp +++ b/src/libkdepim/multiplyingline/multiplyinglineview_p.cpp @@ -1,419 +1,419 @@ /* Copyright (C) 2010 Casey Link Copyright (C) 2009-2010 Klaralvdalens Datakonsult AB, a KDAB Group company Refactored from earlier code by: Copyright (c) 2010 Volker Krause Copyright (c) 2004 Cornelius Schumacher 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 "multiplyinglineview_p.h" #include "libkdepim_debug.h" #include #include #include #include #include #include using namespace KPIM; MultiplyingLineView::MultiplyingLineView(MultiplyingLineFactory *factory, MultiplyingLineEditor *parent) : QScrollArea(parent) , mPage(new QWidget(this)) , mTopLayout(new QVBoxLayout(this)) , mMultiplyingLineFactory(factory) { setWidgetResizable(true); setFrameStyle(QFrame::NoFrame); mPage->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); setWidget(mPage); - mTopLayout->setMargin(0); + mTopLayout->setContentsMargins(0, 0, 0, 0); mTopLayout->setSpacing(0); mPage->setLayout(mTopLayout); } MultiplyingLine *MultiplyingLineView::activeLine() const { return mLines.last(); } MultiplyingLine *MultiplyingLineView::emptyLine() const { for (MultiplyingLine *line : qAsConst(mLines)) { if (line->isEmpty()) { return line; } } return nullptr; } MultiplyingLine *MultiplyingLineView::addLine() { const int maximumRecipients = mMultiplyingLineFactory->maximumRecipients(); if (maximumRecipients != -1) { int numberOfLine = mLines.count(); if (numberOfLine++ >= maximumRecipients) { KMessageBox::sorry(this, i18n("We can not add more recipients. We have reached maximum recipients")); return nullptr; } } MultiplyingLine *line = mMultiplyingLineFactory->newLine(widget()); mTopLayout->addWidget(line); line->setCompletionMode(mCompletionMode); line->show(); connect(line, &MultiplyingLine::returnPressed, this, &MultiplyingLineView::slotReturnPressed); connect(line, &MultiplyingLine::upPressed, this, &MultiplyingLineView::slotUpPressed); connect(line, &MultiplyingLine::downPressed, this, &MultiplyingLineView::slotDownPressed); connect(line, &MultiplyingLine::rightPressed, this, &MultiplyingLineView::focusRight); connect(line, &MultiplyingLine::deleteLine, this, &MultiplyingLineView::slotDecideLineDeletion); connect(line, &MultiplyingLine::completionModeChanged, this, &MultiplyingLineView::setCompletionMode); if (!mLines.isEmpty()) { line->fixTabOrder(mLines.last()->tabOut()); } mLines.append(line); mFirstColumnWidth = line->setColumnWidth(mFirstColumnWidth); mLineHeight = line->minimumSizeHint().height(); line->resize(viewport()->width(), mLineHeight); resizeView(); ensureVisible(0, mLines.count() * mLineHeight, 0, 0); QTimer::singleShot(0, this, &MultiplyingLineView::moveScrollBarToEnd); Q_EMIT lineAdded(line); return line; } void MultiplyingLineView::moveScrollBarToEnd() { // scroll to bottom verticalScrollBar()->triggerAction(QAbstractSlider::SliderToMaximum); } void MultiplyingLineView::slotReturnPressed(MultiplyingLine *line) { if (!line->data()->isEmpty()) { MultiplyingLine *empty = emptyLine(); if (!empty) { empty = addLine(); } activateLine(empty); } } void MultiplyingLineView::slotDownPressed(MultiplyingLine *line) { int pos = mLines.indexOf(line); if (pos >= (mLines.count() - 1)) { Q_EMIT focusDown(); } else if (pos >= 0) { activateLine(mLines.at(pos + 1)); } } void MultiplyingLineView::slotUpPressed(MultiplyingLine *line) { int pos = mLines.indexOf(line); if (pos > 0) { activateLine(mLines.at(pos - 1)); } else { Q_EMIT focusUp(); } } void MultiplyingLineView::slotDecideLineDeletion(MultiplyingLine *line) { if (!line->isEmpty()) { mModified = true; } if (mLines.count() == 1) { line->clear(); } else if (!line->canDeleteLineEdit()) { line->clear(); } else if (mLines.indexOf(line) != mLines.count() - 1) { mCurDelLine = line; slotDeleteLine(); } } void MultiplyingLineView::slotDeleteLine() { if (!mCurDelLine) { return; } MultiplyingLine *line = mCurDelLine; line->aboutToBeDeleted(); int pos = mLines.indexOf(line); if (mCurDelLine->isActive()) { int newPos; if (pos == 0) { newPos = pos + 1; } else { newPos = pos - 1; } // if there is something left to activate, do so if (mLines.at(newPos)) { mLines.at(newPos)->activate(); } } mLines.removeAll(line); line->hide(); line->setParent(nullptr); line->deleteLater(); if (pos > 0) { Q_EMIT lineDeleted(pos); } resizeView(); } void MultiplyingLineView::resizeView() { if (mDynamicSizeHint) { if (!mAutoResize) { if (mLines.count() < 6) { setMinimumHeight(mLineHeight * mLines.count()); } else { setMinimumHeight(mLineHeight * 5); setMaximumHeight(mLineHeight * mLines.count()); } } else { setMinimumHeight(mLineHeight * mLines.count()); } } parentWidget()->layout()->activate(); Q_EMIT sizeHintChanged(); } void MultiplyingLineView::activateLine(MultiplyingLine *line) { line->activate(); ensureWidgetVisible(line); } void MultiplyingLineView::resizeEvent(QResizeEvent *ev) { QScrollArea::resizeEvent(ev); const int numberLine(mLines.count()); for (int i = 0; i < numberLine; ++i) { mLines.at(i)->resize(ev->size().width(), mLineHeight); } ensureVisible(0, numberLine * mLineHeight, 0, 0); } QSize MultiplyingLineView::sizeHint() const { if (mDynamicSizeHint) { return QSize(200, mLineHeight * mLines.count()); } else { return QScrollArea::sizeHint(); } } QSize MultiplyingLineView::minimumSizeHint() const { if (mDynamicSizeHint) { int height; int numLines = 5; if (mLines.count() < numLines) { height = mLineHeight * mLines.count(); } else { height = mLineHeight * numLines; } return QSize(200, height); } else { return QScrollArea::minimumSizeHint(); } } QList MultiplyingLineView::allData() const { QList data; QListIterator it(mLines); while (it.hasNext()) { MultiplyingLine *line = it.next(); if (!line->data()->isEmpty()) { data.append(line->data()); } } return data; } void MultiplyingLineView::setCompletionMode(KCompletion::CompletionMode mode) { if (mCompletionMode == mode) { return; } mCompletionMode = mode; QListIterator it(mLines); while (it.hasNext()) { MultiplyingLine *line = it.next(); line->blockSignals(true); line->setCompletionMode(mode); line->blockSignals(false); } Q_EMIT completionModeChanged(mode); //report change to MultiplyingLineEditor } void MultiplyingLineView::removeData(const MultiplyingLineData::Ptr &data) { // search a line which matches recipient and type QListIterator it(mLines); MultiplyingLine *line = nullptr; while (it.hasNext()) { line = it.next(); if (line->data() == data) { break; } } if (line) { line->slotPropagateDeletion(); } } bool MultiplyingLineView::isModified() const { if (mModified) { return true; } QListIterator it(mLines); MultiplyingLine *line = nullptr; while (it.hasNext()) { line = it.next(); if (line->isModified()) { return true; } } return false; } void MultiplyingLineView::clearModified() { mModified = false; QListIterator it(mLines); while (it.hasNext()) { MultiplyingLine *line = it.next(); line->clearModified(); } } void MultiplyingLineView::setFocus() { if (!mLines.empty() && mLines.last()->isActive()) { setFocusBottom(); } else { setFocusTop(); } } void MultiplyingLineView::setFocusTop() { if (!mLines.empty()) { MultiplyingLine *line = mLines.first(); if (line) { line->activate(); } else { qCWarning(LIBKDEPIM_LOG) << "No first"; } } else { qCWarning(LIBKDEPIM_LOG) << "No first"; } } void MultiplyingLineView::setFocusBottom() { MultiplyingLine *line = mLines.last(); if (line) { ensureWidgetVisible(line); line->activate(); } else { qCWarning(LIBKDEPIM_LOG) << "No last"; } } int MultiplyingLineView::setFirstColumnWidth(int w) { mFirstColumnWidth = w; QListIterator it(mLines); while (it.hasNext()) { MultiplyingLine *line = it.next(); mFirstColumnWidth = line->setColumnWidth(mFirstColumnWidth); } resizeView(); return mFirstColumnWidth; } QList< MultiplyingLine * > MultiplyingLineView::lines() const { return mLines; } void MultiplyingLineView::setAutoResize(bool resize) { mAutoResize = resize; if (mAutoResize) { setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); setMaximumHeight(QWIDGETSIZE_MAX); } else { setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); } } bool MultiplyingLineView::autoResize() { return mAutoResize; } void MultiplyingLineView::setDynamicSizeHint(bool dynamic) { mDynamicSizeHint = dynamic; } bool MultiplyingLineView::dynamicSizeHint() const { return mDynamicSizeHint; } diff --git a/src/libkdepim/progresswidget/progressdialog.cpp b/src/libkdepim/progresswidget/progressdialog.cpp index 4213198..541d210 100644 --- a/src/libkdepim/progresswidget/progressdialog.cpp +++ b/src/libkdepim/progresswidget/progressdialog.cpp @@ -1,436 +1,436 @@ /** -*- c++ -*- * progressdialog.cpp * * Copyright (c) 2004 Till Adam , * David Faure * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * In addition, as a special exception, the copyright holders give * permission to link the code of this program with any edition of * the Qt library by Trolltech AS, Norway (or with modified versions * of Qt that use the same license as Qt), and distribute linked * combinations including the two. You must obey the GNU General * Public License in all respects for all of the code used other than * Qt. If you modify this file, you may extend this exception to * your version of the file, but you are not obligated to do so. If * you do not wish to do so, delete this exception statement from * your version. */ #include "progressdialog.h" #include "ssllabel.h" #include #include #include #include #include #include #include #include #include #include #include #include using namespace KPIM; static const int MAX_LABEL_WIDTH = 650; TransactionItemView::TransactionItemView(QWidget *parent, const QString &name) : QScrollArea(parent) { setObjectName(name); setFrameStyle(NoFrame); mBigBox = new QWidget(this); QVBoxLayout *mBigBoxVBoxLayout = new QVBoxLayout(mBigBox); - mBigBoxVBoxLayout->setMargin(0); + mBigBoxVBoxLayout->setContentsMargins(0, 0, 0, 0); setWidget(mBigBox); setWidgetResizable(true); setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); } TransactionItem *TransactionItemView::addTransactionItem(ProgressItem *item, bool first) { TransactionItem *ti = new TransactionItem(mBigBox, item, first); mBigBox->layout()->addWidget(ti); resize(mBigBox->width(), mBigBox->height()); return ti; } void TransactionItemView::resizeEvent(QResizeEvent *event) { // Tell the layout in the parent (progressdialog) that our size changed updateGeometry(); QSize sz = parentWidget()->sizeHint(); int currentWidth = parentWidget()->width(); // Don't resize to sz.width() every time when it only reduces a little bit if (currentWidth < sz.width() || currentWidth > sz.width() + 100) { currentWidth = sz.width(); } parentWidget()->resize(currentWidth, sz.height()); QScrollArea::resizeEvent(event); } QSize TransactionItemView::sizeHint() const { return minimumSizeHint(); } QSize TransactionItemView::minimumSizeHint() const { int f = 2 * frameWidth(); // Make room for a vertical scrollbar in all cases, to avoid a horizontal one int vsbExt = verticalScrollBar()->sizeHint().width(); int minw = topLevelWidget()->width() / 3; int maxh = topLevelWidget()->height() / 2; QSize sz(mBigBox->minimumSizeHint()); sz.setWidth(qMax(sz.width(), minw) + f + vsbExt); sz.setHeight(qMin(sz.height(), maxh) + f); return sz; } void TransactionItemView::slotLayoutFirstItem() { //This slot is called whenever a TransactionItem is deleted, so this is a //good place to call updateGeometry(), so our parent takes the new size //into account and resizes. updateGeometry(); /* The below relies on some details in Qt's behaviour regarding deleting objects. This slot is called from the destroyed signal of an item just going away. That item is at that point still in the list of chilren, but since the vtable is already gone, it will have type QObject. The first one with both the right name and the right class therefor is what will be the first item very shortly. That's the one we want to remove the hline for. */ TransactionItem *ti = mBigBox->findChild(QStringLiteral("TransactionItem")); if (ti) { ti->hideHLine(); } } // ---------------------------------------------------------------------------- TransactionItem::TransactionItem(QWidget *parent, ProgressItem *item, bool first) : QWidget(parent) , mCancelButton(nullptr) , mItem(item) { QVBoxLayout *vboxLayout = new QVBoxLayout(this); vboxLayout->setSpacing(2); - vboxLayout->setMargin(2); + vboxLayout->setContentsMargins(2, 2, 2, 2); setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed)); mFrame = new QFrame(this); mFrame->setFrameShape(QFrame::HLine); mFrame->setFrameShadow(QFrame::Raised); mFrame->show(); layout()->addWidget(mFrame); QWidget *h = new QWidget(this); QHBoxLayout *hHBoxLayout = new QHBoxLayout(h); - hHBoxLayout->setMargin(0); + hHBoxLayout->setContentsMargins(0, 0, 0, 0); hHBoxLayout->setSpacing(5); layout()->addWidget(h); mItemLabel = new QLabel(fontMetrics().elidedText(item->label(), Qt::ElideRight, MAX_LABEL_WIDTH), h); h->layout()->addWidget(mItemLabel); h->setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed)); mProgress = new QProgressBar(h); hHBoxLayout->addWidget(mProgress); mProgress->setMaximum(100); mProgress->setValue(item->progress()); h->layout()->addWidget(mProgress); if (item->canBeCanceled()) { mCancelButton = new QPushButton(QIcon::fromTheme(QStringLiteral("dialog-cancel")), QString(), h); hHBoxLayout->addWidget(mCancelButton); mCancelButton->setToolTip(i18n("Cancel this operation.")); connect(mCancelButton, &QAbstractButton::clicked, this, &TransactionItem::slotItemCanceled); h->layout()->addWidget(mCancelButton); } h = new QWidget(this); hHBoxLayout = new QHBoxLayout(h); - hHBoxLayout->setMargin(0); + hHBoxLayout->setContentsMargins(0, 0, 0, 0); hHBoxLayout->setSpacing(5); h->setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed)); layout()->addWidget(h); mSSLLabel = new SSLLabel(h); hHBoxLayout->addWidget(mSSLLabel); mSSLLabel->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed)); h->layout()->addWidget(mSSLLabel); mItemStatus = new QLabel(h); hHBoxLayout->addWidget(mItemStatus); mItemStatus->setTextFormat(Qt::RichText); mItemStatus->setText( fontMetrics().elidedText(item->status(), Qt::ElideRight, MAX_LABEL_WIDTH)); h->layout()->addWidget(mItemStatus); setCryptoStatus(item->cryptoStatus()); if (first) { hideHLine(); } } KPIM::TransactionItem::~TransactionItem() { } void TransactionItem::hideHLine() { mFrame->hide(); } void TransactionItem::setProgress(int progress) { mProgress->setValue(progress); } void TransactionItem::setLabel(const QString &label) { mItemLabel->setText(fontMetrics().elidedText(label, Qt::ElideRight, MAX_LABEL_WIDTH)); } void TransactionItem::setStatus(const QString &status) { mItemStatus->setText(fontMetrics().elidedText(status, Qt::ElideRight, MAX_LABEL_WIDTH)); } void TransactionItem::setCryptoStatus(KPIM::ProgressItem::CryptoStatus status) { switch (status) { case KPIM::ProgressItem::Encrypted: mSSLLabel->setEncrypted(SSLLabel::Encrypted); break; case KPIM::ProgressItem::Unencrypted: mSSLLabel->setEncrypted(SSLLabel::Unencrypted); break; case KPIM::ProgressItem::Unknown: mSSLLabel->setEncrypted(SSLLabel::Unknown); break; } mSSLLabel->setState(mSSLLabel->lastState()); } void TransactionItem::setTotalSteps(int totalSteps) { mProgress->setMaximum(totalSteps); } void TransactionItem::slotItemCanceled() { if (mItem) { mItem->cancel(); } } void TransactionItem::addSubTransaction(ProgressItem *item) { Q_UNUSED(item); } // --------------------------------------------------------------------------- ProgressDialog::ProgressDialog(QWidget *alignWidget, QWidget *parent) : OverlayWidget(alignWidget, parent) { // Qt Bug: Sunken is not applied for RTL layouts correctly (is not mirrored). // For now let's just use Plain, which is fine for this. if (layoutDirection() == Qt::LeftToRight) { setFrameStyle(QFrame::Panel | QFrame::Sunken); // QFrame } else { setFrameStyle(QFrame::Panel | QFrame::Plain); // QFrame } setAutoFillBackground(true); mScrollView = new TransactionItemView(this, QStringLiteral("ProgressScrollView")); layout()->addWidget(mScrollView); /* * Get the singleton ProgressManager item which will inform us of * appearing and vanishing items. */ ProgressManager *pm = ProgressManager::instance(); connect(pm, &ProgressManager::progressItemAdded, this, &ProgressDialog::slotTransactionAdded); connect(pm, &ProgressManager::progressItemCompleted, this, &ProgressDialog::slotTransactionCompleted); connect(pm, &ProgressManager::progressItemProgress, this, &ProgressDialog::slotTransactionProgress); connect(pm, &ProgressManager::progressItemStatus, this, &ProgressDialog::slotTransactionStatus); connect(pm, &ProgressManager::progressItemLabel, this, &ProgressDialog::slotTransactionLabel); connect(pm, &ProgressManager::progressItemCryptoStatus, this, &ProgressDialog::slotTransactionCryptoStatus); connect(pm, &ProgressManager::progressItemUsesBusyIndicator, this, &ProgressDialog::slotTransactionUsesBusyIndicator); connect(pm, &ProgressManager::showProgressDialog, this, &ProgressDialog::slotShow); } void ProgressDialog::closeEvent(QCloseEvent *e) { e->accept(); hide(); } bool ProgressDialog::wasLastShown() const { return mWasLastShown; } /* * Destructor */ ProgressDialog::~ProgressDialog() { // no need to delete child widgets. } void ProgressDialog::setShowTypeProgressItem(unsigned int type) { mShowTypeProgressItem = type; } void ProgressDialog::slotTransactionAdded(ProgressItem *item) { if (item->typeProgressItem() == mShowTypeProgressItem) { if (item->parent()) { if (TransactionItem *parent = mTransactionsToListviewItems.value(item->parent())) { parent->addSubTransaction(item); } } else { const bool first = mTransactionsToListviewItems.empty(); TransactionItem *ti = mScrollView->addTransactionItem(item, first); if (ti) { mTransactionsToListviewItems.insert(item, ti); } if (first && mWasLastShown) { QTimer::singleShot(1000, this, &ProgressDialog::slotShow); } } } } void ProgressDialog::slotTransactionCompleted(ProgressItem *item) { if (TransactionItem *ti = mTransactionsToListviewItems.value(item)) { mTransactionsToListviewItems.remove(item); ti->setItemComplete(); QTimer::singleShot(3000, ti, &QObject::deleteLater); // see the slot for comments as to why that works connect(ti, &QObject::destroyed, mScrollView, &TransactionItemView::slotLayoutFirstItem); } // This was the last item, hide. if (mTransactionsToListviewItems.empty()) { QTimer::singleShot(3000, this, &ProgressDialog::slotHide); } } void ProgressDialog::slotTransactionCanceled(ProgressItem *) { } void ProgressDialog::slotTransactionProgress(ProgressItem *item, unsigned int progress) { if (TransactionItem *ti = mTransactionsToListviewItems.value(item)) { ti->setProgress(progress); } } void ProgressDialog::slotTransactionStatus(ProgressItem *item, const QString &status) { if (TransactionItem *ti = mTransactionsToListviewItems.value(item)) { ti->setStatus(status); } } void ProgressDialog::slotTransactionLabel(ProgressItem *item, const QString &label) { if (TransactionItem *ti = mTransactionsToListviewItems.value(item)) { ti->setLabel(label); } } void ProgressDialog::slotTransactionCryptoStatus(ProgressItem *item, KPIM::ProgressItem::CryptoStatus value) { if (TransactionItem *ti = mTransactionsToListviewItems.value(item)) { ti->setCryptoStatus(value); } } void ProgressDialog::slotTransactionUsesBusyIndicator(KPIM::ProgressItem *item, bool value) { if (TransactionItem *ti = mTransactionsToListviewItems.value(item)) { if (value) { ti->setTotalSteps(0); } else { ti->setTotalSteps(100); } } } void ProgressDialog::slotShow() { setVisible(true); } void ProgressDialog::slotHide() { // check if a new item showed up since we started the timer. If not, hide if (mTransactionsToListviewItems.isEmpty()) { setVisible(false); } } void ProgressDialog::slotClose() { mWasLastShown = false; setVisible(false); } void ProgressDialog::setVisible(bool b) { OverlayWidget::setVisible(b); Q_EMIT visibilityChanged(b); } void ProgressDialog::slotToggleVisibility() { /* Since we are only hiding with a timeout, there is a short period of * time where the last item is still visible, but clicking on it in * the statusbarwidget should not display the dialog, because there * are no items to be shown anymore. Guard against that. */ if (!isHidden() || !mTransactionsToListviewItems.isEmpty()) { const bool showNow = isHidden(); setVisible(showNow); mWasLastShown = showNow; } } diff --git a/src/libkdepim/progresswidget/statusbarprogresswidget.cpp b/src/libkdepim/progresswidget/statusbarprogresswidget.cpp index e7ffbdc..61d0040 100644 --- a/src/libkdepim/progresswidget/statusbarprogresswidget.cpp +++ b/src/libkdepim/progresswidget/statusbarprogresswidget.cpp @@ -1,321 +1,321 @@ /* statusbarprogresswidget.cpp (C) 2004 Till Adam Don Sanders David Faure Copyright 2004 David Faure Includes StatusbarProgressWidget which is based on KIOLittleProgressDlg by Matt Koss KMail is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. KMail is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA In addition, as a special exception, the copyright holders give permission to link the code of this program with any edition of the Qt library by Trolltech AS, Norway (or with modified versions of Qt that use the same license as Qt), and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than Qt. If you modify this file, you may extend this exception to your version of the file, but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. */ #include "statusbarprogresswidget.h" #include "progressdialog.h" #include "ssllabel.h" using KPIM::SSLLabel; #include "progressmanager.h" using KPIM::ProgressItem; using KPIM::ProgressManager; #include #include #include #include #include #include #include #include #include #include #include using namespace KPIM; //----------------------------------------------------------------------------- StatusbarProgressWidget::StatusbarProgressWidget(ProgressDialog *progressDialog, QWidget *parent, bool button) : QFrame(parent) , mShowButton(button) , mProgressDialog(progressDialog) { int w = fontMetrics().boundingRect(QStringLiteral(" 999.9 kB/s 00:00:01 ")).width() + 8; QHBoxLayout *boxLayout = new QHBoxLayout(this); boxLayout->setObjectName(QStringLiteral("boxLayout")); - boxLayout->setMargin(0); + boxLayout->setContentsMargins(0, 0, 0, 0); boxLayout->setSpacing(0); mButton = new QPushButton(this); mButton->setObjectName(QStringLiteral("button")); mButton->setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum)); mButton->setIcon(QIcon::fromTheme(QStringLiteral("go-up"))); boxLayout->addWidget(mButton); mStackedWidget = new QStackedWidget(this); mStackedWidget->setObjectName(QStringLiteral("stackedwidget")); int maximumHeight = qMax(mButton->iconSize().height(), fontMetrics().height()); mStackedWidget->setMaximumHeight(maximumHeight); boxLayout->addWidget(mStackedWidget); mSslLabel = new SSLLabel(this); mSslLabel->setObjectName(QStringLiteral("ssllabel")); boxLayout->addWidget(mSslLabel); mButton->setToolTip(i18n("Open detailed progress dialog")); mProgressBar = new QProgressBar(this); mProgressBar->setObjectName(QStringLiteral("progressbar")); mProgressBar->installEventFilter(this); mProgressBar->setMinimumWidth(w); mStackedWidget->insertWidget(1, mProgressBar); mLabel = new QLabel(QString(), this); mLabel->setObjectName(QStringLiteral("emptylabel")); mLabel->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter); mLabel->installEventFilter(this); mLabel->setMinimumWidth(w); mStackedWidget->insertWidget(2, mLabel); mButton->setMaximumHeight(maximumHeight); setFixedWidth(600); mMode = Clean; setMode(); connect(mButton, &QAbstractButton::clicked, this, &StatusbarProgressWidget::slotProgressButtonClicked); connect(ProgressManager::instance(), &ProgressManager::progressItemAdded, this, &StatusbarProgressWidget::slotProgressItemAdded); connect(ProgressManager::instance(), &ProgressManager::progressItemCompleted, this, &StatusbarProgressWidget::slotProgressItemCompleted); connect(ProgressManager::instance(), &ProgressManager::progressItemUsesBusyIndicator, this, &StatusbarProgressWidget::updateBusyMode); connect(progressDialog, &ProgressDialog::visibilityChanged, this, &StatusbarProgressWidget::slotProgressDialogVisible); mDelayTimer = new QTimer(this); mDelayTimer->setSingleShot(true); connect(mDelayTimer, &QTimer::timeout, this, &StatusbarProgressWidget::slotShowItemDelayed); mCleanTimer = new QTimer(this); mCleanTimer->setSingleShot(true); connect(mCleanTimer, &QTimer::timeout, this, &StatusbarProgressWidget::slotClean); } void StatusbarProgressWidget::setShowTypeProgressItem(unsigned int type) { mShowTypeProgressItem = type; } // There are three cases: no progressitem, one progressitem (connect to it directly), // or many progressitems (display busy indicator). Let's call them 0,1,N. // In slot..Added we can only end up in 1 or N. // In slot..Removed we can end up in 0, 1, or we can stay in N if we were already. void StatusbarProgressWidget::updateBusyMode(KPIM::ProgressItem *item) { if (item->typeProgressItem() == mShowTypeProgressItem) { connectSingleItem(); // if going to 1 item if (mCurrentItem) { // Exactly one item delete mBusyTimer; mBusyTimer = nullptr; mDelayTimer->start(1000); } else { // N items if (!mBusyTimer) { mBusyTimer = new QTimer(this); connect(mBusyTimer, &QTimer::timeout, this, &StatusbarProgressWidget::slotBusyIndicator); mDelayTimer->start(1000); } } } } void StatusbarProgressWidget::slotProgressItemAdded(ProgressItem *item) { if (item->parent()) { return; // we are only interested in top level items } updateBusyMode(item); } void StatusbarProgressWidget::slotProgressItemCompleted(ProgressItem *item) { if (item->parent()) { item->deleteLater(); item = nullptr; return; // we are only interested in top level items } item->deleteLater(); item = nullptr; connectSingleItem(); // if going back to 1 item if (ProgressManager::instance()->isEmpty()) { // No item // Done. In 5s the progress-widget will close, then we can clean up the statusbar mCleanTimer->start(5000); } else if (mCurrentItem) { // Exactly one item delete mBusyTimer; mBusyTimer = nullptr; activateSingleItemMode(); } } void StatusbarProgressWidget::connectSingleItem() { if (mCurrentItem) { disconnect(mCurrentItem, &ProgressItem::progressItemProgress, this, &StatusbarProgressWidget::slotProgressItemProgress); mCurrentItem = nullptr; } mCurrentItem = ProgressManager::instance()->singleItem(); if (mCurrentItem) { connect(mCurrentItem, &ProgressItem::progressItemProgress, this, &StatusbarProgressWidget::slotProgressItemProgress); } } void StatusbarProgressWidget::activateSingleItemMode() { mProgressBar->setMaximum(100); mProgressBar->setValue(mCurrentItem->progress()); mProgressBar->setTextVisible(true); } void StatusbarProgressWidget::slotShowItemDelayed() { bool noItems = ProgressManager::instance()->isEmpty(); if (mCurrentItem) { activateSingleItemMode(); } else if (!noItems) { // N items mProgressBar->setMaximum(0); mProgressBar->setTextVisible(false); Q_ASSERT(mBusyTimer); if (mBusyTimer) { mBusyTimer->start(100); } } if (!noItems && mMode != Progress) { mMode = Progress; setMode(); } } void StatusbarProgressWidget::slotBusyIndicator() { const int p = mProgressBar->value(); mProgressBar->setValue(p + 10); } void StatusbarProgressWidget::slotProgressItemProgress(ProgressItem *item, unsigned int value) { Q_ASSERT(item == mCurrentItem); // the only one we should be connected to Q_UNUSED(item); mProgressBar->setValue(value); } void StatusbarProgressWidget::setMode() { switch (mMode) { case Clean: if (mShowButton) { mButton->hide(); } mSslLabel->setState(SSLLabel::Done); // show the empty label in order to make the status bar look better mStackedWidget->show(); mStackedWidget->setCurrentWidget(mLabel); break; case Progress: mStackedWidget->show(); mStackedWidget->setCurrentWidget(mProgressBar); if (mShowButton) { mShowDetailedProgress = mProgressDialog->wasLastShown(); updateProgressButton(); mButton->show(); } mSslLabel->setState(mSslLabel->lastState()); break; } } void StatusbarProgressWidget::slotClean() { // check if a new item showed up since we started the timer. If not, clear if (ProgressManager::instance()->isEmpty()) { mProgressBar->setValue(0); //m_pLabel->clear(); mMode = Clean; setMode(); } } bool StatusbarProgressWidget::eventFilter(QObject *obj, QEvent *ev) { if (ev->type() == QEvent::MouseButtonPress) { QMouseEvent *e = static_cast(ev); if (e->button() == Qt::LeftButton && mMode == Progress) { // toggle view on left mouse button // Consensus seems to be that we should show/hide the fancy dialog when the user // clicks anywhere in the small one. slotProgressButtonClicked(); return true; } } return QFrame::eventFilter(obj, ev); } void StatusbarProgressWidget::updateProgressButton() { if (!mShowDetailedProgress) { mButton->setIcon(QIcon::fromTheme(QStringLiteral("go-up"))); mButton->setToolTip(i18n("Show detailed progress window")); } else { mButton->setIcon(QIcon::fromTheme(QStringLiteral("go-down"))); mButton->setToolTip(i18n("Hide detailed progress window")); } } void StatusbarProgressWidget::slotProgressButtonClicked() { mProgressDialog->slotToggleVisibility(); mShowDetailedProgress = !mProgressDialog->isHidden(); updateProgressButton(); setFixedWidth(qMax(600, mProgressDialog->width())); } void StatusbarProgressWidget::slotProgressDialogVisible(bool b) { // Update the hide/show button when the detailed one is shown/hidden if (b) { setMode(); } } diff --git a/src/libkdepim/widgets/kwidgetlister.cpp b/src/libkdepim/widgets/kwidgetlister.cpp index 8b5d5da..ffe1f17 100644 --- a/src/libkdepim/widgets/kwidgetlister.cpp +++ b/src/libkdepim/widgets/kwidgetlister.cpp @@ -1,287 +1,287 @@ /* -*- c++ -*- kwidgetlister.cpp This file is part of libkdepim. Copyright (c) 2001 Marc Mutz This library is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA In addition, as a special exception, the copyright holders give permission to link the code of this library with any edition of the Qt library by Trolltech AS, Norway (or with modified versions of Qt that use the same license as Qt), and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than Qt. If you modify this file, you may extend this exception to your version of the file, but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. */ #include "kwidgetlister.h" #include #include #include #include #include #include #include using namespace KPIM; class Q_DECL_HIDDEN KWidgetLister::Private { public: Private(KWidgetLister *qq) : q(qq) { } ~Private() { qDeleteAll(mWidgetList); mWidgetList.clear(); } void enableControls(); KWidgetLister *q = nullptr; QPushButton *mBtnMore = nullptr; QPushButton *mBtnFewer = nullptr; QPushButton *mBtnClear = nullptr; QVBoxLayout *mLayout = nullptr; QWidget *mButtonBox = nullptr; QList mWidgetList; int mMinWidgets = 0; int mMaxWidgets = 0; }; void KWidgetLister::Private::enableControls() { const int count = mWidgetList.count(); const bool isMaxWidgets = (count >= mMaxWidgets); const bool isMinWidgets = (count <= mMinWidgets); if (mBtnMore) { mBtnMore->setEnabled(!isMaxWidgets); } if (mBtnFewer) { mBtnFewer->setEnabled(!isMinWidgets); } } KWidgetLister::KWidgetLister(bool fewerMoreButton, int minWidgets, int maxWidgets, QWidget *parent) : QWidget(parent) , d(new Private(this)) { d->mMinWidgets = qMax(minWidgets, 1); d->mMaxWidgets = qMax(maxWidgets, d->mMinWidgets + 1); init(fewerMoreButton); } KWidgetLister::~KWidgetLister() { delete d; } void KWidgetLister::init(bool fewerMoreButton) { //--------- the button box d->mLayout = new QVBoxLayout(this); - d->mLayout->setMargin(0); + d->mLayout->setContentsMargins(0, 0, 0, 0); d->mLayout->setSpacing(4); d->mButtonBox = new QWidget(this); QHBoxLayout *mButtonBoxHBoxLayout = new QHBoxLayout(d->mButtonBox); - mButtonBoxHBoxLayout->setMargin(0); + mButtonBoxHBoxLayout->setContentsMargins(0, 0, 0, 0); d->mLayout->addWidget(d->mButtonBox); if (fewerMoreButton) { d->mBtnMore = new QPushButton(d->mButtonBox); mButtonBoxHBoxLayout->addWidget(d->mBtnMore); KGuiItem::assign(d->mBtnMore, KGuiItem(i18nc("more widgets", "More"), QStringLiteral("list-add"))); mButtonBoxHBoxLayout->setStretchFactor(d->mBtnMore, 0); d->mBtnFewer = new QPushButton(d->mButtonBox); mButtonBoxHBoxLayout->addWidget(d->mBtnFewer); KGuiItem::assign(d->mBtnFewer, KGuiItem(i18nc("fewer widgets", "Fewer"), QStringLiteral("list-remove"))); mButtonBoxHBoxLayout->setStretchFactor(d->mBtnFewer, 0); } QWidget *spacer = new QWidget(d->mButtonBox); mButtonBoxHBoxLayout->addWidget(spacer); mButtonBoxHBoxLayout->setStretchFactor(spacer, 1); d->mBtnClear = new QPushButton(d->mButtonBox); mButtonBoxHBoxLayout->addWidget(d->mBtnClear); KGuiItem::assign(d->mBtnClear, KStandardGuiItem::clear()); // FIXME a useful whats this. KStandardGuiItem::clear() returns a text with an edit box d->mBtnClear->setWhatsThis(QString()); mButtonBoxHBoxLayout->setStretchFactor(d->mBtnClear, 0); //---------- connect everything if (fewerMoreButton) { connect(d->mBtnMore, &QPushButton::clicked, this, &KWidgetLister::slotMore); connect(d->mBtnFewer, &QPushButton::clicked, this, &KWidgetLister::slotFewer); } connect(d->mBtnClear, &QPushButton::clicked, this, &KWidgetLister::slotClear); d->enableControls(); } void KWidgetLister::slotMore() { // the class should make certain that slotMore can't // be called when mMaxWidgets are on screen. assert(d->mWidgetList.count() < d->mMaxWidgets); addWidgetAtEnd(); // adjustSize(); d->enableControls(); } void KWidgetLister::slotFewer() { // the class should make certain that slotFewer can't // be called when mMinWidgets are on screen. assert(d->mWidgetList.count() > d->mMinWidgets); removeLastWidget(); // adjustSize(); d->enableControls(); } void KWidgetLister::slotClear() { setNumberOfShownWidgetsTo(d->mMinWidgets); // clear remaining widgets for (QWidget *widget : qAsConst(d->mWidgetList)) { clearWidget(widget); } // adjustSize(); d->enableControls(); Q_EMIT clearWidgets(); } void KWidgetLister::addWidgetAtEnd(QWidget *widget) { if (!widget) { widget = this->createWidget(this); } d->mLayout->insertWidget(d->mLayout->indexOf(d->mButtonBox), widget); d->mWidgetList.append(widget); widget->show(); d->enableControls(); Q_EMIT widgetAdded(); Q_EMIT widgetAdded(widget); } void KWidgetLister::removeLastWidget() { // The layout will take care that the // widget is removed from screen, too. delete d->mWidgetList.takeLast(); d->enableControls(); Q_EMIT widgetRemoved(); } void KWidgetLister::clearWidget(QWidget *widget) { Q_UNUSED(widget); } QWidget *KWidgetLister::createWidget(QWidget *parent) { return new QWidget(parent); } void KWidgetLister::setNumberOfShownWidgetsTo(int aNum) { int superfluousWidgets = qMax(d->mWidgetList.count() - aNum, 0); int missingWidgets = qMax(aNum - d->mWidgetList.count(), 0); // remove superfluous widgets for (; superfluousWidgets; superfluousWidgets--) { removeLastWidget(); } // add missing widgets for (; missingWidgets; missingWidgets--) { addWidgetAtEnd(); } } QList KWidgetLister::widgets() const { return d->mWidgetList; } int KWidgetLister::widgetsMinimum() const { return d->mMinWidgets; } int KWidgetLister::widgetsMaximum() const { return d->mMaxWidgets; } void KWidgetLister::removeWidget(QWidget *widget) { // The layout will take care that the // widget is removed from screen, too. if (d->mWidgetList.count() <= widgetsMinimum()) { return; } const int index = d->mWidgetList.indexOf(widget); QWidget *w = d->mWidgetList.takeAt(index); w->deleteLater(); w = nullptr; d->enableControls(); Q_EMIT widgetRemoved(widget); Q_EMIT widgetRemoved(); } void KWidgetLister::addWidgetAfterThisWidget(QWidget *currentWidget, QWidget *widget) { if (!widget) { widget = this->createWidget(this); } int index = d->mLayout->indexOf(currentWidget ? currentWidget : d->mButtonBox) + 1; d->mLayout->insertWidget(index, widget); if (currentWidget) { index = d->mWidgetList.indexOf(currentWidget); d->mWidgetList.insert(index + 1, widget); } else { d->mWidgetList.append(widget); } widget->show(); d->enableControls(); Q_EMIT widgetAdded(); Q_EMIT widgetAdded(widget); } diff --git a/src/libkdepim/widgets/progressindicatorlabel.cpp b/src/libkdepim/widgets/progressindicatorlabel.cpp index 4b51c59..d732f76 100644 --- a/src/libkdepim/widgets/progressindicatorlabel.cpp +++ b/src/libkdepim/widgets/progressindicatorlabel.cpp @@ -1,100 +1,100 @@ /* Copyright (c) 2013-2019 Montel Laurent 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 "progressindicatorlabel.h" #include "progressindicatorwidget.h" #include using namespace KPIM; class KPIM::ProgressIndicatorLabelPrivate { public: ProgressIndicatorLabelPrivate(const QString &_label, ProgressIndicatorLabel *qq) : labelStr(_label) , q(qq) { QHBoxLayout *lay = new QHBoxLayout(q); - lay->setMargin(0); + lay->setContentsMargins(0, 0, 0, 0); indicator = new ProgressIndicatorWidget; lay->addWidget(indicator); label = new QLabel; lay->addWidget(label); } ~ProgressIndicatorLabelPrivate() { } void setActiveLabel(const QString &str) { if (indicator->isActive()) { label->setText(str); } } void start() { indicator->start(); label->setText(labelStr); } void stop() { indicator->stop(); label->clear(); } QString labelStr; QLabel *label = nullptr; ProgressIndicatorWidget *indicator = nullptr; ProgressIndicatorLabel *q; }; ProgressIndicatorLabel::ProgressIndicatorLabel(const QString &label, QWidget *parent) : QWidget(parent) , d(new ProgressIndicatorLabelPrivate(label, this)) { } ProgressIndicatorLabel::ProgressIndicatorLabel(QWidget *parent) : QWidget(parent) , d(new ProgressIndicatorLabelPrivate(QString(), this)) { } ProgressIndicatorLabel::~ProgressIndicatorLabel() { delete d; } void ProgressIndicatorLabel::start() { d->start(); } void ProgressIndicatorLabel::stop() { d->stop(); } void ProgressIndicatorLabel::setActiveLabel(const QString &label) { d->setActiveLabel(label); } diff --git a/src/libkdepimakonadi/addressline/addresslineedit/addresseelineedit_p.cpp b/src/libkdepimakonadi/addressline/addresslineedit/addresseelineedit_p.cpp index 6a878f8..98df98f 100644 --- a/src/libkdepimakonadi/addressline/addresslineedit/addresseelineedit_p.cpp +++ b/src/libkdepimakonadi/addressline/addresslineedit/addresseelineedit_p.cpp @@ -1,993 +1,993 @@ /* Copyright (c) 2015-2019 Montel Laurent This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "addresseelineedit_p.h" #include "addressline/completionorder/completionordereditor.h" #include "addresseelineeditmanager.h" #include "addresseelineedit.h" #include "kmailcompletion.h" #include "libkdepimakonadi_debug.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if !DISABLE_AKONADI_SEARCH #include #endif #include #include #include using namespace KPIM; AddresseeLineEditPrivate::AddresseeLineEditPrivate(KPIM::AddresseeLineEdit *qq, bool enableCompletion) : QObject(qq) , q(qq) , mRecentAddressConfig(nullptr) , mUseCompletion(enableCompletion) , mCompletionInitialized(false) , mSmartPaste(false) , mLastSearchMode(false) , mSearchExtended(false) , mUseSemicolonAsSeparator(false) , mEnableBalooSearch(true) , mEnableAkonadiSearch(true) , mExpandIntern(true) , mShowRecentAddresses(true) , mCanDeleteLineEdit(true) { mDelayedQueryTimer = new QTimer(this); mDelayedQueryTimer->setSingleShot(true); connect(mDelayedQueryTimer, &QTimer::timeout, this, &AddresseeLineEditPrivate::slotTriggerDelayedQueries); } AddresseeLineEditPrivate::~AddresseeLineEditPrivate() { if (AddresseeLineEditManager::self()->addressLineEdit() == q) { AddresseeLineEditManager::self()->stopLDAPLookup(); } } void AddresseeLineEditPrivate::restartTime(const QString &searchString) { if (useCompletion()) { AddresseeLineEditManager::self()->restartLdap(searchString, q); } } static const QString s_completionItemIndentString = QStringLiteral(" "); class SourceWithWeight { public: int weight; // the weight of the source int index; // index into AddresseeLineEditStatic::self()->completionSources QString sourceName; // the name of the source, e.g. "LDAP Server" bool operator<(const SourceWithWeight &other) const { if (weight > other.weight) { return true; } if (weight < other.weight) { return false; } return sourceName < other.sourceName; } }; void AddresseeLineEditPrivate::init() { if (mToolButton) { return; } mToolButton = new QToolButton(q); mToolButton->setVisible(false); mToolButton->setCursor(Qt::ArrowCursor); const int size = q->sizeHint().height() - 5; mToolButton->setFixedSize(size, size); int padding = (q->sizeHint().height() - size) / 2; mToolButton->move(2, padding); mToolButton->setStyleSheet(QStringLiteral("QToolButton { border: none; }")); connect(mToolButton, &QToolButton::clicked, q, &AddresseeLineEdit::iconClicked); if (!AddresseeLineEditManager::self()) { AddresseeLineEditManager::self()->completion()->setOrder(KCompletion::Weighted); AddresseeLineEditManager::self()->completion()->setIgnoreCase(true); } if (mUseCompletion) { AddresseeLineEditManager::self()->initializeLdap(); AddresseeLineEditManager::self()->setBalooCompletionSource(q->addCompletionSource(i18nc("@title:group", "Contacts found in your data"), -1)); AddresseeLineEditManager::self()->updateLDAPWeights(); if (!mCompletionInitialized) { q->setCompletionObject(AddresseeLineEditManager::self()->completion(), false); connect(q, &KLineEdit::completion, this, &AddresseeLineEditPrivate::slotCompletion); connect(q, &AddresseeLineEdit::returnPressed, this, &AddresseeLineEditPrivate::slotReturnPressed); KCompletionBox *box = q->completionBox(); connect(box, QOverload::of(&KCompletionBox::activated), this, &AddresseeLineEditPrivate::slotPopupCompletion); connect(box, &KCompletionBox::userCancelled, this, &AddresseeLineEditPrivate::slotUserCancelled); connect(AddresseeLineEditManager::self()->ldapTimer(), &QTimer::timeout, this, &AddresseeLineEditPrivate::slotStartLDAPLookup); connect(AddresseeLineEditManager::self()->ldapSearch(), QOverload::of(&KLDAP::LdapClientSearch::searchData), this, &AddresseeLineEditPrivate::slotLDAPSearchData); mCompletionInitialized = true; } } connect(q, &AddresseeLineEdit::textCompleted, q, &AddresseeLineEdit::slotEditingFinished); connect(q, &AddresseeLineEdit::editingFinished, q, &AddresseeLineEdit::slotEditingFinished); } void AddresseeLineEditPrivate::setIcon(const QIcon &icon, const QString &tooltip) { if (icon.isNull()) { mToolButton->setVisible(false); q->setStyleSheet(QString()); } else { mToolButton->setIcon(icon); mToolButton->setToolTip(tooltip); const int padding = mToolButton->width() - q->style()->pixelMetric(QStyle::PM_DefaultFrameWidth); q->setStyleSheet(QStringLiteral("QLineEdit { padding-left: %1px }").arg(padding)); mToolButton->setVisible(true); } } void AddresseeLineEditPrivate::searchInBaloo() { #if !DISABLE_AKONADI_SEARCH const QString trimmedString = mSearchString.trimmed(); Akonadi::Search::PIM::ContactCompleter com(trimmedString, 20); const QStringList listEmail = AddresseeLineEditManager::self()->cleanupEmailList(com.complete()); for (const QString &email : listEmail) { addCompletionItem(email, 1, AddresseeLineEditManager::self()->balooCompletionSource()); } doCompletion(mLastSearchMode); #endif } void AddresseeLineEditPrivate::setCompletedItems(const QStringList &items, bool autoSuggest) { KCompletionBox *completionBox = q->completionBox(); if (!items.isEmpty() && !(items.count() == 1 && mSearchString == items.first())) { completionBox->clear(); const int numberOfItems(items.count()); for (int i = 0; i < numberOfItems; ++i) { QListWidgetItem *item = new QListWidgetItem(items.at(i), completionBox); if (!items.at(i).startsWith(s_completionItemIndentString)) { item->setFlags(item->flags() & ~Qt::ItemIsSelectable); - item->setBackgroundColor(AddresseeLineEditManager::self()->alternateColor()); + item->setBackground(AddresseeLineEditManager::self()->alternateColor()); } completionBox->addItem(item); } if (!completionBox->isVisible()) { if (!mSearchString.isEmpty()) { completionBox->setCancelledText(mSearchString); } completionBox->popup(); // we have to install the event filter after popup(), since that // calls show(), and that's where KCompletionBox installs its filter. // We want to be first, though, so do it now. if (AddresseeLineEditManager::self()->completion()->order() == KCompletion::Weighted) { qApp->installEventFilter(q); } } QListWidgetItem *item = completionBox->item(1); if (item) { completionBox->blockSignals(true); completionBox->setCurrentItem(item); item->setSelected(true); completionBox->blockSignals(false); } if (autoSuggest) { const int index = items.first().indexOf(mSearchString); const QString newText = items.first().mid(index); q->callSetUserSelection(false); q->callSetCompletedText(newText, true); } } else { if (completionBox && completionBox->isVisible()) { completionBox->hide(); completionBox->setItems(QStringList()); } } } void AddresseeLineEditPrivate::addCompletionItem(const QString &string, int weight, int completionItemSource, const QStringList *keyWords) { // Check if there is an exact match for item already, and use the // maximum weight if so. Since there's no way to get the information // from KCompletion, we have to keep our own QMap. // We also update the source since the item should always be shown from the source with the highest weight AddresseeLineEditManager::CompletionItemsMap::iterator it = AddresseeLineEditManager::self()->completionItemMap.find(string); if (it != AddresseeLineEditManager::self()->completionItemMap.end()) { weight = qMax((*it).first, weight); (*it).first = weight; (*it).second = completionItemSource; } else { AddresseeLineEditManager::self()->completionItemMap.insert(string, qMakePair(weight, completionItemSource)); } AddresseeLineEditManager::self()->completion()->addItem(string, weight); if (keyWords && !keyWords->isEmpty()) { AddresseeLineEditManager::self()->completion()->addItemWithKeys(string, weight, keyWords); // see kmailcompletion.cpp } } const QStringList KPIM::AddresseeLineEditPrivate::adjustedCompletionItems(bool fullSearch) { QStringList items = fullSearch ? AddresseeLineEditManager::self()->completion()->allMatches(mSearchString) : AddresseeLineEditManager::self()->completion()->substringCompletion(mSearchString); //force items to be sorted by email items.sort(); // For weighted mode, the algorithm is the following: // In the first loop, we add each item to its section (there is one section per completion source) // We also add spaces in front of the items. // The sections are appended to the items list. // In the second loop, we then walk through the sections and add all the items in there to the // sorted item list, which is the final result. // // The algo for non-weighted mode is different. int lastSourceIndex = -1; unsigned int i = 0; // Maps indices of the items list, which are section headers/source items, // to a QStringList which are the items of that section/source. QMap sections; QStringList sortedItems; for (QStringList::Iterator it = items.begin(); it != items.end(); ++it, ++i) { AddresseeLineEditManager::CompletionItemsMap::const_iterator cit = AddresseeLineEditManager::self()->completionItemMap.constFind(*it); if (cit == AddresseeLineEditManager::self()->completionItemMap.constEnd()) { continue; } const int index = (*cit).second; if (AddresseeLineEditManager::self()->completion()->order() == KCompletion::Weighted) { if (lastSourceIndex == -1 || lastSourceIndex != index) { const QString sourceLabel(AddresseeLineEditManager::self()->completionSources.at(index)); if (sections.find(index) == sections.end()) { it = items.insert(it, sourceLabel); ++it; //skip new item } lastSourceIndex = index; } (*it) = (*it).prepend(s_completionItemIndentString); // remove preferred email sort added in addContact() (*it).replace(QLatin1String(" <"), QStringLiteral(" <")); } sections[ index ].append(*it); if (AddresseeLineEditManager::self()->completion()->order() == KCompletion::Sorted) { sortedItems.append(*it); } } if (AddresseeLineEditManager::self()->completion()->order() == KCompletion::Weighted) { // Sort the sections QVector sourcesAndWeights; const int numberOfCompletionSources(AddresseeLineEditManager::self()->completionSources.count()); sourcesAndWeights.reserve(numberOfCompletionSources); for (int i = 0; i < numberOfCompletionSources; ++i) { SourceWithWeight sww; sww.sourceName = AddresseeLineEditManager::self()->completionSources.at(i); sww.weight = AddresseeLineEditManager::self()->completionSourceWeights[sww.sourceName]; sww.index = i; sourcesAndWeights.append(sww); } std::sort(sourcesAndWeights.begin(), sourcesAndWeights.end()); // Add the sections and their items to the final sortedItems result list const int numberOfSources(sourcesAndWeights.size()); for (int i = 0; i < numberOfSources; ++i) { const SourceWithWeight source = sourcesAndWeights.at(i); const QStringList sectionItems = sections[source.index]; if (!sectionItems.isEmpty()) { sortedItems.append(source.sourceName); for (const QString &itemInSection : sectionItems) { sortedItems.append(itemInSection); } } } } else { sortedItems.sort(); } return sortedItems; } void AddresseeLineEditPrivate::updateSearchString() { mSearchString = q->text(); int n = -1; bool inQuote = false; const int searchStringLength = mSearchString.length(); for (int i = 0; i < searchStringLength; ++i) { const QChar searchChar = mSearchString.at(i); if (searchChar == QLatin1Char('"')) { inQuote = !inQuote; } if (searchChar == QLatin1Char('\\') && (i + 1) < searchStringLength && mSearchString.at(i + 1) == QLatin1Char('"')) { ++i; } if (inQuote) { continue; } if (i < searchStringLength && (searchChar == QLatin1Char(',') || (mUseSemicolonAsSeparator && searchChar == QLatin1Char(';')))) { n = i; } } if (n >= 0) { ++n; // Go past the "," const int len = mSearchString.length(); // Increment past any whitespace... while (n < len && mSearchString.at(n).isSpace()) { ++n; } mPreviousAddresses = mSearchString.left(n); mSearchString = mSearchString.mid(n).trimmed(); } else { mPreviousAddresses.clear(); } } void AddresseeLineEditPrivate::slotTriggerDelayedQueries() { const QString strSearch = mSearchString.trimmed(); if (strSearch.size() <= 2) { return; } if (mEnableBalooSearch) { searchInBaloo(); } // We send a contactsearch job through akonadi. // This not only searches baloo but also servers if remote search is enabled if (mEnableAkonadiSearch) { akonadiPerformSearch(); } } void AddresseeLineEditPrivate::startSearches() { if (!mDelayedQueryTimer->isActive()) { mDelayedQueryTimer->start(50); } } void AddresseeLineEditPrivate::akonadiPerformSearch() { qCDebug(LIBKDEPIMAKONADI_LOG) << "searching akonadi with:" << mSearchString; // first, kill all job still in flight, they are no longer current for (const QPointer &job : qAsConst(AddresseeLineEditManager::self()->akonadiJobsInFlight)) { if (!job.isNull()) { job.data()->kill(); } } AddresseeLineEditManager::self()->akonadiJobsInFlight.clear(); // now start new jobs Akonadi::ContactSearchJob *contactJob = new Akonadi::ContactSearchJob(AddresseeLineEditManager::self()->akonadiSession()); contactJob->fetchScope().setAncestorRetrieval(Akonadi::ItemFetchScope::Parent); contactJob->setQuery(Akonadi::ContactSearchJob::NameOrEmail, mSearchString, Akonadi::ContactSearchJob::ContainsWordBoundaryMatch); connect(contactJob, &Akonadi::ItemSearchJob::itemsReceived, this, &AddresseeLineEditPrivate::slotAkonadiHandleItems); connect(contactJob, &KJob::result, this, &AddresseeLineEditPrivate::slotAkonadiSearchResult); Akonadi::ContactGroupSearchJob *groupJob = new Akonadi::ContactGroupSearchJob(AddresseeLineEditManager::self()->akonadiSession()); groupJob->fetchScope().setAncestorRetrieval(Akonadi::ItemFetchScope::Parent); groupJob->setQuery(Akonadi::ContactGroupSearchJob::Name, mSearchString, Akonadi::ContactGroupSearchJob::ContainsMatch); connect(contactJob, &Akonadi::ItemSearchJob::itemsReceived, this, &AddresseeLineEditPrivate::slotAkonadiHandleItems); connect(groupJob, &KJob::result, this, &AddresseeLineEditPrivate::slotAkonadiSearchResult); AddresseeLineEditManager::self()->akonadiJobsInFlight.append(contactJob); AddresseeLineEditManager::self()->akonadiJobsInFlight.append(groupJob); akonadiHandlePending(); } void AddresseeLineEditPrivate::akonadiHandlePending() { qCDebug(LIBKDEPIMAKONADI_LOG) << "Pending items: " << AddresseeLineEditManager::self()->akonadiPendingItems.size(); Akonadi::Item::List::iterator it = AddresseeLineEditManager::self()->akonadiPendingItems.begin(); while (it != AddresseeLineEditManager::self()->akonadiPendingItems.end()) { const Akonadi::Item item = *it; const AddresseeLineEditManager::collectionInfo sourceIndex = AddresseeLineEditManager::self()->akonadiCollectionToCompletionSourceMap.value(item.parentCollection().id(), AddresseeLineEditManager::collectionInfo()); if (sourceIndex.index >= 0) { qCDebug(LIBKDEPIMAKONADI_LOG) << "identified collection: " << AddresseeLineEditManager::self()->completionSources[sourceIndex.index]; if (sourceIndex.enabled) { q->addItem(item, 1, sourceIndex.index); } // remove from the pending it = AddresseeLineEditManager::self()->akonadiPendingItems.erase(it); } else { ++it; } } } void AddresseeLineEditPrivate::doCompletion(bool ctrlT) { mLastSearchMode = ctrlT; const KCompletion::CompletionMode mode = q->completionMode(); if (mode == KCompletion::CompletionNone) { return; } AddresseeLineEditManager::self()->completion()->setOrder(KCompletion::Weighted); // cursor at end of string - or Ctrl+T pressed for substring completion? if (ctrlT) { const QStringList completions = adjustedCompletionItems(false); if (completions.count() == 1) { q->setText(mPreviousAddresses + completions.first().trimmed()); } // Make sure the completion popup is closed if no matching items were found setCompletedItems(completions, true); q->cursorAtEnd(); q->setCompletionMode(mode); //set back to previous mode return; } switch (mode) { case KCompletion::CompletionPopupAuto: if (mSearchString.isEmpty()) { break; } //else: fall-through to the CompletionPopup case Q_FALLTHROUGH(); case KCompletion::CompletionPopup: { const QStringList items = adjustedCompletionItems(false); setCompletedItems(items, false); break; } case KCompletion::CompletionShell: { const QString match = AddresseeLineEditManager::self()->completion()->makeCompletion(mSearchString); if (!match.isNull() && match != mSearchString) { q->setText(mPreviousAddresses + match); q->setModified(true); q->cursorAtEnd(); } break; } case KCompletion::CompletionMan: // Short-Auto in fact case KCompletion::CompletionAuto: //force autoSuggest in KLineEdit::keyPressed or setCompletedText will have no effect q->setCompletionMode(q->completionMode()); if (!mSearchString.isEmpty()) { //if only our \" is left, remove it since user has not typed it either if (mSearchExtended && mSearchString == QLatin1String("\"")) { mSearchExtended = false; mSearchString.clear(); q->setText(mPreviousAddresses); break; } QString match = AddresseeLineEditManager::self()->completion()->makeCompletion(mSearchString); if (!match.isEmpty()) { if (match != mSearchString) { const QString adds = mPreviousAddresses + match; q->callSetCompletedText(adds); } } else { if (!mSearchString.startsWith(QLatin1Char('\"'))) { //try with quoted text, if user has not type one already match = AddresseeLineEditManager::self()->completion()->makeCompletion(QLatin1String("\"") + mSearchString); if (!match.isEmpty() && match != mSearchString) { mSearchString = QLatin1String("\"") + mSearchString; mSearchExtended = true; q->setText(mPreviousAddresses + mSearchString); q->callSetCompletedText(mPreviousAddresses + match); } } else if (mSearchExtended) { //our added \" does not work anymore, remove it mSearchString = mSearchString.mid(1); mSearchExtended = false; q->setText(mPreviousAddresses + mSearchString); //now try again match = AddresseeLineEditManager::self()->completion()->makeCompletion(mSearchString); if (!match.isEmpty() && match != mSearchString) { const QString adds = mPreviousAddresses + match; q->setCompletedText(adds); } } } } break; case KCompletion::CompletionNone: break; } } void AddresseeLineEditPrivate::slotCompletion() { // Called by KLineEdit's keyPressEvent for CompletionModes // Auto,Popup -> new text, update search string. // not called for CompletionShell, this is been taken care of // in AddresseeLineEdit::keyPressEvent updateSearchString(); if (q->completionBox()) { q->completionBox()->setCancelledText(mSearchString); } startSearches(); doCompletion(false); } void AddresseeLineEditPrivate::slotPopupCompletion(const QString &completion) { QString c = completion.trimmed(); if (c.endsWith(QLatin1Char(')'))) { c = completion.mid(0, completion.lastIndexOf(QLatin1String(" ("))).trimmed(); } q->setText(mPreviousAddresses + c); q->cursorAtEnd(); updateSearchString(); q->emitTextCompleted(); } void AddresseeLineEditPrivate::slotReturnPressed(const QString &) { if (!q->completionBox()->selectedItems().isEmpty()) { slotPopupCompletion(q->completionBox()->selectedItems().constFirst()->text()); } } void AddresseeLineEditPrivate::slotStartLDAPLookup() { if (AddresseeLineEditManager::self()->isOnline()) { const KCompletion::CompletionMode mode = q->completionMode(); if (mode == KCompletion::CompletionNone) { return; } if (!AddresseeLineEditManager::self()->ldapSearch()->isAvailable()) { return; } if (AddresseeLineEditManager::self()->addressLineEdit() != q) { return; } AddresseeLineEditManager::self()->startLoadingLDAPEntries(); } } void AddresseeLineEditPrivate::slotLDAPSearchData(const KLDAP::LdapResult::List &results) { if (results.isEmpty() || AddresseeLineEditManager::self()->addressLineEdit() != q) { return; } for (const KLDAP::LdapResult &result : results) { KContacts::Addressee contact; contact.setNameFromString(result.name); contact.setEmails(result.email); QString ou; if (AddresseeLineEditManager::self()->showOU()) { const int depth = result.dn.depth(); for (int i = 0; i < depth; ++i) { const QString rdnStr = result.dn.rdnString(i); if (rdnStr.startsWith(QLatin1String("ou="), Qt::CaseInsensitive)) { ou = rdnStr.mid(3); break; } } } if (!AddresseeLineEditManager::self()->isLdapClientToCompletionSourceMapContains(result.clientNumber)) { AddresseeLineEditManager::self()->updateLDAPWeights(); // we got results from a new source, so update the completion sources } q->addContact(contact, result.completionWeight, AddresseeLineEditManager::self()->ldapClientToCompletionSourceValue(result.clientNumber), ou); } if ((q->hasFocus() || q->completionBox()->hasFocus()) && q->completionMode() != KCompletion::CompletionNone && q->completionMode() != KCompletion::CompletionShell) { q->setText(mPreviousAddresses + mSearchString); // only complete again if the user didn't change the selection while // we were waiting; otherwise the completion box will be closed const QListWidgetItem *current = q->completionBox()->currentItem(); if (!current || mSearchString.trimmed() != current->text().trimmed()) { doCompletion(mLastSearchMode); } } } void AddresseeLineEditPrivate::slotEditCompletionOrder() { if (mUseCompletion) { init(); // for AddresseeLineEditStatic::self()->ldapSearch QPointer dlg = new CompletionOrderEditor(AddresseeLineEditManager::self()->ldapSearch(), nullptr); if (dlg->exec()) { AddresseeLineEditManager::self()->updateCompletionOrder(); } delete dlg; } } KLDAP::LdapClientSearch *AddresseeLineEditPrivate::ldapSearch() { init(); // for AddresseeLineEditStatic::self()->ldapSearch return AddresseeLineEditManager::self()->ldapSearch(); } void AddresseeLineEditPrivate::slotUserCancelled(const QString &cancelText) { if (AddresseeLineEditManager::self()->addressLineEdit() == q) { AddresseeLineEditManager::self()->stopLDAPLookup(); } q->callUserCancelled(mPreviousAddresses + cancelText); // in KLineEdit } void AddresseeLineEditPrivate::slotAkonadiHandleItems(const Akonadi::Item::List &items) { /* We have to fetch the collections of the items, so that the source name can be correctly labeled.*/ for (const Akonadi::Item &item : items) { // check the local cache of collections const Akonadi::Collection::Id colId = item.parentCollection().id(); const AddresseeLineEditManager::collectionInfo sourceIndex = AddresseeLineEditManager::self()->akonadiCollectionToCompletionSourceMap.value(colId, AddresseeLineEditManager::collectionInfo()); if (sourceIndex.index == -1) { qCDebug(LIBKDEPIMAKONADI_LOG) << "Fetching New collection: " << colId; // the collection isn't there, start the fetch job. Akonadi::CollectionFetchJob *collectionJob = new Akonadi::CollectionFetchJob(item.parentCollection(), Akonadi::CollectionFetchJob::Base, AddresseeLineEditManager::self()->akonadiSession()); connect(collectionJob, &Akonadi::CollectionFetchJob::collectionsReceived, this, &AddresseeLineEditPrivate::slotAkonadiCollectionsReceived); /* we don't want to start multiple fetch jobs for the same collection, so insert the collection with an index value of -2 */ AddresseeLineEditManager::collectionInfo info; info.index = -2; AddresseeLineEditManager::self()->akonadiCollectionToCompletionSourceMap.insert(colId, info); AddresseeLineEditManager::self()->akonadiPendingItems.append(item); } else if (sourceIndex.index == -2) { /* fetch job already started, don't need to start another one, so just append the item as pending */ AddresseeLineEditManager::self()->akonadiPendingItems.append(item); } else { if (sourceIndex.enabled) { q->addItem(item, 1, sourceIndex.index); } } } if (!items.isEmpty()) { const QListWidgetItem *current = q->completionBox()->currentItem(); if (!current || mSearchString.trimmed() != current->text().trimmed()) { doCompletion(mLastSearchMode); } } } void AddresseeLineEditPrivate::slotAkonadiSearchResult(KJob *job) { if (job->error()) { qCWarning(LIBKDEPIMAKONADI_LOG) << "Akonadi search job failed: " << job->errorString(); } const int index = AddresseeLineEditManager::self()->akonadiJobsInFlight.indexOf(qobject_cast(job)); if (index != -1) { AddresseeLineEditManager::self()->akonadiJobsInFlight.remove(index); } } void AddresseeLineEditPrivate::slotAkonadiCollectionsReceived( const Akonadi::Collection::List &collections) { KSharedConfig::Ptr config = KSharedConfig::openConfig(QStringLiteral("kpimcompletionorder")); KConfigGroup groupCompletionWeights(config, "CompletionWeights"); KConfigGroup groupCompletionEnabled(config, "CompletionEnabled"); for (const Akonadi::Collection &collection : collections) { if (collection.isValid()) { const QString sourceString = collection.displayName(); const Akonadi::Collection::Id colId = collection.id(); const int weight = groupCompletionWeights.readEntry(QString::number(colId), 1); const int index = q->addCompletionSource(sourceString, weight); AddresseeLineEditManager::collectionInfo info(index, groupCompletionEnabled.readEntry(QString::number(colId), true)); qCDebug(LIBKDEPIMAKONADI_LOG) << "\treceived: " << sourceString << "index: " << index << " enabled: " << info.enabled; AddresseeLineEditManager::self()->akonadiCollectionToCompletionSourceMap.insert(colId, info); } } // now that we have added the new collections, recheck our list of pending contacts akonadiHandlePending(); // do completion const QListWidgetItem *current = q->completionBox()->currentItem(); if (!current || mSearchString.trimmed() != current->text().trimmed()) { doCompletion(mLastSearchMode); } } void AddresseeLineEditPrivate::slotToggleExpandGroups(bool checked) { AddresseeLineEditManager::self()->setAutoGroupExpand(checked); } void AddresseeLineEditPrivate::slotShowOUChanged(bool checked) { AddresseeLineEditManager::self()->setShowOU(checked); } void AddresseeLineEditPrivate::updateBalooBlackList() { AddresseeLineEditManager::self()->loadBalooBlackList(); q->removeCompletionSource(i18nc("@title:group", "Contacts found in your data")); AddresseeLineEditManager::self()->setBalooCompletionSource(q->addCompletionSource(i18nc("@title:group", "Contacts found in your data"), -1)); } void AddresseeLineEditPrivate::updateCompletionOrder() { AddresseeLineEditManager::self()->updateCompletionOrder(); } bool AddresseeLineEditPrivate::canDeleteLineEdit() const { return mCanDeleteLineEdit; } void AddresseeLineEditPrivate::setCanDeleteLineEdit(bool inprogressToConfigureCompletion) { mCanDeleteLineEdit = inprogressToConfigureCompletion; } KConfig *AddresseeLineEditPrivate::recentAddressConfig() const { return mRecentAddressConfig; } bool AddresseeLineEditPrivate::showRecentAddresses() const { return mShowRecentAddresses; } void AddresseeLineEditPrivate::setRecentAddressConfig(KConfig *config) { mRecentAddressConfig = config; } KContacts::ContactGroup::List AddresseeLineEditPrivate::groups() const { return mGroups; } void AddresseeLineEditPrivate::setGroups(const KContacts::ContactGroup::List &groups) { mGroups = groups; } QList AddresseeLineEditPrivate::mightBeGroupJobs() const { return mMightBeGroupJobs; } void AddresseeLineEditPrivate::setMightBeGroupJobs(const QList &mightBeGroupJobs) { mMightBeGroupJobs = mightBeGroupJobs; } bool AddresseeLineEditPrivate::autoGroupExpand() const { return AddresseeLineEditManager::self()->autoGroupExpand(); } void AddresseeLineEditPrivate::setAutoGroupExpand(bool autoGroupExpand) { AddresseeLineEditManager::self()->setAutoGroupExpand(autoGroupExpand); } void AddresseeLineEditPrivate::setExpandIntern(bool b) { mExpandIntern = b; } bool AddresseeLineEditPrivate::expandIntern() const { return mExpandIntern; } bool AddresseeLineEditPrivate::useSemicolonAsSeparator() const { return mUseSemicolonAsSeparator; } void AddresseeLineEditPrivate::setUseSemicolonAsSeparator(bool useSemicolonAsSeparator) { mUseSemicolonAsSeparator = useSemicolonAsSeparator; } bool AddresseeLineEditPrivate::enableBalooSearch() const { return mEnableBalooSearch; } void AddresseeLineEditPrivate::setEnableBalooSearch(bool enableBalooSearch) { mEnableBalooSearch = enableBalooSearch; } bool AddresseeLineEditPrivate::enableAkonadiSearch() const { return mEnableAkonadiSearch; } void AddresseeLineEditPrivate::setEnableAkonadiSearch(bool enableAkonadiSearch) { mEnableAkonadiSearch = enableAkonadiSearch; } QString AddresseeLineEditPrivate::searchString() const { return mSearchString; } void AddresseeLineEditPrivate::setSearchString(const QString &searchString) { mSearchString = searchString; } bool AddresseeLineEditPrivate::searchExtended() const { return mSearchExtended; } void AddresseeLineEditPrivate::setSearchExtended(bool searchExtended) { mSearchExtended = searchExtended; } bool AddresseeLineEditPrivate::smartPaste() const { return mSmartPaste; } void AddresseeLineEditPrivate::setSmartPaste(bool smartPaste) { mSmartPaste = smartPaste; } bool AddresseeLineEditPrivate::completionInitialized() const { return mCompletionInitialized; } bool AddresseeLineEditPrivate::useCompletion() const { return mUseCompletion; } void AddresseeLineEditPrivate::setUseCompletion(bool useCompletion) { mUseCompletion = useCompletion; } bool AddresseeLineEditPrivate::showOU() const { return AddresseeLineEditManager::self()->showOU(); } void AddresseeLineEditPrivate::removeCompletionSource(const QString &source) { AddresseeLineEditManager::self()->removeCompletionSource(source); } int AddresseeLineEditPrivate::addCompletionSource(const QString &source, int weight) { return AddresseeLineEditManager::self()->addCompletionSource(source, weight); } void AddresseeLineEditPrivate::mightBeGroupJobsClear() { mMightBeGroupJobs.clear(); } bool AddresseeLineEditPrivate::groupsIsEmpty() const { return mGroups.isEmpty(); } void AddresseeLineEditPrivate::setShowRecentAddresses(bool b) { mShowRecentAddresses = b; } void AddresseeLineEditPrivate::groupsClear() { mGroups.clear(); } void AddresseeLineEditPrivate::addGroups(const KContacts::ContactGroup::List &lst) { mGroups << lst; } void AddresseeLineEditPrivate::mightBeGroupJobsRemoveOne(Akonadi::ContactGroupSearchJob *search) { mMightBeGroupJobs.removeOne(search); } void AddresseeLineEditPrivate::mightBeGroupJobsAdd(Akonadi::ContactGroupSearchJob *job) { mMightBeGroupJobs.append(job); } diff --git a/src/libkdepimakonadi/addressline/blacklistbaloocompletion/blacklistbalooemailcompletionwidget.cpp b/src/libkdepimakonadi/addressline/blacklistbaloocompletion/blacklistbalooemailcompletionwidget.cpp index 296581d..5bc15ed 100644 --- a/src/libkdepimakonadi/addressline/blacklistbaloocompletion/blacklistbalooemailcompletionwidget.cpp +++ b/src/libkdepimakonadi/addressline/blacklistbaloocompletion/blacklistbalooemailcompletionwidget.cpp @@ -1,287 +1,287 @@ /* Copyright (c) 2015-2019 Montel Laurent 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 "blacklistbalooemailcompletionwidget.h" #include "blacklistbalooemaillist.h" #include "blacklistbalooemailsearchjob.h" #include "blacklistbalooemailutil.h" #include "blacklistbalooemailwarning.h" #include #include #include #include #include #include #include #include #include #include "libkdepimakonadi_debug.h" using namespace KPIM; BlackListBalooEmailCompletionWidget::BlackListBalooEmailCompletionWidget(QWidget *parent) : QWidget(parent) , mLimit(500) { QVBoxLayout *mainLayout = new QVBoxLayout(this); QHBoxLayout *searchLayout = new QHBoxLayout; mainLayout->addLayout(searchLayout); QLabel *lab = new QLabel(i18n("Search email:"), this); lab->setObjectName(QStringLiteral("search_label")); searchLayout->addWidget(lab); mSearchLineEdit = new KLineEdit(this); mSearchLineEdit->setPlaceholderText(i18n("Research is done from 3 characters")); mSearchLineEdit->setFocus(); mSearchLineEdit->setClearButtonEnabled(true); mSearchLineEdit->setTrapReturnKey(true); mSearchLineEdit->setObjectName(QStringLiteral("search_lineedit")); connect(mSearchLineEdit, &KLineEdit::returnPressed, this, &BlackListBalooEmailCompletionWidget::slotCheckIfUpdateBlackListIsNeeded); searchLayout->addWidget(mSearchLineEdit); mSearchButton = new QPushButton(QIcon::fromTheme(QStringLiteral("edit-find")), i18n("Search"), this); mSearchButton->setObjectName(QStringLiteral("search_button")); connect(mSearchButton, &QAbstractButton::clicked, this, &BlackListBalooEmailCompletionWidget::slotCheckIfUpdateBlackListIsNeeded); mSearchButton->setEnabled(false); searchLayout->addWidget(mSearchButton); mShowAllBlackListedEmails = new QPushButton(i18n("Show Blacklisted Emails"), this); mShowAllBlackListedEmails->setObjectName(QStringLiteral("show_blacklisted_email_button")); connect(mShowAllBlackListedEmails, &QAbstractButton::clicked, this, &BlackListBalooEmailCompletionWidget::slotShowAllBlacklistedEmail); searchLayout->addWidget(mShowAllBlackListedEmails); mEmailList = new BlackListBalooEmailList(this); mEmailList->setObjectName(QStringLiteral("email_list")); mainLayout->addWidget(mEmailList); mBlackListWarning = new BlackListBalooEmailWarning(this); mBlackListWarning->setObjectName(QStringLiteral("backlistwarning")); connect(mBlackListWarning, &BlackListBalooEmailWarning::newSearch, this, &BlackListBalooEmailCompletionWidget::slotSearch); connect(mBlackListWarning, &BlackListBalooEmailWarning::saveChanges, this, &BlackListBalooEmailCompletionWidget::slotSaveChanges); mainLayout->addWidget(mBlackListWarning); QHBoxLayout *searchLineLayout = new QHBoxLayout; mainLayout->addLayout(searchLineLayout); QHBoxLayout *selectElementLayout = new QHBoxLayout; searchLineLayout->addLayout(selectElementLayout); mSelectButton = new QPushButton(i18n("&Select"), this); mSelectButton->setObjectName(QStringLiteral("select_email")); connect(mSelectButton, &QAbstractButton::clicked, this, &BlackListBalooEmailCompletionWidget::slotSelectEmails); selectElementLayout->addWidget(mSelectButton); mUnselectButton = new QPushButton(i18n("&Unselect"), this); mUnselectButton->setObjectName(QStringLiteral("unselect_email")); connect(mUnselectButton, &QAbstractButton::clicked, this, &BlackListBalooEmailCompletionWidget::slotUnselectEmails); selectElementLayout->addWidget(mUnselectButton); mMoreResult = new QLabel(i18n("More result..."), this); mMoreResult->setObjectName(QStringLiteral("moreresultlabel")); selectElementLayout->addWidget(mMoreResult); mMoreResult->setContextMenuPolicy(Qt::NoContextMenu); connect(mMoreResult, &QLabel::linkActivated, this, &BlackListBalooEmailCompletionWidget::slotLinkClicked); mMoreResult->setVisible(false); selectElementLayout->addStretch(1); connect(mSearchLineEdit, &QLineEdit::textChanged, this, &BlackListBalooEmailCompletionWidget::slotSearchLineEditChanged); mSearchInResultLineEdit = new KListWidgetSearchLine(this, mEmailList); mSearchInResultLineEdit->setObjectName(QStringLiteral("searchinresultlineedit")); mSearchInResultLineEdit->setClearButtonEnabled(true); mSearchInResultLineEdit->setPlaceholderText(i18n("Search in result...")); searchLineLayout->addStretch(0); mNumberOfEmailsFound = new QLabel(this); mNumberOfEmailsFound->setObjectName(QStringLiteral("numberofemailsfound")); searchLineLayout->addWidget(mNumberOfEmailsFound); searchLineLayout->addWidget(mSearchInResultLineEdit); QHBoxLayout *excludeDomainLayout = new QHBoxLayout; - excludeDomainLayout->setMargin(0); + excludeDomainLayout->setContentsMargins(0, 0, 0, 0); mainLayout->addLayout(excludeDomainLayout); QLabel *excludeDomainLabel = new QLabel(i18n("Exclude domain names:"), this); excludeDomainLabel->setObjectName(QStringLiteral("domain_label")); excludeDomainLayout->addWidget(excludeDomainLabel); mExcludeDomainLineEdit = new KLineEdit(this); excludeDomainLayout->addWidget(mExcludeDomainLineEdit); mExcludeDomainLineEdit->setObjectName(QStringLiteral("domain_lineedit")); mExcludeDomainLineEdit->setClearButtonEnabled(true); mExcludeDomainLineEdit->setTrapReturnKey(true); mExcludeDomainLineEdit->setPlaceholderText(i18n("Separate domain with \'%1\'", QLatin1Char(','))); connect(mEmailList, &QListWidget::itemSelectionChanged, this, &BlackListBalooEmailCompletionWidget::slotSelectionChanged); slotSelectionChanged(); } BlackListBalooEmailCompletionWidget::~BlackListBalooEmailCompletionWidget() { } void BlackListBalooEmailCompletionWidget::slotSelectionChanged() { bool selectionIsNotEmpty = !mEmailList->selectedItems().isEmpty(); mSelectButton->setEnabled(selectionIsNotEmpty); mUnselectButton->setEnabled(selectionIsNotEmpty); } void BlackListBalooEmailCompletionWidget::load() { KSharedConfig::Ptr config = KSharedConfig::openConfig(QStringLiteral("kpimbalooblacklist")); KConfigGroup group(config, "AddressLineEdit"); const QStringList lst = group.readEntry("ExcludeDomain", QStringList()); mEmailList->setExcludeDomain(lst); mExcludeDomainLineEdit->setText(lst.join(QLatin1Char(','))); mOriginalExcludeDomain = lst; slotSelectionChanged(); } void BlackListBalooEmailCompletionWidget::slotUnselectEmails() { const QList lstSelected = mEmailList->selectedItems(); for (QListWidgetItem *item : lstSelected) { item->setCheckState(Qt::Unchecked); } } void BlackListBalooEmailCompletionWidget::slotSelectEmails() { const QList lstSelected = mEmailList->selectedItems(); for (QListWidgetItem *item : lstSelected) { item->setCheckState(Qt::Checked); } } void BlackListBalooEmailCompletionWidget::slotSearchLineEditChanged(const QString &text) { mSearchButton->setEnabled(text.trimmed().count() > 2); hideMoreResultAndChangeLimit(); } void BlackListBalooEmailCompletionWidget::hideMoreResultAndChangeLimit() { mMoreResult->setVisible(false); mLimit = 500; } void BlackListBalooEmailCompletionWidget::slotSearch() { const QString searchEmail = mSearchLineEdit->text().trimmed(); if (searchEmail.length() > 2) { mSearchInResultLineEdit->clear(); KPIM::BlackListBalooEmailSearchJob *job = new KPIM::BlackListBalooEmailSearchJob(this); job->setSearchEmail(searchEmail); job->setLimit(mLimit); connect(job, &BlackListBalooEmailSearchJob::emailsFound, this, &BlackListBalooEmailCompletionWidget::slotEmailFound); job->start(); } } void BlackListBalooEmailCompletionWidget::slotEmailFound(const QStringList &list) { const int numberOfEmails = mEmailList->setEmailFound(list); mMoreResult->setVisible(numberOfEmails == mLimit); mEmailList->scrollToBottom(); if (numberOfEmails == 0) { mNumberOfEmailsFound->setText(i18n("No email found.")); } else { mNumberOfEmailsFound->setText(i18np("1 email found", "%1 emails found", numberOfEmails)); } } void BlackListBalooEmailCompletionWidget::setEmailBlackList(const QStringList &list) { mEmailList->setEmailBlackList(list); } void BlackListBalooEmailCompletionWidget::slotSaveChanges() { //TODO avoid to save a lot. const QHash result = mEmailList->blackListItemChanged(); KSharedConfig::Ptr config = KSharedConfig::openConfig(QStringLiteral("kpimbalooblacklist")); KConfigGroup group(config, "AddressLineEdit"); QStringList blackList = group.readEntry("BalooBackList", QStringList()); KPIM::BlackListBalooEmailUtil util; util.initialBlackList(blackList); util.newBlackList(result); blackList = util.createNewBlackList(); group.writeEntry("BalooBackList", blackList); group.sync(); mEmailList->setEmailBlackList(blackList); slotSearch(); } void BlackListBalooEmailCompletionWidget::slotCheckIfUpdateBlackListIsNeeded() { const QHash result = mEmailList->blackListItemChanged(); if (result.isEmpty()) { slotSearch(); } else { mBlackListWarning->animatedShow(); } } void BlackListBalooEmailCompletionWidget::save() { const QString domain = mExcludeDomainLineEdit->text().remove(QLatin1Char(' ')); const QStringList newExcludeDomain = domain.split(QLatin1Char(','), QString::SkipEmptyParts); bool needToSave = (mOriginalExcludeDomain != newExcludeDomain); KSharedConfig::Ptr config = KSharedConfig::openConfig(QStringLiteral("kpimbalooblacklist")); KConfigGroup group(config, "AddressLineEdit"); const QHash result = mEmailList->blackListItemChanged(); if (!result.isEmpty()) { needToSave = true; QStringList blackList = group.readEntry("BalooBackList", QStringList()); KPIM::BlackListBalooEmailUtil util; util.initialBlackList(blackList); util.newBlackList(result); blackList = util.createNewBlackList(); group.writeEntry("BalooBackList", blackList); } if (needToSave) { group.writeEntry("ExcludeDomain", newExcludeDomain); group.sync(); } config->reparseConfiguration(); } void BlackListBalooEmailCompletionWidget::slotLinkClicked(const QString &link) { if (link == QLatin1String("more_result")) { mLimit += 200; slotSearch(); } } void BlackListBalooEmailCompletionWidget::slotShowAllBlacklistedEmail() { KSharedConfig::Ptr config = KSharedConfig::openConfig(QStringLiteral("kpimbalooblacklist")); KConfigGroup group(config, "AddressLineEdit"); const QStringList balooBlackList = group.readEntry("BalooBackList", QStringList()); slotEmailFound(balooBlackList); } diff --git a/src/libkdepimakonadi/addressline/completionorder/completionorderwidget.cpp b/src/libkdepimakonadi/addressline/completionorder/completionorderwidget.cpp index 1eb04ab..f5af080 100644 --- a/src/libkdepimakonadi/addressline/completionorder/completionorderwidget.cpp +++ b/src/libkdepimakonadi/addressline/completionorder/completionorderwidget.cpp @@ -1,456 +1,456 @@ /* Copyright (c) 2015-2019 Montel Laurent 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 "completionorderwidget.h" #include #include "Libkdepim/LdapClient" #include "ldap/ldapclientsearch.h" #include "ldap/ldapclientsearchconfig.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace KPIM; CompletionOrderEditorAdaptor::CompletionOrderEditorAdaptor(QObject *parent) : QDBusAbstractAdaptor(parent) { setAutoRelaySignals(true); } class LDAPCompletionItem : public CompletionItem { public: LDAPCompletionItem(KLDAP::LdapClient *ldapClient) : mLdapClient(ldapClient) { mWeight = mLdapClient->completionWeight(); } QString label() const override { return i18n("LDAP server %1", mLdapClient->server().host()); } QIcon icon() const override { return QIcon::fromTheme(QStringLiteral("kmail")); } int completionWeight() const override { return mWeight; } void save(CompletionOrderWidget *) override { KConfig *config = KLDAP::LdapClientSearchConfig::config(); KConfigGroup group(config, "LDAP"); group.writeEntry(QStringLiteral("SelectedCompletionWeight%1").arg(mLdapClient->clientNumber()), mWeight); group.sync(); } bool hasEnableSupport() const override { return false; } bool isEnabled() const override { return true; } void setIsEnabled(bool b) override { Q_UNUSED(b); } protected: void setCompletionWeight(int weight) override { mWeight = weight; } private: KLDAP::LdapClient *mLdapClient = nullptr; int mWeight; }; class SimpleCompletionItem : public CompletionItem { public: SimpleCompletionItem(CompletionOrderWidget *editor, const QString &label, const QString &identifier, int weight, bool enableSupport = false) : mLabel(label) , mIdentifier(identifier) , mHasEnableSupport(enableSupport) , mEnabled(true) { KConfigGroup groupCompletionWeights(editor->configFile(), "CompletionWeights"); mWeight = groupCompletionWeights.readEntry(mIdentifier, weight); if (mHasEnableSupport) { KConfigGroup groupEnabled(editor->configFile(), "CompletionEnabled"); mEnabled = groupEnabled.readEntry(mIdentifier, true); } } bool isEnabled() const override { return mEnabled; } bool hasEnableSupport() const override { return mHasEnableSupport; } void setIcon(const QIcon &icon) { mIcon = icon; } QString label() const override { return mLabel; } QIcon icon() const override { return mIcon; } int completionWeight() const override { return mWeight; } void setIsEnabled(bool b) override { mEnabled = b; } void save(CompletionOrderWidget *editor) override { KConfigGroup group(editor->configFile(), "CompletionWeights"); group.writeEntry(mIdentifier, mWeight); if (mHasEnableSupport) { KConfigGroup groupEnabled(editor->configFile(), "CompletionEnabled"); groupEnabled.writeEntry(mIdentifier, isEnabled()); } } protected: void setCompletionWeight(int weight) override { mWeight = weight; } private: QString mLabel; QString mIdentifier; int mWeight; QIcon mIcon; bool mHasEnableSupport; bool mEnabled; }; ///////// class CompletionViewItem : public QTreeWidgetItem { public: CompletionViewItem(QTreeWidget *parent, CompletionItem *item) : QTreeWidgetItem(parent) { setItem(item); } void setItem(CompletionItem *item) { mItem = item; setText(0, mItem->label()); setIcon(0, mItem->icon()); if (mItem->hasEnableSupport()) { setFlags(flags() | Qt::ItemIsUserCheckable); setCheckState(0, mItem->isEnabled() ? Qt::Checked : Qt::Unchecked); } else { setFlags(flags() & ~Qt::ItemIsUserCheckable); } } CompletionItem *item() const { return mItem; } bool operator<(const QTreeWidgetItem &other) const override { const QTreeWidgetItem *otherItem = &other; const CompletionViewItem *completionItem = static_cast(otherItem); // item with weight 100 should be on the top -> reverse sorting return mItem->completionWeight() > completionItem->item()->completionWeight(); } private: CompletionItem *mItem = nullptr; }; CompletionOrderWidget::CompletionOrderWidget(QWidget *parent) : QWidget(parent) , mConfig(QStringLiteral("kpimcompletionorder")) , mCollectionModel(nullptr) , mLdapSearch(nullptr) , mDirty(false) { new CompletionOrderEditorAdaptor(this); QDBusConnection::sessionBus().registerObject(QStringLiteral("/"), this, QDBusConnection::ExportAdaptors); QHBoxLayout *hbox = new QHBoxLayout(this); QWidget *page = new QWidget(this); QHBoxLayout *pageHBoxLayout = new QHBoxLayout(page); - pageHBoxLayout->setMargin(0); + pageHBoxLayout->setContentsMargins(0, 0, 0, 0); hbox->addWidget(page); mListView = new QTreeWidget(page); mListView->setObjectName(QStringLiteral("listview")); pageHBoxLayout->addWidget(mListView); mListView->setColumnCount(1); mListView->setAlternatingRowColors(true); mListView->setIndentation(0); mListView->setAllColumnsShowFocus(true); mListView->setHeaderHidden(true); mListView->setSortingEnabled(true); QWidget *upDownBox = new QWidget(page); QVBoxLayout *upDownBoxVBoxLayout = new QVBoxLayout(upDownBox); - upDownBoxVBoxLayout->setMargin(0); + upDownBoxVBoxLayout->setContentsMargins(0, 0, 0, 0); pageHBoxLayout->addWidget(upDownBox); mUpButton = new QPushButton(upDownBox); upDownBoxVBoxLayout->addWidget(mUpButton); mUpButton->setAutoRepeat(true); mUpButton->setObjectName(QStringLiteral("mUpButton")); mUpButton->setIcon(QIcon::fromTheme(QStringLiteral("go-up"))); mUpButton->setEnabled(false); // b/c no item is selected yet mUpButton->setToolTip(i18n("Move Up")); mUpButton->setFocusPolicy(Qt::StrongFocus); mDownButton = new QPushButton(upDownBox); upDownBoxVBoxLayout->addWidget(mDownButton); mDownButton->setAutoRepeat(true); mDownButton->setObjectName(QStringLiteral("mDownButton")); mDownButton->setIcon(QIcon::fromTheme(QStringLiteral("go-down"))); mDownButton->setEnabled(false); // b/c no item is selected yet mDownButton->setToolTip(i18n("Move Down")); mDownButton->setFocusPolicy(Qt::StrongFocus); QWidget *spacer = new QWidget(upDownBox); upDownBoxVBoxLayout->addWidget(spacer); upDownBoxVBoxLayout->setStretchFactor(spacer, 100); connect(mListView, &QTreeWidget::itemSelectionChanged, this, &CompletionOrderWidget::slotSelectionChanged); connect(mListView, &QTreeWidget::currentItemChanged, this, &CompletionOrderWidget::slotSelectionChanged); connect(mListView, &QTreeWidget::itemChanged, this, &CompletionOrderWidget::slotItemChanged); connect(mUpButton, &QAbstractButton::clicked, this, &CompletionOrderWidget::slotMoveUp); connect(mDownButton, &QAbstractButton::clicked, this, &CompletionOrderWidget::slotMoveDown); } CompletionOrderWidget::~CompletionOrderWidget() { } void CompletionOrderWidget::save() { if (mDirty) { int w = 100; //Clean up order KConfigGroup group(configFile(), "CompletionWeights"); group.deleteGroup(); for (int itemIndex = 0; itemIndex < mListView->topLevelItemCount(); ++itemIndex) { CompletionViewItem *item = static_cast(mListView->topLevelItem(itemIndex)); item->item()->setCompletionWeight(w); item->item()->setIsEnabled(item->checkState(0) == Qt::Checked); item->item()->save(this); --w; } Q_EMIT completionOrderChanged(); } } KConfig *CompletionOrderWidget::configFile() { return &mConfig; } void CompletionOrderWidget::addRecentAddressItem() { //Be default it's the first. SimpleCompletionItem *item = new SimpleCompletionItem(this, i18n("Recent Addresses"), QStringLiteral("Recent Addresses"), 10); item->setIcon(QIcon::fromTheme(QStringLiteral("kmail"))); new CompletionViewItem(mListView, item); } void CompletionOrderWidget::addCompletionItemForCollection(const QModelIndex &index) { const Akonadi::Collection collection = index.data(Akonadi::EntityTreeModel::CollectionRole).value(); if (!collection.isValid()) { return; } SimpleCompletionItem *item = new SimpleCompletionItem(this, index.data().toString(), QString::number(collection.id()), 60, true); item->setIcon(index.data(Qt::DecorationRole).value()); new CompletionViewItem(mListView, item); } void CompletionOrderWidget::loadCompletionItems() { if (mLdapSearch) { // The first step is to gather all the data, creating CompletionItem objects for (KLDAP::LdapClient *client : mLdapSearch->clients()) { new CompletionViewItem(mListView, new LDAPCompletionItem(client)); } } Akonadi::ChangeRecorder *monitor = new Akonadi::ChangeRecorder(this); monitor->fetchCollection(true); monitor->setCollectionMonitored(Akonadi::Collection::root()); monitor->setMimeTypeMonitored(KContacts::Addressee::mimeType(), true); monitor->setMimeTypeMonitored(KContacts::ContactGroup::mimeType(), true); Akonadi::EntityTreeModel *model = new Akonadi::EntityTreeModel(monitor, this); model->setItemPopulationStrategy(Akonadi::EntityTreeModel::NoItemPopulation); KDescendantsProxyModel *descendantsProxy = new KDescendantsProxyModel(this); descendantsProxy->setDisplayAncestorData(true); descendantsProxy->setSourceModel(model); Akonadi::CollectionFilterProxyModel *mimeTypeProxy = new Akonadi::CollectionFilterProxyModel(this); mimeTypeProxy->addMimeTypeFilters(QStringList() << KContacts::Addressee::mimeType() << KContacts::ContactGroup::mimeType()); mimeTypeProxy->setSourceModel(descendantsProxy); mimeTypeProxy->setExcludeVirtualCollections(true); mCollectionModel = mimeTypeProxy; connect(mimeTypeProxy, &QAbstractItemModel::rowsInserted, this, &CompletionOrderWidget::rowsInserted); for (int row = 0; row < mCollectionModel->rowCount(); ++row) { addCompletionItemForCollection(mCollectionModel->index(row, 0)); } addRecentAddressItem(); mListView->sortItems(0, Qt::AscendingOrder); mDirty = false; } void CompletionOrderWidget::setLdapClientSearch(KLDAP::LdapClientSearch *ldapSearch) { mLdapSearch = ldapSearch; } void CompletionOrderWidget::rowsInserted(const QModelIndex &parent, int start, int end) { for (int row = start; row <= end; ++row) { addCompletionItemForCollection(mCollectionModel->index(row, 0, parent)); } mListView->sortItems(0, Qt::AscendingOrder); } void CompletionOrderWidget::slotItemChanged() { mDirty = true; } void CompletionOrderWidget::slotSelectionChanged() { QTreeWidgetItem *item = mListView->currentItem(); mDownButton->setEnabled(item && mListView->itemBelow(item)); mUpButton->setEnabled(item && mListView->itemAbove(item)); } static void swapItems(CompletionViewItem *one, CompletionViewItem *other) { CompletionItem *oneCompletion = one->item(); CompletionItem *otherCompletion = other->item(); int weight = otherCompletion->completionWeight(); otherCompletion->setCompletionWeight(oneCompletion->completionWeight()); oneCompletion->setCompletionWeight(weight); one->setItem(oneCompletion); other->setItem(otherCompletion); } void CompletionOrderWidget::slotMoveUp() { CompletionViewItem *item = static_cast(mListView->currentItem()); if (!item) { return; } CompletionViewItem *above = static_cast(mListView->itemAbove(item)); if (!above) { return; } swapItems(item, above); mListView->sortItems(0, Qt::AscendingOrder); slotSelectionChanged(); mDirty = true; } void CompletionOrderWidget::slotMoveDown() { CompletionViewItem *item = static_cast(mListView->currentItem()); if (!item) { return; } CompletionViewItem *below = static_cast(mListView->itemBelow(item)); if (!below) { return; } swapItems(item, below); mListView->sortItems(0, Qt::AscendingOrder); slotSelectionChanged(); mDirty = true; } diff --git a/src/libkdepimakonadi/widgets/tagwidgets.cpp b/src/libkdepimakonadi/widgets/tagwidgets.cpp index a122a01..e5eb9ce 100644 --- a/src/libkdepimakonadi/widgets/tagwidgets.cpp +++ b/src/libkdepimakonadi/widgets/tagwidgets.cpp @@ -1,160 +1,160 @@ /* Copyright (c) 2014 Christian Mollekopf This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "tagwidgets.h" #include #include #include #include #include #include #include #include #include #include "libkdepimakonadi_debug.h" using namespace KPIM; class KPIM::TagWidgetPrivate { public: TagWidgetPrivate() { } Akonadi::TagWidget *mTagWidget = nullptr; Akonadi::Tag::List mTagList; QStringList mCachedTagNames; }; TagWidget::TagWidget(QWidget *parent) : QWidget(parent) , d(new KPIM::TagWidgetPrivate) { d->mTagWidget = new Akonadi::TagWidget(this); connect(d->mTagWidget, &Akonadi::TagWidget::selectionChanged, this, &TagWidget::onSelectionChanged); QHBoxLayout *l = new QHBoxLayout(this); - l->setMargin(0); + l->setContentsMargins(0, 0, 0, 0); l->setSpacing(0); l->addWidget(d->mTagWidget); } TagWidget::~TagWidget() { delete d; } void TagWidget::onSelectionChanged(const Akonadi::Tag::List &tags) { Q_UNUSED(tags); d->mCachedTagNames.clear(); const Akonadi::Tag::List tagsSelection(d->mTagWidget->selection()); d->mCachedTagNames.reserve(tagsSelection.count()); for (const Akonadi::Tag &tag : tagsSelection) { d->mCachedTagNames << tag.name(); } Q_EMIT selectionChanged(d->mCachedTagNames); Q_EMIT selectionChanged(tags); } void TagWidget::setSelection(const QStringList &tagNames) { d->mTagList.clear(); d->mCachedTagNames = tagNames; for (const QString &name : tagNames) { //TODO fetch by GID instead, we don't really want to create tags here Akonadi::TagCreateJob *tagCreateJob = new Akonadi::TagCreateJob(Akonadi::Tag::genericTag(name), this); tagCreateJob->setMergeIfExisting(true); connect(tagCreateJob, &Akonadi::TagCreateJob::result, this, &TagWidget::onTagCreated); } } void TagWidget::onTagCreated(KJob *job) { if (job->error()) { qCWarning(LIBKDEPIMAKONADI_LOG) << "Failed to create tag " << job->errorString(); return; } Akonadi::TagCreateJob *createJob = static_cast(job); d->mTagList << createJob->tag(); d->mTagWidget->setSelection(d->mTagList); } QStringList TagWidget::selection() const { return d->mCachedTagNames; } class KPIM::TagSelectionDialogPrivate { public: TagSelectionDialogPrivate() { } Akonadi::Tag::List mTagList; }; TagSelectionDialog::TagSelectionDialog(QWidget *parent) : Akonadi::TagSelectionDialog(parent) , d(new KPIM::TagSelectionDialogPrivate) { } TagSelectionDialog::~TagSelectionDialog() { delete d; } void TagSelectionDialog::setSelection(const QStringList &tagNames) { d->mTagList.clear(); for (const QString &name : tagNames) { //TODO fetch by GID instead, we don't really want to create tags here Akonadi::TagCreateJob *tagCreateJob = new Akonadi::TagCreateJob(Akonadi::Tag::genericTag(name), this); tagCreateJob->setMergeIfExisting(true); connect(tagCreateJob, &Akonadi::TagCreateJob::result, this, &TagSelectionDialog::onTagCreated); } } void TagSelectionDialog::onTagCreated(KJob *job) { if (job->error()) { qCWarning(LIBKDEPIMAKONADI_LOG) << "Failed to create tag " << job->errorString(); return; } Akonadi::TagCreateJob *createJob = static_cast(job); d->mTagList << createJob->tag(); Akonadi::TagSelectionDialog::setSelection(d->mTagList); } QStringList TagSelectionDialog::selection() const { QStringList list; const Akonadi::Tag::List lst = Akonadi::TagSelectionDialog::selection(); list.reserve(lst.count()); for (const Akonadi::Tag &tag : lst) { list << tag.name(); } return list; } Akonadi::Tag::List TagSelectionDialog::tagSelection() const { return Akonadi::TagSelectionDialog::selection(); }