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