1 /* This file is part of the KDE project
2    Copyright (C) 2004 Cedric Pasteur <cedric.pasteur@free.fr>
3    Copyright (C) 2005-2010 Jarosław Staniek <staniek@kde.org>
4 
5    This library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Library General Public
7    License as published by the Free Software Foundation; either
8    version 2 of the License, or (at your option) any later version.
9 
10    This library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Library General Public License for more details.
14 
15    You should have received a copy of the GNU Library General Public License
16    along with this library; see the file COPYING.LIB.  If not, write to
17    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19 */
20 
21 #include <QLayout>
22 #include <QClipboard>
23 #include <QApplication>
24 #include <QDomDocument>
25 #include <QStackedWidget>
26 #include <QDebug>
27 #include <QMimeData>
28 
29 #include <KMessageBox>
30 #include <KAcceleratorManager>
31 #include <KLocalizedString>
32 
33 #include <KPropertySet>
34 
35 #include "kformdesigner_export.h"
36 #include "WidgetInfo.h"
37 #include "formIO.h"
38 #include "container.h"
39 #include "objecttree.h"
40 #include "form.h"
41 #include "widgetlibrary.h"
42 #include "events.h"
43 #include "utils.h"
44 #include "widgetwithsubpropertiesinterface.h"
getTemplateOrThis(const NamedDecl & ND)45 #include <kexiutils/utils.h>
46 
47 #include "commands.h"
48 
49 #include <memory>
50 #include <limits.h>
51 
52 using namespace KFormDesigner;
53 
54 // Command
isPrivateProtoDecl(const NamedDecl & ND)55 
56 Command::Command(Command *parent)
57         : KUndo2Command(parent)
58         , m_blockRedoOnce(false)
59 {
60 }
61 
62 Command::Command(const QString &text, Command *parent)
63         : KUndo2Command(parent)
64         , m_blockRedoOnce(false)
65 {
66     Q_UNUSED(text);
67 }
68 
69 Command::~Command()
70 {
71 }
72 
73 void Command::blockRedoOnce()
74 {
75     m_blockRedoOnce = true;
76 }
77 
78 void Command::redo()
79 {
80     if (m_blockRedoOnce) {
shouldCollectIncludePath(index::SymbolKind Kind)81         m_blockRedoOnce = false;
82         return;
83     }
84     execute();
85 }
86 
87 void Command::debug() const
88 {
89     qDebug() << *this;
90 }
91 
92 //! qDebug() stream operator. Writes command @a c to the debug output in a nicely formatted way.
93 KFORMDESIGNER_EXPORT QDebug KFormDesigner::operator<<(QDebug dbg, const Command &c)
94 {
95     dbg.nospace() << "Command";
96     const int count = c.childCount();
97     dbg.space() << "name=" << c.text() << "#=" << count;
98     for (int i = 0; i < count; i++) {
99         dbg.nospace() << "- subcommand" << i+1 << ":" << *static_cast<const Command*>(c.child(i)) << "\n";
100     }
101     return dbg.space();
getTokenRange(SourceLocation TokLoc,const SourceManager & SM,const LangOptions & LangOpts)102 }
103 
104 // PropertyCommand
105 
106 namespace KFormDesigner
107 {
108 class Q_DECL_HIDDEN PropertyCommand::Private
109 {
110 public:
111     Private()
112         : uniqueId(0)
113     {
114     }
115 
116     Form *form;
117     QVariant value;
118     QHash<QByteArray, QVariant> oldValues; //!< (widget_name -> value) hash
119     QByteArray propertyName;
120     int uniqueId;
121 };
122 }
123 
124 PropertyCommand::PropertyCommand(Form& form, const QByteArray &wname,
125                                  const QVariant &oldValue, const QVariant &value,
126                                  const QByteArray &propertyName,
127                                  Command *parent)
128         : Command(parent), d( new Private )
129 {
130     d->form = &form;
131     d->value = value;
132     d->propertyName = propertyName;
133     d->oldValues.insert(wname, oldValue);
toRefKind(index::SymbolRoleSet Roles,bool Spelled=false)134     init();
135 }
136 
137 PropertyCommand::PropertyCommand(Form& form, const QHash<QByteArray, QVariant> &oldValues,
138                                  const QVariant &value, const QByteArray &propertyName,
139                                  Command *parent)
140         : Command(parent), d( new Private )
141 {
142     d->form = &form;
143     d->value = value;
144     d->propertyName = propertyName;
145     d->oldValues = oldValues;
146     init();
indexableRelation(const index::SymbolRelation & R)147 }
148 
149 PropertyCommand::~PropertyCommand()
150 {
151     delete d;
152 }
153 
154 void PropertyCommand::init()
155 {
156     if (d->oldValues.count() > 1) {
157         setText(kundo2_i18n("Change <resource>%1</resource> property for multiple widgets",
158                              QString(d->propertyName)));
159     }
160     else {
161         setText(kundo2_i18n("Change <resource>%1</resource> property for widget <resource>%2</resource>",
162                             QString(d->propertyName), QString(d->oldValues.constBegin().key())));
163     }
164 }
165 
166 void PropertyCommand::debug() const
getRefContainer(const Decl * Enclosing,const SymbolCollector::Options & Opts)167 {
168     qDebug() << *this;
169 }
170 
171 Form* PropertyCommand::form() const
172 {
173     return d->form;
174 }
175 
176 int PropertyCommand::id() const
177 {
178     return 1;
179 }
180 
181 QVariant PropertyCommand::value() const
182 {
183     return d->value;
184 }
185 
186 void PropertyCommand::setValue(const QVariant &value)
187 {
188     d->value = value;
189 }
190 
191 void PropertyCommand::setUniqueId(int id)
192 {
193     d->uniqueId = id;
194 }
195 
196 void PropertyCommand::execute()
197 {
HeaderFileURICache(const std::shared_ptr<Preprocessor> & PP,const SourceManager & SM,const SymbolCollector::Options & Opts)198     QWidget *selected = d->form->selectedWidget();
199     bool reSelectWidgets = true;
200     if (selected
201         && d->oldValues.count() == 1
202         && d->oldValues.contains(selected->objectName().toLatin1()) )
203     {
204         // do not reselect widget; this e.g. avoids removing resize handles
205         reSelectWidgets = false;
toURI(const FileEntry * FE)206     }
207 
208     if (reSelectWidgets) {
209         d->form->selectFormWidget();
210     }
211 
212     d->form->setUndoing(true);
213 
214     if (reSelectWidgets) {
215         d->form->selectWidgets(d->oldValues.keys(), Form::ReplacePreviousSelection);
216     }
217 
toURI(llvm::StringRef Path)218     // set property for multiple widgets
219     for (QHash<QByteArray, QVariant>::ConstIterator oldValuesIt( d->oldValues.constBegin() );
220          oldValuesIt != d->oldValues.constEnd(); ++oldValuesIt)
221     {
222         ObjectTreeItem* item = d->form->objectTree()->lookup(oldValuesIt.key());
223         if (item) { //we're checking for item!=0 because the name could be of a form widget
224             QWidget *widget = item->widget();
225             WidgetWithSubpropertiesInterface* subpropIface = dynamic_cast<WidgetWithSubpropertiesInterface*>(widget);
226             QWidget *subWidget = (subpropIface && subpropIface->subwidget()) ? subpropIface->subwidget() : widget;
227             if (subWidget && -1 != subWidget->metaObject()->indexOfProperty(d->propertyName)) {
getIncludeHeader(FileID FID)228                 item->widget()->setProperty(d->propertyName, d->value);
229             }
230         }
231     }
232     d->form->propertySet()->changeProperty(d->propertyName, d->value);
233     d->form->setUndoing(false);
234 }
235 
236 void PropertyCommand::undo()
237 {
toURIInternal(llvm::StringRef Path)238     d->form->selectFormWidget();
239     d->form->setUndoing(true);
240 
241     QHash<QByteArray, QVariant>::ConstIterator endIt = d->oldValues.constEnd();
242     for (QHash<QByteArray, QVariant>::ConstIterator it = d->oldValues.constBegin(); it != endIt; ++it) {
243         ObjectTreeItem* item = d->form->objectTree()->lookup(it.key());
244         if (!item)
245             continue; //better this than a crash
246         QWidget *widget = item->widget();
247         d->form->selectWidget(widget, Form::AddToPreviousSelection | Form::LastSelection | Form::Raise);
248 
249         WidgetWithSubpropertiesInterface* subpropIface = dynamic_cast<WidgetWithSubpropertiesInterface*>(widget);
250         QWidget *subWidget = (subpropIface && subpropIface->subwidget()) ? subpropIface->subwidget() : widget;
251         if (subWidget && -1 != subWidget->metaObject()->indexOfProperty(d->propertyName)) {
252             qDebug() << "OLD" << d->propertyName << subWidget->property(d->propertyName);
getIncludeHeaderUncached(FileID FID)253             qDebug() << "NEW" << d->propertyName << it.value();
254             subWidget->setProperty(d->propertyName, it.value());
255         }
256     }
257 
258     d->form->propertySet()->changeProperty(d->propertyName, d->oldValues.constBegin().value());
259     d->form->setUndoing(false);
260 }
261 
262 bool PropertyCommand::mergeWith(const KUndo2Command * command)
263 {
264     if (id() != command->id())
265         return false;
266     const PropertyCommand* propertyCommand = static_cast<const PropertyCommand*>(command);
267     if (d->uniqueId > 0 && propertyCommand->d->uniqueId == d->uniqueId) {
268         if (d->oldValues.count() == propertyCommand->d->oldValues.count()) {
269             d->value = propertyCommand->value();
270             return true;
271         }
272     }
273     return false;
274 }
275 
276 QByteArray PropertyCommand::propertyName() const
277 {
278     return d->propertyName;
279 }
280 
281 const QHash<QByteArray, QVariant>& PropertyCommand::oldValues() const
isSelfContainedHeader(FileID FID,const FileEntry * FE)282 {
283     return d->oldValues;
284 }
285 
286 QByteArray PropertyCommand::widgetName() const
287 {
288     if (d->oldValues.count() != 1)
289         return QByteArray();
290     return d->oldValues.keys().first();
291 }
292 
293 QVariant PropertyCommand::oldValue() const
294 {
295     if (d->oldValues.count() != 1)
296         return QVariant();
isIf(llvm::StringRef Line)297     return d->oldValues.constBegin().value();
298 }
299 
300 KFORMDESIGNER_EXPORT QDebug KFormDesigner::operator<<(QDebug dbg, const PropertyCommand &c)
301 {
302     dbg.nospace() << "PropertyCommand text=" << c.text() << "widgets=" << c.oldValues().keys()
303         << "value=" << c.value() << "oldValues=" << c.oldValues().values();
304     return dbg.space();
305 }
isErrorAboutInclude(llvm::StringRef Line)306 
307 // GeometryPropertyCommand (for multiple widgets)
308 
309 namespace KFormDesigner
310 {
311 class Q_DECL_HIDDEN GeometryPropertyCommand::Private
312 {
313 public:
314     Private()
315     {
316     }
317 
318     Form *form;
319     QStringList names;
320     QPoint pos;
321     QPoint oldPos;
322 };
323 }
324 
325 GeometryPropertyCommand::GeometryPropertyCommand(Form& form,
326                                                  const QStringList &names,
327                                                  const QPoint& oldPos,
328                                                  Command *parent)
329         : Command(parent), d( new Private )
330 {
331     d->form = &form;
332     d->names = names;
getTokenLocation(SourceLocation TokLoc)333     d->oldPos = oldPos;
334     setText( kundo2_i18n("Move multiple widgets") );
335 }
336 
337 GeometryPropertyCommand::~GeometryPropertyCommand()
338 {
339     delete d;
340 }
341 
342 int GeometryPropertyCommand::id() const
343 {
344     return 2;
345 }
346 
347 void GeometryPropertyCommand::debug() const
SymbolCollector(Options Opts)348 {
349     qDebug() << *this;
350 }
initialize(ASTContext & Ctx)351 
352 void GeometryPropertyCommand::execute()
353 {
354     d->form->setUndoing(true);
355     int dx = d->pos.x() - d->oldPos.x();
356     int dy = d->pos.y() - d->oldPos.y();
357 
358     // We move every widget in our list by (dx, dy)
359     foreach (const QString& widgetName, d->names) {
360         ObjectTreeItem* item = d->form->objectTree()->lookup(widgetName);
361         if (!item)
362             continue; //better this than a crash
363         QWidget *w = item->widget();
364         w->move(w->x() + dx, w->y() + dy);
365     }
366     d->form->setUndoing(false);
367 }
368 
369 void GeometryPropertyCommand::undo()
370 {
371     d->form->setUndoing(true);
372     int dx = d->pos.x() - d->oldPos.x();
373     int dy = d->pos.y() - d->oldPos.y();
374 
375     // We move every widget in our list by (-dx, -dy) to undo the move
376     foreach (const QString& widgetName, d->names) {
377         ObjectTreeItem* item = d->form->objectTree()->lookup(widgetName);
378         if (!item)
379             continue; //better this than a crash
380         QWidget *w = item->widget();
381         w->move(w->x() - dx, w->y() - dy);
382     }
383     d->form->setUndoing(false);
384 }
385 
386 QPoint GeometryPropertyCommand::pos() const
387 {
388     return d->pos;
389 }
390 
391 void GeometryPropertyCommand::setPos(const QPoint& pos)
392 {
393     d->pos = pos;
394 }
395 
396 QPoint GeometryPropertyCommand::oldPos() const
397 {
398     return d->oldPos;
399 }
400 
401 KFORMDESIGNER_EXPORT QDebug KFormDesigner::operator<<(QDebug dbg, const GeometryPropertyCommand &c)
402 {
403     dbg.nospace() << "GeometryPropertyCommand pos=" << c.pos() << "oldPos=" << c.oldPos()
404         << "widgets=" << c.d->names;
405     return dbg.space();
406 }
407 
408 // AlignWidgetsCommand
409 
410 namespace KFormDesigner
handleDeclOccurrence(const Decl * D,index::SymbolRoleSet Roles,llvm::ArrayRef<index::SymbolRelation> Relations,SourceLocation Loc,index::IndexDataConsumer::ASTNodeInfo ASTNode)411 {
412 class Q_DECL_HIDDEN AlignWidgetsCommand::Private
413 {
414 public:
415     Private()
416     {
417     }
418 
419     Form *form;
420     Form::WidgetAlignment alignment;
421     QHash<QByteArray, QPoint> pos;
422 };
423 }
424 
425 AlignWidgetsCommand::AlignWidgetsCommand(Form &form, Form::WidgetAlignment alignment,
426                                          const QWidgetList &list, Command *parent)
427         : Command(parent), d( new Private )
428 {
429     d->form = &form;
430     d->alignment = alignment;
431     foreach (QWidget *w, list) {
432         d->pos.insert(qPrintable(w->objectName()), w->pos());
433     }
434 
435     switch (d->alignment) {
436     case Form::AlignToGrid:
437         setText( kundo2_i18n("Align Widgets to Grid") );
438         break;
439     case Form::AlignToLeft:
440         setText( kundo2_i18n("Align Widgets to Left") );
441         break;
442     case Form::AlignToRight:
443         setText( kundo2_i18n("Align Widgets to Right") );
444         break;
445     case Form::AlignToTop:
446         setText( kundo2_i18n("Align Widgets to Top") );
447         break;
448     case Form::AlignToBottom:
449         setText( kundo2_i18n("Align Widgets to Bottom") );
450         break;
451     default:;
452     }
453 }
454 
455 AlignWidgetsCommand::~AlignWidgetsCommand()
456 {
457     delete d;
458 }
459 
460 int AlignWidgetsCommand::id() const
461 {
462     return 3;
463 }
464 
465 void AlignWidgetsCommand::debug() const
466 {
467     qDebug() << *this;
468 }
469 
470 void AlignWidgetsCommand::execute()
471 {
472     // To avoid creation of GeometryPropertyCommand
473     d->form->selectFormWidget();
474 
475     QWidgetList list;
476     foreach (const QByteArray& name, d->pos.keys()) {
477         ObjectTreeItem *item = d->form->objectTree()->lookup(name);
478         if (item && item->widget())
479             list.append(item->widget());
480     }
481 
482     const int gridX = d->form->gridSize();
483     const int gridY = d->form->gridSize();
484     QWidget *parentWidget = d->form->selectedWidgets()->first()->parentWidget();
485 
486     switch (d->alignment) {
487     case Form::AlignToGrid: {
488         foreach (QWidget *w, list) {
489             const int tmpx = alignValueToGrid(w->x(), gridX);
490             const int tmpy = alignValueToGrid(w->y(), gridY);
491             if ((tmpx != w->x()) || (tmpy != w->y()))
492                 w->move(tmpx, tmpy);
493         }
494         break;
495     }
496     case Form::AlignToLeft: {
497         int tmpx = parentWidget->width();
498         foreach (QWidget *w, list) {
499             if (w->x() < tmpx)
500                 tmpx = w->x();
501         }
502         foreach (QWidget *w, list) {
503             w->move(tmpx, w->y());
504         }
505         break;
506     }
507     case Form::AlignToRight: {
508         int tmpx = 0;
509         foreach (QWidget *w, list) {
510             if (w->x() + w->width() > tmpx)
511                 tmpx = w->x() + w->width();
512         }
513         foreach (QWidget *w, list) {
514             w->move(tmpx - w->width(), w->y());
515         }
516         break;
517     }
518     case Form::AlignToTop: {
519         int tmpy = parentWidget->height();
520         foreach (QWidget *w, list) {
521             if (w->y() < tmpy)
522                 tmpy = w->y();
523         }
524         foreach (QWidget *w, list) {
525             w->move(w->x(), tmpy);
526         }
527         break;
528     }
529     case Form::AlignToBottom: {
530         int tmpy = 0;
531         foreach (QWidget *w, list) {
532             if (w->y() + w->height() > tmpy)
533                 tmpy = w->y() + w->height();
534         }
handleMacros(const MainFileMacros & MacroRefsToIndex)535         foreach (QWidget *w, list) {
536             w->move(w->x(), tmpy - w->height());
537         }
538         break;
539     }
540     default:
541         return;
542     }
543 
544     // We restore selection
545     foreach (QWidget *w, list) {
546         d->form->selectWidget(w, Form::AddToPreviousSelection | Form::LastSelection | Form::Raise);
547     }
548 }
549 
550 void AlignWidgetsCommand::undo()
551 {
552     // To avoid creation of GeometryPropertyCommand
553     d->form->selectFormWidget();
554     // We move widgets to their original pos
555     QHash<QByteArray, QPoint>::ConstIterator endIt = d->pos.constEnd();
556     for (QHash<QByteArray, QPoint>::ConstIterator it = d->pos.constBegin(); it != endIt; ++it) {
557         ObjectTreeItem *item = d->form->objectTree()->lookup(it.key());
558         if (item && item->widget())
559         {
560             item->widget()->move(d->pos.value(qPrintable(item->widget()->objectName())));
561             // we restore selection
562             d->form->selectWidget(item->widget(), Form::AddToPreviousSelection | Form::LastSelection | Form::Raise);
563         }
564     }
565 }
566 
567 KFORMDESIGNER_EXPORT QDebug KFormDesigner::operator<<(QDebug dbg, const AlignWidgetsCommand &c)
568 {
569     dbg.nospace() << "AlignWidgetsCommand text=" << c.text() << "form=" << c.d->form->widget()->objectName()
570         << "widgets=" << c.d->pos.keys();
571     return dbg.space();
572 }
handleMacroOccurrence(const IdentifierInfo * Name,const MacroInfo * MI,index::SymbolRoleSet Roles,SourceLocation Loc)573 
574 // AdjustSizeCommand
575 
576 namespace KFormDesigner
577 {
578 class Q_DECL_HIDDEN AdjustSizeCommand::Private
579 {
580 public:
581     Private()
582     {
583     }
584 
585     Form *form;
586     AdjustSizeCommand::Adjustment type;
587     QHash<QByteArray, QPoint> pos;
588     QHash<QByteArray, QSize> sizes;
589 };
590 }
591 
592 AdjustSizeCommand::AdjustSizeCommand(Form& form, Adjustment type, const QWidgetList &list,
593                                      Command *parent)
594         : Command(parent), d( new Private )
595 {
596     d->form = &form;
597     d->type = type;
598     foreach (QWidget *w, list) {
599         if (w->parentWidget() && KexiUtils::objectIsA(w->parentWidget(), "QStackedWidget")) {
600             w = w->parentWidget(); // widget is WidgetStack page
601             if (w->parentWidget() && w->parentWidget()->inherits("QTabWidget")) // widget is tabwidget page
602                 w = w->parentWidget();
603         }
604 
605         d->sizes.insert(qPrintable(w->objectName()), w->size());
606         if (d->type == SizeToGrid) // SizeToGrid also move widgets
607             d->pos.insert(qPrintable(w->objectName()), w->pos());
608     }
609 
610     switch (d->type) {
611     case SizeToGrid:
612         setText( kundo2_i18n("Resize Widgets to Grid") );
613         break;
614     case SizeToFit:
615         setText( kundo2_i18n("Resize Widgets to Fit Contents") );
616         break;
617     case SizeToSmallWidth:
618         setText( kundo2_i18n("Resize Widgets to Narrowest") );
619         break;
620     case SizeToBigWidth:
621         setText( kundo2_i18n("Resize Widgets to Widest") );
622         break;
623     case SizeToSmallHeight:
624         setText( kundo2_i18n("Resize Widgets to Shortest") );
625         break;
626     case SizeToBigHeight:
627         setText( kundo2_i18n("Resize Widgets to Tallest") );
628         break;
629     default:;
630     }
631 }
632 
633 AdjustSizeCommand::~AdjustSizeCommand()
634 {
635     delete d;
636 }
637 
638 int AdjustSizeCommand::id() const
639 {
640     return 4;
641 }
642 
643 void AdjustSizeCommand::debug() const
644 {
645     qDebug() << *this;
646 }
647 
648 void AdjustSizeCommand::execute()
649 {
650     // To avoid creation of GeometryPropertyCommand
651     d->form->selectFormWidget();
652 
653     int gridX = d->form->gridSize();
654     int gridY = d->form->gridSize();
655     int tmpw = 0, tmph = 0;
656 
657     QWidgetList list;
658     QHash<QByteArray, QSize>::ConstIterator endIt = d->sizes.constEnd();
processRelations(const NamedDecl & ND,const SymbolID & ID,ArrayRef<index::SymbolRelation> Relations)659     for (QHash<QByteArray, QSize>::ConstIterator it = d->sizes.constBegin(); it != endIt; ++it) {
660         ObjectTreeItem *item = d->form->objectTree()->lookup(it.key());
661         if (item && item->widget())
662             list.append(item->widget());
663     }
664 
665     switch (d->type) {
666     case SizeToGrid: {
667         int tmpx = 0, tmpy = 0;
668         // same as in 'Align to Grid' + for the size
669         foreach (QWidget *w, list) {
670             tmpx = alignValueToGrid(w->x(), gridX);
671             tmpy = alignValueToGrid(w->y(), gridY);
672             tmpw = alignValueToGrid(w->width(), gridX);
673             tmph = alignValueToGrid(w->height(), gridY);
674             if ((tmpx != w->x()) || (tmpy != w->y()))
675                 w->move(tmpx, tmpy);
676             if ((tmpw != w->width()) || (tmph != w->height()))
677                 w->resize(tmpw, tmph);
678         }
679         break;
680     }
681 
682     case SizeToFit: {
683         foreach (QWidget *w, list) {
684             ObjectTreeItem *item = d->form->objectTree()->lookup(w->objectName());
685             if (item && !item->children()->isEmpty()) { // container
686                 QSize s;
687                 if (item->container() && item->container()->layout())
setIncludeLocation(const Symbol & S,SourceLocation Loc)688                     s = w->sizeHint();
689                 else
690                     s = getSizeFromChildren(item);
691                 // minimum size for containers
692                 if (s.width()  <  30)
693                     s.setWidth(30);
694                 if (s.height() < 30)
695                     s.setHeight(30);
696                 // small hack for flow layouts
finish()697                 int type = item->container() ? item->container()->layoutType() : Form::NoLayout;
698                 if (type == Form::HFlow)
__anon862648190302(const SymbolID &ID) 699                     s.setWidth(s.width() + 5);
700                 else if (type == Form::VFlow)
701                     s.setHeight(s.height() + 5);
702                 w->resize(s);
703             }
704             else if (item && item->container()) { // empty container
705                 w->resize(item->container()->form()->gridSize() * 5,
706                           item->container()->form()->gridSize() * 5); // basic size
707             }
708             else {
709                 QSize sizeHint(w->sizeHint());
710                 if (sizeHint.isValid())
711                     w->resize(sizeHint);
712             }
713         }
714         break;
715     }
716 
717     case SizeToSmallWidth: {
718         foreach (QWidget *w, list) {
719             if ((tmpw == 0) || (w->width() < tmpw))
720                 tmpw = w->width();
721         }
722 
723         foreach (QWidget *w, list) {
724             if (tmpw != w->width())
725                 w->resize(tmpw, w->height());
726         }
727         break;
728     }
729 
730     case SizeToBigWidth: {
731         foreach (QWidget *w, list) {
732             if (w->width() > tmpw)
733                 tmpw = w->width();
734         }
735 
736         foreach (QWidget *w, list) {
737             if (tmpw != w->width())
738                 w->resize(tmpw, w->height());
739         }
740         break;
741     }
742 
743     case SizeToSmallHeight: {
744         foreach (QWidget *w, list) {
745             if ((tmph == 0) || (w->height() < tmph))
746                 tmph = w->height();
747         }
748 
749         foreach (QWidget *w, list) {
750             if (tmph != w->height())
751                 w->resize(w->width(), tmph);
752         }
753         break;
754     }
755 
756     case SizeToBigHeight: {
757         foreach (QWidget *w, list) {
758             if (w->height() > tmph)
759                 tmph = w->height();
760         }
__anon862648190402(SymbolID ID, const SymbolRef &LocAndRole, bool Spelled = false) 761 
762         foreach (QWidget *w, list) {
763             if (tmph != w->height())
764                 w->resize(w->width(), tmph);
765         }
766         break;
767     }
768 
769     default:
770         break;
771     }
772 
773     // We restore selection
774     foreach (QWidget *w, list) {
775         d->form->selectWidget(w, Form::AddToPreviousSelection | Form::LastSelection | Form::Raise);
776     }
777 }
778 
779 QSize AdjustSizeCommand::getSizeFromChildren(ObjectTreeItem *item)
780 {
781     if (!item->container()) { // multi pages containers (eg tabwidget)
782         QSize s;
783         // get size for each container, and keep the biggest one
784         foreach (ObjectTreeItem *titem, *item->children()) {
785             s = s.expandedTo(getSizeFromChildren(titem));
786         }
787         return s;
788     }
789 
790     int tmpw = 0, tmph = 0;
791     foreach (ObjectTreeItem *titem, *item->children()) {
792         if (!titem->widget())
793             continue;
794         tmpw = qMax(tmpw, titem->widget()->geometry().right());
795         tmph = qMax(tmph, titem->widget()->geometry().bottom());
796     }
797 
798     return QSize(tmpw, tmph) + QSize(10, 10);
799 }
800 
801 void AdjustSizeCommand::undo()
802 {
803     // To avoid creation of GeometryPropertyCommand
804     d->form->selectFormWidget();
805     // We resize widgets to their original size
806     QHash<QByteArray, QSize>::ConstIterator endIt = d->sizes.constEnd();
807     for (QHash<QByteArray, QSize>::ConstIterator it = d->sizes.constBegin(); it != endIt; ++it) {
808         ObjectTreeItem *item = d->form->objectTree()->lookup(it.key());
809         if (item && item->widget()) {
810             item->widget()->resize(d->sizes[qPrintable(item->widget()->objectName())]);
811             if (d->type == SizeToGrid)
812                 item->widget()->move(d->pos[qPrintable(item->widget()->objectName())]);
813             d->form->selectWidget(item->widget(),
addDeclaration(const NamedDecl & ND,SymbolID ID,bool IsMainFileOnly)814                 Form::AddToPreviousSelection | Form::LastSelection | Form::Raise); // restore selection
815         }
816     }
817 }
818 
819 KFORMDESIGNER_EXPORT QDebug KFormDesigner::operator<<(QDebug dbg, const AdjustSizeCommand &c)
820 {
821     dbg.nospace() << "AdjustSizeCommand text=" << c.text() << "form="
822         << c.d->form->widget()->objectName() << "widgets=" << c.d->sizes.keys();
823     return dbg.space();
824 }
825 
826 // InsertWidgetCommand
827 
828 namespace KFormDesigner
829 {
830 class Q_DECL_HIDDEN InsertWidgetCommand::Private
831 {
832 public:
833     Private()
834     {
835     }
836 
837     Form *form;
838     QString containerName;
839     QPoint pos;
840     QByteArray widgetName;
841     QByteArray _class;
842     QRect insertRect;
843 };
844 }
845 
846 InsertWidgetCommand::InsertWidgetCommand(const Container& container, Command *parent)
847         : Command(parent), d( new Private )
848 {
849     d->form = container.form();
850     d->containerName = container.widget()->objectName();
851     //qDebug() << "containerName:" << d->containerName;
852     d->_class = d->form->selectedClass();
853     d->pos = container.selectionOrInsertingBegin();
854     d->widgetName = d->form->objectTree()->generateUniqueName(
855                  d->form->library()->namePrefix(d->_class).toLatin1(),
856                  /* !numberSuffixRequired */false);
857     //qDebug() << "widgetName:" << d->widgetName;
858     d->insertRect = container.selectionOrInsertingRectangle();
859     init();
860 }
861 
862 InsertWidgetCommand::InsertWidgetCommand(const Container& container,
863                                          const QByteArray& className, const QPoint& pos,
864                                          const QByteArray& namePrefix, Command *parent)
865         : Command(parent), d( new Private )
866 {
867     d->form = container.form();
868     d->containerName = container.widget()->objectName();
869     //qDebug() << "containerName:" << d->containerName;
870     d->_class = className;
871     d->pos = pos;
872     //d->insertRect is null (default)
873     //qDebug() << "namePrefix:" << namePrefix;
874     if (namePrefix.isEmpty()) {
875         d->widgetName = d->form->objectTree()->generateUniqueName(
876                      d->form->library()->namePrefix(d->_class).toLatin1());
877     } else {
878         d->widgetName = d->form->objectTree()->generateUniqueName(
879                      namePrefix, false /* !numberSuffixRequired */);
880     }
881     //qDebug() << "widgetName:" << d->widgetName;
882     init();
883 }
884 
885 InsertWidgetCommand::~InsertWidgetCommand()
addDefinition(const NamedDecl & ND,const Symbol & DeclSym)886 {
887     delete d;
888 }
889 
890 int InsertWidgetCommand::id() const
891 {
892     return 6;
893 }
894 
895 void InsertWidgetCommand::debug() const
896 {
897     qDebug() << *this;
898 }
899 
900 void InsertWidgetCommand::init()
901 {
902     if (!d->widgetName.isEmpty()) {
shouldIndexFile(FileID FID)903         setText( kundo2_i18n("Insert widget <resource>%1</resource>", QString(d->widgetName)) );
904     }
905     else {
906         setText( kundo2_i18n("Insert widget") );
907     }
908 }
909 
910 void InsertWidgetCommand::execute()
911 {
912     if (!d->form->objectTree())
913         return;
914     ObjectTreeItem* titem = d->form->objectTree()->lookup(d->containerName);
915     if (!titem) {
916         return; //better this than a crash
917     }
918     Container *container = titem->container();
919     if (!container) {
920         return;
921     }
922     WidgetFactory::CreateWidgetOptions options = WidgetFactory::DesignViewMode | WidgetFactory::AnyOrientation;
923     if (d->form->library()->internalProperty(d->_class, "orientationSelectionPopup").toBool()) {
924         if (d->insertRect.isValid()) {
925             if (d->insertRect.width() < d->insertRect.height()) {
926                 options |= WidgetFactory::VerticalOrientation;
927                 options ^= WidgetFactory::AnyOrientation;
928             } else if (d->insertRect.width() > d->insertRect.height()) {
929                 options |= WidgetFactory::HorizontalOrientation;
930                 options ^= WidgetFactory::AnyOrientation;
931             }
932         }
933         if (options & WidgetFactory::AnyOrientation) {
934             options ^= WidgetFactory::AnyOrientation;
935             options |= d->form->library()->showOrientationSelectionPopup(
936                            d->_class, container->widget(),
937                            d->form->widget()->mapToGlobal(d->pos));
938             if (options & WidgetFactory::AnyOrientation)
939                 return; //cancelled
940         }
941     } else
942         options |= WidgetFactory::AnyOrientation;
943 
944     QWidget *w = d->form->library()->createWidget(d->_class, container->widget(), d->widgetName,
945                                                   container, options);
946 
947     if (!w) {
948         d->form->abortWidgetInserting();
949         WidgetInfo *winfo = d->form->library()->widgetInfoForClassName(d->_class);
950         KMessageBox::sorry(d->form ? d->form->widget() : 0,
951                            xi18nc("@info",
952                                   "Could not insert widget of type <resource>%1</resource>. "
953                                   "A problem with widget's creation encountered.",
954                                   winfo ? winfo->name() : QString()));
955         qWarning() << "widget creation failed";
956         return;
957     }
958     Q_ASSERT(!w->objectName().isEmpty());
959 //! @todo allow setting this for data view mode as well
960     if (d->form->mode() == Form::DesignMode) {
961         //don't generate accelerators for widgets in design mode
962         KAcceleratorManager::setNoAccel(w);
963     }
964 
965     // if the insertRect is invalid (ie only one point), we use widget' size hint
966     if (((d->insertRect.width() < 21) && (d->insertRect.height() < 21))) {
967         QSize s = w->sizeHint();
968 
969         if (s.isEmpty())
970             s = QSize(20, 20); // Minimum size to avoid creating a (0,0) widget
971         int x, y;
972         if (d->insertRect.isValid()) {
973             x = d->insertRect.x();
974             y = d->insertRect.y();
975         } else {
976             x = d->pos.x();
977             y = d->pos.y();
978         }
979         d->insertRect = QRect(x, y, s.width() + 16/* add some space so more text can be entered*/,
980                              s.height());
981     }
982 
983     // fix widget size is align-to-grid is enabled
984     if (d->form->isSnapToGridEnabled()) {
985         const int grid = d->form->gridSize();
986         int v = alignValueToGrid(d->insertRect.width(), grid);
987         if (v < d->insertRect.width()) // do not allow to make the widget smaller
988             v += grid;
989         d->insertRect.setWidth( v );
990         v = alignValueToGrid(d->insertRect.height(), grid);
991         if (v < d->insertRect.height()) // do not allow to make the widget smaller
992             v += grid;
993         d->insertRect.setHeight( v );
994     }
995 
996     w->move(d->insertRect.x(), d->insertRect.y());
997     w->resize(d->insertRect.size());
998     w->show();
999 
1000     d->form->abortWidgetInserting();
1001 
1002     // ObjectTreeItem object already exists for widgets which corresponds to a Container
1003     // it's already created in Container's constructor
1004     ObjectTreeItem *item = d->form->objectTree()->lookup(d->widgetName);
1005     if (!item) { //not yet created...
1006         //qDebug() << "Creating ObjectTreeItem:";
1007         item = new ObjectTreeItem(d->form->library()->displayName(d->_class), d->widgetName, w, container);
1008         d->form->objectTree()->addItem(container->objectTree(), item);
1009     }
1010     //assign item for its widget if it supports DesignTimeDynamicChildWidgetHandler interface
1011     //(e.g. KexiDBAutoField)
1012     DesignTimeDynamicChildWidgetHandler *childHandler = dynamic_cast<DesignTimeDynamicChildWidgetHandler*>(w);
1013     if (d->form->mode() == Form::DesignMode && childHandler) {
1014         childHandler->assignItem(item);
1015     }
1016 
1017     // We add the autoSaveProperties in the modifProp list of the ObjectTreeItem, so that they are saved later
1018     QList<QByteArray> list(
1019         d->form->library()->autoSaveProperties(
1020            w->metaObject()->className())
1021     );
1022     foreach (const QByteArray& name, list) {
1023         if (-1 != w->metaObject()->indexOfProperty(name))
1024             item->addModifiedProperty(name, w->property(name));
1025     }
1026 
1027     container->reloadLayout(); // reload the layout to take the new wigdet into account
1028 
1029     container->selectWidget(w);
1030     if (!d->form->isRedoing() && !d->form->library()->internalProperty(w->metaObject()->className(),
1031             "dontStartEditingOnInserting").toBool())
1032     {
1033         // edit the widget on creation
1034         d->form->library()->startInlineEditing(
1035             w->metaObject()->className(), w, item->container() ? item->container() : container);
1036     }
1037 //! @todo update widget's width for entered text's metrics
1038     //qDebug() << "widget added " << this;
1039 }
1040 
1041 void InsertWidgetCommand::undo()
1042 {
1043     ObjectTreeItem* titem = d->form->objectTree()->lookup(d->widgetName);
1044     if (!titem)
1045         return; //better this than a crash
1046     QWidget *widget = titem->widget();
1047     Container *container = d->form->objectTree()->lookup(d->containerName)->container();
1048     container->deleteWidget(widget);
1049 }
1050 
1051 KFORMDESIGNER_EXPORT QDebug KFormDesigner::operator<<(QDebug dbg, const InsertWidgetCommand &c)
1052 {
1053     dbg.nospace() << "InsertWidgetCommand text=" << c.text() << "generatedName=" << c.d->widgetName
1054         << "container=" << c.d->containerName
1055         << "form=" << c.d->form->widget()->objectName() << "class=" << c.d->_class
1056         << "rect=" << c.d->insertRect << "pos=" << c.d->pos;
1057     return dbg.space();
1058 }
1059 
1060 QByteArray InsertWidgetCommand::widgetName() const
1061 {
1062     return d->widgetName;
1063 }
1064 
1065 // PasteWidgetCommand
1066 
1067 namespace KFormDesigner
1068 {
1069 class Q_DECL_HIDDEN PasteWidgetCommand::Private
1070 {
1071 public:
1072     Private()
1073     {
1074     }
1075 
1076     Form *form;
1077     QString data;
1078     QString containerName;
1079     QPoint pos;
1080     QStringList names;
1081 };
1082 }
1083 
1084 PasteWidgetCommand::PasteWidgetCommand(const QDomDocument &domDoc, const Container& container,
1085                                        const QPoint& p, Command *parent)
1086     : Command(parent), d( new Private )
1087 {
1088     d->form = container.form();
1089     d->data = domDoc.toString();
1090     d->containerName = container.widget()->objectName();
1091     d->pos = p;
1092 
1093     if (domDoc.firstChildElement("UI").firstChildElement("widget").isNull())
1094         return;
1095 
1096     QRect boundingRect;
1097     for (QDomNode n = domDoc.firstChildElement("UI").firstChild(); !n.isNull();
1098          n = n.nextSibling())
1099     { // more than one widget
1100         const QDomElement el = n.toElement();
1101         if (el.tagName() != "widget")
1102             continue;
1103 
1104         QDomElement rect;
1105         for (QDomNode n = el.firstChild(); !n.isNull(); n = n.nextSibling()) {
1106             if ((n.toElement().tagName() == "property") && (n.toElement().attribute("name") == "geometry"))
1107                 rect = n.firstChild().toElement();
1108         }
1109         QDomElement x = rect.firstChildElement("x");
1110         QDomElement y = rect.firstChildElement("y");
1111         QDomElement w = rect.firstChildElement("width");
1112         QDomElement h = rect.firstChildElement("height");
1113 
1114         int rx = x.text().toInt();
1115         int ry = y.text().toInt();
1116         int rw = w.text().toInt();
1117         int rh = h.text().toInt();
1118         QRect r(rx, ry, rw, rh);
1119         boundingRect = boundingRect.united(r);
1120     }
1121     setText( kundo2_i18n("Paste") );
1122 }
1123 
1124 
1125 PasteWidgetCommand::~PasteWidgetCommand()
1126 {
1127     delete d;
1128 }
1129 
1130 int PasteWidgetCommand::id() const
1131 {
1132     return 9;
1133 }
1134 
1135 void PasteWidgetCommand::debug() const
1136 {
1137     qDebug() << *this;
1138 }
1139 
1140 void PasteWidgetCommand::execute()
1141 {
1142     ObjectTreeItem* titem = d->form->objectTree()->lookup(d->containerName);
1143     if (!titem)
1144         return; //better this than a crash
1145     Container *container = titem->container();
1146     QString errMsg;
1147     int errLine;
1148     int errCol;
1149     QDomDocument domDoc("UI");
1150     bool parsed = domDoc.setContent(d->data, false, &errMsg, &errLine, &errCol);
1151 
1152     if (!parsed) {
1153         qWarning() << errMsg;
1154         qWarning() << "line: " << errLine << "col: " << errCol;
1155         return;
1156     }
1157 
1158     //qDebug() << domDoc.toString();
1159     if (!domDoc.firstChildElement("UI").hasChildNodes()) // nothing in the doc
1160         return;
1161 
1162     QDomElement el = domDoc.firstChildElement("UI").firstChildElement("widget");
1163     if (el.isNull())
1164         return;
1165     QDomNode n;
1166     for (n = el.nextSibling(); !n.isNull() && n.toElement().tagName() != "widget"; n = n.nextSibling()) {
1167     }
1168     if (n.isNull()) {
1169         // only one "widget" child tag, so we can paste it at cursor pos
1170         QDomElement el = domDoc.firstChildElement("UI").firstChildElement("widget").toElement();
1171         fixNames(el);
1172         if (d->pos.isNull())
1173             fixPos(el, container);
1174         else
1175             changePos(el, d->pos);
1176 
1177         d->form->setInteractiveMode(false);
1178         FormIO::loadWidget(container, el, 0, 0);
1179         d->form->setInteractiveMode(true);
1180     }
1181     else {
1182         int minX = INT_MAX, minY = INT_MAX;
1183         if (!d->pos.isNull()) {
1184             // compute top-left point for the united rectangles
1185             for (n = domDoc.firstChildElement("UI").firstChild(); !n.isNull(); n = n.nextSibling()) {
1186                 // more than one "widget" child tag
1187                 if (n.toElement().tagName() != "widget") {
1188                     continue;
1189                 }
1190                 QDomElement el = n.toElement();
1191                 QDomElement rectEl;
1192                 for (QDomNode n2 = el.firstChild(); !n2.isNull(); n2 = n2.nextSibling()) {
1193                     if ((n2.toElement().tagName() == "property") && (n2.toElement().attribute("name") == "geometry")) {
1194                         rectEl = n2.firstChild().toElement();
1195                         break;
1196                     }
1197                 }
1198                 int x = rectEl.firstChildElement("x").text().toInt();
1199                 if (x < minX)
1200                     minX = x;
1201                 int y = rectEl.firstChildElement("y").text().toInt();
1202                 if (y < minY)
1203                     minY = y;
1204             }
1205         }
1206         for (n = domDoc.firstChildElement("UI").firstChild(); !n.isNull(); n = n.nextSibling()) {
1207             // more than one "widget" child tag
1208             if (n.toElement().tagName() != "widget") {
1209                 continue;
1210             }
1211             QDomElement el = n.toElement();
1212             fixNames(el);
1213             if (d->pos.isNull()) {
1214                 fixPos(el, container);
1215             }
1216             else {
1217                 moveWidgetBy(
1218                     el, container,
1219                     QPoint(-minX, -minY) + d->pos // fix position by subtracting the original
1220                                                   // offset and adding the new one
1221                 );
1222             }
1223 
1224             d->form->setInteractiveMode(false);
1225             FormIO::loadWidget(container, el, 0, 0);
1226             d->form->setInteractiveMode(true);
1227         }
1228     }
1229 
1230     d->names.clear();
1231     // We store the names of all the created widgets, to delete them later
1232     for (n = domDoc.firstChildElement("UI").firstChild(); !n.isNull(); n = n.nextSibling()) {
1233         if (n.toElement().tagName() != "widget") {
1234             continue;
1235         }
1236         for (QDomNode m = n.firstChild(); !m.isNull(); m = m.nextSibling()) {
1237             if ((m.toElement().tagName() == "property") && (m.toElement().attribute("name") == "name")) {
1238                 d->names.append(m.toElement().text());
1239                 break;
1240             }
1241         }
1242     }
1243 
1244     container->form()->selectFormWidget();
1245     foreach (const QString& widgetName, d->names) { // We select all the pasted widgets
1246         ObjectTreeItem *item = d->form->objectTree()->lookup(widgetName);
1247         if (item) {
1248             container->selectWidget(item->widget(),
1249                 Form::AddToPreviousSelection | Form::LastSelection | Form::Raise);
1250         }
1251     }
1252 }
1253 
1254 void PasteWidgetCommand::undo()
1255 {
1256     ObjectTreeItem* titem = d->form->objectTree()->lookup(d->containerName);
1257     if (!titem)
1258         return; //better this than a crash
1259     Container *container = titem->container();
1260     // We just delete all the widgets we have created
1261     foreach (const QString& widgetName, d->names) {
1262         ObjectTreeItem* titem = container->form()->objectTree()->lookup(widgetName);
1263         if (!titem) {
1264             continue; //better this than a crash
1265         }
1266         QWidget *w = titem->widget();
1267         container->deleteWidget(w);
1268     }
1269 }
1270 
1271 void PasteWidgetCommand::changePos(QDomElement &el, const QPoint &newPos)
1272 {
1273     QDomElement rect;
1274     // Find the widget geometry if there is one
1275     for (QDomNode n = el.firstChild(); !n.isNull(); n = n.nextSibling()) {
1276         if ((n.toElement().tagName() == "property") && (n.toElement().attribute("name") == "geometry")) {
1277             rect = n.firstChild().toElement();
1278             break;
1279         }
1280     }
1281 
1282     QDomElement x = rect.firstChildElement("x");
1283     x.removeChild(x.firstChild());
1284     QDomText valueX = el.ownerDocument().createTextNode(QString::number(newPos.x()));
1285     x.appendChild(valueX);
1286 
1287     QDomElement y = rect.firstChildElement("y");
1288     y.removeChild(y.firstChild());
1289     QDomText valueY = el.ownerDocument().createTextNode(QString::number(newPos.y()));
1290     y.appendChild(valueY);
1291 }
1292 
1293 void PasteWidgetCommand::fixPos(QDomElement &el, Container *container)
1294 {
1295     moveWidgetBy(el, container, QPoint(0, 0));
1296 }
1297 
1298 void PasteWidgetCommand::moveWidgetBy(QDomElement &el, Container *container, const QPoint &p)
1299 {
1300     QDomElement rect;
1301     for (QDomNode n = el.firstChild(); !n.isNull(); n = n.nextSibling()) {
1302         if ((n.toElement().tagName() == "property") && (n.toElement().attribute("name") == "geometry")) {
1303             rect = n.firstChild().toElement();
1304             break;
1305         }
1306     }
1307 
1308     QDomElement x = rect.firstChildElement("x");
1309     QDomElement y = rect.firstChildElement("y");
1310     QDomElement wi = rect.firstChildElement("width");
1311     QDomElement h = rect.firstChildElement("height");
1312 
1313     int rx = x.text().toInt();
1314     int ry = y.text().toInt();
1315     int rw = wi.text().toInt();
1316     int rh = h.text().toInt();
1317     QRect r(rx + p.x(), ry + p.y(), rw, rh);
1318     //qDebug() << "Moving widget by " << p << "from " << rx << ry << "to" << r.topLeft();
1319 
1320     QWidget *w = d->form->widget()->childAt(r.x() + 6, r.y() + 6);
1321 
1322     while (w && (w->geometry() == r)) { // there is already a widget there, with the same size
1323         w = d->form->widget()->childAt(w->x() + 16, w->y() + 16);
1324         r.translate(10, 10);
1325     }
1326 
1327     // the pasted wigdet should stay inside container's boundaries
1328     if (r.x() < 0)
1329         r.moveLeft(0);
1330     else if (r.right() > container->widget()->width())
1331         r.moveLeft(container->widget()->width() - r.width());
1332 
1333     if (r.y() < 0)
1334         r.moveTop(0);
1335     else if (r.bottom() > container->widget()->height())
1336         r.moveTop(container->widget()->height() - r.height());
1337 
1338     if (r != QRect(rx, ry, rw, rh)) {
1339         changePos(el, QPoint(r.x(), r.y()));
1340     }
1341 }
1342 
1343 void
1344 PasteWidgetCommand::fixNames(QDomElement &el)
1345 {
1346     QString wname;
1347     for (QDomNode n = el.firstChild(); !n.isNull(); n = n.nextSibling()) {
1348         if ((n.toElement().tagName() == "property") && (n.toElement().attribute("name") == "name")) {
1349             wname = n.toElement().text();
1350             while (d->form->objectTree()->lookup(wname)) { // name already exists
1351                 bool ok;
1352                 int num = wname.right(1).toInt(&ok, 10);
1353                 if (ok)
1354                     wname = wname.left(wname.length() - 1) + QString::number(num + 1);
1355                 else
1356                     wname += "2";
1357             }
1358             if (wname != n.toElement().text()) { // we change the name, so we recreate the element
1359                 n.removeChild(n.firstChild());
1360                 QDomElement type = el.ownerDocument().createElement("string");
1361                 QDomText valueE = el.ownerDocument().createTextNode(wname);
1362                 type.appendChild(valueE);
1363                 n.toElement().appendChild(type);
1364             }
1365 
1366         }
1367         if (n.toElement().tagName() == "widget") { // fix child widgets names
1368             QDomElement child = n.toElement();
1369             fixNames(child);
1370         }
1371     }
1372 }
1373 
1374 KFORMDESIGNER_EXPORT QDebug KFormDesigner::operator<<(QDebug dbg, const PasteWidgetCommand &c)
1375 {
1376     dbg.nospace() << "PasteWidgetCommand pos=" << c.d->pos
1377         << "widgets=" << c.d->names << "container=" << c.d->containerName
1378         << "form=" << c.d->form->widget()->objectName()
1379         << "data=" << (c.d->data.left(80) + "...");
1380     return dbg.space();
1381 }
1382 
1383 // DeleteWidgetCommand
1384 
1385 namespace KFormDesigner
1386 {
1387 class Q_DECL_HIDDEN DeleteWidgetCommand::Private
1388 {
1389 public:
1390     Private()
1391     {
1392     }
1393 
1394     Form *form;
1395     QDomDocument domDoc;
1396     QHash<QByteArray, QByteArray> containers;
1397     QHash<QByteArray, QByteArray> parents;
1398 };
1399 }
1400 
1401 DeleteWidgetCommand::DeleteWidgetCommand(Form& form, const QWidgetList &list, Command *parent)
1402         : Command(parent), d( new Private )
1403 {
1404     d->form = &form;
1405     KFormDesigner::widgetsToXML(d->domDoc,
1406         d->containers, d->parents, *d->form, list);
1407     setText( kundo2_i18n("Delete widget") );
1408 }
1409 
1410 DeleteWidgetCommand::~DeleteWidgetCommand()
1411 {
1412     delete d;
1413 }
1414 
1415 int DeleteWidgetCommand::id() const
1416 {
1417     return 10;
1418 }
1419 
1420 void DeleteWidgetCommand::debug() const
1421 {
1422     qDebug() << *this;
1423 }
1424 
1425 void DeleteWidgetCommand::execute()
1426 {
1427     QHash<QByteArray, QByteArray>::ConstIterator endIt = d->containers.constEnd();
1428     for (QHash<QByteArray, QByteArray>::ConstIterator it = d->containers.constBegin(); it != endIt; ++it) {
1429         ObjectTreeItem *item = d->form->objectTree()->lookup(it.key());
1430         if (!item || !item->widget())
1431             continue;
1432 
1433         Container *cont = d->form->parentContainer(item->widget());
1434         cont->deleteWidget(item->widget());
1435     }
1436 }
1437 
1438 void DeleteWidgetCommand::undo()
1439 {
1440     QByteArray wname;
1441     d->form->setInteractiveMode(false);
1442     for (QDomNode n = d->domDoc.firstChildElement("UI").firstChild(); !n.isNull(); n = n.nextSibling()) {
1443 #ifdef KFD_SIGSLOTS
1444         if (n.toElement().tagName() == "connections") // restore the widget connections
1445             d->form->connectionBuffer()->load(n);
1446 #endif
1447         if (n.toElement().tagName() != "widget")
1448             continue;
1449         // We need first to know the name of the widget
1450         for (QDomNode m = n.firstChild(); !m.isNull(); n = m.nextSibling()) {
1451             if ((m.toElement().tagName() == "property") && (m.toElement().attribute("name") == "name")) {
1452                 wname = m.toElement().text().toLatin1();
1453                 break;
1454             }
1455         }
1456 
1457         ObjectTreeItem* titem = d->form->objectTree()->lookup(d->containers.value(wname));
1458         if (!titem)
1459             return; //better this than a crash
1460         Container *cont = titem->container();
1461         ObjectTreeItem *parent = d->form->objectTree()->lookup(d->parents.value(wname));
1462         QDomElement widg = n.toElement();
1463         if (parent)
1464             FormIO::loadWidget(cont, widg, parent->widget(), 0);
1465         else
1466             FormIO::loadWidget(cont, widg, 0, 0);
1467     }
1468     d->form->setInteractiveMode(true);
1469 }
1470 
1471 KFORMDESIGNER_EXPORT QDebug KFormDesigner::operator<<(QDebug dbg, const DeleteWidgetCommand &c)
1472 {
1473     dbg.nospace() << "DeleteWidgetCommand containers=" << c.d->containers.keys()
1474         << "parents=" << c.d->parents.keys() << "form=" << c.d->form->widget()->objectName();
1475     return dbg.space();
1476 }
1477 
1478 // DuplicateWidgetCommand
1479 
1480 namespace KFormDesigner
1481 {
1482 class Q_DECL_HIDDEN DuplicateWidgetCommand::Private
1483 {
1484 public:
1485     Private()
1486      : pasteCommand(0)
1487     {
1488     }
1489     ~Private()
1490     {
1491         delete pasteCommand;
1492     }
1493 
1494     Form *form;
1495     QDomDocument domDoc;
1496     QHash<QByteArray, QByteArray> containers;
1497     QHash<QByteArray, QByteArray> parents;
1498     PasteWidgetCommand *pasteCommand;
1499 };
1500 }
1501 
1502 DuplicateWidgetCommand::DuplicateWidgetCommand(
1503     const Container& container,
1504     const QWidgetList &list,
1505     const QPoint& copyToPoint,
1506     Command *parent)
1507         : Command(parent), d( new Private )
1508 {
1509     d->form = container.form();
1510     QDomDocument docToCopy;
1511     KFormDesigner::widgetsToXML(docToCopy,
1512         d->containers, d->parents, *d->form, list);
1513 
1514     d->pasteCommand = new PasteWidgetCommand(docToCopy, container, copyToPoint);
1515     setText( kundo2_i18n("Duplicate widget") );
1516 }
1517 
1518 DuplicateWidgetCommand::~DuplicateWidgetCommand()
1519 {
1520     delete d;
1521 }
1522 
1523 int DuplicateWidgetCommand::id() const
1524 {
1525     return 11;
1526 }
1527 
1528 void DuplicateWidgetCommand::debug() const
1529 {
1530     qDebug() << *this;
1531 }
1532 
1533 void DuplicateWidgetCommand::execute()
1534 {
1535     d->pasteCommand->execute();
1536 }
1537 
1538 void DuplicateWidgetCommand::undo()
1539 {
1540     d->pasteCommand->undo();
1541 }
1542 
1543 KFORMDESIGNER_EXPORT QDebug KFormDesigner::operator<<(QDebug dbg, const DuplicateWidgetCommand &c)
1544 {
1545     dbg.nospace() << "DuplicateWidgetCommand containers=" << c.d->containers.keys()
1546         << "parents=" << c.d->parents.keys() << "form=" << c.d->form->widget()->objectName();
1547     return dbg.space();
1548 }
1549 
1550 // CutWidgetCommand
1551 
1552 namespace KFormDesigner
1553 {
1554 class Q_DECL_HIDDEN CutWidgetCommand::Private
1555 {
1556 public:
1557     Private()
1558      : data(0)
1559     {
1560     }
1561 
1562     ~Private()
1563     {
1564         delete data;
1565     }
1566 
1567     QMimeData *data;
1568 };
1569 }
1570 
1571 CutWidgetCommand::CutWidgetCommand(Form& form, const QWidgetList &list, Command *parent)
1572         : DeleteWidgetCommand(form, list, parent), d2( new Private )
1573 {
1574     setText( kundo2_i18n("Cut") );
1575 }
1576 
1577 CutWidgetCommand::~CutWidgetCommand()
1578 {
1579     delete d2;
1580 }
1581 
1582 int CutWidgetCommand::id() const
1583 {
1584     return 12;
1585 }
1586 
1587 void CutWidgetCommand::debug() const
1588 {
1589     qDebug() << *this;
1590 }
1591 
1592 void CutWidgetCommand::execute()
1593 {
1594     DeleteWidgetCommand::execute();
1595     delete d2->data;
1596     QClipboard *cb = QApplication::clipboard();
1597     d2->data = KFormDesigner::deepCopyOfMimeData(cb->mimeData()); // save clipboard contents
1598     // d->domDoc has been filled in DeleteWidgetCommand ctor
1599     KFormDesigner::copyToClipboard(d->domDoc.toString());
1600 }
1601 
1602 void CutWidgetCommand::undo()
1603 {
1604     DeleteWidgetCommand::undo();
1605     QClipboard *cb = QApplication::clipboard();
1606     cb->setMimeData(KFormDesigner::deepCopyOfMimeData(d2->data)); // restore prev. clipboard contents
1607 }
1608 
1609 KFORMDESIGNER_EXPORT QDebug KFormDesigner::operator<<(QDebug dbg, const CutWidgetCommand &c)
1610 {
1611     dbg.nospace() << "CutWidgetCommand containers=" << c.d->containers.keys()
1612         << "parents=" << c.d->parents.keys() << "form=" << c.d->form->widget()->objectName()
1613         << "data=" << (c.d2->data->text().left(80) + "...");
1614     return dbg.space();
1615 }
1616 
1617 // PropertyCommandGroup
1618 
1619 namespace KFormDesigner
1620 {
1621 class Q_DECL_HIDDEN PropertyCommandGroup::Private
1622 {
1623 public:
1624     Private()
1625     {
1626     }
1627 };
1628 }
1629 
1630 PropertyCommandGroup::PropertyCommandGroup(const QString &text, Command *parent)
1631         : Command(text, parent), d( new Private() )
1632 {
1633 }
1634 
1635 PropertyCommandGroup::~PropertyCommandGroup()
1636 {
1637     delete d;
1638 }
1639 
1640 int PropertyCommandGroup::id() const
1641 {
1642     return 13;
1643 }
1644 
1645 void PropertyCommandGroup::debug() const
1646 {
1647     qDebug() << *this;
1648 }
1649 
1650 void PropertyCommandGroup::execute()
1651 {
1652     KUndo2Command::redo();
1653 }
1654 
1655 KFORMDESIGNER_EXPORT QDebug KFormDesigner::operator<<(QDebug dbg, const PropertyCommandGroup &c)
1656 {
1657     dbg.nospace() << "PropertyCommandGroup" << static_cast<const Command&>(c);
1658     return dbg.space();
1659 }
1660 
1661 // InlineTextEditingCommand
1662 
1663 namespace KFormDesigner
1664 {
1665 class Q_DECL_HIDDEN InlineTextEditingCommand::Private
1666 {
1667 public:
1668     Private()
1669      : oldTextKnown(false)
1670     {
1671     }
1672     Form *form;
1673     QPointer<QWidget> widget;
1674     QByteArray editedWidgetClass;
1675     QString text;
1676     QString oldText;
1677     /*! Used to make sure that oldText is set only on the first execution
1678         of InlineTextEditingCommand::execute() */
1679     bool oldTextKnown;
1680 };
1681 }
1682 
1683 InlineTextEditingCommand::InlineTextEditingCommand(
1684     Form& form, QWidget *widget, const QByteArray &editedWidgetClass,
1685     const QString &text, Command *parent)
1686  : Command(parent)
1687  , d( new Private )
1688 {
1689     d->form = &form;
1690     d->widget = widget;
1691     d->editedWidgetClass = editedWidgetClass;
1692     d->text = text;
1693     d->widget = widget;
1694 }
1695 
1696 InlineTextEditingCommand::~InlineTextEditingCommand()
1697 {
1698     delete d;
1699 }
1700 
1701 int InlineTextEditingCommand::id() const
1702 {
1703     return 14;
1704 }
1705 
1706 void InlineTextEditingCommand::debug() const
1707 {
1708     qDebug() << *this;
1709 }
1710 
1711 void InlineTextEditingCommand::execute()
1712 {
1713     WidgetInfo *wi = d->form->library()->widgetInfoForClassName(d->editedWidgetClass);
1714     if (!wi)
1715         return;
1716 
1717     QString oldText;
1718     d->form->setSlotPropertyChangedEnabled(false);
1719     bool ok = wi->factory()->changeInlineText(d->form, d->widget, d->text, oldText);
1720     if (!ok && wi && wi->inheritedClass()) {
1721         ok = wi->inheritedClass()->factory()->changeInlineText(d->form, d->widget, d->text, oldText);
1722     }
1723     d->form->setSlotPropertyChangedEnabled(true);
1724     if (!ok)
1725         return;
1726     if (!d->oldTextKnown) {
1727         d->oldText = oldText;
1728         d->oldTextKnown = true;
1729     }
1730 }
1731 
1732 void InlineTextEditingCommand::undo()
1733 {
1734     WidgetInfo *wi = d->form->library()->widgetInfoForClassName(d->editedWidgetClass);
1735     if (!wi)
1736         return;
1737 
1738     QString dummy;
1739     d->form->setSlotPropertyChangedEnabled(false);
1740     bool ok = wi->factory()->changeInlineText(d->form, d->widget, d->oldText, dummy);
1741     if (!ok && wi && wi->inheritedClass()) {
1742         ok = wi->inheritedClass()->factory()->changeInlineText(d->form, d->widget, d->oldText, dummy);
1743     }
1744     d->form->setSlotPropertyChangedEnabled(true);
1745 }
1746 
1747 bool InlineTextEditingCommand::mergeWith(const KUndo2Command * command)
1748 {
1749     if (id() != command->id())
1750         return false;
1751     const InlineTextEditingCommand* inlineTextEditingCommand = static_cast<const InlineTextEditingCommand*>(command);
1752     if (   form() == inlineTextEditingCommand->form()
1753         && text() == inlineTextEditingCommand->oldText())
1754     {
1755         //qDebug() << "Changed from" << text() << "to" << inlineTextEditingCommand->text();
1756         d->text = inlineTextEditingCommand->text();
1757         return true;
1758     }
1759     return false;
1760 }
1761 
1762 Form* InlineTextEditingCommand::form() const
1763 {
1764     return d->form;
1765 }
1766 
1767 QString InlineTextEditingCommand::text() const
1768 {
1769     return d->text;
1770 }
1771 
1772 QString InlineTextEditingCommand::oldText() const
1773 {
1774     return d->oldText;
1775 }
1776 
1777 KFORMDESIGNER_EXPORT QDebug KFormDesigner::operator<<(QDebug dbg, const InlineTextEditingCommand &c)
1778 {
1779     dbg.nospace() << "InlineTextEditingCommand" << static_cast<const Command&>(c);
1780     return dbg.space();
1781 }
1782 
1783 //  Tab related commands (to allow tab creation/deletion undoing)
1784 
1785 namespace KFormDesigner
1786 {
1787 class Q_DECL_HIDDEN InsertPageCommand::Private
1788 {
1789 public:
1790     Private()
1791     {
1792     }
1793     Form *form;
1794     QString containername;
1795     QString name;
1796     QString parentname;
1797 };
1798 }
1799 
1800 InsertPageCommand::InsertPageCommand(Container *container, QWidget *parent)
1801         : Command()
1802         , d(new Private)
1803 {
1804     d->containername = container->widget()->objectName();
1805     d->form = container->form();
1806     d->parentname = parent->objectName();
1807     setText( kundo2_i18n("Add Page") );
1808 }
1809 
1810 InsertPageCommand::~InsertPageCommand()
1811 {
1812     delete d;
1813 }
1814 
1815 int InsertPageCommand::id() const
1816 {
1817     return 15;
1818 }
1819 
1820 void InsertPageCommand::debug() const
1821 {
1822     qDebug() << *this;
1823 }
1824 
1825 void InsertPageCommand::execute()
1826 {
1827     execute(QString(), QString(), -1);
1828 }
1829 
1830 void InsertPageCommand::execute(const QString& pageWidgetName, const QString& pageName, int pageIndex)
1831 {
1832     Container *container = d->form->objectTree()->lookup(d->containername)->container();
1833     QWidget *parent = d->form->objectTree()->lookup(d->parentname)->widget();
1834     if (d->name.isEmpty()) {
1835         if (pageWidgetName.isEmpty()) {
1836             d->name = container->form()->objectTree()->generateUniqueName(
1837                          container->form()->library()->displayName("QWidget").toLatin1(),
1838                          /*!numberSuffixRequired*/false);
1839         }
1840         else {
1841             d->name = pageWidgetName;
1842         }
1843     }
1844 
1845     QWidget *page = container->form()->library()->createWidget(
1846         "QWidget", parent, d->name.toLatin1(), container);
1847     page->setAutoFillBackground(true);
1848     ObjectTreeItem *item = container->form()->objectTree()->lookup(d->name);
1849 
1850     QByteArray classname = parent->metaObject()->className();
1851     if (classname == "KFDTabWidget") {
1852         TabWidgetBase *tab = qobject_cast<TabWidgetBase*>(parent);
1853         const QString realPageName = pageName.isEmpty() ?
1854             xi18n("Page %1", tab->count() + 1) : pageName;
1855         if (pageIndex < 0)
1856             pageIndex = tab->count();
1857         tab->insertTab(pageIndex, page, realPageName);
1858         tab->setCurrentWidget(page);
1859         item->addModifiedProperty("title", realPageName);
1860     } else if (classname == "QStackedWidget" || /* compat */ classname == "QWidgetStack") {
1861         QStackedWidget *stack = qobject_cast<QStackedWidget*>(parent);
1862         if (stack) {
1863             stack->addWidget(page);
1864             stack->setCurrentWidget(page);
1865             item->addModifiedProperty("stackIndex", stack->indexOf(page));
1866         }
1867     }
1868 }
1869 
1870 void InsertPageCommand::undo()
1871 {
1872     undo(QString());
1873 }
1874 
1875 void InsertPageCommand::undo(const QString& name)
1876 {
1877     if (!name.isEmpty()) {
1878         d->name = name;
1879     }
1880     ObjectTreeItem *item = d->form->objectTree()->lookup(d->name);
1881     if (!item) {
1882         return;
1883     }
1884     QWidget *page = item->widget();
1885     if (!page) {
1886         return;
1887     }
1888     ObjectTreeItem *parentItem = d->form->objectTree()->lookup(d->parentname);
1889     if (!parentItem) {
1890         return;
1891     }
1892     QWidget *parent = parentItem->widget();
1893     if (!parent) {
1894         return;
1895     }
1896 
1897     QWidgetList list;
1898     list.append(page);
1899     DeleteWidgetCommand command(*d->form, list);
1900 
1901     QByteArray classname = parent->metaObject()->className();
1902     if (classname == "KFDTabWidget") {
1903         TabWidgetBase *tab = qobject_cast<TabWidgetBase*>(parent);
1904         tab->removeTab(tab->indexOf(page));
1905     } else if (classname == "QStackedWidget" || /* compat */ classname == "QWidgetStack") {
1906         QStackedWidget *stack = qobject_cast<QStackedWidget*>(parent);
1907         int index = stack->indexOf(page);
1908         if (index > 0)
1909             index--;
1910         else if (index < (stack->count()-1))
1911             index++;
1912         else
1913             index = -1;
1914 
1915         if (index >= 0)
1916             stack->setCurrentIndex(index);
1917         stack->removeWidget(page);
1918     }
1919 
1920     command.execute();
1921 }
1922 
1923 KFORMDESIGNER_EXPORT QDebug KFormDesigner::operator<<(QDebug dbg, const InsertPageCommand &c)
1924 {
1925     dbg.nospace() << "InsertPageCommand" << static_cast<const Command&>(c);
1926     return dbg.space();
1927 }
1928 
1929 namespace KFormDesigner
1930 {
1931 class Q_DECL_HIDDEN RemovePageCommand::Private
1932 {
1933 public:
1934     Private()
1935      : pageIndex(-1)
1936     {
1937     }
1938     Form *form;
1939     QString containername;
1940     QString name;
1941     QString pageName;
1942     int pageIndex;
1943     QString parentname;
1944     InsertPageCommand *insertCommand;
1945 };
1946 }
1947 
1948 RemovePageCommand::RemovePageCommand(Container *container, QWidget *parent)
1949         : Command()
1950         , d(new Private)
1951 {
1952     d->containername = container->widget()->objectName();
1953     d->form = container->form();
1954     TabWidgetBase *tab = qobject_cast<TabWidgetBase*>(parent);
1955     if (tab) {
1956         d->name = tab->currentWidget()->objectName();
1957         d->pageName = tab->tabText(tab->currentIndex());
1958         d->pageIndex = tab->currentIndex();
1959     }
1960     d->parentname = parent->objectName();
1961     d->insertCommand = new InsertPageCommand(container, parent);
1962     setText( kundo2_i18n("Delete Page") );
1963 }
1964 
1965 RemovePageCommand::~RemovePageCommand()
1966 {
1967     delete d->insertCommand;
1968     delete d;
1969 }
1970 
1971 int RemovePageCommand::id() const
1972 {
1973     return 16;
1974 }
1975 
1976 void RemovePageCommand::debug() const
1977 {
1978     qDebug() << *this;
1979 }
1980 
1981 void RemovePageCommand::execute()
1982 {
1983     d->insertCommand->undo(d->name);
1984 }
1985 
1986 void RemovePageCommand::undo()
1987 {
1988     d->insertCommand->execute(d->name, d->pageName, d->pageIndex);
1989 }
1990 
1991 int RemovePageCommand::pageIndex() const
1992 {
1993     return d->pageIndex;
1994 }
1995 
1996 QString RemovePageCommand::pageName() const
1997 {
1998     return d->pageName;
1999 }
2000 
2001 KFORMDESIGNER_EXPORT QDebug KFormDesigner::operator<<(QDebug dbg, const RemovePageCommand &c)
2002 {
2003     dbg.nospace() << "RemovePageCommand" << static_cast<const Command&>(c);
2004     return dbg.space();
2005 }
2006