diff --git a/src/libkdepim/ldap/ldapsearchdialog.cpp b/src/libkdepim/ldap/ldapsearchdialog.cpp index a61fe4d..57d4bf5 100644 --- a/src/libkdepim/ldap/ldapsearchdialog.cpp +++ b/src/libkdepim/ldap/ldapsearchdialog.cpp @@ -1,944 +1,946 @@ /* * 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 #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 }; ContactListModel(QObject *parent) : QAbstractTableModel(parent) { } void addContact(const KLDAP::LdapAttrMap &contact, const QString &server) { + beginResetModel(); mContactList.append(contact); mServerList.append(server); - reset(); + 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(); - reset(); + 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"); break; case 1: return i18nc("@title:column Column containing email addresses", "Email"); break; case 2: return i18n("Home Number"); break; case 3: return i18n("Work Number"); break; case 4: return i18n("Mobile Number"); break; case 5: return i18n("Fax Number"); break; case 6: return i18n("Company"); break; case 7: return i18n("Organization"); break; case 8: return i18n("Street"); break; case 9: return i18nc("@title:column Column containing the residential state of the address", "State"); break; case 10: return i18n("Country"); break; case 11: return i18n("Zip Code"); break; case 12: return i18n("Postal Address"); break; case 13: return i18n("City"); break; case 14: return i18n("Department"); break; case 15: return i18n("Description"); break; case 16: return i18n("User ID"); break; case 17: return i18nc("@title:column Column containing title of the person", "Title"); break; default: return QVariant(); break; } 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(", ")); break; case 1: return join(map.value(QStringLiteral("mail")), QStringLiteral(", ")); break; case 2: return join(map.value(QStringLiteral("homePhone")), QStringLiteral(", ")); break; case 3: return join(map.value(QStringLiteral("telephoneNumber")), QStringLiteral(", ")); break; case 4: return join(map.value(QStringLiteral("mobile")), QStringLiteral(", ")); break; case 5: return join(map.value(QStringLiteral("facsimileTelephoneNumber")), QStringLiteral(", ")); break; case 6: return join(map.value(QStringLiteral("Company")), QStringLiteral(", ")); break; case 7: return join(map.value(QStringLiteral("o")), QStringLiteral(", ")); break; case 8: return join(map.value(QStringLiteral("street")), QStringLiteral(", ")); break; case 9: return join(map.value(QStringLiteral("st")), QStringLiteral(", ")); break; case 10: return join(map.value(QStringLiteral("co")), QStringLiteral(", ")); break; case 11: return join(map.value(QStringLiteral("postalCode")), QStringLiteral(", ")); break; case 12: return join(map.value(QStringLiteral("postalAddress")), QStringLiteral(", ")); break; case 13: return join(map.value(QStringLiteral("l")), QStringLiteral(", ")); break; case 14: return join(map.value(QStringLiteral("department")), QStringLiteral(", ")); break; case 15: return join(map.value(QStringLiteral("description")), QStringLiteral(", ")); break; case 16: return join(map.value(QStringLiteral("uid")), QStringLiteral(", ")); break; case 17: return join(map.value(QStringLiteral("title")), QStringLiteral(", ")); break; default: return QVariant(); break; } return QVariant(); } private: QVector mContactList; QStringList mServerList; }; class Q_DECL_HIDDEN LdapSearchDialog::Private { public: Private(LdapSearchDialog *qq) : q(qq) , mNumHosts(0) , mIsConfigured(false) , mFilterCombo(nullptr) , mSearchType(nullptr) , mSearchEdit(nullptr) , mRecursiveCheckbox(nullptr) , mResultView(nullptr) , mSearchButton(nullptr) , mModel(nullptr) , progressIndication(nullptr) , sortproxy(nullptr) , searchLine(nullptr) , user1Button(nullptr) { } 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")); QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Cancel); QVBoxLayout *mainLayout = new QVBoxLayout(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); 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, SIGNAL(clicked(QModelIndex)), SLOT(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); 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, SIGNAL(clicked()), this, SLOT(slotSelectAll())); button = buttons->addButton(i18n("Unselect All"), QDialogButtonBox::ActionRole); connect(button, SIGNAL(clicked()), this, SLOT(slotUnselectAll())); buttonLayout->addWidget(buttons); d->user1Button->setText(i18n("Add Selected")); user2Button->setText(i18n("Configure LDAP Servers...")); connect(d->mRecursiveCheckbox, SIGNAL(toggled(bool)), this, SLOT(slotSetScope(bool))); 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, SIGNAL(error(QString)), q, SLOT(slotError(QString))); 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/progresswidget/progressmanager.h b/src/libkdepim/progresswidget/progressmanager.h index 2fb9ab9..1b36965 100644 --- a/src/libkdepim/progresswidget/progressmanager.h +++ b/src/libkdepim/progresswidget/progressmanager.h @@ -1,425 +1,425 @@ /* progressmanager.h This file is part of libkdepim. Copyright (C) 2004 Till Adam This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KDEPIM_PROGRESSMANAGER_H #define KDEPIM_PROGRESSMANAGER_H #include "kdepim_export.h" #include #include #include #include -#include +#include namespace KPIM { class ProgressItem; class ProgressManager; typedef QMap ProgressItemMap; class KDEPIM_EXPORT ProgressItem : public QObject { Q_OBJECT friend class ProgressManager; public: enum CryptoStatus { Encrypted, Unencrypted, Unknown }; /** * @return The id string which uniquely identifies the operation * represented by this item. */ const QString &id() const; /** * @return The parent item of this one, if there is one. */ ProgressItem *parent() const; /** * @return The user visible string to be used to represent this item. */ const QString &label() const; /** * @param v Set the user visible string identifying this item. */ void setLabel(const QString &v); /** * @return The string to be used for showing this item's current status. */ const QString &status() const; /** * Set the string to be used for showing this item's current status. * @param v The status string. */ void setStatus(const QString &v); /** * @return Whether this item can be canceled. */ bool canBeCanceled() const; /** * @param b Set if can be canceled */ void setCanBeCanceled(bool b); /** * @return Whether this item uses secure communication * (Account uses ssl, for example.). */ CryptoStatus cryptoStatus() const; /** * Set whether this item uses crypted communication, so listeners * can display a nice crypto icon. * @param v The value. */ void setCryptoStatus(ProgressItem::CryptoStatus v); /** * @return whether this item uses a busy indicator instead of real progress display */ bool usesBusyIndicator() const; /** * Sets whether this item uses a busy indicator instead of real progress for its progress bar. * If it uses a busy indicator, you are still responsible for calling setProgress() from time to * time to update the busy indicator. */ void setUsesBusyIndicator(bool useBusyIndicator); /** * @return The current progress value of this item in percent. */ unsigned int progress() const; /** * Set the progress (percentage of completion) value of this item. * @param v The percentage value. */ void setProgress(unsigned int v); /** * Tell the item it has finished. This will Q_EMIT progressItemCompleted() * result in the destruction of the item after all slots connected to this * signal have executed. This is the only way to get rid of an item and * needs to be called even if the item is canceled. Don't use the item * after this has been called on it. */ void setComplete(); /** * Reset the progress value of this item to 0 and the status string to * the empty string. */ void reset(); void cancel(); // Often needed values for calculating progress. void setTotalItems(unsigned int v); unsigned int totalItems() const; void setCompletedItems(unsigned int v); void incCompletedItems(unsigned int v = 1); unsigned int completedItems() const; /** * Recalculate progress according to total/completed items and update. */ void updateProgress(); void addChild(ProgressItem *kiddo); void removeChild(ProgressItem *kiddo); bool canceled() const; unsigned int typeProgressItem() const; void setTypeProgressItem(unsigned int); Q_SIGNALS: /** * Emitted when a new ProgressItem is added. * @param The ProgressItem that was added. */ void progressItemAdded(KPIM::ProgressItem *); /** * Emitted when the progress value of an item changes. * @param The item which got a new value. * @param The value, for convenience. */ void progressItemProgress(KPIM::ProgressItem *, unsigned int); /** * Emitted when a progress item was completed. The item will be * deleted afterwards, so slots connected to this are the last * chance to work with this item. * @param The completed item. */ void progressItemCompleted(KPIM::ProgressItem *); /** * Emitted when an item was canceled. It will _not_ go away immediately, * only when the owner sets it complete, which will usually happen. Can be * used to visually indicate the canceled status of an item. Should be used * by the owner of the item to make sure it is set completed even if it is * canceled. There is a ProgressManager::slotStandardCancelHandler which * simply sets the item completed and can be used if no other work needs to * be done on cancel. * @param The canceled item; */ void progressItemCanceled(KPIM::ProgressItem *); /** * Emitted when the status message of an item changed. Should be used by * progress dialogs to update the status message for an item. * @param The updated item. * @param The new message. */ void progressItemStatus(KPIM::ProgressItem *, const QString &); /** * Emitted when the label of an item changed. Should be used by * progress dialogs to update the label of an item. * @param The updated item. * @param The new label. */ void progressItemLabel(KPIM::ProgressItem *, const QString &); /** * Emitted when the crypto status of an item changed. Should be used by * progress dialogs to update the crypto indicator of an item. * @param The updated item. * @param The new state. */ void progressItemCryptoStatus(KPIM::ProgressItem *, KPIM::ProgressItem::CryptoStatus); /** * Emitted when the busy indicator state of an item changes. Should be used * by progress dialogs so that they can adjust the display of the progress bar * to the new mode. * @param item The updated item * @param value True if the item uses a busy indicator now, false otherwise */ void progressItemUsesBusyIndicator(KPIM::ProgressItem *item, bool value); protected: /* Only to be used by our good friend the ProgressManager */ ProgressItem(ProgressItem *parent, const QString &id, const QString &label, const QString &status, bool isCancellable, CryptoStatus cryptoStatus); virtual ~ProgressItem(); private: QString mId; QString mLabel; QString mStatus; - QWeakPointer mParent; + QPointer mParent; bool mCanBeCanceled; unsigned int mProgress; ProgressItemMap mChildren; unsigned int mTotal; unsigned int mCompleted; CryptoStatus mCryptoStatus; unsigned int mType; bool mWaitingForKids; bool mCanceled; bool mUsesBusyIndicator; bool mCompletedCalled; }; struct ProgressManagerPrivate; /** * The ProgressManager singleton keeps track of all ongoing transactions * and notifies observers (progress dialogs) when their progress percent value * changes, when they are completed (by their owner), and when they are canceled. * Each ProgressItem emits those signals individually and the singleton * broadcasts them. Use the ::createProgressItem() statics to acquire an item * and then call ->setProgress( int percent ) on it every time you want to * update the item and ->setComplete() when the operation is done. This will * delete the item. Connect to the item's progressItemCanceled() signal to be * notified when the user cancels the transaction using one of the observing * progress dialogs or by calling item->cancel() in some other way. The owner * is responsible for calling setComplete() on the item, even if it is canceled. * Use the standardCancelHandler() slot if that is all you want to do on cancel. * * Note that if you request an item with a certain id and there is already * one with that id, there will not be a new one created but the existing * one will be returned. This is convenient for accessing items that are * needed regularly without the to store a pointer to them or to add child * items to parents by id. */ class KDEPIM_EXPORT ProgressManager : public QObject { Q_OBJECT friend struct ProgressManagerPrivate; public: virtual ~ProgressManager(); /** * @return The singleton instance of this class. */ static ProgressManager *instance(); /** * Use this to acquire a unique id number which can be used to discern * an operation from all others going on at the same time. Use that * number as the id string for your progressItem to ensure it is unique. * @return */ static QString getUniqueID(); /** * Creates a ProgressItem with a unique id and the given label. * This is the simplest way to acquire a progress item. It will not * have a parent and will be set to be cancellable and not using crypto. */ static ProgressItem *createProgressItem(unsigned int progressType, const QString &label); /** * Creates a ProgressItem with a unique id and the given label. * This is the simplest way to acquire a progress item. It will not * have a parent and will be set to be cancellable and not using crypto. */ static ProgressItem *createProgressItem(const QString &label); /** * Creates a new progressItem with the given parent, id, label and initial * status. * * @param parent Specify an already existing item as the parent of this one. * @param id Used to identify this operation for cancel and progress info. * @param label The text to be displayed by progress handlers * @param status Additional text to be displayed for the item. * @param canBeCanceled can the user cancel this operation? * @param usesCrypto does the operation use secure transports (SSL) * Cancelling the parent will cancel the children as well (if they can be * canceled) and ongoing children prevent parents from finishing. * @return The ProgressItem representing the operation. */ static ProgressItem *createProgressItem(ProgressItem *parent, const QString &id, const QString &label, const QString &status = QString(), bool canBeCanceled = true, KPIM::ProgressItem::CryptoStatus cryptoStatus = KPIM::ProgressItem::Unencrypted); /** * Use this version if you have the id string of the parent and want to * add a subjob to it. */ static ProgressItem *createProgressItem(const QString &parent, const QString &id, const QString &label, const QString &status = QString(), bool canBeCanceled = true, KPIM::ProgressItem::CryptoStatus cryptoStatus = KPIM::ProgressItem::Unencrypted); /** * Version without a parent. */ static ProgressItem *createProgressItem(const QString &id, const QString &label, const QString &status = QString(), bool canBeCanceled = true, KPIM::ProgressItem::CryptoStatus cryptoStatus = KPIM::ProgressItem::Unencrypted); /** * @return true when there are no more progress items. */ bool isEmpty() const; /** * @return the only top level progressitem when there's only one. * Returns 0 if there is no item, or more than one top level item. * Since this is used to calculate the overall progress, it will also return * 0 if there is an item which uses a busy indicator, since that will invalidate * the overall progress. */ ProgressItem *singleItem() const; /** * Ask all listeners to show the progress dialog, because there is * something that wants to be shown. */ static void emitShowProgressDialog(); ProgressItem *progressItem(const QString &id) const; Q_SIGNALS: /** @see ProgressItem::progressItemAdded() */ void progressItemAdded(KPIM::ProgressItem *); /** @see ProgressItem::progressItemProgress() */ void progressItemProgress(KPIM::ProgressItem *, unsigned int); /** @see ProgressItem::progressItemCompleted() */ void progressItemCompleted(KPIM::ProgressItem *); /** @see ProgressItem::progressItemCanceled() */ void progressItemCanceled(KPIM::ProgressItem *); /** @see ProgressItem::progressItemStatus() */ void progressItemStatus(KPIM::ProgressItem *, const QString &); /** @see ProgressItem::progressItemLabel() */ void progressItemLabel(KPIM::ProgressItem *, const QString &); /** @see ProgressItem::progressItemCryptoStatus() */ void progressItemCryptoStatus(KPIM::ProgressItem *, KPIM::ProgressItem::CryptoStatus); /** @see ProgressItem::progressItemUsesBusyIndicator */ void progressItemUsesBusyIndicator(KPIM::ProgressItem *, bool); /** * Emitted when an operation requests the listeners to be shown. * Use emitShowProgressDialog() to trigger it. */ void showProgressDialog(); public Q_SLOTS: /** * Calls setCompleted() on the item, to make sure it goes away. * Provided for convenience. * @param item the canceled item. */ void slotStandardCancelHandler(KPIM::ProgressItem *item); /** * Aborts all running jobs. Bound to "Esc" */ void slotAbortAll(); private Q_SLOTS: void slotTransactionCompleted(KPIM::ProgressItem *item); private: ProgressManager(); // prevent unsolicited copies ProgressManager(const ProgressManager &); ProgressItem *createProgressItemImpl(ProgressItem *parent, const QString &id, const QString &label, const QString &status, bool cancellable, ProgressItem::CryptoStatus cryptoStatus, unsigned int progressType = 0); ProgressItem *createProgressItemImpl(const QString &parent, const QString &id, const QString &label, const QString &status, bool cancellable, ProgressItem::CryptoStatus cryptoStatus, unsigned int progressType = 0); void emitShowProgressDialogImpl(); QHash< QString, ProgressItem * > mTransactions; static unsigned int uID; }; } #endif // __KPIM_PROGRESSMANAGER_H__ diff --git a/src/libkdepimakonadi/addressline/addresslineedit/addresseelineedit_p.cpp b/src/libkdepimakonadi/addressline/addresslineedit/addresseelineedit_p.cpp index 9b827f5..4ecfde1 100644 --- a/src/libkdepimakonadi/addressline/addresslineedit/addresseelineedit_p.cpp +++ b/src/libkdepimakonadi/addressline/addresslineedit/addresseelineedit_p.cpp @@ -1,985 +1,985 @@ /* Copyright (c) 2015-2017 Montel Laurent This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2, as published by the Free Software Foundation. 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 "addressline/completionorder/completionordereditor.h" #include "addresseelineeditmanager.h" #include "addresseelineedit_p.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 #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() { 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, SIGNAL(returnPressed(QString)), this, SLOT(slotReturnPressed(QString))); KCompletionBox *box = q->completionBox(); connect(box, SIGNAL(activated(QString)), this, SLOT(slotPopupCompletion(QString))); connect(box, &KCompletionBox::userCancelled, this, &AddresseeLineEditPrivate::slotUserCancelled); connect(AddresseeLineEditManager::self()->ldapTimer(), &QTimer::timeout, this, &AddresseeLineEditPrivate::slotStartLDAPLookup); connect(AddresseeLineEditManager::self()->ldapSearch(), SIGNAL(searchData(KLDAP::LdapResult::List)), SLOT(slotLDAPSearchData(KLDAP::LdapResult::List))); 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() { 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); } 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()); } 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 QList 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 QWeakPointer &job : qAsConst(AddresseeLineEditManager::self()->akonadiJobsInFlight)) { + 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: default: // fall through 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(QStringLiteral("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/addresslineedit/addresseelineeditmanager.h b/src/libkdepimakonadi/addressline/addresslineedit/addresseelineeditmanager.h index 2a77b3a..2b7a2cd 100644 --- a/src/libkdepimakonadi/addressline/addresslineedit/addresseelineeditmanager.h +++ b/src/libkdepimakonadi/addressline/addresslineedit/addresseelineeditmanager.h @@ -1,136 +1,136 @@ /* Copyright (c) 2015-2017 Montel Laurent This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2, as published by the Free Software Foundation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef ADDRESSEELINEEDITSTATIC_H #define ADDRESSEELINEEDITSTATIC_H #include #include #include #include #include -#include +#include #include #include class QTimer; class QNetworkConfigurationManager; namespace KPIM { class KMailCompletion; class AddresseeLineEdit; class AddresseeLineEditAkonadi; class AddresseeLineEditLdap; class AddresseeLineEditBaloo; class AddresseeLineEditManager { public: typedef QMap< QString, QPair > CompletionItemsMap; AddresseeLineEditManager(); ~AddresseeLineEditManager(); static AddresseeLineEditManager *self(); void updateCollectionWeights(); void updateLDAPWeights(); void updateCompletionOrder(); int addCompletionSource(const QString &source, int weight); void removeCompletionSource(const QString &source); CompletionItemsMap completionItemMap; QStringList completionSources; // The weights associated with the completion sources in s_static->completionSources. // Both are maintained by addCompletionSource(), don't attempt to modifiy those yourself. QMap completionSourceWeights; // holds the cached mapping from akonadi collection id to the completion source index struct collectionInfo { collectionInfo() : index(-1) , enabled(true) { } collectionInfo(int idx, bool _enabled) : index(idx) , enabled(_enabled) { } int index; bool enabled; }; QMap akonadiCollectionToCompletionSourceMap; // a list of akonadi items (contacts) that have not had their collection fetched yet Akonadi::Item::List akonadiPendingItems; - QVector > akonadiJobsInFlight; + QVector > akonadiJobsInFlight; //Ldap void initializeLdap(); QString ldapText() const; void setLdapText(const QString &ldapText); KLDAP::LdapClientSearch *ldapSearch() const; QTimer *ldapTimer() const; bool isLdapClientToCompletionSourceMapContains(int value) const; int ldapClientToCompletionSourceValue(int value) const; void stopLDAPLookup(); void startLoadingLDAPEntries(); void restartLdap(const QString &searchString, AddresseeLineEdit *addressLine); //Baloo int balooCompletionSource() const; void setBalooCompletionSource(int value); //Akonadi Akonadi::Session *akonadiSession() const; KMailCompletion *completion() const; AddresseeLineEdit *addressLineEdit() const; void setAddressLineEdit(AddresseeLineEdit *addressLineEdit); bool isOnline() const; void loadBalooBlackList(); QStringList cleanupEmailList(const QStringList &inputList); QStringList balooBlackList() const; bool showOU() const; void setShowOU(bool showOU); bool autoGroupExpand() const; void setAutoGroupExpand(bool checked); QColor alternateColor() const; private: mutable QColor mAlternateColor; bool mShowOU = false; bool mAutoGroupExpand = false; KMailCompletion *mCompletion = nullptr; AddresseeLineEditAkonadi *mAddresseeLineEditAkonadi = nullptr; AddresseeLineEditLdap *mAddressessLineEditLdap = nullptr; AddresseeLineEditBaloo *mAddressessLineEditBaloo = nullptr; QNetworkConfigurationManager *mNetworkConfigMgr = nullptr; }; } #endif // ADDRESSEELINEEDITSTATIC_H diff --git a/src/libkdepimakonadi/progresswidget/agentprogressmonitor.cpp b/src/libkdepimakonadi/progresswidget/agentprogressmonitor.cpp index f69349d..d7c2108 100644 --- a/src/libkdepimakonadi/progresswidget/agentprogressmonitor.cpp +++ b/src/libkdepimakonadi/progresswidget/agentprogressmonitor.cpp @@ -1,126 +1,126 @@ /* Copyright (c) 2009 Constantin Berzan 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 "agentprogressmonitor.h" #include -#include +#include using namespace Akonadi; using namespace KPIM; AgentProgressMonitor::AgentProgressMonitor(const AgentInstance &agent, ProgressItem *item) : QObject(item) , mAgent(agent) , mItem(item) { connect(AgentManager::self(), &AgentManager::instanceProgressChanged, this, &AgentProgressMonitor::instanceProgressChanged); connect(AgentManager::self(), &AgentManager::instanceStatusChanged, this, &AgentProgressMonitor::instanceStatusChanged); connect(Akonadi::AgentManager::self(), &AgentManager::instanceRemoved, this, &AgentProgressMonitor::instanceRemoved); connect(Akonadi::AgentManager::self(), &AgentManager::instanceNameChanged, this, &AgentProgressMonitor::instanceNameChanged); // TODO connect to instanceError, instanceWarning, instanceOnline ? // and do what? connect(item, &ProgressItem::progressItemCanceled, this, &AgentProgressMonitor::abort); // TODO handle offline case } AgentProgressMonitor::~AgentProgressMonitor() { } void AgentProgressMonitor::abort() { mAgent.abortCurrentTask(); } void AgentProgressMonitor::instanceRemoved(const Akonadi::AgentInstance &instance) { Q_UNUSED(instance); if (!mItem.data()) { return; } mItem.data()->disconnect(this); // avoid abort call mItem.data()->cancel(); if (mItem.data()) { mItem.data()->setComplete(); } } void AgentProgressMonitor::instanceProgressChanged(const AgentInstance &instance) { if (!mItem.data()) { return; } if (mAgent == instance) { // compares identifiers mAgent = instance; // keeps copy of current status const int progress = mAgent.progress(); if (progress >= 0) { mItem.data()->setProgress(progress); } } } void AgentProgressMonitor::instanceStatusChanged(const AgentInstance &instance) { if (!mItem.data()) { return; } if (mAgent == instance) { // compares identifiers mAgent = instance; // keeps copy of current status mItem.data()->setStatus(mAgent.statusMessage()); switch (mAgent.status()) { case AgentInstance::Idle: if (mItem.data()) { mItem.data()->setComplete(); } break; case AgentInstance::Running: break; case AgentInstance::Broken: mItem.data()->disconnect(this); // avoid abort call mItem.data()->cancel(); if (mItem.data()) { mItem.data()->setComplete(); } break; default: Q_ASSERT(false); } } } void AgentProgressMonitor::instanceNameChanged(const Akonadi::AgentInstance &instance) { if (!mItem.data()) { return; } mItem.data()->setLabel(instance.name()); } diff --git a/src/libkdepimakonadi/progresswidget/agentprogressmonitor.h b/src/libkdepimakonadi/progresswidget/agentprogressmonitor.h index 7791d6f..692a676 100644 --- a/src/libkdepimakonadi/progresswidget/agentprogressmonitor.h +++ b/src/libkdepimakonadi/progresswidget/agentprogressmonitor.h @@ -1,54 +1,54 @@ /* Copyright (c) 2009 Constantin Berzan This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KDEPIM_AGENTPROGRESSMONITOR_H #define KDEPIM_AGENTPROGRESSMONITOR_H #include // ProgressItem #include namespace KPIM { /** * @internal * * This class automatically updates a ProgressItem based on * Akonadi::AgentManager's signals, and places the abort() call if the * ProgressItem has been cancelled. */ class AgentProgressMonitor : public QObject { Q_OBJECT public: // used by our friend ProgressManager AgentProgressMonitor(const Akonadi::AgentInstance &mAgent, ProgressItem *mItem); ~AgentProgressMonitor(); private: void abort(); void instanceProgressChanged(const Akonadi::AgentInstance &); void instanceStatusChanged(const Akonadi::AgentInstance &); void instanceRemoved(const Akonadi::AgentInstance &); void instanceNameChanged(const Akonadi::AgentInstance &); Akonadi::AgentInstance mAgent; - QWeakPointer const mItem; + QPointer const mItem; }; } #endif