File indexing completed on 2024-04-14 05:44:24
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 "kregexpeditorprivate.h" 0008 0009 #include <KIconLoader> 0010 #include <KLocalizedString> 0011 #include <QIcon> 0012 0013 #include <KMessageBox> 0014 #include <QLineEdit> 0015 0016 #include <QApplication> 0017 #include <QFile> 0018 #include <QHBoxLayout> 0019 #include <QLabel> 0020 #include <QShortcut> 0021 #include <QSplitter> 0022 #include <QStandardPaths> 0023 #include <QTextStream> 0024 #include <QTimer> 0025 #include <QToolButton> 0026 0027 #include "infopage.h" 0028 #include "regexp.h" 0029 #include "regexpbuttons.h" 0030 #include "regexpconverter.h" 0031 #include "scrollededitorwindow.h" 0032 #include "userdefinedregexps.h" 0033 #include "verifier.h" 0034 0035 KRegExpEditorPrivate::KRegExpEditorPrivate(QWidget *parent) 0036 : QMainWindow(parent) 0037 , _updating(false) 0038 , _autoVerify(true) 0039 , _matchGreedy(false) 0040 { 0041 setMinimumSize(730, 300); 0042 setWindowFlags(Qt::Widget); 0043 0044 // The DockWindows. 0045 _regExpButtons = new RegExpButtons(this, QStringLiteral("RegExpButton")); 0046 addToolBar(Qt::LeftToolBarArea, _regExpButtons); 0047 0048 _userRegExps = new UserDefinedRegExps(/*verArea1*/ this, /*"KRegExpEditorPrivate::userRegExps"*/ i18n("Compound regular expression:")); 0049 _userRegExps->setWhatsThis( 0050 i18n("In this window you will find predefined regular expressions. Both regular expressions " 0051 "you have developed and saved, and regular expressions shipped with the system.")); 0052 addDockWidget(Qt::LeftDockWidgetArea, _userRegExps); 0053 0054 // Editor window 0055 _editor = new QSplitter(Qt::Vertical, this); 0056 _editor->setObjectName(QStringLiteral("KRegExpEditorPrivate::_editor")); 0057 0058 _scrolledEditorWindow = new RegExpScrolledEditorWindow(_editor); 0059 _scrolledEditorWindow->setWhatsThis( 0060 i18n("In this window you will develop your regular expressions. " 0061 "Select one of the actions from the action buttons above, and click the mouse in this " 0062 "window to insert the given action.")); 0063 0064 _info = new InfoPage(this); 0065 _info->setObjectName(QStringLiteral("_info")); 0066 _verifier = new Verifier(_editor); 0067 connect(_verifier, &QTextEdit::textChanged, this, &KRegExpEditorPrivate::maybeVerify); 0068 _verifier->setWhatsThis( 0069 i18n("<p>Type in some text in this window, and see what the regular expression you have developed matches.</p>" 0070 "<p>Each second match will be colored in red and each other match will be colored blue, simply so you " 0071 "can distinguish them from each other.</p>" 0072 "<p>If you select part of the regular expression in the editor window, then this part will be " 0073 "highlighted - This allows you to <i>debug</i> your regular expressions</p>")); 0074 0075 _editor->hide(); 0076 _editor->setSizes(QList<int>() << _editor->height() / 2 << _editor->height() / 2); 0077 0078 QWidget *centralWidget = new QWidget(this); 0079 QHBoxLayout *layout = new QHBoxLayout(centralWidget); 0080 layout->setContentsMargins(0, 0, 0, 0); 0081 layout->addWidget(_editor); 0082 layout->addWidget(_info); 0083 setCentralWidget(centralWidget); 0084 0085 // Connect the buttons 0086 connect(_regExpButtons, SIGNAL(clicked(int)), _scrolledEditorWindow, SLOT(slotInsertRegExp(int))); 0087 connect(_regExpButtons, &RegExpButtons::doSelect, _scrolledEditorWindow, &RegExpScrolledEditorWindow::slotDoSelect); 0088 connect(_userRegExps, SIGNAL(load(RegExp *)), _scrolledEditorWindow, SLOT(slotInsertRegExp(RegExp *))); 0089 0090 connect(_regExpButtons, SIGNAL(clicked(int)), _userRegExps, SLOT(slotUnSelect())); 0091 connect(_regExpButtons, SIGNAL(doSelect()), _userRegExps, SLOT(slotUnSelect())); 0092 connect(_userRegExps, &UserDefinedRegExps::load, _regExpButtons, &RegExpButtons::slotUnSelect); 0093 0094 connect(_scrolledEditorWindow, &RegExpScrolledEditorWindow::doneEditing, _regExpButtons, &RegExpButtons::slotSelectNewAction); 0095 connect(_scrolledEditorWindow, &RegExpScrolledEditorWindow::doneEditing, _userRegExps, &UserDefinedRegExps::slotSelectNewAction); 0096 0097 connect(_regExpButtons, &RegExpButtons::clicked, this, &KRegExpEditorPrivate::slotShowEditor); 0098 connect(_userRegExps, &UserDefinedRegExps::load, this, &KRegExpEditorPrivate::slotShowEditor); 0099 connect(_regExpButtons, &RegExpButtons::doSelect, this, &KRegExpEditorPrivate::slotShowEditor); 0100 0101 connect(_scrolledEditorWindow, SIGNAL(savedRegexp()), _userRegExps, SLOT(slotPopulateUserRegexps())); 0102 0103 connect(_scrolledEditorWindow, &RegExpScrolledEditorWindow::anythingSelected, this, &KRegExpEditorPrivate::anythingSelected); 0104 connect(_scrolledEditorWindow, &RegExpScrolledEditorWindow::anythingOnClipboard, this, &KRegExpEditorPrivate::anythingOnClipboard); 0105 connect(_scrolledEditorWindow, &RegExpScrolledEditorWindow::canSave, this, &KRegExpEditorPrivate::canSave); 0106 0107 connect(_scrolledEditorWindow, &RegExpScrolledEditorWindow::verifyRegExp, this, &KRegExpEditorPrivate::maybeVerify); 0108 0109 // Line Edit 0110 QDockWidget *editDock = new QDockWidget(i18n("ASCII syntax:"), this); 0111 editDock->setFeatures(QDockWidget::NoDockWidgetFeatures); 0112 addDockWidget(Qt::BottomDockWidgetArea, editDock); 0113 0114 QWidget *editDockWidget = new QWidget(editDock); 0115 editDock->setWidget(editDockWidget); 0116 QHBoxLayout *dockLayout = new QHBoxLayout(editDockWidget); 0117 dockLayout->setContentsMargins(0, 0, 0, 0); 0118 0119 _regexpEdit = new QLineEdit(editDockWidget); 0120 dockLayout->addWidget(_regexpEdit); 0121 _regexpEdit->setFocus(Qt::OtherFocusReason); 0122 _regexpEdit->setClearButtonEnabled(true); 0123 _regexpEdit->setWhatsThis( 0124 i18n("<p>This is the regular expression in ASCII syntax. You are likely only " 0125 "to be interested in this if you are a programmer, and need to " 0126 "develop a regular expression using QRegExp.</p>" 0127 "<p>You may develop your regular expression both by using the graphical " 0128 "editor, and by typing the regular expression in this line edit.</p>")); 0129 0130 QPixmap pix = KIconLoader::global()->loadIcon(QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("kregexpeditor/pics/error.png")), 0131 KIconLoader::Toolbar); 0132 _error = new QLabel(editDockWidget); 0133 _error->setPixmap(pix); 0134 dockLayout->addWidget(_error); 0135 _error->hide(); 0136 0137 _timer = new QTimer(this); 0138 _timer->setSingleShot(true); 0139 0140 connect(_scrolledEditorWindow, &RegExpScrolledEditorWindow::change, this, &KRegExpEditorPrivate::slotUpdateLineEdit); 0141 connect(_regexpEdit, &QLineEdit::textChanged, this, &KRegExpEditorPrivate::slotTriggerUpdate); 0142 connect(_timer, &QTimer::timeout, this, &KRegExpEditorPrivate::slotTimeout); 0143 0144 // Push an initial empty element on the stack. 0145 _undoStack.push(_scrolledEditorWindow->regExp()); 0146 } 0147 0148 KRegExpEditorPrivate::~KRegExpEditorPrivate() 0149 { 0150 qDeleteAll(_undoStack); 0151 qDeleteAll(_redoStack); 0152 } 0153 0154 QString KRegExpEditorPrivate::regexp() 0155 { 0156 RegExp *regexp = _scrolledEditorWindow->regExp(); 0157 QString res = RegExpConverter::current()->toStr(regexp, false); 0158 delete regexp; 0159 return res; 0160 } 0161 0162 void KRegExpEditorPrivate::slotUpdateEditor(const QString &txt) 0163 { 0164 _updating = true; 0165 bool ok; 0166 if (!RegExpConverter::current()->canParse()) { 0167 // This can happend if the application set a text through the API. 0168 // qDebug("cannot parse"); 0169 } else { 0170 RegExp *result = RegExpConverter::current()->parse(txt, &ok); 0171 if (ok) { 0172 const QList<CompoundRegExp *> list = _userRegExps->regExps(); 0173 for (CompoundRegExp *regExp : list) { 0174 result->replacePart(regExp); 0175 } 0176 0177 _scrolledEditorWindow->slotSetRegExp(result); 0178 _error->hide(); 0179 maybeVerify(); 0180 recordUndoInfo(); 0181 result->check(_errorMap); 0182 } else { 0183 _error->show(); 0184 if (_autoVerify) { 0185 _verifier->clearRegexp(); 0186 } 0187 } 0188 delete result; 0189 } 0190 _updating = false; 0191 } 0192 0193 void KRegExpEditorPrivate::slotUpdateLineEdit() 0194 { 0195 if (_updating) { 0196 return; 0197 } 0198 _updating = true; 0199 0200 RegExp *regexp = _scrolledEditorWindow->regExp(); 0201 regexp->check(_errorMap); 0202 0203 QString str = RegExpConverter::current()->toStr(regexp, false); 0204 _regexpEdit->setText(str); 0205 delete regexp; 0206 0207 recordUndoInfo(); 0208 0209 _updating = false; 0210 } 0211 0212 void KRegExpEditorPrivate::recordUndoInfo() 0213 { 0214 Q_ASSERT(_updating); 0215 0216 // Update undo/redo stacks 0217 RegExp *regexp = _scrolledEditorWindow->regExp(); 0218 if (regexp->toXmlString() != _undoStack.top()->toXmlString()) { 0219 _undoStack.push(regexp); 0220 qDeleteAll(_redoStack); 0221 _redoStack = QStack<RegExp *>(); 0222 emitUndoRedoSignals(); 0223 } 0224 } 0225 0226 void KRegExpEditorPrivate::slotRedo() 0227 { 0228 if (!_redoStack.isEmpty()) { 0229 _undoStack.push(_redoStack.pop()); 0230 _scrolledEditorWindow->slotSetRegExp(_undoStack.top()); 0231 slotUpdateLineEdit(); 0232 emitUndoRedoSignals(); 0233 maybeVerify(); 0234 } 0235 } 0236 0237 void KRegExpEditorPrivate::slotUndo() 0238 { 0239 if (_undoStack.count() > 1) { 0240 _redoStack.push(_undoStack.pop()); 0241 _scrolledEditorWindow->slotSetRegExp(_undoStack.top()); 0242 slotUpdateLineEdit(); 0243 emitUndoRedoSignals(); 0244 maybeVerify(); 0245 } 0246 } 0247 0248 void KRegExpEditorPrivate::slotCut() 0249 { 0250 _scrolledEditorWindow->slotCut(); 0251 } 0252 0253 void KRegExpEditorPrivate::slotCopy() 0254 { 0255 _scrolledEditorWindow->slotCopy(); 0256 } 0257 0258 void KRegExpEditorPrivate::slotPaste() 0259 { 0260 _scrolledEditorWindow->slotPaste(); 0261 } 0262 0263 void KRegExpEditorPrivate::slotSave() 0264 { 0265 _scrolledEditorWindow->slotSave(); 0266 } 0267 0268 void KRegExpEditorPrivate::slotShowEditor() 0269 { 0270 _info->hide(); 0271 _editor->show(); 0272 } 0273 0274 void KRegExpEditorPrivate::emitUndoRedoSignals() 0275 { 0276 Q_EMIT canUndo(_undoStack.count() > 1); 0277 Q_EMIT changes(_undoStack.count() > 1); 0278 Q_EMIT canRedo(_redoStack.count() > 0); 0279 } 0280 0281 void KRegExpEditorPrivate::slotSetRegexp(const QString ®exp) 0282 { 0283 _regexpEdit->setText(regexp); 0284 } 0285 0286 void KRegExpEditorPrivate::slotTriggerUpdate() 0287 { 0288 /* ### Guess this timeout value should be configurable somewhere, or (even 0289 * better: do some kind of benchmark each time the editor view gets updated 0290 * to measure how long it takes on the client system to render the editor 0291 * with the current complexity. That way we'd get good response times for 0292 * simple regexps, and flicker-free display for complex regexps. 0293 * - Frerich 0294 */ 0295 if (!_updating) { 0296 _timer->start(300); 0297 slotShowEditor(); 0298 } 0299 } 0300 0301 void KRegExpEditorPrivate::slotTimeout() 0302 { 0303 slotUpdateEditor(_regexpEdit->text()); 0304 } 0305 0306 void KRegExpEditorPrivate::setMatchText(const QString &text) 0307 { 0308 bool autoVerify = _autoVerify; 0309 _autoVerify = false; 0310 _verifier->setText(text); 0311 _autoVerify = autoVerify; 0312 } 0313 0314 void KRegExpEditorPrivate::maybeVerify() 0315 { 0316 if (_autoVerify) { 0317 doVerify(); 0318 } 0319 } 0320 0321 void KRegExpEditorPrivate::doVerify() 0322 { 0323 bool autoVerify = _autoVerify; // prevent loop due to verify emit changed, which calls maybeVerify 0324 _autoVerify = false; 0325 RegExp *regexp = _scrolledEditorWindow->regExp(); 0326 0327 _verifier->verify(RegExpConverter::current()->toStr(regexp, true)); 0328 delete regexp; 0329 _autoVerify = autoVerify; 0330 } 0331 0332 void KRegExpEditorPrivate::setAutoVerify(bool on) 0333 { 0334 _autoVerify = on; 0335 if (!_autoVerify) { 0336 _verifier->clearRegexp(); 0337 } else { 0338 doVerify(); 0339 } 0340 } 0341 0342 void KRegExpEditorPrivate::setVerifyText(const QString &fileName) 0343 { 0344 bool autoVerify = _autoVerify; 0345 _autoVerify = false; 0346 QFile file(fileName); 0347 if (!file.open(QIODevice::ReadOnly)) { 0348 KMessageBox::error(nullptr, i18n("Could not open file '%1' for reading", fileName)); 0349 } else { 0350 QTextStream s(&file); 0351 QString txt = s.readAll(); 0352 file.close(); 0353 RegExp *regexp = _scrolledEditorWindow->regExp(); 0354 _verifier->setText(txt); 0355 _verifier->verify(RegExpConverter::current()->toStr(regexp, true)); 0356 delete regexp; 0357 } 0358 _autoVerify = autoVerify; 0359 } 0360 0361 void KRegExpEditorPrivate::setCaseSensitive(bool b) 0362 { 0363 _verifier->setCaseSensitive(b); 0364 } 0365 0366 void KRegExpEditorPrivate::setMinimal(bool b) 0367 { 0368 _verifier->setMinimal(b); 0369 } 0370 0371 void KRegExpEditorPrivate::setSyntax(RegExpConverter *converter) 0372 { 0373 RegExpConverter::setCurrent(converter); 0374 if (converter->canParse()) { 0375 _regexpEdit->setReadOnly(false); 0376 _regexpEdit->setBackgroundRole(QPalette::Base); 0377 } else { 0378 _regexpEdit->setReadOnly(true); 0379 _regexpEdit->setBackgroundRole(QPalette::Window); 0380 } 0381 _regExpButtons->setFeatures(converter->features()); 0382 _verifier->setHighlighter(converter->highlighter(_verifier)); 0383 slotUpdateLineEdit(); 0384 } 0385 0386 void KRegExpEditorPrivate::showHelp() 0387 { 0388 _info->show(); 0389 _editor->hide(); 0390 } 0391 0392 void KRegExpEditorPrivate::setMatchGreedy(bool b) 0393 { 0394 _matchGreedy = b; 0395 _verifier->setMinimal(!b); 0396 doVerify(); 0397 } 0398 0399 #include "moc_kregexpeditorprivate.cpp"