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