File indexing completed on 2024-04-14 05:44:22

0001 /*
0002  *  SPDX-FileCopyrightText: 2002-2003 Jesper K. Pedersen <blackie@kde.org>
0003  *
0004  *  SPDX-License-Identifier: LGPL-2.0-only
0005  **/
0006 
0007 #include "characterswidget.h"
0008 
0009 #include <QApplication>
0010 #include <QCheckBox>
0011 #include <QDialogButtonBox>
0012 #include <QGroupBox>
0013 #include <QLabel>
0014 #include <QPainter>
0015 #include <QPushButton>
0016 #include <QVBoxLayout>
0017 
0018 #include "charselector.h"
0019 #include "myfontmetrics.h"
0020 #include "regexpconverter.h"
0021 #include "textrangeregexp.h"
0022 
0023 CharacterEdits *CharactersWidget::_configWindow = nullptr;
0024 
0025 CharactersWidget::CharactersWidget(RegExpEditorWindow *editorWindow, QWidget *parent)
0026     : RegExpWidget(editorWindow, parent)
0027 {
0028     _regexp = new TextRangeRegExp(false /* not used */);
0029 }
0030 
0031 CharactersWidget::CharactersWidget(TextRangeRegExp *regexp, RegExpEditorWindow *editorWindow, QWidget *parent)
0032     : RegExpWidget(editorWindow, parent)
0033 {
0034     _regexp = dynamic_cast<TextRangeRegExp *>(regexp->clone());
0035     Q_ASSERT(_regexp);
0036 }
0037 
0038 CharactersWidget::~CharactersWidget()
0039 {
0040     delete _regexp;
0041 }
0042 
0043 QSize CharactersWidget::sizeHint() const
0044 {
0045     QFontMetrics metrics = fontMetrics();
0046     _textSize = HackCalculateFontSize(metrics, title());
0047     //  _textSize = metrics.size(0, title());
0048 
0049     QSize headerSize(_textSize.width() + 4 * bdSize, _textSize.height());
0050 
0051     _contentSize = HackCalculateFontSize(metrics, text());
0052     //  _contentSize = metrics.size(0, text());
0053 
0054     return QSize(qMax(headerSize.width(), bdSize + _contentSize.width() + bdSize + 2 * pw),
0055                  headerSize.height() + bdSize + _contentSize.height() + bdSize + 2 * pw);
0056 }
0057 
0058 void CharactersWidget::paintEvent(QPaintEvent *e)
0059 {
0060     QSize mySize = sizeHint();
0061 
0062     QPainter painter(this);
0063     drawPossibleSelection(painter, mySize);
0064 
0065     int center = _textSize.height() / 2;
0066     int offset = 0;
0067 
0068     // draw the horizontal line and the label
0069     painter.drawLine(pw, center, bdSize, center);
0070     offset += pw + 2 * bdSize;
0071 
0072     painter.drawText(offset, 0, _textSize.width(), _textSize.height(), 0, title());
0073     offset += _textSize.width() + bdSize;
0074 
0075     painter.drawLine(offset, center, mySize.width(), center);
0076 
0077     // Draw the rest of the lines
0078     int y = mySize.width() - pw;
0079     int x = mySize.height() - pw;
0080     painter.drawLine(0, center, 0, mySize.height());
0081     painter.drawLine(y, center, y, mySize.height());
0082     painter.drawLine(0, x, y, x);
0083 
0084     // Draw the text
0085     painter.drawText(bdSize, bdSize + _textSize.height(), _contentSize.width(), _contentSize.height(), 0, text());
0086 
0087     RegExpWidget::paintEvent(e);
0088 }
0089 
0090 RegExp *CharactersWidget::regExp() const
0091 {
0092     RegExp *r = _regexp->clone();
0093     r->setSelected(isSelected());
0094     return r;
0095 }
0096 
0097 QString CharactersWidget::text() const
0098 {
0099     QString res = QString();
0100 
0101     if (_regexp->wordChar()) {
0102         res += i18n("- A word character\n");
0103     }
0104 
0105     if (_regexp->nonWordChar()) {
0106         res += i18n("- A non-word character\n");
0107     }
0108 
0109     if (_regexp->digit()) {
0110         res += i18n("- A digit character\n");
0111     }
0112 
0113     if (_regexp->nonDigit()) {
0114         res += i18n("- A non-digit character\n");
0115     }
0116 
0117     if (_regexp->space()) {
0118         res += i18n("- A space character\n");
0119     }
0120 
0121     if (_regexp->nonSpace()) {
0122         res += i18n("- A non-space character\n");
0123     }
0124 
0125     // Single characters
0126     QStringList chars = _regexp->chars();
0127     if (!chars.isEmpty()) {
0128         QString str = chars.join(QStringLiteral(", "));
0129         res += QStringLiteral("- ") + str + QStringLiteral("\n");
0130     }
0131 
0132     // Ranges characters
0133     for (const StringPair &elm : _regexp->range()) {
0134         QString fromText = elm.first;
0135         QString toText = elm.second;
0136 
0137         res += i18nc("a range of characters", "- from %1 to %2\n", fromText, toText);
0138     }
0139     return res.left(res.length() - 1);
0140 }
0141 
0142 QString CharactersWidget::title() const
0143 {
0144     if (_regexp->negate()) {
0145         return i18n("Any Character Except");
0146     } else {
0147         return i18n("One of Following Characters");
0148     }
0149 }
0150 
0151 RegExpWidget *CharactersWidget::findWidgetToEdit(QPointF globalPos)
0152 {
0153     if (QRectF(mapToGlobal(QPointF(0, 0)), size()).contains(globalPos)) {
0154         return this;
0155     } else {
0156         return nullptr;
0157     }
0158 }
0159 
0160 int CharactersWidget::edit()
0161 {
0162     if (_configWindow == nullptr) {
0163         QApplication::setOverrideCursor(Qt::WaitCursor);
0164         // No parent here, as this window should continue to exists.
0165         _configWindow = new CharacterEdits(nullptr);
0166         _configWindow->setObjectName(QStringLiteral("CharactersWidget::_configWindow"));
0167         QApplication::restoreOverrideCursor();
0168     }
0169 
0170     _configWindow->move(QCursor::pos() - QPoint(_configWindow->sizeHint().width() / 2, _configWindow->sizeHint().height() / 2));
0171     _configWindow->setRegexp(_regexp);
0172     int ret = _configWindow->exec();
0173     if (ret == QDialog::Accepted) {
0174         _editorWindow->updateContent(nullptr);
0175         update();
0176     }
0177     return ret;
0178 }
0179 
0180 void CharacterEdits::addCharacter(const QString &txt)
0181 {
0182     const KMultiFormListBoxEntryList list = _single->elements();
0183     for (KMultiFormListBoxEntry *e : list) {
0184         SingleEntry *entry = dynamic_cast<SingleEntry *>(e);
0185         if (entry && entry->isEmpty()) {
0186             entry->setText(txt);
0187             return;
0188         }
0189     }
0190 
0191     SingleEntry *entry = new SingleEntry(_single);
0192     entry->setText(txt);
0193     _single->append(entry);
0194 }
0195 
0196 void CharacterEdits::addRange(const QString &from, const QString &to)
0197 {
0198     const KMultiFormListBoxEntryList list = _range->elements();
0199     for (KMultiFormListBoxEntry *e : list) {
0200         RangeEntry *entry = dynamic_cast<RangeEntry *>(e);
0201         if (entry && entry->isEmpty()) {
0202             entry->setFrom(from);
0203             entry->setTo(to);
0204             return;
0205         }
0206     }
0207 
0208     RangeEntry *entry = new RangeEntry(_range);
0209     entry->setFrom(from);
0210     entry->setTo(to);
0211     _range->append(entry);
0212 }
0213 
0214 void CharacterEdits::setRegexp(TextRangeRegExp *regexp)
0215 {
0216     _regexp = regexp;
0217     negate->setChecked(regexp->negate());
0218     digit->setChecked(regexp->digit());
0219     _nonDigit->setChecked(regexp->nonDigit());
0220     space->setChecked(regexp->space());
0221     _nonSpace->setChecked(regexp->nonSpace());
0222     wordChar->setChecked(regexp->wordChar());
0223     _nonWordChar->setChecked(regexp->nonWordChar());
0224 
0225     bool enabled = (RegExpConverter::current()->features() & RegExpConverter::CharacterRangeNonItems);
0226     _nonWordChar->setEnabled(enabled);
0227     _nonDigit->setEnabled(enabled);
0228     _nonSpace->setEnabled(enabled);
0229 
0230     // Characters
0231 
0232     const KMultiFormListBoxEntryList list1 = _single->elements();
0233     for (KMultiFormListBoxEntry *e : list1) {
0234         SingleEntry *entry = dynamic_cast<SingleEntry *>(e);
0235         if (entry) {
0236             entry->setText(QString());
0237         }
0238     }
0239     QStringList list2 = regexp->chars();
0240     for (QStringList::Iterator it2(list2.begin()); it2 != list2.end(); ++it2) {
0241         addCharacter(*it2);
0242     }
0243 
0244     // Ranges
0245     KMultiFormListBoxEntryList list3 = _range->elements();
0246     for (KMultiFormListBoxEntry *e : list3) {
0247         RangeEntry *entry = dynamic_cast<RangeEntry *>(e);
0248         if (entry) {
0249             entry->setFrom(QString());
0250             entry->setTo(QString());
0251         }
0252     }
0253 
0254     const auto range = _regexp->range();
0255     for (const StringPair &elm : range) {
0256         QString from = elm.first;
0257         QString to = elm.second;
0258         addRange(from, to);
0259     }
0260 }
0261 
0262 CharacterEdits::CharacterEdits(QWidget *parent)
0263     : QDialog(parent)
0264 {
0265     setWindowTitle(i18n("Specify Characters"));
0266     QVBoxLayout *mainLayout = new QVBoxLayout;
0267     setLayout(mainLayout);
0268 
0269     setObjectName(QStringLiteral("CharacterEdits"));
0270     QWidget *top = new QWidget(this);
0271     QVBoxLayout *topLayout = new QVBoxLayout(top);
0272     topLayout->setContentsMargins(0, 0, 0, 0);
0273     mainLayout->addWidget(top);
0274 
0275     negate = new QCheckBox(i18n("Do not match the characters specified here"));
0276     topLayout->addWidget(negate);
0277 
0278     // The predefined box
0279     QGroupBox *predefined = new QGroupBox(i18n("Predefined Character Ranges"));
0280     topLayout->addWidget(predefined);
0281     QGridLayout *predefinedLayout = new QGridLayout(predefined);
0282 
0283     wordChar = new QCheckBox(i18n("A word character"));
0284     predefinedLayout->addWidget(wordChar, 0, 0);
0285     digit = new QCheckBox(i18n("A digit character"));
0286     predefinedLayout->addWidget(digit, 0, 1);
0287     space = new QCheckBox(i18n("A space character"));
0288     predefinedLayout->addWidget(space, 0, 2);
0289 
0290     _nonWordChar = new QCheckBox(i18n("A non-word character"));
0291     predefinedLayout->addWidget(_nonWordChar, 1, 0);
0292     _nonDigit = new QCheckBox(i18n("A non-digit character"));
0293     predefinedLayout->addWidget(_nonDigit, 1, 1);
0294     _nonSpace = new QCheckBox(i18n("A non-space character"));
0295     predefinedLayout->addWidget(_nonSpace, 1, 2);
0296 
0297     // Single characters
0298     QGroupBox *singleBox = new QGroupBox(i18n("Single Characters"));
0299     QVBoxLayout *groupLayout = new QVBoxLayout(singleBox);
0300     topLayout->addWidget(singleBox);
0301     _single = new KMultiFormListBox(new SingleFactory(), KMultiFormListBox::MultiVisible);
0302     groupLayout->addWidget(_single);
0303     _single->addElement();
0304     _single->addElement();
0305     _single->addElement();
0306 
0307     QWidget *moreW = new QWidget();
0308     groupLayout->addWidget(moreW);
0309     QHBoxLayout *moreLay = new QHBoxLayout(moreW);
0310     QPushButton *more = new QPushButton(i18n("More Entries"));
0311     moreLay->addWidget(more);
0312     moreLay->addStretch(1);
0313     moreLay->setContentsMargins(0, 0, 0, 0);
0314 
0315     connect(more, &QAbstractButton::clicked, _single, &KMultiFormListBox::addElement);
0316 
0317     // Ranges
0318     QGroupBox *rangeBox = new QGroupBox(i18n("Character Ranges"));
0319     groupLayout = new QVBoxLayout(rangeBox);
0320     topLayout->addWidget(rangeBox);
0321 
0322     _range = new KMultiFormListBox(new RangeFactory(), KMultiFormListBox::MultiVisible);
0323     groupLayout->addWidget(_range);
0324     _range->addElement();
0325     _range->addElement();
0326     _range->addElement();
0327 
0328     moreW = new QWidget();
0329     groupLayout->addWidget(moreW);
0330     moreLay = new QHBoxLayout(moreW);
0331     more = new QPushButton(i18n("More Entries"));
0332     moreLay->addWidget(more);
0333     moreLay->addStretch(1);
0334     moreLay->setContentsMargins(0, 0, 0, 0);
0335     connect(more, &QAbstractButton::clicked, _range, &KMultiFormListBox::addElement);
0336 
0337     QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
0338     QPushButton *okButton = buttonBox->button(QDialogButtonBox::Ok);
0339     okButton->setDefault(true);
0340     okButton->setShortcut(Qt::CTRL | Qt::Key_Return);
0341     connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
0342     connect(okButton, &QAbstractButton::clicked, this, &CharacterEdits::slotOK);
0343     mainLayout->addWidget(buttonBox);
0344 }
0345 
0346 void CharacterEdits::slotOK()
0347 {
0348     _regexp->setNegate(negate->isChecked());
0349 
0350     _regexp->setWordChar(wordChar->isChecked());
0351     _regexp->setNonWordChar(_nonWordChar->isChecked());
0352 
0353     _regexp->setDigit(digit->isChecked());
0354     _regexp->setNonDigit(_nonDigit->isChecked());
0355 
0356     _regexp->setSpace(space->isChecked());
0357     _regexp->setNonSpace(_nonSpace->isChecked());
0358 
0359     // single characters
0360     _regexp->clearChars();
0361     const KMultiFormListBoxEntryList list = _single->elements();
0362     for (KMultiFormListBoxEntry *e : list) {
0363         SingleEntry *entry = dynamic_cast<SingleEntry *>(e);
0364         if (entry && !entry->isEmpty()) {
0365             _regexp->addCharacter(entry->text());
0366         }
0367     }
0368 
0369     // Ranges
0370     _regexp->clearRange();
0371     const auto elements = _range->elements();
0372     for (KMultiFormListBoxEntry *e : elements) {
0373         RangeEntry *entry = dynamic_cast<RangeEntry *>(e);
0374         if (entry && !entry->isEmpty()) {
0375             _regexp->addRange(entry->fromText(), entry->toText());
0376         }
0377     }
0378     accept();
0379 }
0380 
0381 SingleEntry::SingleEntry(QWidget *parent)
0382     : KMultiFormListBoxEntry(parent)
0383 {
0384     QHBoxLayout *layout = new QHBoxLayout(this);
0385     layout->setContentsMargins(0, 0, 0, 0);
0386     _selector = new CharSelector(this);
0387     layout->addWidget(_selector);
0388     layout->addStretch(1);
0389 }
0390 
0391 QString SingleEntry::text() const
0392 {
0393     return _selector->text();
0394 }
0395 
0396 void SingleEntry::setText(const QString &text)
0397 {
0398     _selector->setText(text);
0399 }
0400 
0401 bool SingleEntry::isEmpty() const
0402 {
0403     return _selector->isEmpty();
0404 }
0405 
0406 RangeEntry::RangeEntry(QWidget *parent)
0407     : KMultiFormListBoxEntry(parent)
0408 {
0409     QHBoxLayout *layout = new QHBoxLayout(this);
0410     layout->setContentsMargins(0, 0, 0, 0);
0411 
0412     QLabel *label = new QLabel(i18n("From:"), this);
0413     _from = new CharSelector(this);
0414     layout->addWidget(label);
0415     layout->addWidget(_from);
0416 
0417     layout->addStretch(1);
0418 
0419     label = new QLabel(i18nc("end of range", "To:"), this);
0420     _to = new CharSelector(this);
0421     layout->addWidget(label);
0422     layout->addWidget(_to);
0423 }
0424 
0425 QString RangeEntry::fromText() const
0426 {
0427     return _from->text();
0428 }
0429 
0430 QString RangeEntry::toText() const
0431 {
0432     return _to->text();
0433 }
0434 
0435 void RangeEntry::setFrom(const QString &text)
0436 {
0437     _from->setText(text);
0438 }
0439 
0440 void RangeEntry::setTo(const QString &text)
0441 {
0442     _to->setText(text);
0443 }
0444 
0445 bool RangeEntry::isEmpty() const
0446 {
0447     return _from->isEmpty() || _to->isEmpty();
0448 }
0449 
0450 #include "moc_characterswidget.cpp"