File indexing completed on 2022-11-23 11:29:38

0001 /*
0002   Copyright © 2017 James D. Smith <smithjd15@gmail.com>
0003 
0004   This program is free software; you can redistribute it and/or
0005   modify it under the terms of the GNU General Public License as
0006   published by the Free Software Foundation; either version 2 of
0007   the License or (at your option) version 3 or any later version
0008   accepted by the membership of KDE e.V. (or its successor approved
0009   by the membership of KDE e.V.), which shall act as a proxy
0010   defined in Section 14 of version 3 of the license.
0011 
0012   This program is distributed in the hope that it will be useful,
0013   but WITHOUT ANY WARRANTY; without even the implied warranty of
0014   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0015   GNU General Public License for more details.
0016 
0017   You should have received a copy of the GNU General Public License
0018   along with this program.  If not, see <http://www.gnu.org/licenses/>.
0019 */
0020 
0021 #include "advanced-presence-dialog.h"
0022 #include "global-presence-chooser.h"
0023 
0024 #include <QHBoxLayout>
0025 #include <QVBoxLayout>
0026 #include <QModelIndex>
0027 #include <QComboBox>
0028 #include <QDialogButtonBox>
0029 
0030 #include <KLocalizedString>
0031 
0032 #include <QLabel>
0033 #include <QCheckBox>
0034 #include <QLineEdit>
0035 #include <QKeyEvent>
0036 
0037 #include <KTp/presence.h>
0038 #include <KTp/global-presence.h>
0039 #include <KTp/Models/presence-model.h>
0040 #include <KTp/Models/accounts-list-model.h>
0041 
0042 //A sneaky class that adds an extra entries to the end of the presence model.
0043 class AccountPresenceModelExtended : public QAbstractListModel
0044 {
0045     Q_OBJECT
0046 public:
0047     AccountPresenceModelExtended(KTp::PresenceModel *presenceModel, QObject *parent);
0048     int rowCount(const QModelIndex &parent = QModelIndex()) const;
0049     QVariant data(const QModelIndex &index, int role) const;
0050     /** Adds a presence to the model which is to be used when the presence has been set externally and we need to show it, but not save it to the config*/
0051     QModelIndex addTemporaryPresence(const KTp::Presence &presence);
0052     void removeTemporaryPresence();
0053 private slots:
0054     void sourceRowsInserted(const QModelIndex &index, int start, int end);
0055     void sourceRowsRemoved(const QModelIndex &index, int start, int end);
0056 private:
0057     KTp::Presence m_temporaryPresence;
0058     KTp::PresenceModel *m_model;
0059 };
0060 
0061 AccountPresenceModelExtended::AccountPresenceModelExtended(KTp::PresenceModel *presenceModel, QObject *parent) :
0062     QAbstractListModel(parent),
0063     m_model(presenceModel)
0064 {
0065     connect(m_model, &QAbstractItemModel::rowsInserted, this, &AccountPresenceModelExtended::sourceRowsInserted);
0066     connect(m_model, &QAbstractItemModel::rowsRemoved, this, &AccountPresenceModelExtended::sourceRowsRemoved);
0067 }
0068 
0069 //return number of rows + the extra items added to end of list
0070 int AccountPresenceModelExtended::rowCount(const QModelIndex &parent) const
0071 {
0072     if (parent.isValid()) {
0073         return 0;
0074     }
0075     int rowCount = m_model->rowCount(parent);
0076     if (m_temporaryPresence.isValid()) {
0077         rowCount++;
0078     }
0079     return rowCount;
0080 }
0081 
0082 QVariant AccountPresenceModelExtended::data(const QModelIndex &index, int role) const
0083 {
0084     if (m_temporaryPresence.isValid() && index.row() == rowCount() - 1) {
0085         switch (role) {
0086         case Qt::DisplayRole:
0087             return m_temporaryPresence.statusMessage();
0088         case Qt::DecorationRole:
0089             return m_temporaryPresence.icon();
0090         case KTp::PresenceModel::PresenceRole:
0091             return QVariant::fromValue<KTp::Presence>(m_temporaryPresence);
0092         }
0093     } else {
0094         return m_model->data(m_model->index(index.row()), role);
0095     }
0096     return QVariant();
0097 }
0098 
0099 void AccountPresenceModelExtended::sourceRowsInserted(const QModelIndex &index, int start, int end)
0100 {
0101     beginInsertRows(createIndex(index.row(), 0), start, end);
0102     endInsertRows();
0103 }
0104 
0105 void AccountPresenceModelExtended::sourceRowsRemoved(const QModelIndex &index, int start, int end)
0106 {
0107     beginRemoveRows(createIndex(index.row(), 0), start, end);
0108     endRemoveRows();
0109 }
0110 
0111 void AccountPresenceModelExtended::removeTemporaryPresence()
0112 {
0113     if (!m_temporaryPresence.isValid()) {
0114         return; //if not already set, do nothing.
0115     }
0116 
0117     int row = m_model->rowCount();
0118     beginRemoveRows(QModelIndex(), row, row);
0119     m_temporaryPresence = KTp::Presence();
0120     endRemoveRows();
0121 }
0122 
0123 QModelIndex AccountPresenceModelExtended::addTemporaryPresence(const KTp::Presence &presence)
0124 {
0125     int row = m_model->rowCount();
0126 
0127     //if the temp presence already exists, don't remove and readd it
0128     //but simply replace it
0129     if (m_temporaryPresence.isValid()) {
0130         m_temporaryPresence = presence;
0131         emit dataChanged(this->createIndex(row, 0), this->createIndex(row, 0));
0132     } else {
0133         beginInsertRows(QModelIndex(), row, row);
0134         m_temporaryPresence = presence;
0135         endInsertRows();
0136     }
0137 
0138     return this->createIndex(row, 0);
0139 }
0140 
0141 AdvancedPresenceDialog::AdvancedPresenceDialog(KTp::PresenceModel *presenceModel, KTp::GlobalPresence *globalPresence, QWidget *parent)
0142     : QDialog(parent),
0143       m_presenceModel(presenceModel),
0144       m_accountsModel(new KTp::AccountsListModel()),
0145       m_globalPresence(globalPresence)
0146 {
0147     setupDialog();
0148 }
0149 
0150 void AdvancedPresenceDialog::setupDialog()
0151 {
0152     setWindowTitle(i18n("Advanced Presence Setting"));
0153 
0154     QVBoxLayout *vLayout = new QVBoxLayout();
0155     QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Close, this);
0156     connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
0157 
0158     if (m_globalPresence->enabledAccounts()->accounts().isEmpty()) {
0159         QLabel *emptyBox = new QLabel();
0160         emptyBox->setText(i18n("It appears that you do not have any accounts configured"));
0161         QVBoxLayout *emptyAccountLayout = new QVBoxLayout();
0162         emptyAccountLayout->addWidget(emptyBox);
0163 
0164         vLayout->addWidget(emptyBox);
0165     } else {
0166         m_accountsModel->setAccountSet(m_globalPresence->enabledAccounts());
0167         for (int i = 0; i < m_accountsModel->rowCount(); i++) {
0168             const QModelIndex &index = m_accountsModel->index(i, 0);
0169 
0170             if (!m_accountsModel->data(index, KTp::AccountsListModel::AccountRole).value<Tp::AccountPtr>()->isValid())
0171                 continue;
0172 
0173             QVBoxLayout *vAccountLayout = new QVBoxLayout();
0174             QHBoxLayout *hAccountLayout = new QHBoxLayout();
0175             QHBoxLayout *lHAccountLayout = new QHBoxLayout();
0176 
0177             const QIcon &accountIcon = m_accountsModel->data(index, Qt::DecorationRole).value<QIcon>();
0178             QLabel *icoLabel = new QLabel();
0179             icoLabel->setPixmap(accountIcon.pixmap(accountIcon.actualSize(QSize(16, 16))));
0180             QLabel *label = new QLabel(m_accountsModel->data(index, Qt::DisplayRole).value<QString>());
0181 
0182             auto setComboLineEdit = [=] () {
0183                 if (m_comboBoxes[i]->currentData(KTp::PresenceModel::PresenceRole).value<KTp::Presence>().statusMessage().isEmpty()) {
0184                     m_comboBoxes[i]->lineEdit()->setPlaceholderText(i18n("Set a status message ..."));
0185                     m_comboBoxes[i]->lineEdit()->setReadOnly(false);
0186                 } else {
0187                     m_comboBoxes[i]->lineEdit()->setPlaceholderText(m_comboBoxes[i]->currentData(Qt::DisplayRole).value<QString>());
0188                     m_comboBoxes[i]->lineEdit()->setReadOnly(true);
0189                 }
0190 
0191                 m_comboBoxes[i]->lineEdit()->setToolTip(m_comboBoxes[i]->currentData(Qt::DisplayRole).value<QString>());
0192             };
0193 
0194             QCheckBox *checkBox = new QCheckBox();
0195             checkBox->setChecked(true);
0196             connect(checkBox, &QCheckBox::clicked, [=] (bool checked) {
0197                 m_comboBoxes[i]->setEnabled(checked);
0198                 KTp::Presence presence;
0199                 if (checked) {
0200                     setComboLineEdit();
0201                     presence = m_comboBoxes[i]->currentData(KTp::PresenceModel::PresenceRole).value<KTp::Presence>();
0202                 } else {
0203                     m_comboBoxes[i]->lineEdit()->setPlaceholderText(m_comboBoxes[i]->currentData(Qt::DisplayRole).value<QString>());
0204                     presence.setStatus(Tp::ConnectionPresenceTypeUnset, QLatin1String("unset"), QString());
0205                 }
0206 
0207                 m_accountsModel->setData(index, QVariant::fromValue<KTp::Presence>(presence), KTp::AccountsListModel::StatusHandlerPresenceRole);
0208             });
0209 
0210             KTp::Presence accountPresence = m_accountsModel->data(index, KTp::AccountsListModel::StatusHandlerPresenceRole).value<KTp::Presence>();
0211             if (accountPresence.type() == Tp::ConnectionPresenceTypeUnset) {
0212                 if (m_globalPresence->globalPresence().type() == Tp::ConnectionPresenceTypeUnset) {
0213                     accountPresence = m_accountsModel->data(index, KTp::AccountsListModel::RequestedPresenceRole).value<KTp::Presence>();
0214                 } else {
0215                     accountPresence = m_globalPresence->globalPresence();
0216                     checkBox->setChecked(false);
0217                 }
0218             }
0219 
0220             m_comboBoxes.insert(i, new QComboBox());
0221             m_extendedModels.insert(i, new AccountPresenceModelExtended(m_presenceModel, this));
0222             m_comboBoxes[i]->setModel(m_extendedModels[i]);
0223             m_comboBoxes[i]->setEnabled(checkBox->isChecked());
0224             m_comboBoxes[i]->setEditable(true);
0225             m_comboBoxes[i]->setSizeAdjustPolicy(QComboBox::AdjustToMinimumContentsLengthWithIcon);
0226             m_comboBoxes[i]->setWhatsThis(KDED_STATUS_MESSAGE_PARSER_WHATSTHIS);
0227             m_comboBoxes[i]->installEventFilter(this);
0228 
0229             int width = m_comboBoxes[i]->minimumSizeHint().width();
0230             m_comboBoxes[i]->setMinimumContentsLength(width);
0231 
0232             connect(m_comboBoxes[i], static_cast<void (QComboBox::*)(int)>(&QComboBox::activated), [=] {
0233                 setComboLineEdit();
0234 
0235                 if (m_comboBoxes[i]->currentIndex() < m_presenceModel->rowCount()) {
0236                     m_extendedModels[i]->removeTemporaryPresence();
0237                 }
0238 
0239                 m_accountsModel->setData(index, m_comboBoxes[i]->currentData(KTp::PresenceModel::PresenceRole), KTp::AccountsListModel::StatusHandlerPresenceRole);
0240             });
0241 
0242             const QModelIndexList &matchIndexList = m_presenceModel->match(m_presenceModel->index(0, 0), KTp::PresenceModel::PresenceRole, QVariant::fromValue<KTp::Presence>(accountPresence));
0243             if (!matchIndexList.isEmpty()) {
0244                 m_comboBoxes[i]->setCurrentIndex(matchIndexList.at(0).row());
0245             } else {
0246                 const QModelIndex &tempPresenceIndex = m_extendedModels[i]->addTemporaryPresence(accountPresence);
0247                 m_comboBoxes[i]->setCurrentIndex(tempPresenceIndex.row());
0248             }
0249 
0250             setComboLineEdit();
0251 
0252             lHAccountLayout->addWidget(icoLabel);
0253             lHAccountLayout->addWidget(label, Qt::AlignLeft);
0254             vAccountLayout->addLayout(lHAccountLayout);
0255             hAccountLayout->addWidget(checkBox);
0256             hAccountLayout->addWidget(m_comboBoxes[i]);
0257             vAccountLayout->addLayout(hAccountLayout);
0258             vLayout->addLayout(vAccountLayout);
0259         }
0260     }
0261 
0262     vLayout->addWidget(buttonBox);
0263     setLayout(vLayout);
0264 }
0265 
0266 bool AdvancedPresenceDialog::eventFilter(QObject* obj, QEvent* event)
0267 {
0268     QComboBox *comboBox = qobject_cast<QComboBox*>(obj);
0269 
0270     if (event->type() == QEvent::KeyPress) {
0271         QKeyEvent *keyEvent = static_cast<QKeyEvent*>(event);
0272         if (keyEvent->modifiers() == Qt::NoModifier && (keyEvent->key() == Qt::Key_Return || keyEvent->key() == Qt::Key_Enter) && m_comboBoxes.values().contains(comboBox)) {
0273             const QModelIndex &index = m_accountsModel->index(m_comboBoxes.key(comboBox), 0);
0274             KTp::Presence accountPresence = comboBox->currentData(KTp::PresenceModel::PresenceRole).value<KTp::Presence>();
0275             accountPresence.setStatusMessage(comboBox->lineEdit()->text());
0276 
0277             const QModelIndexList &matchIndexList = m_presenceModel->match(m_presenceModel->index(0, 0), KTp::PresenceModel::PresenceRole, QVariant::fromValue<KTp::Presence>(accountPresence));
0278             if (matchIndexList.isEmpty()) {
0279                 const QModelIndex &tempPresenceIndex = m_extendedModels[index.row()]->addTemporaryPresence(accountPresence);
0280                 comboBox->setCurrentIndex(tempPresenceIndex.row());
0281             }
0282 
0283             comboBox->lineEdit()->setPlaceholderText(comboBox->currentData(Qt::DisplayRole).value<QString>());
0284             comboBox->lineEdit()->setToolTip(comboBox->currentData(Qt::DisplayRole).value<QString>());
0285             comboBox->clearFocus();
0286 
0287             m_accountsModel->setData(index, comboBox->currentData(KTp::PresenceModel::PresenceRole), KTp::AccountsListModel::StatusHandlerPresenceRole);
0288 
0289             return true;
0290         } else {
0291             return false;
0292         }
0293     }
0294 
0295     if (event->type() == QEvent::FocusOut) {
0296         comboBox->clearFocus();
0297     }
0298 
0299     // standard event processing
0300     return QObject::eventFilter(obj, event);
0301 }
0302 
0303 #include "advanced-presence-dialog.moc"