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 }