File indexing completed on 2024-06-23 05:16:25

0001 /*
0002     SPDX-FileCopyrightText: 2010 Casey Link <unnamedrambler@gmail.com>
0003     SPDX-FileCopyrightText: 2009-2010 Klaralvdalens Datakonsult AB, a KDAB Group company <info@kdab.net>
0004 
0005     Refactored from earlier code by:
0006     SPDX-FileCopyrightText: 2010 Volker Krause <vkrause@kde.org>
0007     SPDX-FileCopyrightText: 2004 Cornelius Schumacher <schumacher@kde.org>
0008 
0009     SPDX-License-Identifier: LGPL-2.0-or-later
0010 */
0011 #include "multiplyinglineview_p.h"
0012 
0013 #include "libkdepim_debug.h"
0014 #include <KLocalizedString>
0015 #include <KMessageBox>
0016 
0017 #include <QResizeEvent>
0018 #include <QScrollBar>
0019 #include <QTimer>
0020 #include <QVBoxLayout>
0021 
0022 using namespace KPIM;
0023 
0024 MultiplyingLineView::MultiplyingLineView(MultiplyingLineFactory *factory, MultiplyingLineEditor *parent)
0025     : QScrollArea(parent)
0026     , mPage(new QWidget(this))
0027     , mTopLayout(new QVBoxLayout(this))
0028     , mMultiplyingLineFactory(factory)
0029 {
0030     setWidgetResizable(true);
0031     setFrameStyle(QFrame::NoFrame);
0032 
0033     mPage->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
0034     setWidget(mPage);
0035 
0036     mTopLayout->setContentsMargins(0, 0, 0, 0);
0037     mTopLayout->setSpacing(0);
0038     mPage->setLayout(mTopLayout);
0039 }
0040 
0041 MultiplyingLine *MultiplyingLineView::activeLine() const
0042 {
0043     return mLines.last();
0044 }
0045 
0046 MultiplyingLine *MultiplyingLineView::emptyLine() const
0047 {
0048     for (MultiplyingLine *line : std::as_const(mLines)) {
0049         if (line->isEmpty()) {
0050             return line;
0051         }
0052     }
0053     return nullptr;
0054 }
0055 
0056 MultiplyingLine *MultiplyingLineView::addLine(bool showDialogBox)
0057 {
0058     const int maximumRecipients = mMultiplyingLineFactory->maximumRecipients();
0059     if (maximumRecipients != -1) {
0060         int numberOfLine = mLines.count();
0061         if (numberOfLine++ >= maximumRecipients) {
0062             if (showDialogBox) {
0063                 KMessageBox::error(this, i18n("We can not add more recipients. We have reached maximum recipients"));
0064             }
0065 
0066             return nullptr;
0067         }
0068     }
0069     MultiplyingLine *line = mMultiplyingLineFactory->newLine(widget());
0070 
0071     mTopLayout->addWidget(line);
0072     line->setCompletionMode(mCompletionMode);
0073     line->show();
0074     connect(line, &MultiplyingLine::returnPressed, this, &MultiplyingLineView::slotReturnPressed);
0075     connect(line, &MultiplyingLine::upPressed, this, &MultiplyingLineView::slotUpPressed);
0076     connect(line, &MultiplyingLine::downPressed, this, &MultiplyingLineView::slotDownPressed);
0077     connect(line, &MultiplyingLine::rightPressed, this, &MultiplyingLineView::focusRight);
0078     connect(line, &MultiplyingLine::deleteLine, this, &MultiplyingLineView::slotDecideLineDeletion);
0079     connect(line, &MultiplyingLine::completionModeChanged, this, &MultiplyingLineView::setCompletionMode);
0080 
0081     if (!mLines.isEmpty()) {
0082         line->fixTabOrder(mLines.last()->tabOut());
0083     }
0084     mLines.append(line);
0085     mFirstColumnWidth = line->setColumnWidth(mFirstColumnWidth);
0086     mLineHeight = line->minimumSizeHint().height();
0087     line->resize(viewport()->width(), mLineHeight);
0088     resizeView();
0089     ensureVisible(0, mLines.count() * mLineHeight, 0, 0);
0090 
0091     QTimer::singleShot(0, this, &MultiplyingLineView::moveScrollBarToEnd);
0092 
0093     Q_EMIT lineAdded(line);
0094     return line;
0095 }
0096 
0097 void MultiplyingLineView::moveScrollBarToEnd()
0098 {
0099     // scroll to bottom
0100     verticalScrollBar()->triggerAction(QAbstractSlider::SliderToMaximum);
0101 }
0102 
0103 void MultiplyingLineView::slotReturnPressed(MultiplyingLine *line)
0104 {
0105     if (!line->data()->isEmpty()) {
0106         MultiplyingLine *empty = emptyLine();
0107         if (!empty) {
0108             empty = addLine(true);
0109         }
0110         if (!empty) {
0111             return;
0112         }
0113         activateLine(empty);
0114     }
0115 }
0116 
0117 void MultiplyingLineView::slotDownPressed(MultiplyingLine *line)
0118 {
0119     const int pos = mLines.indexOf(line);
0120     if (pos >= (mLines.count() - 1)) {
0121         Q_EMIT focusDown();
0122     } else if (pos >= 0) {
0123         activateLine(mLines.at(pos + 1));
0124     }
0125 }
0126 
0127 void MultiplyingLineView::slotUpPressed(MultiplyingLine *line)
0128 {
0129     const int pos = mLines.indexOf(line);
0130     if (pos > 0) {
0131         activateLine(mLines.at(pos - 1));
0132     } else {
0133         Q_EMIT focusUp();
0134     }
0135 }
0136 
0137 void MultiplyingLineView::slotDecideLineDeletion(MultiplyingLine *line)
0138 {
0139     if (!line->isEmpty()) {
0140         mModified = true;
0141     }
0142     if (mLines.count() == 1) {
0143         line->clear();
0144     } else if (!line->canDeleteLineEdit()) {
0145         line->clear();
0146     } else if (mLines.indexOf(line) != mLines.count() - 1) {
0147         mCurDelLine = line;
0148         slotDeleteLine();
0149     }
0150 }
0151 
0152 void MultiplyingLineView::slotDeleteLine()
0153 {
0154     if (!mCurDelLine) {
0155         return;
0156     }
0157 
0158     MultiplyingLine *line = mCurDelLine;
0159     line->aboutToBeDeleted();
0160     const int pos = mLines.indexOf(line);
0161 
0162     if (mCurDelLine->isActive()) {
0163         int newPos;
0164         if (pos == 0) {
0165             newPos = pos + 1;
0166         } else {
0167             newPos = pos - 1;
0168         }
0169 
0170         // if there is something left to activate, do so
0171         if (mLines.at(newPos)) {
0172             mLines.at(newPos)->activate();
0173         }
0174     }
0175 
0176     mLines.removeAll(line);
0177     line->hide();
0178     line->setParent(nullptr);
0179     line->deleteLater();
0180 
0181     if (pos > 0) {
0182         Q_EMIT lineDeleted(pos);
0183     }
0184 
0185     resizeView();
0186 }
0187 
0188 void MultiplyingLineView::resizeView()
0189 {
0190     if (mDynamicSizeHint) {
0191         if (!mAutoResize) {
0192             if (mLines.count() < 6) {
0193                 setMinimumHeight(mLineHeight * mLines.count());
0194             } else {
0195                 setMinimumHeight(mLineHeight * 5);
0196                 setMaximumHeight(mLineHeight * mLines.count());
0197             }
0198         } else {
0199             setMinimumHeight(mLineHeight * mLines.count());
0200         }
0201     }
0202 
0203     parentWidget()->layout()->activate();
0204     Q_EMIT sizeHintChanged();
0205 }
0206 
0207 void MultiplyingLineView::activateLine(MultiplyingLine *line)
0208 {
0209     line->activate();
0210     ensureWidgetVisible(line);
0211 }
0212 
0213 void MultiplyingLineView::resizeEvent(QResizeEvent *ev)
0214 {
0215     QScrollArea::resizeEvent(ev);
0216     const int numberLine(mLines.count());
0217     for (int i = 0; i < numberLine; ++i) {
0218         mLines.at(i)->resize(ev->size().width(), mLineHeight);
0219     }
0220     ensureVisible(0, numberLine * mLineHeight, 0, 0);
0221 }
0222 
0223 QSize MultiplyingLineView::sizeHint() const
0224 {
0225     if (mDynamicSizeHint) {
0226         return {200, mLineHeight * static_cast<int>(mLines.count())};
0227     } else {
0228         return QScrollArea::sizeHint();
0229     }
0230 }
0231 
0232 QSize MultiplyingLineView::minimumSizeHint() const
0233 {
0234     if (mDynamicSizeHint) {
0235         int height;
0236         int numLines = 5;
0237         if (mLines.count() < numLines) {
0238             height = mLineHeight * mLines.count();
0239         } else {
0240             height = mLineHeight * numLines;
0241         }
0242         return {200, height};
0243     } else {
0244         return QScrollArea::minimumSizeHint();
0245     }
0246 }
0247 
0248 QList<MultiplyingLineData::Ptr> MultiplyingLineView::allData() const
0249 {
0250     QList<MultiplyingLineData::Ptr> data;
0251 
0252     QListIterator<MultiplyingLine *> it(mLines);
0253     while (it.hasNext()) {
0254         MultiplyingLine *line = it.next();
0255         if (!line->data()->isEmpty()) {
0256             data.append(line->data());
0257         }
0258     }
0259 
0260     return data;
0261 }
0262 
0263 void MultiplyingLineView::setCompletionMode(KCompletion::CompletionMode mode)
0264 {
0265     if (mCompletionMode == mode) {
0266         return;
0267     }
0268     mCompletionMode = mode;
0269 
0270     QListIterator<MultiplyingLine *> it(mLines);
0271     while (it.hasNext()) {
0272         MultiplyingLine *line = it.next();
0273         line->blockSignals(true);
0274         line->setCompletionMode(mode);
0275         line->blockSignals(false);
0276     }
0277     Q_EMIT completionModeChanged(mode); // report change to MultiplyingLineEditor
0278 }
0279 
0280 void MultiplyingLineView::removeData(const MultiplyingLineData::Ptr &data)
0281 {
0282     // search a line which matches recipient and type
0283     QListIterator<MultiplyingLine *> it(mLines);
0284     MultiplyingLine *line = nullptr;
0285     while (it.hasNext()) {
0286         line = it.next();
0287         if (line->data() == data) {
0288             break;
0289         }
0290     }
0291     if (line) {
0292         line->slotPropagateDeletion();
0293     }
0294 }
0295 
0296 bool MultiplyingLineView::isModified() const
0297 {
0298     if (mModified) {
0299         return true;
0300     }
0301 
0302     QListIterator<MultiplyingLine *> it(mLines);
0303     MultiplyingLine *line = nullptr;
0304     while (it.hasNext()) {
0305         line = it.next();
0306         if (line->isModified()) {
0307             return true;
0308         }
0309     }
0310 
0311     return false;
0312 }
0313 
0314 void MultiplyingLineView::clearModified()
0315 {
0316     mModified = false;
0317 
0318     QListIterator<MultiplyingLine *> it(mLines);
0319     while (it.hasNext()) {
0320         MultiplyingLine *line = it.next();
0321         line->clearModified();
0322     }
0323 }
0324 
0325 void MultiplyingLineView::setFocus()
0326 {
0327     if (!mLines.empty() && mLines.constLast()->isActive()) {
0328         setFocusBottom();
0329     } else {
0330         setFocusTop();
0331     }
0332 }
0333 
0334 void MultiplyingLineView::setFocusTop()
0335 {
0336     if (!mLines.empty()) {
0337         MultiplyingLine *line = mLines.constFirst();
0338         if (line) {
0339             line->activate();
0340         } else {
0341             qCWarning(LIBKDEPIM_LOG) << "No first";
0342         }
0343     } else {
0344         qCWarning(LIBKDEPIM_LOG) << "No first";
0345     }
0346 }
0347 
0348 void MultiplyingLineView::setFocusBottom()
0349 {
0350     if (!mLines.empty()) {
0351         MultiplyingLine *line = mLines.constLast();
0352         if (line) {
0353             ensureWidgetVisible(line);
0354             line->activate();
0355         } else {
0356             qCWarning(LIBKDEPIM_LOG) << "No last";
0357         }
0358     }
0359 }
0360 
0361 int MultiplyingLineView::setFirstColumnWidth(int w)
0362 {
0363     mFirstColumnWidth = w;
0364 
0365     QListIterator<MultiplyingLine *> it(mLines);
0366     while (it.hasNext()) {
0367         MultiplyingLine *line = it.next();
0368         mFirstColumnWidth = line->setColumnWidth(mFirstColumnWidth);
0369     }
0370 
0371     resizeView();
0372     return mFirstColumnWidth;
0373 }
0374 
0375 QList<MultiplyingLine *> MultiplyingLineView::lines() const
0376 {
0377     return mLines;
0378 }
0379 
0380 void MultiplyingLineView::setAutoResize(bool resize)
0381 {
0382     mAutoResize = resize;
0383 
0384     if (mAutoResize) {
0385         setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
0386         setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
0387         setMaximumHeight(QWIDGETSIZE_MAX);
0388     } else {
0389         setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
0390         setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
0391     }
0392 }
0393 
0394 bool MultiplyingLineView::autoResize() const
0395 {
0396     return mAutoResize;
0397 }
0398 
0399 void MultiplyingLineView::setDynamicSizeHint(bool dynamic)
0400 {
0401     mDynamicSizeHint = dynamic;
0402 }
0403 
0404 bool MultiplyingLineView::dynamicSizeHint() const
0405 {
0406     return mDynamicSizeHint;
0407 }
0408 
0409 #include "moc_multiplyinglineview_p.cpp"