File indexing completed on 2024-05-12 16:39:44
0001 /* This file is part of the KDE project 0002 Copyright (C) 2004 Cedric Pasteur <cedric.pasteur@free.fr> 0003 Copyright (C) 2005-2010 Jarosław Staniek <staniek@kde.org> 0004 0005 This library is free software; you can redistribute it and/or 0006 modify it under the terms of the GNU Library General Public 0007 License as published by the Free Software Foundation; either 0008 version 2 of the License, or (at your option) any later version. 0009 0010 This library is distributed in the hope that it will be useful, 0011 but WITHOUT ANY WARRANTY; without even the implied warranty of 0012 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0013 Library General Public License for more details. 0014 0015 You should have received a copy of the GNU Library General Public License 0016 along with this library; see the file COPYING.LIB. If not, write to 0017 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 0018 * Boston, MA 02110-1301, USA. 0019 */ 0020 0021 #include <QLayout> 0022 #include <QClipboard> 0023 #include <QApplication> 0024 #include <QDomDocument> 0025 #include <QStackedWidget> 0026 #include <QDebug> 0027 #include <QMimeData> 0028 0029 #include <KMessageBox> 0030 #include <KAcceleratorManager> 0031 #include <KLocalizedString> 0032 0033 #include <KPropertySet> 0034 0035 #include "kformdesigner_export.h" 0036 #include "WidgetInfo.h" 0037 #include "formIO.h" 0038 #include "container.h" 0039 #include "objecttree.h" 0040 #include "form.h" 0041 #include "widgetlibrary.h" 0042 #include "events.h" 0043 #include "utils.h" 0044 #include "widgetwithsubpropertiesinterface.h" 0045 #include <kexiutils/utils.h> 0046 0047 #include "commands.h" 0048 0049 #include <memory> 0050 #include <limits.h> 0051 0052 using namespace KFormDesigner; 0053 0054 // Command 0055 0056 Command::Command(Command *parent) 0057 : KUndo2Command(parent) 0058 , m_blockRedoOnce(false) 0059 { 0060 } 0061 0062 Command::Command(const QString &text, Command *parent) 0063 : KUndo2Command(parent) 0064 , m_blockRedoOnce(false) 0065 { 0066 Q_UNUSED(text); 0067 } 0068 0069 Command::~Command() 0070 { 0071 } 0072 0073 void Command::blockRedoOnce() 0074 { 0075 m_blockRedoOnce = true; 0076 } 0077 0078 void Command::redo() 0079 { 0080 if (m_blockRedoOnce) { 0081 m_blockRedoOnce = false; 0082 return; 0083 } 0084 execute(); 0085 } 0086 0087 void Command::debug() const 0088 { 0089 qDebug() << *this; 0090 } 0091 0092 //! qDebug() stream operator. Writes command @a c to the debug output in a nicely formatted way. 0093 KFORMDESIGNER_EXPORT QDebug KFormDesigner::operator<<(QDebug dbg, const Command &c) 0094 { 0095 dbg.nospace() << "Command"; 0096 const int count = c.childCount(); 0097 dbg.space() << "name=" << c.text() << "#=" << count; 0098 for (int i = 0; i < count; i++) { 0099 dbg.nospace() << "- subcommand" << i+1 << ":" << *static_cast<const Command*>(c.child(i)) << "\n"; 0100 } 0101 return dbg.space(); 0102 } 0103 0104 // PropertyCommand 0105 0106 namespace KFormDesigner 0107 { 0108 class Q_DECL_HIDDEN PropertyCommand::Private 0109 { 0110 public: 0111 Private() 0112 : uniqueId(0) 0113 { 0114 } 0115 0116 Form *form; 0117 QVariant value; 0118 QHash<QByteArray, QVariant> oldValues; //!< (widget_name -> value) hash 0119 QByteArray propertyName; 0120 int uniqueId; 0121 }; 0122 } 0123 0124 PropertyCommand::PropertyCommand(Form& form, const QByteArray &wname, 0125 const QVariant &oldValue, const QVariant &value, 0126 const QByteArray &propertyName, 0127 Command *parent) 0128 : Command(parent), d( new Private ) 0129 { 0130 d->form = &form; 0131 d->value = value; 0132 d->propertyName = propertyName; 0133 d->oldValues.insert(wname, oldValue); 0134 init(); 0135 } 0136 0137 PropertyCommand::PropertyCommand(Form& form, const QHash<QByteArray, QVariant> &oldValues, 0138 const QVariant &value, const QByteArray &propertyName, 0139 Command *parent) 0140 : Command(parent), d( new Private ) 0141 { 0142 d->form = &form; 0143 d->value = value; 0144 d->propertyName = propertyName; 0145 d->oldValues = oldValues; 0146 init(); 0147 } 0148 0149 PropertyCommand::~PropertyCommand() 0150 { 0151 delete d; 0152 } 0153 0154 void PropertyCommand::init() 0155 { 0156 if (d->oldValues.count() > 1) { 0157 setText(kundo2_i18n("Change <resource>%1</resource> property for multiple widgets", 0158 QString(d->propertyName))); 0159 } 0160 else { 0161 setText(kundo2_i18n("Change <resource>%1</resource> property for widget <resource>%2</resource>", 0162 QString(d->propertyName), QString(d->oldValues.constBegin().key()))); 0163 } 0164 } 0165 0166 void PropertyCommand::debug() const 0167 { 0168 qDebug() << *this; 0169 } 0170 0171 Form* PropertyCommand::form() const 0172 { 0173 return d->form; 0174 } 0175 0176 int PropertyCommand::id() const 0177 { 0178 return 1; 0179 } 0180 0181 QVariant PropertyCommand::value() const 0182 { 0183 return d->value; 0184 } 0185 0186 void PropertyCommand::setValue(const QVariant &value) 0187 { 0188 d->value = value; 0189 } 0190 0191 void PropertyCommand::setUniqueId(int id) 0192 { 0193 d->uniqueId = id; 0194 } 0195 0196 void PropertyCommand::execute() 0197 { 0198 QWidget *selected = d->form->selectedWidget(); 0199 bool reSelectWidgets = true; 0200 if (selected 0201 && d->oldValues.count() == 1 0202 && d->oldValues.contains(selected->objectName().toLatin1()) ) 0203 { 0204 // do not reselect widget; this e.g. avoids removing resize handles 0205 reSelectWidgets = false; 0206 } 0207 0208 if (reSelectWidgets) { 0209 d->form->selectFormWidget(); 0210 } 0211 0212 d->form->setUndoing(true); 0213 0214 if (reSelectWidgets) { 0215 d->form->selectWidgets(d->oldValues.keys(), Form::ReplacePreviousSelection); 0216 } 0217 0218 // set property for multiple widgets 0219 for (QHash<QByteArray, QVariant>::ConstIterator oldValuesIt( d->oldValues.constBegin() ); 0220 oldValuesIt != d->oldValues.constEnd(); ++oldValuesIt) 0221 { 0222 ObjectTreeItem* item = d->form->objectTree()->lookup(oldValuesIt.key()); 0223 if (item) { //we're checking for item!=0 because the name could be of a form widget 0224 QWidget *widget = item->widget(); 0225 WidgetWithSubpropertiesInterface* subpropIface = dynamic_cast<WidgetWithSubpropertiesInterface*>(widget); 0226 QWidget *subWidget = (subpropIface && subpropIface->subwidget()) ? subpropIface->subwidget() : widget; 0227 if (subWidget && -1 != subWidget->metaObject()->indexOfProperty(d->propertyName)) { 0228 item->widget()->setProperty(d->propertyName, d->value); 0229 } 0230 } 0231 } 0232 d->form->propertySet()->changeProperty(d->propertyName, d->value); 0233 d->form->setUndoing(false); 0234 } 0235 0236 void PropertyCommand::undo() 0237 { 0238 d->form->selectFormWidget(); 0239 d->form->setUndoing(true); 0240 0241 QHash<QByteArray, QVariant>::ConstIterator endIt = d->oldValues.constEnd(); 0242 for (QHash<QByteArray, QVariant>::ConstIterator it = d->oldValues.constBegin(); it != endIt; ++it) { 0243 ObjectTreeItem* item = d->form->objectTree()->lookup(it.key()); 0244 if (!item) 0245 continue; //better this than a crash 0246 QWidget *widget = item->widget(); 0247 d->form->selectWidget(widget, Form::AddToPreviousSelection | Form::LastSelection | Form::Raise); 0248 0249 WidgetWithSubpropertiesInterface* subpropIface = dynamic_cast<WidgetWithSubpropertiesInterface*>(widget); 0250 QWidget *subWidget = (subpropIface && subpropIface->subwidget()) ? subpropIface->subwidget() : widget; 0251 if (subWidget && -1 != subWidget->metaObject()->indexOfProperty(d->propertyName)) { 0252 qDebug() << "OLD" << d->propertyName << subWidget->property(d->propertyName); 0253 qDebug() << "NEW" << d->propertyName << it.value(); 0254 subWidget->setProperty(d->propertyName, it.value()); 0255 } 0256 } 0257 0258 d->form->propertySet()->changeProperty(d->propertyName, d->oldValues.constBegin().value()); 0259 d->form->setUndoing(false); 0260 } 0261 0262 bool PropertyCommand::mergeWith(const KUndo2Command * command) 0263 { 0264 if (id() != command->id()) 0265 return false; 0266 const PropertyCommand* propertyCommand = static_cast<const PropertyCommand*>(command); 0267 if (d->uniqueId > 0 && propertyCommand->d->uniqueId == d->uniqueId) { 0268 if (d->oldValues.count() == propertyCommand->d->oldValues.count()) { 0269 d->value = propertyCommand->value(); 0270 return true; 0271 } 0272 } 0273 return false; 0274 } 0275 0276 QByteArray PropertyCommand::propertyName() const 0277 { 0278 return d->propertyName; 0279 } 0280 0281 const QHash<QByteArray, QVariant>& PropertyCommand::oldValues() const 0282 { 0283 return d->oldValues; 0284 } 0285 0286 QByteArray PropertyCommand::widgetName() const 0287 { 0288 if (d->oldValues.count() != 1) 0289 return QByteArray(); 0290 return d->oldValues.keys().first(); 0291 } 0292 0293 QVariant PropertyCommand::oldValue() const 0294 { 0295 if (d->oldValues.count() != 1) 0296 return QVariant(); 0297 return d->oldValues.constBegin().value(); 0298 } 0299 0300 KFORMDESIGNER_EXPORT QDebug KFormDesigner::operator<<(QDebug dbg, const PropertyCommand &c) 0301 { 0302 dbg.nospace() << "PropertyCommand text=" << c.text() << "widgets=" << c.oldValues().keys() 0303 << "value=" << c.value() << "oldValues=" << c.oldValues().values(); 0304 return dbg.space(); 0305 } 0306 0307 // GeometryPropertyCommand (for multiple widgets) 0308 0309 namespace KFormDesigner 0310 { 0311 class Q_DECL_HIDDEN GeometryPropertyCommand::Private 0312 { 0313 public: 0314 Private() 0315 { 0316 } 0317 0318 Form *form; 0319 QStringList names; 0320 QPoint pos; 0321 QPoint oldPos; 0322 }; 0323 } 0324 0325 GeometryPropertyCommand::GeometryPropertyCommand(Form& form, 0326 const QStringList &names, 0327 const QPoint& oldPos, 0328 Command *parent) 0329 : Command(parent), d( new Private ) 0330 { 0331 d->form = &form; 0332 d->names = names; 0333 d->oldPos = oldPos; 0334 setText( kundo2_i18n("Move multiple widgets") ); 0335 } 0336 0337 GeometryPropertyCommand::~GeometryPropertyCommand() 0338 { 0339 delete d; 0340 } 0341 0342 int GeometryPropertyCommand::id() const 0343 { 0344 return 2; 0345 } 0346 0347 void GeometryPropertyCommand::debug() const 0348 { 0349 qDebug() << *this; 0350 } 0351 0352 void GeometryPropertyCommand::execute() 0353 { 0354 d->form->setUndoing(true); 0355 int dx = d->pos.x() - d->oldPos.x(); 0356 int dy = d->pos.y() - d->oldPos.y(); 0357 0358 // We move every widget in our list by (dx, dy) 0359 foreach (const QString& widgetName, d->names) { 0360 ObjectTreeItem* item = d->form->objectTree()->lookup(widgetName); 0361 if (!item) 0362 continue; //better this than a crash 0363 QWidget *w = item->widget(); 0364 w->move(w->x() + dx, w->y() + dy); 0365 } 0366 d->form->setUndoing(false); 0367 } 0368 0369 void GeometryPropertyCommand::undo() 0370 { 0371 d->form->setUndoing(true); 0372 int dx = d->pos.x() - d->oldPos.x(); 0373 int dy = d->pos.y() - d->oldPos.y(); 0374 0375 // We move every widget in our list by (-dx, -dy) to undo the move 0376 foreach (const QString& widgetName, d->names) { 0377 ObjectTreeItem* item = d->form->objectTree()->lookup(widgetName); 0378 if (!item) 0379 continue; //better this than a crash 0380 QWidget *w = item->widget(); 0381 w->move(w->x() - dx, w->y() - dy); 0382 } 0383 d->form->setUndoing(false); 0384 } 0385 0386 QPoint GeometryPropertyCommand::pos() const 0387 { 0388 return d->pos; 0389 } 0390 0391 void GeometryPropertyCommand::setPos(const QPoint& pos) 0392 { 0393 d->pos = pos; 0394 } 0395 0396 QPoint GeometryPropertyCommand::oldPos() const 0397 { 0398 return d->oldPos; 0399 } 0400 0401 KFORMDESIGNER_EXPORT QDebug KFormDesigner::operator<<(QDebug dbg, const GeometryPropertyCommand &c) 0402 { 0403 dbg.nospace() << "GeometryPropertyCommand pos=" << c.pos() << "oldPos=" << c.oldPos() 0404 << "widgets=" << c.d->names; 0405 return dbg.space(); 0406 } 0407 0408 // AlignWidgetsCommand 0409 0410 namespace KFormDesigner 0411 { 0412 class Q_DECL_HIDDEN AlignWidgetsCommand::Private 0413 { 0414 public: 0415 Private() 0416 { 0417 } 0418 0419 Form *form; 0420 Form::WidgetAlignment alignment; 0421 QHash<QByteArray, QPoint> pos; 0422 }; 0423 } 0424 0425 AlignWidgetsCommand::AlignWidgetsCommand(Form &form, Form::WidgetAlignment alignment, 0426 const QWidgetList &list, Command *parent) 0427 : Command(parent), d( new Private ) 0428 { 0429 d->form = &form; 0430 d->alignment = alignment; 0431 foreach (QWidget *w, list) { 0432 d->pos.insert(qPrintable(w->objectName()), w->pos()); 0433 } 0434 0435 switch (d->alignment) { 0436 case Form::AlignToGrid: 0437 setText( kundo2_i18n("Align Widgets to Grid") ); 0438 break; 0439 case Form::AlignToLeft: 0440 setText( kundo2_i18n("Align Widgets to Left") ); 0441 break; 0442 case Form::AlignToRight: 0443 setText( kundo2_i18n("Align Widgets to Right") ); 0444 break; 0445 case Form::AlignToTop: 0446 setText( kundo2_i18n("Align Widgets to Top") ); 0447 break; 0448 case Form::AlignToBottom: 0449 setText( kundo2_i18n("Align Widgets to Bottom") ); 0450 break; 0451 default:; 0452 } 0453 } 0454 0455 AlignWidgetsCommand::~AlignWidgetsCommand() 0456 { 0457 delete d; 0458 } 0459 0460 int AlignWidgetsCommand::id() const 0461 { 0462 return 3; 0463 } 0464 0465 void AlignWidgetsCommand::debug() const 0466 { 0467 qDebug() << *this; 0468 } 0469 0470 void AlignWidgetsCommand::execute() 0471 { 0472 // To avoid creation of GeometryPropertyCommand 0473 d->form->selectFormWidget(); 0474 0475 QWidgetList list; 0476 foreach (const QByteArray& name, d->pos.keys()) { 0477 ObjectTreeItem *item = d->form->objectTree()->lookup(name); 0478 if (item && item->widget()) 0479 list.append(item->widget()); 0480 } 0481 0482 const int gridX = d->form->gridSize(); 0483 const int gridY = d->form->gridSize(); 0484 QWidget *parentWidget = d->form->selectedWidgets()->first()->parentWidget(); 0485 0486 switch (d->alignment) { 0487 case Form::AlignToGrid: { 0488 foreach (QWidget *w, list) { 0489 const int tmpx = alignValueToGrid(w->x(), gridX); 0490 const int tmpy = alignValueToGrid(w->y(), gridY); 0491 if ((tmpx != w->x()) || (tmpy != w->y())) 0492 w->move(tmpx, tmpy); 0493 } 0494 break; 0495 } 0496 case Form::AlignToLeft: { 0497 int tmpx = parentWidget->width(); 0498 foreach (QWidget *w, list) { 0499 if (w->x() < tmpx) 0500 tmpx = w->x(); 0501 } 0502 foreach (QWidget *w, list) { 0503 w->move(tmpx, w->y()); 0504 } 0505 break; 0506 } 0507 case Form::AlignToRight: { 0508 int tmpx = 0; 0509 foreach (QWidget *w, list) { 0510 if (w->x() + w->width() > tmpx) 0511 tmpx = w->x() + w->width(); 0512 } 0513 foreach (QWidget *w, list) { 0514 w->move(tmpx - w->width(), w->y()); 0515 } 0516 break; 0517 } 0518 case Form::AlignToTop: { 0519 int tmpy = parentWidget->height(); 0520 foreach (QWidget *w, list) { 0521 if (w->y() < tmpy) 0522 tmpy = w->y(); 0523 } 0524 foreach (QWidget *w, list) { 0525 w->move(w->x(), tmpy); 0526 } 0527 break; 0528 } 0529 case Form::AlignToBottom: { 0530 int tmpy = 0; 0531 foreach (QWidget *w, list) { 0532 if (w->y() + w->height() > tmpy) 0533 tmpy = w->y() + w->height(); 0534 } 0535 foreach (QWidget *w, list) { 0536 w->move(w->x(), tmpy - w->height()); 0537 } 0538 break; 0539 } 0540 default: 0541 return; 0542 } 0543 0544 // We restore selection 0545 foreach (QWidget *w, list) { 0546 d->form->selectWidget(w, Form::AddToPreviousSelection | Form::LastSelection | Form::Raise); 0547 } 0548 } 0549 0550 void AlignWidgetsCommand::undo() 0551 { 0552 // To avoid creation of GeometryPropertyCommand 0553 d->form->selectFormWidget(); 0554 // We move widgets to their original pos 0555 QHash<QByteArray, QPoint>::ConstIterator endIt = d->pos.constEnd(); 0556 for (QHash<QByteArray, QPoint>::ConstIterator it = d->pos.constBegin(); it != endIt; ++it) { 0557 ObjectTreeItem *item = d->form->objectTree()->lookup(it.key()); 0558 if (item && item->widget()) 0559 { 0560 item->widget()->move(d->pos.value(qPrintable(item->widget()->objectName()))); 0561 // we restore selection 0562 d->form->selectWidget(item->widget(), Form::AddToPreviousSelection | Form::LastSelection | Form::Raise); 0563 } 0564 } 0565 } 0566 0567 KFORMDESIGNER_EXPORT QDebug KFormDesigner::operator<<(QDebug dbg, const AlignWidgetsCommand &c) 0568 { 0569 dbg.nospace() << "AlignWidgetsCommand text=" << c.text() << "form=" << c.d->form->widget()->objectName() 0570 << "widgets=" << c.d->pos.keys(); 0571 return dbg.space(); 0572 } 0573 0574 // AdjustSizeCommand 0575 0576 namespace KFormDesigner 0577 { 0578 class Q_DECL_HIDDEN AdjustSizeCommand::Private 0579 { 0580 public: 0581 Private() 0582 { 0583 } 0584 0585 Form *form; 0586 AdjustSizeCommand::Adjustment type; 0587 QHash<QByteArray, QPoint> pos; 0588 QHash<QByteArray, QSize> sizes; 0589 }; 0590 } 0591 0592 AdjustSizeCommand::AdjustSizeCommand(Form& form, Adjustment type, const QWidgetList &list, 0593 Command *parent) 0594 : Command(parent), d( new Private ) 0595 { 0596 d->form = &form; 0597 d->type = type; 0598 foreach (QWidget *w, list) { 0599 if (w->parentWidget() && KexiUtils::objectIsA(w->parentWidget(), "QStackedWidget")) { 0600 w = w->parentWidget(); // widget is WidgetStack page 0601 if (w->parentWidget() && w->parentWidget()->inherits("QTabWidget")) // widget is tabwidget page 0602 w = w->parentWidget(); 0603 } 0604 0605 d->sizes.insert(qPrintable(w->objectName()), w->size()); 0606 if (d->type == SizeToGrid) // SizeToGrid also move widgets 0607 d->pos.insert(qPrintable(w->objectName()), w->pos()); 0608 } 0609 0610 switch (d->type) { 0611 case SizeToGrid: 0612 setText( kundo2_i18n("Resize Widgets to Grid") ); 0613 break; 0614 case SizeToFit: 0615 setText( kundo2_i18n("Resize Widgets to Fit Contents") ); 0616 break; 0617 case SizeToSmallWidth: 0618 setText( kundo2_i18n("Resize Widgets to Narrowest") ); 0619 break; 0620 case SizeToBigWidth: 0621 setText( kundo2_i18n("Resize Widgets to Widest") ); 0622 break; 0623 case SizeToSmallHeight: 0624 setText( kundo2_i18n("Resize Widgets to Shortest") ); 0625 break; 0626 case SizeToBigHeight: 0627 setText( kundo2_i18n("Resize Widgets to Tallest") ); 0628 break; 0629 default:; 0630 } 0631 } 0632 0633 AdjustSizeCommand::~AdjustSizeCommand() 0634 { 0635 delete d; 0636 } 0637 0638 int AdjustSizeCommand::id() const 0639 { 0640 return 4; 0641 } 0642 0643 void AdjustSizeCommand::debug() const 0644 { 0645 qDebug() << *this; 0646 } 0647 0648 void AdjustSizeCommand::execute() 0649 { 0650 // To avoid creation of GeometryPropertyCommand 0651 d->form->selectFormWidget(); 0652 0653 int gridX = d->form->gridSize(); 0654 int gridY = d->form->gridSize(); 0655 int tmpw = 0, tmph = 0; 0656 0657 QWidgetList list; 0658 QHash<QByteArray, QSize>::ConstIterator endIt = d->sizes.constEnd(); 0659 for (QHash<QByteArray, QSize>::ConstIterator it = d->sizes.constBegin(); it != endIt; ++it) { 0660 ObjectTreeItem *item = d->form->objectTree()->lookup(it.key()); 0661 if (item && item->widget()) 0662 list.append(item->widget()); 0663 } 0664 0665 switch (d->type) { 0666 case SizeToGrid: { 0667 // same as in 'Align to Grid' + for the size 0668 foreach (QWidget *w, list) { 0669 int tmpx = alignValueToGrid(w->x(), gridX); 0670 int tmpy = alignValueToGrid(w->y(), gridY); 0671 tmpw = alignValueToGrid(w->width(), gridX); 0672 tmph = alignValueToGrid(w->height(), gridY); 0673 if ((tmpx != w->x()) || (tmpy != w->y())) 0674 w->move(tmpx, tmpy); 0675 if ((tmpw != w->width()) || (tmph != w->height())) 0676 w->resize(tmpw, tmph); 0677 } 0678 break; 0679 } 0680 0681 case SizeToFit: { 0682 foreach (QWidget *w, list) { 0683 ObjectTreeItem *item = d->form->objectTree()->lookup(w->objectName()); 0684 if (item && !item->children()->isEmpty()) { // container 0685 QSize s; 0686 if (item->container() && item->container()->layout()) 0687 s = w->sizeHint(); 0688 else 0689 s = getSizeFromChildren(item); 0690 // minimum size for containers 0691 if (s.width() < 30) 0692 s.setWidth(30); 0693 if (s.height() < 30) 0694 s.setHeight(30); 0695 // small hack for flow layouts 0696 int type = item->container() ? item->container()->layoutType() : Form::NoLayout; 0697 if (type == Form::HFlow) 0698 s.setWidth(s.width() + 5); 0699 else if (type == Form::VFlow) 0700 s.setHeight(s.height() + 5); 0701 w->resize(s); 0702 } 0703 else if (item && item->container()) { // empty container 0704 w->resize(item->container()->form()->gridSize() * 5, 0705 item->container()->form()->gridSize() * 5); // basic size 0706 } 0707 else { 0708 QSize sizeHint(w->sizeHint()); 0709 if (sizeHint.isValid()) 0710 w->resize(sizeHint); 0711 } 0712 } 0713 break; 0714 } 0715 0716 case SizeToSmallWidth: { 0717 foreach (QWidget *w, list) { 0718 if ((tmpw == 0) || (w->width() < tmpw)) 0719 tmpw = w->width(); 0720 } 0721 0722 foreach (QWidget *w, list) { 0723 if (tmpw != w->width()) 0724 w->resize(tmpw, w->height()); 0725 } 0726 break; 0727 } 0728 0729 case SizeToBigWidth: { 0730 foreach (QWidget *w, list) { 0731 if (w->width() > tmpw) 0732 tmpw = w->width(); 0733 } 0734 0735 foreach (QWidget *w, list) { 0736 if (tmpw != w->width()) 0737 w->resize(tmpw, w->height()); 0738 } 0739 break; 0740 } 0741 0742 case SizeToSmallHeight: { 0743 foreach (QWidget *w, list) { 0744 if ((tmph == 0) || (w->height() < tmph)) 0745 tmph = w->height(); 0746 } 0747 0748 foreach (QWidget *w, list) { 0749 if (tmph != w->height()) 0750 w->resize(w->width(), tmph); 0751 } 0752 break; 0753 } 0754 0755 case SizeToBigHeight: { 0756 foreach (QWidget *w, list) { 0757 if (w->height() > tmph) 0758 tmph = w->height(); 0759 } 0760 0761 foreach (QWidget *w, list) { 0762 if (tmph != w->height()) 0763 w->resize(w->width(), tmph); 0764 } 0765 break; 0766 } 0767 0768 default: 0769 break; 0770 } 0771 0772 // We restore selection 0773 foreach (QWidget *w, list) { 0774 d->form->selectWidget(w, Form::AddToPreviousSelection | Form::LastSelection | Form::Raise); 0775 } 0776 } 0777 0778 QSize AdjustSizeCommand::getSizeFromChildren(ObjectTreeItem *item) 0779 { 0780 if (!item->container()) { // multi pages containers (eg tabwidget) 0781 QSize s; 0782 // get size for each container, and keep the biggest one 0783 foreach (ObjectTreeItem *titem, *item->children()) { 0784 s = s.expandedTo(getSizeFromChildren(titem)); 0785 } 0786 return s; 0787 } 0788 0789 int tmpw = 0, tmph = 0; 0790 foreach (ObjectTreeItem *titem, *item->children()) { 0791 if (!titem->widget()) 0792 continue; 0793 tmpw = qMax(tmpw, titem->widget()->geometry().right()); 0794 tmph = qMax(tmph, titem->widget()->geometry().bottom()); 0795 } 0796 0797 return QSize(tmpw, tmph) + QSize(10, 10); 0798 } 0799 0800 void AdjustSizeCommand::undo() 0801 { 0802 // To avoid creation of GeometryPropertyCommand 0803 d->form->selectFormWidget(); 0804 // We resize widgets to their original size 0805 QHash<QByteArray, QSize>::ConstIterator endIt = d->sizes.constEnd(); 0806 for (QHash<QByteArray, QSize>::ConstIterator it = d->sizes.constBegin(); it != endIt; ++it) { 0807 ObjectTreeItem *item = d->form->objectTree()->lookup(it.key()); 0808 if (item && item->widget()) { 0809 item->widget()->resize(d->sizes[qPrintable(item->widget()->objectName())]); 0810 if (d->type == SizeToGrid) 0811 item->widget()->move(d->pos[qPrintable(item->widget()->objectName())]); 0812 d->form->selectWidget(item->widget(), 0813 Form::AddToPreviousSelection | Form::LastSelection | Form::Raise); // restore selection 0814 } 0815 } 0816 } 0817 0818 KFORMDESIGNER_EXPORT QDebug KFormDesigner::operator<<(QDebug dbg, const AdjustSizeCommand &c) 0819 { 0820 dbg.nospace() << "AdjustSizeCommand text=" << c.text() << "form=" 0821 << c.d->form->widget()->objectName() << "widgets=" << c.d->sizes.keys(); 0822 return dbg.space(); 0823 } 0824 0825 // InsertWidgetCommand 0826 0827 namespace KFormDesigner 0828 { 0829 class Q_DECL_HIDDEN InsertWidgetCommand::Private 0830 { 0831 public: 0832 Private() 0833 { 0834 } 0835 0836 Form *form; 0837 QString containerName; 0838 QPoint pos; 0839 QByteArray widgetName; 0840 QByteArray _class; 0841 QRect insertRect; 0842 }; 0843 } 0844 0845 InsertWidgetCommand::InsertWidgetCommand(const Container& container, Command *parent) 0846 : Command(parent), d( new Private ) 0847 { 0848 d->form = container.form(); 0849 d->containerName = container.widget()->objectName(); 0850 //qDebug() << "containerName:" << d->containerName; 0851 d->_class = d->form->selectedClass(); 0852 d->pos = container.selectionOrInsertingBegin(); 0853 d->widgetName = d->form->objectTree()->generateUniqueName( 0854 d->form->library()->namePrefix(d->_class).toLatin1(), 0855 /* !numberSuffixRequired */false); 0856 //qDebug() << "widgetName:" << d->widgetName; 0857 d->insertRect = container.selectionOrInsertingRectangle(); 0858 init(); 0859 } 0860 0861 InsertWidgetCommand::InsertWidgetCommand(const Container& container, 0862 const QByteArray& className, const QPoint& pos, 0863 const QByteArray& namePrefix, Command *parent) 0864 : Command(parent), d( new Private ) 0865 { 0866 d->form = container.form(); 0867 d->containerName = container.widget()->objectName(); 0868 //qDebug() << "containerName:" << d->containerName; 0869 d->_class = className; 0870 d->pos = pos; 0871 //d->insertRect is null (default) 0872 //qDebug() << "namePrefix:" << namePrefix; 0873 if (namePrefix.isEmpty()) { 0874 d->widgetName = d->form->objectTree()->generateUniqueName( 0875 d->form->library()->namePrefix(d->_class).toLatin1()); 0876 } else { 0877 d->widgetName = d->form->objectTree()->generateUniqueName( 0878 namePrefix, false /* !numberSuffixRequired */); 0879 } 0880 //qDebug() << "widgetName:" << d->widgetName; 0881 init(); 0882 } 0883 0884 InsertWidgetCommand::~InsertWidgetCommand() 0885 { 0886 delete d; 0887 } 0888 0889 int InsertWidgetCommand::id() const 0890 { 0891 return 6; 0892 } 0893 0894 void InsertWidgetCommand::debug() const 0895 { 0896 qDebug() << *this; 0897 } 0898 0899 void InsertWidgetCommand::init() 0900 { 0901 if (!d->widgetName.isEmpty()) { 0902 setText( kundo2_i18n("Insert widget <resource>%1</resource>", QString(d->widgetName)) ); 0903 } 0904 else { 0905 setText( kundo2_i18n("Insert widget") ); 0906 } 0907 } 0908 0909 void InsertWidgetCommand::execute() 0910 { 0911 if (!d->form->objectTree()) 0912 return; 0913 ObjectTreeItem* titem = d->form->objectTree()->lookup(d->containerName); 0914 if (!titem) { 0915 return; //better this than a crash 0916 } 0917 Container *container = titem->container(); 0918 if (!container) { 0919 return; 0920 } 0921 WidgetFactory::CreateWidgetOptions options = WidgetFactory::DesignViewMode | WidgetFactory::AnyOrientation; 0922 if (d->form->library()->internalProperty(d->_class, "orientationSelectionPopup").toBool()) { 0923 if (d->insertRect.isValid()) { 0924 if (d->insertRect.width() < d->insertRect.height()) { 0925 options |= WidgetFactory::VerticalOrientation; 0926 options ^= WidgetFactory::AnyOrientation; 0927 } else if (d->insertRect.width() > d->insertRect.height()) { 0928 options |= WidgetFactory::HorizontalOrientation; 0929 options ^= WidgetFactory::AnyOrientation; 0930 } 0931 } 0932 if (options & WidgetFactory::AnyOrientation) { 0933 options ^= WidgetFactory::AnyOrientation; 0934 options |= d->form->library()->showOrientationSelectionPopup( 0935 d->_class, container->widget(), 0936 d->form->widget()->mapToGlobal(d->pos)); 0937 if (options & WidgetFactory::AnyOrientation) 0938 return; //cancelled 0939 } 0940 } else 0941 options |= WidgetFactory::AnyOrientation; 0942 0943 QWidget *w = d->form->library()->createWidget(d->_class, container->widget(), d->widgetName, 0944 container, options); 0945 0946 if (!w) { 0947 d->form->abortWidgetInserting(); 0948 WidgetInfo *winfo = d->form->library()->widgetInfoForClassName(d->_class); 0949 KMessageBox::sorry(d->form ? d->form->widget() : 0, 0950 xi18nc("@info", 0951 "Could not insert widget of type <resource>%1</resource>. " 0952 "A problem with widget's creation encountered.", 0953 winfo ? winfo->name() : QString())); 0954 qWarning() << "widget creation failed"; 0955 return; 0956 } 0957 Q_ASSERT(!w->objectName().isEmpty()); 0958 //! @todo allow setting this for data view mode as well 0959 if (d->form->mode() == Form::DesignMode) { 0960 //don't generate accelerators for widgets in design mode 0961 KAcceleratorManager::setNoAccel(w); 0962 } 0963 0964 // if the insertRect is invalid (ie only one point), we use widget' size hint 0965 if (((d->insertRect.width() < 21) && (d->insertRect.height() < 21))) { 0966 QSize s = w->sizeHint(); 0967 0968 if (s.isEmpty()) 0969 s = QSize(20, 20); // Minimum size to avoid creating a (0,0) widget 0970 int x, y; 0971 if (d->insertRect.isValid()) { 0972 x = d->insertRect.x(); 0973 y = d->insertRect.y(); 0974 } else { 0975 x = d->pos.x(); 0976 y = d->pos.y(); 0977 } 0978 d->insertRect = QRect(x, y, s.width() + 16/* add some space so more text can be entered*/, 0979 s.height()); 0980 } 0981 0982 // fix widget size is align-to-grid is enabled 0983 if (d->form->isSnapToGridEnabled()) { 0984 const int grid = d->form->gridSize(); 0985 int v = alignValueToGrid(d->insertRect.width(), grid); 0986 if (v < d->insertRect.width()) // do not allow to make the widget smaller 0987 v += grid; 0988 d->insertRect.setWidth( v ); 0989 v = alignValueToGrid(d->insertRect.height(), grid); 0990 if (v < d->insertRect.height()) // do not allow to make the widget smaller 0991 v += grid; 0992 d->insertRect.setHeight( v ); 0993 } 0994 0995 w->move(d->insertRect.x(), d->insertRect.y()); 0996 w->resize(d->insertRect.size()); 0997 w->show(); 0998 0999 d->form->abortWidgetInserting(); 1000 1001 // ObjectTreeItem object already exists for widgets which corresponds to a Container 1002 // it's already created in Container's constructor 1003 ObjectTreeItem *item = d->form->objectTree()->lookup(d->widgetName); 1004 if (!item) { //not yet created... 1005 //qDebug() << "Creating ObjectTreeItem:"; 1006 item = new ObjectTreeItem(d->form->library()->displayName(d->_class), d->widgetName, w, container); 1007 d->form->objectTree()->addItem(container->objectTree(), item); 1008 } 1009 //assign item for its widget if it supports DesignTimeDynamicChildWidgetHandler interface 1010 //(e.g. KexiDBAutoField) 1011 DesignTimeDynamicChildWidgetHandler *childHandler = dynamic_cast<DesignTimeDynamicChildWidgetHandler*>(w); 1012 if (d->form->mode() == Form::DesignMode && childHandler) { 1013 childHandler->assignItem(item); 1014 } 1015 1016 // We add the autoSaveProperties in the modifProp list of the ObjectTreeItem, so that they are saved later 1017 QList<QByteArray> list( 1018 d->form->library()->autoSaveProperties( 1019 w->metaObject()->className()) 1020 ); 1021 foreach (const QByteArray& name, list) { 1022 if (-1 != w->metaObject()->indexOfProperty(name)) 1023 item->addModifiedProperty(name, w->property(name)); 1024 } 1025 1026 container->reloadLayout(); // reload the layout to take the new wigdet into account 1027 1028 container->selectWidget(w); 1029 if (!d->form->isRedoing() && !d->form->library()->internalProperty(w->metaObject()->className(), 1030 "dontStartEditingOnInserting").toBool()) 1031 { 1032 // edit the widget on creation 1033 d->form->library()->startInlineEditing( 1034 w->metaObject()->className(), w, item->container() ? item->container() : container); 1035 } 1036 //! @todo update widget's width for entered text's metrics 1037 //qDebug() << "widget added " << this; 1038 } 1039 1040 void InsertWidgetCommand::undo() 1041 { 1042 ObjectTreeItem* titem = d->form->objectTree()->lookup(d->widgetName); 1043 if (!titem) 1044 return; //better this than a crash 1045 QWidget *widget = titem->widget(); 1046 Container *container = d->form->objectTree()->lookup(d->containerName)->container(); 1047 container->deleteWidget(widget); 1048 } 1049 1050 KFORMDESIGNER_EXPORT QDebug KFormDesigner::operator<<(QDebug dbg, const InsertWidgetCommand &c) 1051 { 1052 dbg.nospace() << "InsertWidgetCommand text=" << c.text() << "generatedName=" << c.d->widgetName 1053 << "container=" << c.d->containerName 1054 << "form=" << c.d->form->widget()->objectName() << "class=" << c.d->_class 1055 << "rect=" << c.d->insertRect << "pos=" << c.d->pos; 1056 return dbg.space(); 1057 } 1058 1059 QByteArray InsertWidgetCommand::widgetName() const 1060 { 1061 return d->widgetName; 1062 } 1063 1064 // PasteWidgetCommand 1065 1066 namespace KFormDesigner 1067 { 1068 class Q_DECL_HIDDEN PasteWidgetCommand::Private 1069 { 1070 public: 1071 Private() 1072 { 1073 } 1074 1075 Form *form; 1076 QString data; 1077 QString containerName; 1078 QPoint pos; 1079 QStringList names; 1080 }; 1081 } 1082 1083 PasteWidgetCommand::PasteWidgetCommand(const QDomDocument &domDoc, const Container& container, 1084 const QPoint& p, Command *parent) 1085 : Command(parent), d( new Private ) 1086 { 1087 d->form = container.form(); 1088 d->data = domDoc.toString(); 1089 d->containerName = container.widget()->objectName(); 1090 d->pos = p; 1091 1092 if (domDoc.firstChildElement("UI").firstChildElement("widget").isNull()) 1093 return; 1094 1095 QRect boundingRect; 1096 for (QDomNode n = domDoc.firstChildElement("UI").firstChild(); !n.isNull(); 1097 n = n.nextSibling()) 1098 { // more than one widget 1099 const QDomElement el = n.toElement(); 1100 if (el.tagName() != "widget") 1101 continue; 1102 1103 QDomElement rect; 1104 for (QDomNode n = el.firstChild(); !n.isNull(); n = n.nextSibling()) { 1105 if ((n.toElement().tagName() == "property") && (n.toElement().attribute("name") == "geometry")) 1106 rect = n.firstChild().toElement(); 1107 } 1108 QDomElement x = rect.firstChildElement("x"); 1109 QDomElement y = rect.firstChildElement("y"); 1110 QDomElement w = rect.firstChildElement("width"); 1111 QDomElement h = rect.firstChildElement("height"); 1112 1113 int rx = x.text().toInt(); 1114 int ry = y.text().toInt(); 1115 int rw = w.text().toInt(); 1116 int rh = h.text().toInt(); 1117 QRect r(rx, ry, rw, rh); 1118 boundingRect = boundingRect.united(r); 1119 } 1120 setText( kundo2_i18n("Paste") ); 1121 } 1122 1123 1124 PasteWidgetCommand::~PasteWidgetCommand() 1125 { 1126 delete d; 1127 } 1128 1129 int PasteWidgetCommand::id() const 1130 { 1131 return 9; 1132 } 1133 1134 void PasteWidgetCommand::debug() const 1135 { 1136 qDebug() << *this; 1137 } 1138 1139 void PasteWidgetCommand::execute() 1140 { 1141 ObjectTreeItem* titem = d->form->objectTree()->lookup(d->containerName); 1142 if (!titem) 1143 return; //better this than a crash 1144 Container *container = titem->container(); 1145 QString errMsg; 1146 int errLine; 1147 int errCol; 1148 QDomDocument domDoc("UI"); 1149 bool parsed = domDoc.setContent(d->data, false, &errMsg, &errLine, &errCol); 1150 1151 if (!parsed) { 1152 qWarning() << errMsg; 1153 qWarning() << "line: " << errLine << "col: " << errCol; 1154 return; 1155 } 1156 1157 //qDebug() << domDoc.toString(); 1158 if (!domDoc.firstChildElement("UI").hasChildNodes()) // nothing in the doc 1159 return; 1160 1161 QDomElement el = domDoc.firstChildElement("UI").firstChildElement("widget"); 1162 if (el.isNull()) 1163 return; 1164 QDomNode n; 1165 for (n = el.nextSibling(); !n.isNull() && n.toElement().tagName() != "widget"; n = n.nextSibling()) { 1166 } 1167 if (n.isNull()) { 1168 // only one "widget" child tag, so we can paste it at cursor pos 1169 QDomElement el = domDoc.firstChildElement("UI").firstChildElement("widget").toElement(); 1170 fixNames(el); 1171 if (d->pos.isNull()) 1172 fixPos(el, container); 1173 else 1174 changePos(el, d->pos); 1175 1176 d->form->setInteractiveMode(false); 1177 FormIO::loadWidget(container, el, 0, 0); 1178 d->form->setInteractiveMode(true); 1179 } 1180 else { 1181 int minX = INT_MAX, minY = INT_MAX; 1182 if (!d->pos.isNull()) { 1183 // compute top-left point for the united rectangles 1184 for (n = domDoc.firstChildElement("UI").firstChild(); !n.isNull(); n = n.nextSibling()) { 1185 // more than one "widget" child tag 1186 if (n.toElement().tagName() != "widget") { 1187 continue; 1188 } 1189 QDomElement el = n.toElement(); 1190 QDomElement rectEl; 1191 for (QDomNode n2 = el.firstChild(); !n2.isNull(); n2 = n2.nextSibling()) { 1192 if ((n2.toElement().tagName() == "property") && (n2.toElement().attribute("name") == "geometry")) { 1193 rectEl = n2.firstChild().toElement(); 1194 break; 1195 } 1196 } 1197 int x = rectEl.firstChildElement("x").text().toInt(); 1198 if (x < minX) 1199 minX = x; 1200 int y = rectEl.firstChildElement("y").text().toInt(); 1201 if (y < minY) 1202 minY = y; 1203 } 1204 } 1205 for (n = domDoc.firstChildElement("UI").firstChild(); !n.isNull(); n = n.nextSibling()) { 1206 // more than one "widget" child tag 1207 if (n.toElement().tagName() != "widget") { 1208 continue; 1209 } 1210 QDomElement el = n.toElement(); 1211 fixNames(el); 1212 if (d->pos.isNull()) { 1213 fixPos(el, container); 1214 } 1215 else { 1216 moveWidgetBy( 1217 el, container, 1218 QPoint(-minX, -minY) + d->pos // fix position by subtracting the original 1219 // offset and adding the new one 1220 ); 1221 } 1222 1223 d->form->setInteractiveMode(false); 1224 FormIO::loadWidget(container, el, 0, 0); 1225 d->form->setInteractiveMode(true); 1226 } 1227 } 1228 1229 d->names.clear(); 1230 // We store the names of all the created widgets, to delete them later 1231 for (n = domDoc.firstChildElement("UI").firstChild(); !n.isNull(); n = n.nextSibling()) { 1232 if (n.toElement().tagName() != "widget") { 1233 continue; 1234 } 1235 for (QDomNode m = n.firstChild(); !m.isNull(); m = m.nextSibling()) { 1236 if ((m.toElement().tagName() == "property") && (m.toElement().attribute("name") == "name")) { 1237 d->names.append(m.toElement().text()); 1238 break; 1239 } 1240 } 1241 } 1242 1243 container->form()->selectFormWidget(); 1244 foreach (const QString& widgetName, d->names) { // We select all the pasted widgets 1245 ObjectTreeItem *item = d->form->objectTree()->lookup(widgetName); 1246 if (item) { 1247 container->selectWidget(item->widget(), 1248 Form::AddToPreviousSelection | Form::LastSelection | Form::Raise); 1249 } 1250 } 1251 } 1252 1253 void PasteWidgetCommand::undo() 1254 { 1255 ObjectTreeItem* titem = d->form->objectTree()->lookup(d->containerName); 1256 if (!titem) 1257 return; //better this than a crash 1258 Container *container = titem->container(); 1259 // We just delete all the widgets we have created 1260 foreach (const QString& widgetName, d->names) { 1261 ObjectTreeItem* titem = container->form()->objectTree()->lookup(widgetName); 1262 if (!titem) { 1263 continue; //better this than a crash 1264 } 1265 QWidget *w = titem->widget(); 1266 container->deleteWidget(w); 1267 } 1268 } 1269 1270 void PasteWidgetCommand::changePos(QDomElement &el, const QPoint &newPos) 1271 { 1272 QDomElement rect; 1273 // Find the widget geometry if there is one 1274 for (QDomNode n = el.firstChild(); !n.isNull(); n = n.nextSibling()) { 1275 if ((n.toElement().tagName() == "property") && (n.toElement().attribute("name") == "geometry")) { 1276 rect = n.firstChild().toElement(); 1277 break; 1278 } 1279 } 1280 1281 QDomElement x = rect.firstChildElement("x"); 1282 x.removeChild(x.firstChild()); 1283 QDomText valueX = el.ownerDocument().createTextNode(QString::number(newPos.x())); 1284 x.appendChild(valueX); 1285 1286 QDomElement y = rect.firstChildElement("y"); 1287 y.removeChild(y.firstChild()); 1288 QDomText valueY = el.ownerDocument().createTextNode(QString::number(newPos.y())); 1289 y.appendChild(valueY); 1290 } 1291 1292 void PasteWidgetCommand::fixPos(QDomElement &el, Container *container) 1293 { 1294 moveWidgetBy(el, container, QPoint(0, 0)); 1295 } 1296 1297 void PasteWidgetCommand::moveWidgetBy(QDomElement &el, Container *container, const QPoint &p) 1298 { 1299 QDomElement rect; 1300 for (QDomNode n = el.firstChild(); !n.isNull(); n = n.nextSibling()) { 1301 if ((n.toElement().tagName() == "property") && (n.toElement().attribute("name") == "geometry")) { 1302 rect = n.firstChild().toElement(); 1303 break; 1304 } 1305 } 1306 1307 QDomElement x = rect.firstChildElement("x"); 1308 QDomElement y = rect.firstChildElement("y"); 1309 QDomElement wi = rect.firstChildElement("width"); 1310 QDomElement h = rect.firstChildElement("height"); 1311 1312 int rx = x.text().toInt(); 1313 int ry = y.text().toInt(); 1314 int rw = wi.text().toInt(); 1315 int rh = h.text().toInt(); 1316 QRect r(rx + p.x(), ry + p.y(), rw, rh); 1317 //qDebug() << "Moving widget by " << p << "from " << rx << ry << "to" << r.topLeft(); 1318 1319 QWidget *w = d->form->widget()->childAt(r.x() + 6, r.y() + 6); 1320 1321 while (w && (w->geometry() == r)) { // there is already a widget there, with the same size 1322 w = d->form->widget()->childAt(w->x() + 16, w->y() + 16); 1323 r.translate(10, 10); 1324 } 1325 1326 // the pasted wigdet should stay inside container's boundaries 1327 if (r.x() < 0) 1328 r.moveLeft(0); 1329 else if (r.right() > container->widget()->width()) 1330 r.moveLeft(container->widget()->width() - r.width()); 1331 1332 if (r.y() < 0) 1333 r.moveTop(0); 1334 else if (r.bottom() > container->widget()->height()) 1335 r.moveTop(container->widget()->height() - r.height()); 1336 1337 if (r != QRect(rx, ry, rw, rh)) { 1338 changePos(el, QPoint(r.x(), r.y())); 1339 } 1340 } 1341 1342 void 1343 PasteWidgetCommand::fixNames(QDomElement &el) 1344 { 1345 QString wname; 1346 for (QDomNode n = el.firstChild(); !n.isNull(); n = n.nextSibling()) { 1347 if ((n.toElement().tagName() == "property") && (n.toElement().attribute("name") == "name")) { 1348 wname = n.toElement().text(); 1349 while (d->form->objectTree()->lookup(wname)) { // name already exists 1350 bool ok; 1351 int num = wname.right(1).toInt(&ok, 10); 1352 if (ok) 1353 wname = wname.left(wname.length() - 1) + QString::number(num + 1); 1354 else 1355 wname += "2"; 1356 } 1357 if (wname != n.toElement().text()) { // we change the name, so we recreate the element 1358 n.removeChild(n.firstChild()); 1359 QDomElement type = el.ownerDocument().createElement("string"); 1360 QDomText valueE = el.ownerDocument().createTextNode(wname); 1361 type.appendChild(valueE); 1362 n.toElement().appendChild(type); 1363 } 1364 1365 } 1366 if (n.toElement().tagName() == "widget") { // fix child widgets names 1367 QDomElement child = n.toElement(); 1368 fixNames(child); 1369 } 1370 } 1371 } 1372 1373 KFORMDESIGNER_EXPORT QDebug KFormDesigner::operator<<(QDebug dbg, const PasteWidgetCommand &c) 1374 { 1375 dbg.nospace() << "PasteWidgetCommand pos=" << c.d->pos 1376 << "widgets=" << c.d->names << "container=" << c.d->containerName 1377 << "form=" << c.d->form->widget()->objectName() 1378 << "data=" << (c.d->data.left(80) + "..."); 1379 return dbg.space(); 1380 } 1381 1382 // DeleteWidgetCommand 1383 1384 namespace KFormDesigner 1385 { 1386 class Q_DECL_HIDDEN DeleteWidgetCommand::Private 1387 { 1388 public: 1389 Private() 1390 { 1391 } 1392 1393 Form *form; 1394 QDomDocument domDoc; 1395 QHash<QByteArray, QByteArray> containers; 1396 QHash<QByteArray, QByteArray> parents; 1397 }; 1398 } 1399 1400 DeleteWidgetCommand::DeleteWidgetCommand(Form& form, const QWidgetList &list, Command *parent) 1401 : Command(parent), d( new Private ) 1402 { 1403 d->form = &form; 1404 KFormDesigner::widgetsToXML(d->domDoc, 1405 d->containers, d->parents, *d->form, list); 1406 setText( kundo2_i18n("Delete widget") ); 1407 } 1408 1409 DeleteWidgetCommand::~DeleteWidgetCommand() 1410 { 1411 delete d; 1412 } 1413 1414 int DeleteWidgetCommand::id() const 1415 { 1416 return 10; 1417 } 1418 1419 void DeleteWidgetCommand::debug() const 1420 { 1421 qDebug() << *this; 1422 } 1423 1424 void DeleteWidgetCommand::execute() 1425 { 1426 QHash<QByteArray, QByteArray>::ConstIterator endIt = d->containers.constEnd(); 1427 for (QHash<QByteArray, QByteArray>::ConstIterator it = d->containers.constBegin(); it != endIt; ++it) { 1428 ObjectTreeItem *item = d->form->objectTree()->lookup(it.key()); 1429 if (!item || !item->widget()) 1430 continue; 1431 1432 Container *cont = d->form->parentContainer(item->widget()); 1433 cont->deleteWidget(item->widget()); 1434 } 1435 } 1436 1437 void DeleteWidgetCommand::undo() 1438 { 1439 QByteArray wname; 1440 d->form->setInteractiveMode(false); 1441 for (QDomNode n = d->domDoc.firstChildElement("UI").firstChild(); !n.isNull(); n = n.nextSibling()) { 1442 #ifdef KFD_SIGSLOTS 1443 if (n.toElement().tagName() == "connections") // restore the widget connections 1444 d->form->connectionBuffer()->load(n); 1445 #endif 1446 if (n.toElement().tagName() != "widget") 1447 continue; 1448 // We need first to know the name of the widget 1449 for (QDomNode m = n.firstChild(); !m.isNull(); n = m.nextSibling()) { 1450 if ((m.toElement().tagName() == "property") && (m.toElement().attribute("name") == "name")) { 1451 wname = m.toElement().text().toLatin1(); 1452 break; 1453 } 1454 } 1455 1456 ObjectTreeItem* titem = d->form->objectTree()->lookup(d->containers.value(wname)); 1457 if (!titem) 1458 return; //better this than a crash 1459 Container *cont = titem->container(); 1460 ObjectTreeItem *parent = d->form->objectTree()->lookup(d->parents.value(wname)); 1461 QDomElement widg = n.toElement(); 1462 if (parent) 1463 FormIO::loadWidget(cont, widg, parent->widget(), 0); 1464 else 1465 FormIO::loadWidget(cont, widg, 0, 0); 1466 } 1467 d->form->setInteractiveMode(true); 1468 } 1469 1470 KFORMDESIGNER_EXPORT QDebug KFormDesigner::operator<<(QDebug dbg, const DeleteWidgetCommand &c) 1471 { 1472 dbg.nospace() << "DeleteWidgetCommand containers=" << c.d->containers.keys() 1473 << "parents=" << c.d->parents.keys() << "form=" << c.d->form->widget()->objectName(); 1474 return dbg.space(); 1475 } 1476 1477 // DuplicateWidgetCommand 1478 1479 namespace KFormDesigner 1480 { 1481 class Q_DECL_HIDDEN DuplicateWidgetCommand::Private 1482 { 1483 public: 1484 Private() 1485 : pasteCommand(0) 1486 { 1487 } 1488 ~Private() 1489 { 1490 delete pasteCommand; 1491 } 1492 1493 Form *form; 1494 QDomDocument domDoc; 1495 QHash<QByteArray, QByteArray> containers; 1496 QHash<QByteArray, QByteArray> parents; 1497 PasteWidgetCommand *pasteCommand; 1498 }; 1499 } 1500 1501 DuplicateWidgetCommand::DuplicateWidgetCommand( 1502 const Container& container, 1503 const QWidgetList &list, 1504 const QPoint& copyToPoint, 1505 Command *parent) 1506 : Command(parent), d( new Private ) 1507 { 1508 d->form = container.form(); 1509 QDomDocument docToCopy; 1510 KFormDesigner::widgetsToXML(docToCopy, 1511 d->containers, d->parents, *d->form, list); 1512 1513 d->pasteCommand = new PasteWidgetCommand(docToCopy, container, copyToPoint); 1514 setText( kundo2_i18n("Duplicate widget") ); 1515 } 1516 1517 DuplicateWidgetCommand::~DuplicateWidgetCommand() 1518 { 1519 delete d; 1520 } 1521 1522 int DuplicateWidgetCommand::id() const 1523 { 1524 return 11; 1525 } 1526 1527 void DuplicateWidgetCommand::debug() const 1528 { 1529 qDebug() << *this; 1530 } 1531 1532 void DuplicateWidgetCommand::execute() 1533 { 1534 d->pasteCommand->execute(); 1535 } 1536 1537 void DuplicateWidgetCommand::undo() 1538 { 1539 d->pasteCommand->undo(); 1540 } 1541 1542 KFORMDESIGNER_EXPORT QDebug KFormDesigner::operator<<(QDebug dbg, const DuplicateWidgetCommand &c) 1543 { 1544 dbg.nospace() << "DuplicateWidgetCommand containers=" << c.d->containers.keys() 1545 << "parents=" << c.d->parents.keys() << "form=" << c.d->form->widget()->objectName(); 1546 return dbg.space(); 1547 } 1548 1549 // CutWidgetCommand 1550 1551 namespace KFormDesigner 1552 { 1553 class Q_DECL_HIDDEN CutWidgetCommand::Private 1554 { 1555 public: 1556 Private() 1557 : data(0) 1558 { 1559 } 1560 1561 ~Private() 1562 { 1563 delete data; 1564 } 1565 1566 QMimeData *data; 1567 }; 1568 } 1569 1570 CutWidgetCommand::CutWidgetCommand(Form& form, const QWidgetList &list, Command *parent) 1571 : DeleteWidgetCommand(form, list, parent), d2( new Private ) 1572 { 1573 setText( kundo2_i18n("Cut") ); 1574 } 1575 1576 CutWidgetCommand::~CutWidgetCommand() 1577 { 1578 delete d2; 1579 } 1580 1581 int CutWidgetCommand::id() const 1582 { 1583 return 12; 1584 } 1585 1586 void CutWidgetCommand::debug() const 1587 { 1588 qDebug() << *this; 1589 } 1590 1591 void CutWidgetCommand::execute() 1592 { 1593 DeleteWidgetCommand::execute(); 1594 delete d2->data; 1595 QClipboard *cb = QApplication::clipboard(); 1596 d2->data = KFormDesigner::deepCopyOfMimeData(cb->mimeData()); // save clipboard contents 1597 // d->domDoc has been filled in DeleteWidgetCommand ctor 1598 KFormDesigner::copyToClipboard(d->domDoc.toString()); 1599 } 1600 1601 void CutWidgetCommand::undo() 1602 { 1603 DeleteWidgetCommand::undo(); 1604 QClipboard *cb = QApplication::clipboard(); 1605 cb->setMimeData(KFormDesigner::deepCopyOfMimeData(d2->data)); // restore prev. clipboard contents 1606 } 1607 1608 KFORMDESIGNER_EXPORT QDebug KFormDesigner::operator<<(QDebug dbg, const CutWidgetCommand &c) 1609 { 1610 dbg.nospace() << "CutWidgetCommand containers=" << c.d->containers.keys() 1611 << "parents=" << c.d->parents.keys() << "form=" << c.d->form->widget()->objectName() 1612 << "data=" << (c.d2->data->text().left(80) + "..."); 1613 return dbg.space(); 1614 } 1615 1616 // PropertyCommandGroup 1617 1618 namespace KFormDesigner 1619 { 1620 class Q_DECL_HIDDEN PropertyCommandGroup::Private 1621 { 1622 public: 1623 Private() 1624 { 1625 } 1626 }; 1627 } 1628 1629 PropertyCommandGroup::PropertyCommandGroup(const QString &text, Command *parent) 1630 : Command(text, parent), d( new Private() ) 1631 { 1632 } 1633 1634 PropertyCommandGroup::~PropertyCommandGroup() 1635 { 1636 delete d; 1637 } 1638 1639 int PropertyCommandGroup::id() const 1640 { 1641 return 13; 1642 } 1643 1644 void PropertyCommandGroup::debug() const 1645 { 1646 qDebug() << *this; 1647 } 1648 1649 void PropertyCommandGroup::execute() 1650 { 1651 KUndo2Command::redo(); 1652 } 1653 1654 KFORMDESIGNER_EXPORT QDebug KFormDesigner::operator<<(QDebug dbg, const PropertyCommandGroup &c) 1655 { 1656 dbg.nospace() << "PropertyCommandGroup" << static_cast<const Command&>(c); 1657 return dbg.space(); 1658 } 1659 1660 // InlineTextEditingCommand 1661 1662 namespace KFormDesigner 1663 { 1664 class Q_DECL_HIDDEN InlineTextEditingCommand::Private 1665 { 1666 public: 1667 Private() 1668 : oldTextKnown(false) 1669 { 1670 } 1671 Form *form; 1672 QPointer<QWidget> widget; 1673 QByteArray editedWidgetClass; 1674 QString text; 1675 QString oldText; 1676 /*! Used to make sure that oldText is set only on the first execution 1677 of InlineTextEditingCommand::execute() */ 1678 bool oldTextKnown; 1679 }; 1680 } 1681 1682 InlineTextEditingCommand::InlineTextEditingCommand( 1683 Form& form, QWidget *widget, const QByteArray &editedWidgetClass, 1684 const QString &text, Command *parent) 1685 : Command(parent) 1686 , d( new Private ) 1687 { 1688 d->form = &form; 1689 d->widget = widget; 1690 d->editedWidgetClass = editedWidgetClass; 1691 d->text = text; 1692 d->widget = widget; 1693 } 1694 1695 InlineTextEditingCommand::~InlineTextEditingCommand() 1696 { 1697 delete d; 1698 } 1699 1700 int InlineTextEditingCommand::id() const 1701 { 1702 return 14; 1703 } 1704 1705 void InlineTextEditingCommand::debug() const 1706 { 1707 qDebug() << *this; 1708 } 1709 1710 void InlineTextEditingCommand::execute() 1711 { 1712 WidgetInfo *wi = d->form->library()->widgetInfoForClassName(d->editedWidgetClass); 1713 if (!wi) 1714 return; 1715 1716 QString oldText; 1717 d->form->setSlotPropertyChangedEnabled(false); 1718 bool ok = wi->factory()->changeInlineText(d->form, d->widget, d->text, &oldText); 1719 if (!ok && wi && wi->inheritedClass()) { 1720 ok = wi->inheritedClass()->factory()->changeInlineText(d->form, d->widget, d->text, &oldText); 1721 } 1722 d->form->setSlotPropertyChangedEnabled(true); 1723 if (!ok) 1724 return; 1725 if (!d->oldTextKnown) { 1726 d->oldText = oldText; 1727 d->oldTextKnown = true; 1728 } 1729 } 1730 1731 void InlineTextEditingCommand::undo() 1732 { 1733 WidgetInfo *wi = d->form->library()->widgetInfoForClassName(d->editedWidgetClass); 1734 if (!wi) 1735 return; 1736 1737 d->form->setSlotPropertyChangedEnabled(false); 1738 bool ok = wi->factory()->changeInlineText(d->form, d->widget, d->oldText); 1739 if (!ok && wi->inheritedClass()) { 1740 ok = wi->inheritedClass()->factory()->changeInlineText(d->form, d->widget, d->oldText); 1741 Q_UNUSED(ok) 1742 } 1743 d->form->setSlotPropertyChangedEnabled(true); 1744 } 1745 1746 bool InlineTextEditingCommand::mergeWith(const KUndo2Command * command) 1747 { 1748 if (id() != command->id()) 1749 return false; 1750 const InlineTextEditingCommand* inlineTextEditingCommand = static_cast<const InlineTextEditingCommand*>(command); 1751 if ( form() == inlineTextEditingCommand->form() 1752 && text() == inlineTextEditingCommand->oldText()) 1753 { 1754 //qDebug() << "Changed from" << text() << "to" << inlineTextEditingCommand->text(); 1755 d->text = inlineTextEditingCommand->text(); 1756 return true; 1757 } 1758 return false; 1759 } 1760 1761 Form* InlineTextEditingCommand::form() const 1762 { 1763 return d->form; 1764 } 1765 1766 QString InlineTextEditingCommand::text() const 1767 { 1768 return d->text; 1769 } 1770 1771 QString InlineTextEditingCommand::oldText() const 1772 { 1773 return d->oldText; 1774 } 1775 1776 KFORMDESIGNER_EXPORT QDebug KFormDesigner::operator<<(QDebug dbg, const InlineTextEditingCommand &c) 1777 { 1778 dbg.nospace() << "InlineTextEditingCommand" << static_cast<const Command&>(c); 1779 return dbg.space(); 1780 } 1781 1782 // Tab related commands (to allow tab creation/deletion undoing) 1783 1784 namespace KFormDesigner 1785 { 1786 class Q_DECL_HIDDEN InsertPageCommand::Private 1787 { 1788 public: 1789 Private() 1790 { 1791 } 1792 Form *form; 1793 QString containername; 1794 QString name; 1795 QString parentname; 1796 }; 1797 } 1798 1799 InsertPageCommand::InsertPageCommand(Container *container, QWidget *parent) 1800 : Command() 1801 , d(new Private) 1802 { 1803 d->containername = container->widget()->objectName(); 1804 d->form = container->form(); 1805 d->parentname = parent->objectName(); 1806 setText( kundo2_i18n("Add Page") ); 1807 } 1808 1809 InsertPageCommand::~InsertPageCommand() 1810 { 1811 delete d; 1812 } 1813 1814 int InsertPageCommand::id() const 1815 { 1816 return 15; 1817 } 1818 1819 void InsertPageCommand::debug() const 1820 { 1821 qDebug() << *this; 1822 } 1823 1824 void InsertPageCommand::execute() 1825 { 1826 execute(QString(), QString(), -1); 1827 } 1828 1829 void InsertPageCommand::execute(const QString& pageWidgetName, const QString& pageName, int pageIndex) 1830 { 1831 Container *container = d->form->objectTree()->lookup(d->containername)->container(); 1832 QWidget *parent = d->form->objectTree()->lookup(d->parentname)->widget(); 1833 if (d->name.isEmpty()) { 1834 if (pageWidgetName.isEmpty()) { 1835 d->name = container->form()->objectTree()->generateUniqueName( 1836 container->form()->library()->displayName("QWidget").toLatin1(), 1837 /*!numberSuffixRequired*/false); 1838 } 1839 else { 1840 d->name = pageWidgetName; 1841 } 1842 } 1843 1844 QWidget *page = container->form()->library()->createWidget( 1845 "QWidget", parent, d->name.toLatin1(), container); 1846 page->setAutoFillBackground(true); 1847 ObjectTreeItem *item = container->form()->objectTree()->lookup(d->name); 1848 1849 QByteArray classname = parent->metaObject()->className(); 1850 if (classname == "KFDTabWidget") { 1851 TabWidgetBase *tab = qobject_cast<TabWidgetBase*>(parent); 1852 const QString realPageName = pageName.isEmpty() ? 1853 xi18n("Page %1", tab->count() + 1) : pageName; 1854 if (pageIndex < 0) 1855 pageIndex = tab->count(); 1856 tab->insertTab(pageIndex, page, realPageName); 1857 tab->setCurrentWidget(page); 1858 item->addModifiedProperty("title", realPageName); 1859 } else if (classname == "QStackedWidget" || /* compat */ classname == "QWidgetStack") { 1860 QStackedWidget *stack = qobject_cast<QStackedWidget*>(parent); 1861 if (stack) { 1862 stack->addWidget(page); 1863 stack->setCurrentWidget(page); 1864 item->addModifiedProperty("stackIndex", stack->indexOf(page)); 1865 } 1866 } 1867 } 1868 1869 void InsertPageCommand::undo() 1870 { 1871 undo(QString()); 1872 } 1873 1874 void InsertPageCommand::undo(const QString& name) 1875 { 1876 if (!name.isEmpty()) { 1877 d->name = name; 1878 } 1879 ObjectTreeItem *item = d->form->objectTree()->lookup(d->name); 1880 if (!item) { 1881 return; 1882 } 1883 QWidget *page = item->widget(); 1884 if (!page) { 1885 return; 1886 } 1887 ObjectTreeItem *parentItem = d->form->objectTree()->lookup(d->parentname); 1888 if (!parentItem) { 1889 return; 1890 } 1891 QWidget *parent = parentItem->widget(); 1892 if (!parent) { 1893 return; 1894 } 1895 1896 QWidgetList list; 1897 list.append(page); 1898 DeleteWidgetCommand command(*d->form, list); 1899 1900 QByteArray classname = parent->metaObject()->className(); 1901 if (classname == "KFDTabWidget") { 1902 TabWidgetBase *tab = qobject_cast<TabWidgetBase*>(parent); 1903 tab->removeTab(tab->indexOf(page)); 1904 } else if (classname == "QStackedWidget" || /* compat */ classname == "QWidgetStack") { 1905 QStackedWidget *stack = qobject_cast<QStackedWidget*>(parent); 1906 int index = stack->indexOf(page); 1907 if (index > 0) 1908 index--; 1909 else if (index < (stack->count()-1)) 1910 index++; 1911 else 1912 index = -1; 1913 1914 if (index >= 0) 1915 stack->setCurrentIndex(index); 1916 stack->removeWidget(page); 1917 } 1918 1919 command.execute(); 1920 } 1921 1922 KFORMDESIGNER_EXPORT QDebug KFormDesigner::operator<<(QDebug dbg, const InsertPageCommand &c) 1923 { 1924 dbg.nospace() << "InsertPageCommand" << static_cast<const Command&>(c); 1925 return dbg.space(); 1926 } 1927 1928 namespace KFormDesigner 1929 { 1930 class Q_DECL_HIDDEN RemovePageCommand::Private 1931 { 1932 public: 1933 Private() 1934 : pageIndex(-1) 1935 { 1936 } 1937 Form *form; 1938 QString containername; 1939 QString name; 1940 QString pageName; 1941 int pageIndex; 1942 QString parentname; 1943 InsertPageCommand *insertCommand; 1944 }; 1945 } 1946 1947 RemovePageCommand::RemovePageCommand(Container *container, QWidget *parent) 1948 : Command() 1949 , d(new Private) 1950 { 1951 d->containername = container->widget()->objectName(); 1952 d->form = container->form(); 1953 TabWidgetBase *tab = qobject_cast<TabWidgetBase*>(parent); 1954 if (tab) { 1955 d->name = tab->currentWidget()->objectName(); 1956 d->pageName = tab->tabText(tab->currentIndex()); 1957 d->pageIndex = tab->currentIndex(); 1958 } 1959 d->parentname = parent->objectName(); 1960 d->insertCommand = new InsertPageCommand(container, parent); 1961 setText( kundo2_i18n("Delete Page") ); 1962 } 1963 1964 RemovePageCommand::~RemovePageCommand() 1965 { 1966 delete d->insertCommand; 1967 delete d; 1968 } 1969 1970 int RemovePageCommand::id() const 1971 { 1972 return 16; 1973 } 1974 1975 void RemovePageCommand::debug() const 1976 { 1977 qDebug() << *this; 1978 } 1979 1980 void RemovePageCommand::execute() 1981 { 1982 d->insertCommand->undo(d->name); 1983 } 1984 1985 void RemovePageCommand::undo() 1986 { 1987 d->insertCommand->execute(d->name, d->pageName, d->pageIndex); 1988 } 1989 1990 int RemovePageCommand::pageIndex() const 1991 { 1992 return d->pageIndex; 1993 } 1994 1995 QString RemovePageCommand::pageName() const 1996 { 1997 return d->pageName; 1998 } 1999 2000 KFORMDESIGNER_EXPORT QDebug KFormDesigner::operator<<(QDebug dbg, const RemovePageCommand &c) 2001 { 2002 dbg.nospace() << "RemovePageCommand" << static_cast<const Command&>(c); 2003 return dbg.space(); 2004 }