File indexing completed on 2024-04-14 05:44:25
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 "regexpeditorwindow.h" 0008 0009 #include <QApplication> 0010 #include <QClipboard> 0011 #include <QDrag> 0012 #include <QFileInfo> 0013 #include <QHBoxLayout> 0014 #include <QInputDialog> 0015 #include <QMenu> 0016 #include <QMimeData> 0017 #include <QMouseEvent> 0018 #include <QPainter> 0019 #include <QShortcut> 0020 #include <QTextStream> 0021 0022 #include <KLocalizedString> 0023 #include <KMessageBox> 0024 #include <QIcon> 0025 0026 #include "concwidget.h" 0027 #include "regexp.h" 0028 #include "regexpconverter.h" 0029 #include "userdefinedregexps.h" 0030 0031 RegExpEditorWindow::RegExpEditorWindow(QWidget *parent) 0032 : QWidget(parent /*, Qt::WPaintUnclipped*/) 0033 { 0034 _top = new ConcWidget(this, this); 0035 _layout = new QHBoxLayout(this); 0036 _layout->addWidget(_top); 0037 _top->setToplevel(); 0038 _menu = nullptr; 0039 _insertInAction = false; 0040 _pasteInAction = false; 0041 _pasteData = nullptr; 0042 0043 _PosEdit = QPoint(0, 0); 0044 0045 (void)new QShortcut(Qt::CTRL | Qt::Key_C, this, SLOT(slotCopy())); 0046 (void)new QShortcut(Qt::CTRL | Qt::Key_X, this, SLOT(slotCut())); 0047 (void)new QShortcut(Qt::Key_Delete, this, SLOT(slotCut())); 0048 (void)new QShortcut(Qt::Key_Backspace, this, SLOT(slotCut())); 0049 (void)new QShortcut(Qt::CTRL | Qt::Key_V, this, SLOT(slotStartPasteAction())); 0050 (void)new QShortcut(Qt::Key_Escape, this, SLOT(slotEndActions())); 0051 (void)new QShortcut(Qt::CTRL | Qt::Key_S, this, SLOT(slotSave())); 0052 0053 connect(this, &RegExpEditorWindow::change, this, &RegExpEditorWindow::emitVerifyRegExp); 0054 } 0055 0056 RegExpEditorWindow::~RegExpEditorWindow() 0057 { 0058 delete _pasteData; 0059 } 0060 0061 RegExp *RegExpEditorWindow::regExp() const 0062 { 0063 return _top->regExp(); 0064 } 0065 0066 void RegExpEditorWindow::mousePressEvent(QMouseEvent *event) 0067 { 0068 setFocus(); 0069 updateContent(nullptr); 0070 0071 _start = event->pos(); 0072 _lastPoint = QPoint(0, 0); 0073 0074 if (pointSelected(event->globalPosition())) { 0075 _isDndOperation = true; 0076 } else { 0077 _isDndOperation = false; 0078 _selection = QRect(); 0079 _top->updateSelection(false); 0080 0081 QWidget::mousePressEvent(event); 0082 } 0083 grabMouse(); 0084 } 0085 0086 bool RegExpEditorWindow::pointSelected(const QPointF &p) const 0087 { 0088 QRectF rect = _top->selectionRect(); 0089 return rect.contains(p); 0090 } 0091 0092 void RegExpEditorWindow::mouseMoveEvent(QMouseEvent *event) 0093 { 0094 if (_isDndOperation) { 0095 if ((_start - event->pos()).manhattanLength() > QApplication::startDragDistance()) { 0096 RegExp *regexp = _top->selection(); 0097 if (!regexp) { 0098 return; 0099 } 0100 0101 QDrag *drag = new QDrag(this); 0102 QMimeData *mimeData = new QMimeData; 0103 0104 mimeData->setText(RegExpConverter::current()->toStr(regexp, false)); 0105 mimeData->setData(QStringLiteral("KRegExpEditor/widgetdrag"), regexp->toXmlString().toLocal8Bit()); 0106 delete regexp; 0107 0108 drag->setMimeData(mimeData); 0109 0110 Qt::DropAction dropAction = drag->exec(Qt::MoveAction | Qt::CopyAction, Qt::CopyAction); 0111 if (dropAction == Qt::MoveAction) { 0112 slotDeleteSelection(); 0113 } else { 0114 clearSelection(true); 0115 } 0116 0117 releaseMouse(); 0118 Q_EMIT change(); 0119 Q_EMIT canSave(_top->hasAnyChildren()); 0120 } 0121 } else { 0122 _top->updateSelection(false); 0123 0124 Q_EMIT scrolling(event->pos()); 0125 0126 _lastPoint = event->pos(); 0127 0128 update(); 0129 0130 _selection = QRectF(mapToGlobal(_start), mapToGlobal(_lastPoint)).normalized(); 0131 } 0132 } 0133 0134 void RegExpEditorWindow::mouseReleaseEvent(QMouseEvent *event) 0135 { 0136 releaseMouse(); 0137 QWidget::mouseReleaseEvent(event); 0138 0139 _lastPoint = QPoint(0, 0); 0140 0141 _top->validateSelection(); 0142 _top->updateAll(); 0143 Q_EMIT anythingSelected(hasSelection()); 0144 if (hasSelection()) { 0145 Q_EMIT verifyRegExp(); 0146 } 0147 } 0148 0149 bool RegExpEditorWindow::selectionOverlap(const QPointF &pos, QSize size) const 0150 { 0151 QRectF child(pos, size); 0152 0153 return _selection.intersects(child) && !child.contains(_selection); 0154 } 0155 0156 bool RegExpEditorWindow::hasSelection() const 0157 { 0158 return _top->hasSelection(); 0159 } 0160 0161 void RegExpEditorWindow::clearSelection(bool update) 0162 { 0163 _top->clearSelection(); 0164 if (update) { 0165 _top->updateAll(); 0166 } 0167 Q_EMIT anythingSelected(false); 0168 } 0169 0170 void RegExpEditorWindow::slotInsertRegExp(RegExpType type) 0171 { 0172 _insertInAction = true; 0173 _insertTp = type; 0174 0175 updateCursorUnderPoint(); 0176 setFocus(); 0177 } 0178 0179 void RegExpEditorWindow::slotInsertRegExp(RegExp *regexp) 0180 { 0181 delete _pasteData; 0182 0183 _pasteData = regexp->clone(); 0184 _pasteInAction = true; 0185 updateCursorUnderPoint(); 0186 setFocus(); 0187 } 0188 0189 void RegExpEditorWindow::slotDoSelect() 0190 { 0191 _pasteInAction = false; 0192 _insertInAction = false; 0193 0194 // I need to update the cursor recursively, as a repaint may not have been issued yet 0195 // when this method is invoked. This means that when the repaint comes, the cursor may 0196 // move to an other widget. 0197 _top->updateCursorRecursively(); 0198 } 0199 0200 void RegExpEditorWindow::slotDeleteSelection() 0201 { 0202 if (!hasSelection()) { 0203 KMessageBox::information(this, i18n("There is no selection."), i18n("Missing Selection")); 0204 } else { 0205 _top->deleteSelection(); 0206 } 0207 updateContent(nullptr); 0208 } 0209 0210 void RegExpEditorWindow::updateContent(QWidget *focusChild) 0211 { 0212 QPoint p(0, 0); 0213 if (focusChild) { 0214 p = focusChild->mapTo(this, QPoint(0, 0)); 0215 } 0216 0217 _top->update(); 0218 Q_EMIT contentChanged(p); 0219 } 0220 0221 QSize RegExpEditorWindow::sizeHint() const 0222 { 0223 return _top->sizeHint(); 0224 } 0225 0226 void RegExpEditorWindow::paintEvent(QPaintEvent *event) 0227 { 0228 QPainter p(this); 0229 0230 p.setPen(Qt::DotLine); 0231 0232 if (!_lastPoint.isNull()) { 0233 p.drawRect(QRectF(_start, _lastPoint)); 0234 } 0235 0236 QWidget::paintEvent(event); 0237 } 0238 0239 void RegExpEditorWindow::slotCut() 0240 { 0241 cut(QCursor::pos()); 0242 Q_EMIT change(); 0243 Q_EMIT canSave(_top->hasAnyChildren()); 0244 } 0245 0246 void RegExpEditorWindow::cut(QPointF pos) 0247 { 0248 cutCopyAux(pos); 0249 slotDeleteSelection(); 0250 } 0251 0252 void RegExpEditorWindow::slotCopy() 0253 { 0254 copy(QCursor::pos()); 0255 } 0256 0257 void RegExpEditorWindow::copy(QPointF pos) 0258 { 0259 cutCopyAux(pos); 0260 clearSelection(true); 0261 } 0262 0263 void RegExpEditorWindow::cutCopyAux(QPointF pos) 0264 { 0265 if (!hasSelection()) { 0266 RegExpWidget *widget = _top->widgetUnderPoint(pos, true); 0267 if (!widget) { 0268 KMessageBox::information(this, i18n("There is no widget under cursor."), i18n("Invalid Operation")); 0269 return; 0270 } else { 0271 widget->updateSelection(true); // HACK! 0272 } 0273 } 0274 0275 RegExp *regexp = _top->selection(); 0276 0277 QMimeData *mimeData = new QMimeData; 0278 mimeData->setText(RegExpConverter::current()->toStr(regexp, false)); 0279 mimeData->setData(QStringLiteral("KRegExpEditor/widgetdrag"), regexp->toXmlString().toLocal8Bit()); 0280 0281 delete regexp; 0282 0283 QClipboard *clipboard = qApp->clipboard(); 0284 clipboard->setMimeData(mimeData); 0285 Q_EMIT anythingOnClipboard(true); 0286 Q_EMIT canSave(_top->hasAnyChildren()); 0287 } 0288 0289 void RegExpEditorWindow::slotStartPasteAction() 0290 { 0291 QString str = QString::fromLatin1(qApp->clipboard()->mimeData()->data(QStringLiteral("KRegExpEditor/widgetdrag"))); 0292 if (str.isEmpty()) { 0293 return; 0294 } 0295 0296 RegExp *regexp = WidgetFactory::createRegExp(str); 0297 if (regexp) { 0298 slotInsertRegExp(regexp); 0299 } 0300 } 0301 0302 void RegExpEditorWindow::slotEndActions() 0303 { 0304 Q_EMIT doneEditing(); 0305 Q_EMIT change(); 0306 Q_EMIT canSave(_top->hasAnyChildren()); 0307 } 0308 0309 void RegExpEditorWindow::showRMBMenu(bool enableCutCopy) 0310 { 0311 enum CHOICES { CUT, COPY, PASTE, SAVE, EDIT }; 0312 0313 if (!_menu) { 0314 _menu = new QMenu(nullptr); 0315 0316 _cutAction = _menu->addAction(getIcon(QStringLiteral("edit-cut")), i18n("C&ut")); 0317 connect(_cutAction, &QAction::triggered, this, &RegExpEditorWindow::slotCut); 0318 0319 _copyAction = _menu->addAction(getIcon(QStringLiteral("edit-copy")), i18n("&Copy")); 0320 connect(_copyAction, &QAction::triggered, this, &RegExpEditorWindow::slotCopy); 0321 0322 _pasteAction = _menu->addAction(getIcon(QStringLiteral("edit-paste")), i18n("&Paste")); 0323 connect(_pasteAction, &QAction::triggered, this, &RegExpEditorWindow::slotStartPasteAction); 0324 0325 _menu->addSeparator(); 0326 0327 _editAction = _menu->addAction(getIcon(QStringLiteral("document-properties")), i18n("&Edit")); 0328 connect(_editAction, &QAction::triggered, this, &RegExpEditorWindow::editWidget); 0329 0330 _saveAction = _menu->addAction(getIcon(QStringLiteral("document-save")), i18n("&Save Regular Expression...")); 0331 connect(_saveAction, &QAction::triggered, this, &RegExpEditorWindow::slotSave); 0332 } 0333 0334 _cutAction->setEnabled(enableCutCopy); 0335 _copyAction->setEnabled(enableCutCopy); 0336 0337 if (!qApp->clipboard()->mimeData()->hasFormat(QStringLiteral("KRegExpEditor/widgetdrag"))) { 0338 _pasteAction->setEnabled(false); 0339 } else { 0340 _pasteAction->setEnabled(true); 0341 } 0342 0343 _saveAction->setEnabled(_top->hasAnyChildren()); 0344 0345 _PosEdit = QCursor::pos(); 0346 0347 RegExpWidget *editWidget = _top->findWidgetToEdit(_PosEdit); 0348 0349 _editAction->setEnabled(editWidget); 0350 0351 _menu->exec(_PosEdit.toPoint()); 0352 0353 _PosEdit = QPoint(0, 0); 0354 0355 Q_EMIT change(); 0356 Q_EMIT canSave(_top->hasAnyChildren()); 0357 } 0358 0359 void RegExpEditorWindow::applyRegExpToSelection(RegExpType tp) 0360 { 0361 _top->applyRegExpToSelection(tp); 0362 } 0363 0364 void RegExpEditorWindow::slotSave() 0365 { 0366 QString dir = WidgetWinItem::path(); 0367 QString txt; 0368 0369 bool ok = false; 0370 const QString tmp = QInputDialog::getText(this, i18n("Name for Regular Expression"), i18n("Enter name:"), QLineEdit::Normal, QString(), &ok); 0371 if (!ok) { 0372 return; 0373 } 0374 if (tmp.trimmed().isEmpty()) { 0375 KMessageBox::error(this, i18n("Empty name is not supported"), i18n("Save Regular Expression")); 0376 return; 0377 } 0378 txt = tmp; 0379 0380 QString fileName = dir + QLatin1Char('/') + txt + QStringLiteral(".regexp"); 0381 QFileInfo finfo(fileName); 0382 if (finfo.exists()) { 0383 int answer = KMessageBox::warningContinueCancel(this, 0384 i18n("<p>Overwrite named regular expression <b>%1</b></p>", txt), 0385 QString(), 0386 KStandardGuiItem::overwrite()); 0387 if (answer != KMessageBox::Continue) { 0388 return; 0389 } 0390 } 0391 0392 QFile file(fileName); 0393 if (!file.open(QIODevice::WriteOnly)) { 0394 KMessageBox::error(this, i18n("Could not open file for writing: %1", fileName)); 0395 return; 0396 } 0397 0398 // Convert to XML. 0399 RegExp *regexp = _top->regExp(); 0400 QString xml = regexp->toXmlString(); 0401 delete regexp; 0402 0403 QTextStream stream(&file); 0404 stream << xml; 0405 0406 file.close(); 0407 Q_EMIT savedRegexp(); 0408 } 0409 0410 void RegExpEditorWindow::slotSetRegExp(RegExp *regexp) 0411 { 0412 // I have no clue why the following line is necesarry, but if it is not here 0413 // then the editor area is messed up when calling slotSetRegExp before starting the eventloop. 0414 qApp->processEvents(); 0415 0416 delete _top; 0417 RegExpWidget *widget = WidgetFactory::createWidget(regexp, this, this); 0418 if (!(_top = dynamic_cast<ConcWidget *>(widget))) { 0419 // It was not a ConcWidget 0420 _top = new ConcWidget(this, widget, this); 0421 } 0422 _top->setToplevel(); 0423 0424 _top->show(); 0425 _layout->addWidget(_top); 0426 clearSelection(true); // HACK? 0427 Q_EMIT canSave(_top->hasAnyChildren()); 0428 } 0429 0430 void RegExpEditorWindow::updateCursorUnderPoint() 0431 { 0432 RegExpWidget *widget = _top->widgetUnderPoint(QCursor::pos(), false); 0433 if (widget) { 0434 widget->updateCursorShape(); 0435 } 0436 } 0437 0438 void RegExpEditorWindow::emitVerifyRegExp() 0439 { 0440 Q_EMIT verifyRegExp(); 0441 } 0442 0443 void RegExpEditorWindow::editWidget() 0444 { 0445 auto EditPos = _PosEdit.isNull() ? QCursor::pos() : _PosEdit; 0446 RegExpWidget *editWidget = _top->findWidgetToEdit(EditPos); 0447 if (editWidget) { 0448 editWidget->edit(); 0449 } 0450 } 0451 0452 QIcon RegExpEditorWindow::getIcon(const QString &name) 0453 { 0454 return QIcon::fromTheme(name); 0455 } 0456 0457 #include "moc_regexpeditorwindow.cpp"