File indexing completed on 2024-04-21 05:51:36
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 "concwidget.h" 0008 0009 #include <QMouseEvent> 0010 #include <QPainter> 0011 0012 #include "concregexp.h" 0013 #include "dragaccepter.h" 0014 0015 ConcWidget::ConcWidget(RegExpEditorWindow *editorWindow, QWidget *parent) 0016 : MultiContainerWidget(editorWindow, parent) 0017 { 0018 init(); 0019 DragAccepter *accepter = new DragAccepter(editorWindow, this); 0020 accepter->show(); 0021 _children.append(accepter); 0022 } 0023 0024 ConcWidget::ConcWidget(RegExpEditorWindow *editorWindow, RegExpWidget *child, QWidget *parent) 0025 : MultiContainerWidget(editorWindow, parent) 0026 { 0027 init(); 0028 DragAccepter *accepter = new DragAccepter(editorWindow, this); 0029 _children.append(accepter); 0030 child->setParent(this); 0031 addNewChild(accepter, child); 0032 } 0033 0034 ConcWidget::ConcWidget(RegExpEditorWindow *editorWindow, ConcWidget *origConc, unsigned int start, unsigned int end) 0035 : MultiContainerWidget(editorWindow, nullptr) 0036 { 0037 Q_UNUSED(start); 0038 Q_UNUSED(end); 0039 init(); 0040 _children.prepend(new DragAccepter(editorWindow, this)); 0041 QMutableListIterator<RegExpWidget *> i(origConc->_children); 0042 while (i.hasNext()) { 0043 RegExpWidget *child = i.next(); 0044 i.remove(); 0045 _children.prepend(child); 0046 child->setParent(this); 0047 } 0048 0049 _children.prepend(new DragAccepter(editorWindow, this)); 0050 } 0051 0052 ConcWidget::ConcWidget(ConcRegExp *regexp, RegExpEditorWindow *editorWindow, QWidget *parent) 0053 : MultiContainerWidget(editorWindow, parent) 0054 { 0055 init(); 0056 DragAccepter *accepter = new DragAccepter(editorWindow, this); 0057 _children.append(accepter); 0058 0059 const RegExpList list = regexp->children(); 0060 for (RegExp *r : list) { 0061 RegExpWidget *child = WidgetFactory::createWidget(r, editorWindow, this); 0062 append(child); 0063 } 0064 } 0065 0066 void ConcWidget::init() 0067 { 0068 _maxSelectedHeight = 0; 0069 } 0070 0071 QSize ConcWidget::sizeHint() const 0072 { 0073 int childrenWidth = 0; 0074 int childrenHeight = 0; 0075 0076 for (const RegExpWidget *regExpWidget : std::as_const(_children)) { 0077 QSize thisChildSize = regExpWidget->sizeHint(); 0078 childrenWidth += thisChildSize.width(); 0079 childrenHeight = qMax(childrenHeight, thisChildSize.height()); 0080 } 0081 0082 return QSize(childrenWidth, childrenHeight); 0083 } 0084 0085 void ConcWidget::paintEvent(QPaintEvent *e) 0086 { 0087 Q_ASSERT(dynamic_cast<DragAccepter *>(_children.at(0))); 0088 // if this fails, then I should check the location of the show() 0089 Q_ASSERT(_children.count() == 1 || (_children.count() >= 3 && dynamic_cast<DragAccepter *>(_children.at(_children.count() - 1)))); 0090 0091 if (_children.count() == 1) { 0092 // There is only an accepter, lets give it all the space. 0093 _children.at(0)->setGeometry(0, 0, size().width(), size().height()); 0094 } else { 0095 QSize myReqSize = sizeHint(); 0096 QSize mySize(qMax(myReqSize.width(), size().width()), qMax(myReqSize.height(), size().height())); 0097 0098 // If the widget needs less space than it can get then this space should 0099 // be given to the leftmost and rightmost accepter. So lets calculate 0100 // this extra space. 0101 int extra = 0; 0102 if (size().width() > myReqSize.width()) { 0103 extra = (size().width() - myReqSize.width()) / 2; 0104 } 0105 0106 QPainter painter(this); 0107 0108 drawPossibleSelection(painter, mySize); 0109 0110 int lastHeight = 0; 0111 int offset = 0; 0112 0113 for (int i = 1; i < _children.count(); i += 2) { 0114 DragAccepter *accepter = dynamic_cast<DragAccepter *>(_children.at(i - 1)); 0115 if (!accepter) { 0116 continue; 0117 } 0118 0119 RegExpWidget *child = _children.at(i); 0120 0121 QSize childSize = child->sizeHint(); 0122 QSize curChildSize = child->size(); 0123 0124 //----------------------------- first place the accepter 0125 int x = offset; 0126 int w = accepter->sizeHint().width(); 0127 if (i == 1) { 0128 w += extra; 0129 } 0130 0131 int h = qMax(lastHeight, childSize.height()); 0132 int y = (mySize.height() - h) / 2; 0133 accepter->setGeometry(x, y, w, h); 0134 0135 offset += w; 0136 lastHeight = childSize.height(); 0137 0138 //------------------------------ Draw Accepter selection 0139 if (accepter->isSelected()) { 0140 y = (mySize.height() - _maxSelectedHeight) / 2; 0141 h = _maxSelectedHeight; 0142 painter.fillRect(x, y, w, h, QBrush(Qt::gray)); 0143 } 0144 0145 //-------------------------------------- place the child 0146 x = offset; 0147 h = childSize.height(); 0148 w = childSize.width(); 0149 y = (mySize.height() - h) / 2; 0150 child->setGeometry(x, y, w, h); 0151 if (childSize != curChildSize) { 0152 // I resized the child, so give it a chance to relect thus. 0153 child->update(); 0154 } 0155 0156 offset += w; 0157 0158 //------------------------------ Draw Accepter selection 0159 if (child->isSelected()) { 0160 y = (mySize.height() - _maxSelectedHeight) / 2; 0161 h = _maxSelectedHeight; 0162 painter.fillRect(x, y, w, h, QBrush(Qt::gray)); 0163 } 0164 } 0165 0166 //---------------------- Finally place the last accepter. 0167 DragAccepter *accepter = static_cast<DragAccepter *>(_children.at(_children.count() - 1)); 0168 // dynamic_cast is ASSERTed at top 0169 int x = offset; 0170 int h = lastHeight; 0171 int w = accepter->sizeHint().width() + extra; 0172 int y = (mySize.height() - h) / 2; 0173 accepter->setGeometry(x, y, w, h); 0174 } 0175 MultiContainerWidget::paintEvent(e); 0176 } 0177 0178 void ConcWidget::mousePressEvent(QMouseEvent *event) 0179 { 0180 if (event->button() == Qt::RightButton) { 0181 _editorWindow->showRMBMenu(_editorWindow->hasSelection()); 0182 } else { 0183 RegExpWidget::mousePressEvent(event); 0184 } 0185 } 0186 0187 void ConcWidget::sizeAccepter(DragAccepter *accepter, int height, int totHeight) 0188 { 0189 if (accepter->height() != height) { 0190 accepter->resize(accepter->width(), height); 0191 } 0192 0193 if (accepter->y() != (totHeight - height) / 2) { 0194 accepter->move(accepter->x(), (totHeight - height) / 2); 0195 } 0196 } 0197 0198 RegExp *ConcWidget::regExp() const 0199 { 0200 QList<RegExpWidget *>::const_iterator it = _children.constBegin(); 0201 ++it; // start with the second element. 0202 0203 if (_children.count() == 3) { 0204 // Exactly one child (and two drag accepters) 0205 return (*it)->regExp(); 0206 } else { 0207 ConcRegExp *regexp = new ConcRegExp(isSelected()); 0208 0209 for (; it != _children.constEnd(); it += 2) { 0210 regexp->addRegExp((*it)->regExp()); 0211 } 0212 return regexp; 0213 } 0214 } 0215 0216 bool ConcWidget::updateSelection(bool parentSelected) 0217 { 0218 bool isSel = _isSelected; 0219 bool changed = MultiContainerWidget::updateSelection(parentSelected); 0220 0221 _maxSelectedHeight = 0; 0222 0223 QList<RegExpWidget *>::const_iterator it = _children.constBegin(); 0224 ++it; // Skip past the first DragAccepter 0225 for (; it != _children.constEnd(); it += 2) { 0226 if ((*it)->isSelected()) { 0227 _maxSelectedHeight = qMax(_maxSelectedHeight, (*it)->sizeHint().height()); 0228 } 0229 } 0230 0231 changed = changed || isSel != _isSelected; 0232 if (changed) { 0233 repaint(); 0234 } 0235 0236 return changed; 0237 } 0238 0239 void ConcWidget::getSelectionIndexes(int *start, int *end) 0240 { 0241 *start = -1; 0242 *end = -1; 0243 0244 // Start with element at index 1, and skip every second element, as we 0245 // know they are dragAccepters. 0246 for (int index = 1; index < _children.count(); index += 2) { 0247 RegExpWidget *child = _children.at(index); 0248 0249 if (child->isSelected()) { 0250 // The child is selected at topmost level. 0251 if (*start == -1) { 0252 *start = index; 0253 } 0254 } else if (*start != -1) { 0255 // Found the end of the selection. 0256 *end = index - 2; 0257 break; 0258 } 0259 } 0260 0261 if (*start != -1 && *end == -1) { 0262 *end = _children.count() - 2; 0263 } 0264 } 0265 0266 void ConcWidget::applyRegExpToSelection(RegExpType type) 0267 { 0268 int start, end; 0269 getSelectionIndexes(&start, &end); 0270 0271 if (start == -1) { 0272 // No item selected at top level 0273 0274 QList<RegExpWidget *>::const_iterator it = _children.constBegin(); 0275 ++it; // Skip past the first DragAccepter 0276 for (; it != _children.constEnd(); it += 2) { 0277 if ((*it)->hasSelection()) { 0278 (*it)->applyRegExpToSelection(type); 0279 break; 0280 } 0281 } 0282 } else { 0283 // Apply RegExp to selection. 0284 RegExpWidget *newElm = WidgetFactory::createWidget(_editorWindow, this, type); 0285 0286 if (newElm) { 0287 ConcWidget *subSequence = new ConcWidget(_editorWindow, this, start, end); 0288 newElm->setConcChild(subSequence); 0289 0290 subSequence->resize(0, 0); // see note (1) 0291 subSequence->setParent(newElm); 0292 _children.insert(start, newElm); 0293 newElm->show(); 0294 } 0295 } 0296 } 0297 0298 bool ConcWidget::isSelected() const 0299 { 0300 // A ConcWidget should be considered selected when all its elements has been selected 0301 // otherwise empty ConcWidgets may be left behind when for example selection is deleted. 0302 bool allSelected = true; 0303 QList<RegExpWidget *>::const_iterator it = _children.constBegin(); 0304 ++it; // Skip past first DragAccepter. 0305 for (; (it != _children.constEnd()) && allSelected; it += 2) { 0306 allSelected = (*it)->isSelected(); 0307 } 0308 0309 return allSelected; 0310 } 0311 0312 RegExp *ConcWidget::selection() const 0313 { 0314 if (isSelected()) { 0315 return regExp(); 0316 } 0317 0318 bool foundAny = false; 0319 bool foundMoreThanOne = false; 0320 RegExp *regexp = nullptr; 0321 0322 QList<RegExpWidget *>::const_iterator it = _children.constBegin(); 0323 ++it; // Skip past the first DragAccepter 0324 for (; it != _children.constEnd(); it += 2) { 0325 if ((*it)->hasSelection()) { 0326 if (!foundAny) { 0327 regexp = (*it)->selection(); 0328 foundAny = true; 0329 } else if (!foundMoreThanOne) { 0330 ConcRegExp *reg = new ConcRegExp(isSelected()); 0331 reg->addRegExp(regexp); 0332 reg->addRegExp((*it)->selection()); 0333 regexp = reg; 0334 foundMoreThanOne = true; 0335 } else { 0336 dynamic_cast<ConcRegExp *>(regexp)->addRegExp((*it)->selection()); 0337 } 0338 } 0339 } 0340 0341 Q_ASSERT(foundAny); 0342 return regexp; 0343 } 0344 0345 void ConcWidget::addNewConcChild(DragAccepter *accepter, ConcWidget *other) 0346 { 0347 for (int i = 0; i < _children.count(); i += 2) { 0348 RegExpWidget *ch = _children.at(i); 0349 if (ch == accepter) { 0350 // Move all the element from the `child' ConcWidget to this one. 0351 // Do not copy the first one as this is a dragAccepter, and we place the widgets 0352 // after this drag accepter. 0353 // We must take them in pairs to avoid breaking the invariant for paintEvent, 0354 // namely that every second element is a dragAccepter 0355 for (unsigned int j = other->_children.count() - 1; j > 0; j -= 2) { 0356 RegExpWidget *newChildA = other->_children.takeAt(j); 0357 newChildA->setParent(this); 0358 _children.insert(i + 1, newChildA); 0359 RegExpWidget *newChildB = other->_children.takeAt(j - 1); 0360 newChildB->setParent(this); 0361 _children.insert(i + 1, newChildB); 0362 newChildA->show(); 0363 newChildB->show(); 0364 } 0365 delete other; 0366 return; 0367 } 0368 } 0369 qFatal("accepter not found"); 0370 } 0371 0372 bool ConcWidget::validateSelection() const 0373 { 0374 bool cont = true; 0375 QList<RegExpWidget *>::const_iterator it = _children.constBegin(); 0376 ++it; // skip past the DragAccepter. 0377 for (; (it != _children.constEnd()) && cont; it += 2) { 0378 cont = (*it)->validateSelection(); 0379 } 0380 return cont; 0381 }