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 }