File indexing completed on 2024-05-19 12:54:34

0001 /* This file is part of the KDE project
0002    Copyright (C) 2003 Lucijan Busch <lucijan@gmx.at>
0003    Copyright (C) 2004 Cedric Pasteur <cedric.pasteur@free.fr>
0004    Copyright (C) 2004-2017 Jarosław Staniek <staniek@kde.org>
0005 
0006    This library is free software; you can redistribute it and/or
0007    modify it under the terms of the GNU Library General Public
0008    License as published by the Free Software Foundation; either
0009    version 2 of the License, or (at your option) any later version.
0010 
0011    This library is distributed in the hope that it will be useful,
0012    but WITHOUT ANY WARRANTY; without even the implied warranty of
0013    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0014    Library General Public License for more details.
0015 
0016    You should have received a copy of the GNU Library General Public License
0017    along with this library; see the file COPYING.LIB.  If not, write to
0018    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0019  * Boston, MA 02110-1301, USA.
0020 */
0021 
0022 #include "form_p.h"
0023 #include "WidgetInfo.h"
0024 #include "FormWidget.h"
0025 #include "container.h"
0026 #include "objecttree.h"
0027 #include "formIO.h"
0028 #include "FormWidgetInterface.h"
0029 #include "widgetlibrary.h"
0030 #include "events.h"
0031 #include "utils.h"
0032 #include "widgetwithsubpropertiesinterface.h"
0033 #include "tabstopdialog.h"
0034 #include <kexiutils/utils.h>
0035 #include <KexiIcon.h>
0036 #include <core/kexi.h>
0037 #include <KexiMainWindowIface.h>
0038 
0039 #include <kundo2stack.h>
0040 
0041 #include <KDb>
0042 #include <KDbUtils>
0043 
0044 #include <KMessageBox>
0045 #include <KActionCollection>
0046 #include <KTextEdit>
0047 #include <KLocalizedString>
0048 
0049 #include <QApplication>
0050 #include <QClipboard>
0051 #include <QLabel>
0052 #include <QDomDocument>
0053 #include <QLineEdit>
0054 #include <QMenu>
0055 #include <QAction>
0056 #include <QDebug>
0057 #include <QFontDialog>
0058 #include <QMimeData>
0059 #include <QTimer>
0060 
0061 using namespace KFormDesigner;
0062 
0063 Form::Form(WidgetLibrary *library, Mode mode, KActionCollection &col, ActionGroup &group)
0064     : QObject(library), d(new FormPrivate(this, library, mode, col, group))
0065 {
0066 }
0067 
0068 Form::Form(Form *parent)
0069     : QObject(parent->library())
0070     , d(new FormPrivate(this, parent->library(), parent->mode(), *parent->actionCollection(),
0071                         *parent->widgetActionGroup()))
0072 {
0073 }
0074 
0075 Form::~Form()
0076 {
0077     emit destroying();
0078     delete d;
0079 }
0080 
0081 WidgetLibrary* Form::library() const
0082 {
0083     return d->library;
0084 }
0085 
0086 KActionCollection  *Form::actionCollection() const
0087 {
0088     return d->collection;
0089 }
0090 
0091 KFormDesigner::ActionGroup* Form::widgetActionGroup() const
0092 {
0093     return d->widgetActionGroup;
0094 }
0095 
0096 void Form::setFeatures(Features features)
0097 {
0098     d->features = features;
0099 }
0100 
0101 Form::Features Form::features() const
0102 {
0103     return d->features;
0104 }
0105 
0106 QWidget* Form::widget() const
0107 {
0108     if (d->topTree)
0109         return d->topTree->widget();
0110     else if (d->toplevel)
0111         return d->toplevel->widget();
0112     else // preview form
0113         return d->widget;
0114 }
0115 
0116 FormWidget* Form::formWidget() const
0117 {
0118     return d->formWidget;
0119 }
0120 
0121 ObjectTree* Form::objectTree() const
0122 {
0123     return d->topTree;
0124 }
0125 
0126 QWidgetList* Form::selectedWidgets() const
0127 {
0128     return &(d->selected);
0129 }
0130 
0131 QWidget* Form::selectedWidget() const
0132 {
0133     return d->selected.count() == 1 ? d->selected.first() : 0;
0134 }
0135 
0136 void Form::setInteractiveMode(bool interactive)
0137 {
0138     d->interactive = interactive;
0139 }
0140 
0141 bool Form::interactiveMode() const
0142 {
0143     return d->interactive;
0144 }
0145 
0146 Form::Mode Form::mode() const
0147 {
0148     return d->mode;
0149 }
0150 
0151 bool Form::isModified() const
0152 {
0153     return d->modified;
0154 }
0155 
0156 void Form::setModified(bool set)
0157 {
0158     d->modified = set;
0159     emit modified(set);
0160 }
0161 
0162 int Form::gridSize() const
0163 {
0164     return d->gridSize;
0165 }
0166 
0167 void Form::setGridSize(int gridSize)
0168 {
0169     d->gridSize = gridSize;
0170 }
0171 
0172 int Form::defaultMargin() const
0173 {
0174     return 11;
0175 }
0176 
0177 int Form::defaultSpacing() const
0178 {
0179     return 6;
0180 }
0181 
0182 QString Form::fileName() const
0183 {
0184     return d->filename;
0185 }
0186 
0187 void Form::setFileName(const QString &file)
0188 {
0189     d->filename = file;
0190 }
0191 
0192 void Form::clearUndoStack()
0193 {
0194     d->undoStack.clear();
0195 }
0196 
0197 void Form::setUndoStackClean()
0198 {
0199     d->undoStack.setClean();
0200 }
0201 
0202 #ifdef KFD_SIGSLOTS
0203 ConnectionBuffer* Form::connectionBuffer() const
0204 {
0205     return d->connBuffer;
0206 }
0207 
0208 void Form::setConnectionBuffer(ConnectionBuffer *b)
0209 {
0210     if (b != d->connBuffer) {
0211         delete d->connBuffer;
0212     }
0213     d->connBuffer = b;
0214 }
0215 #endif
0216 
0217 #ifdef KEXI_PIXMAP_COLLECTIONS_SUPPORT
0218 PixmapCollection* Form::pixmapCollection() const
0219 {
0220     return d->pixcollection;
0221 }
0222 #endif
0223 
0224 void Form::setPixmapsStoredInline(bool set)
0225 {
0226     d->pixmapsStoredInline = set;
0227 }
0228 
0229 bool Form::pixmapsStoredInline() const
0230 {
0231     return d->pixmapsStoredInline;
0232 }
0233 
0234 ObjectTreeList* Form::tabStops()
0235 {
0236     return &(d->tabstops);
0237 }
0238 
0239 bool Form::autoTabStops() const {
0240     return d->autoTabstops;
0241 }
0242 
0243 void Form::setAutoTabStops(bool autoTab)
0244 {
0245     d->autoTabstops = autoTab;
0246 }
0247 
0248 QHash<QByteArray, QString>* Form::headerProperties()
0249 {
0250     return &d->headerProperties;
0251 }
0252 
0253 //////////////// Container -related functions ///////////////////////
0254 
0255 Container* Form::toplevelContainer() const
0256 {
0257     return d->toplevel;
0258 }
0259 
0260 void Form::createToplevel(QWidget *container, FormWidget *formWidget, const QByteArray &)
0261 {
0262     //qDebug() << "container=" << (container ? container->objectName() : "<NULL>")
0263     //         << "formWidget=" << formWidget;
0264 
0265     setFormWidget(formWidget);
0266     d->toplevel = new Container(0, container, this);
0267     d->toplevel->setObjectName(objectName());
0268     d->topTree = new ObjectTree(xi18n("Form"), container->objectName(), container, d->toplevel);
0269     d->toplevel->setObjectTree(d->topTree);
0270     d->toplevel->setForm(this);
0271 
0272 //! @todo pixmapcollection
0273 #ifdef KEXI_PIXMAP_COLLECTIONS_SUPPORT
0274     d->pixcollection = new PixmapCollection(container->objectName(), this);
0275 #endif
0276 
0277     d->topTree->setWidget(container);
0278 //! @todo copy caption in Kexi from object's caption
0279 // d->topTree->addModifiedProperty("caption", name());
0280 //d->topTree->addModifiedProperty("icon");
0281 
0282     connect(container, SIGNAL(destroyed()), this, SLOT(formDeleted()));
0283     //qDebug() << "d->toplevel=" << d->toplevel;
0284 
0285     // alter the style
0286     delete d->designModeStyle;
0287     d->designModeStyle = 0;
0288     if (d->mode == DesignMode) {
0289         d->designModeStyle = new DesignModeStyle(d->topTree->widget()->style()->objectName());
0290         d->designModeStyle->setParent(this);
0291         d->topTree->widget()->setStyle(d->designModeStyle);
0292     }
0293 }
0294 
0295 Container* Form::activeContainer()
0296 {
0297     if (d->selected.isEmpty())
0298         return d->toplevel;
0299 
0300     ObjectTreeItem *it;
0301     if (d->selected.count() == 1)
0302         it = d->topTree->lookup(d->selected.last()->objectName());
0303     else
0304         it = commonParentContainer(d->selected);
0305 
0306     if (!it)
0307         return 0;
0308     if (it->container())
0309         return it->container();
0310     else
0311         return it->parent()->container();
0312 }
0313 
0314 ObjectTreeItem* Form::commonParentContainer(const QWidgetList& wlist)
0315 {
0316     // create a list of all widget parents
0317     QSet<QWidget*> parents;
0318     foreach (QWidget *w, wlist) {
0319         parents.insert(w->parentWidget());
0320     }
0321 
0322     QWidgetList parentsList(parents.toList());
0323     removeChildrenFromList(parentsList);
0324 
0325     // one widget remains == the container we are looking for
0326     ObjectTreeItem *item;
0327     if (parentsList.count() == 1) {
0328         item = d->topTree->lookup(parentsList.first()->objectName());
0329     }
0330     else {
0331         // we need to go one level up
0332         item =  commonParentContainer(parentsList);
0333     }
0334     return item;
0335 }
0336 
0337 Container* Form::parentContainer(QWidget *w) const
0338 {
0339     if (!w)
0340         return 0;
0341     ObjectTreeItem *it = d->topTree->lookup(w->objectName());
0342     if (!it || !it->parent())
0343         return 0;
0344 
0345     if (it->parent()->container())
0346         return it->parent()->container();
0347     else
0348         return it->parent()->parent()->container();
0349 }
0350 
0351 void Form::setMode(Mode mode)
0352 {
0353     d->mode = mode;
0354     if (d->mode == DesignMode) {
0355         d->designModeStyle = new DesignModeStyle(d->widget->style()->objectName());
0356         d->designModeStyle->setParent(this);
0357         d->widget->setStyle(d->designModeStyle);
0358         return;
0359     }
0360 
0361     ObjectTreeHash hash(*(d->topTree->hash()));
0362     foreach (ObjectTreeItem *item, hash) {
0363         library()->previewWidget(
0364             item->widget()->metaObject()->className(),
0365             item->widget(), d->toplevel
0366         );
0367     }
0368 
0369     d->widget = d->topTree->widget();
0370     delete d->topTree;
0371     d->topTree = 0;
0372     delete d->toplevel;
0373     d->toplevel = 0;
0374 
0375     // alter the style
0376     delete d->designModeStyle;
0377     d->designModeStyle = 0;
0378 }
0379 
0380 
0381 ///////////////////////////// Selection stuff ///////////////////////
0382 
0383 void Form::selectWidget(QWidget *w, WidgetSelectionFlags flags)
0384 {
0385     if (!d->selectWidgetEnabled) {
0386         return;
0387     }
0388     if (selectedWidget() && !checkNameValidity(d->propertySet.propertyValue("objectName").toString(), CheckValidityOnly)) {
0389         // current selection has invalid objectName: don't allow to switch so user is able to fix it!
0390         //qDebug() << "disallow!";
0391         return;
0392     }
0393 
0394     d->selectWidgetEnabled = false;
0395     selectWidgetInternal(w, flags);
0396     d->selectWidgetEnabled = true;
0397 }
0398 
0399 void Form::selectWidgetInternal(QWidget *w, WidgetSelectionFlags flags)
0400 {
0401     if (!w) {
0402         selectWidget(widget());
0403         return;
0404     }
0405     //qDebug() << "selected count=" << d->selected.count();
0406     if (!d->selected.isEmpty()) {
0407         //qDebug() << "first=" << d->selected.first();
0408     }
0409     //qDebug() << w;
0410 
0411     if (d->selected.count() == 1 && d->selected.first() == w) {
0412         return;
0413     }
0414 
0415     if (d->selected.isEmpty() || w == widget() || (d->selected.first() == widget())) {
0416         flags |= ReplacePreviousSelection;
0417     }
0418 
0419     //raise selected widget and all possible parents
0420     QWidget *wtmp = w;
0421     while (!(flags & DontRaise) && wtmp && wtmp->parentWidget() && (wtmp != widget())) {
0422         wtmp->raise();
0423         if (d->resizeHandles.value( wtmp->objectName() ))
0424             d->resizeHandles.value( wtmp->objectName() )->raise();
0425         wtmp = wtmp->parentWidget();
0426     }
0427 
0428     if (wtmp)
0429         wtmp->setFocus();
0430 
0431     if (flags & ReplacePreviousSelection) {
0432         d->selected.clear();
0433         qDeleteAll(d->resizeHandles);
0434         d->resizeHandles.clear();
0435     }
0436     d->selected.append(w);
0437     emitSelectionChanged(w, flags);
0438     emitActionSignals();
0439 
0440     // WidgetStack and TabWidget pages widgets shouldn't have resize handles, but their parent
0441 //! @todo move special case to a factory?
0442 #if 0
0443     if (!isTopLevelWidget(w) && w->parentWidget()
0444             && KexiUtils::objectIsA(w->parentWidget(), "QWidgetStack")) {
0445         w = w->parentWidget();
0446         if (w->parentWidget() && w->parentWidget()->inherits("QTabWidget"))
0447             w = w->parentWidget();
0448     }
0449 #endif
0450 
0451     if (w && w != widget()) {
0452         ResizeHandleSet *handles = new ResizeHandleSet(w, this);
0453         d->resizeHandles.insert(w->objectName(), handles);
0454         connect(handles, SIGNAL(geometryChangeStarted()),
0455                 parentContainer(w), SLOT(startChangingGeometryPropertyForSelectedWidget()));
0456         connect(handles, SIGNAL(geometryChanged(QRect)),
0457                 parentContainer(w), SLOT(setGeometryPropertyForSelectedWidget(QRect)));
0458     }
0459 }
0460 
0461 void Form::selectWidgets(const QList<QWidget*>& widgets, WidgetSelectionFlags flags)
0462 {
0463     int i = 0;
0464     const int count = widgets.count();
0465     foreach (QWidget* widget, widgets) {
0466         if (i == 1) {
0467             flags |= AddToPreviousSelection;
0468         }
0469         if (i == (count - 1)) {
0470             flags = LastSelection;
0471         }
0472         selectWidget(widget, flags);
0473         ++i;
0474     }
0475 }
0476 
0477 QList<QWidget*> Form::widgetsForNames(const QList<QByteArray>& names) const
0478 {
0479     QList<QWidget*> widgets;
0480     foreach (const QByteArray& name, names) {
0481         ObjectTreeItem* item = objectTree()->lookup(name);
0482         if (item) { //we're checking for item!=0 because the name could be of a form widget
0483             widgets.append(item->widget());
0484         }
0485     }
0486     return widgets;
0487 }
0488 
0489 void Form::selectWidgets(const QList<QByteArray>& names, WidgetSelectionFlags flags)
0490 {
0491     selectWidgets(widgetsForNames(names), flags);
0492 }
0493 
0494 bool Form::isTopLevelWidget(QWidget *w) const
0495 {
0496     /* should not be used, just check w==formWidget() instead? */
0497     ObjectTreeItem *item = objectTree()->lookup(w->objectName());
0498     if (!item)
0499         return true;
0500 
0501     return !item->parent();
0502 }
0503 
0504 ResizeHandleSet* Form::resizeHandlesForWidget(QWidget* w)
0505 {
0506     return d->resizeHandles.value(w->objectName());
0507 }
0508 
0509 void Form::deselectWidget(QWidget *w)
0510 {
0511     d->selected.removeOne(w);
0512     ResizeHandleSet *set = d->resizeHandles.take(w->objectName());
0513     delete set;
0514 }
0515 
0516 void Form::selectFormWidget()
0517 {
0518     selectWidget(widget());
0519 }
0520 
0521 void Form::clearSelection()
0522 {
0523     d->selected.clear();
0524     qDeleteAll(d->resizeHandles);
0525     d->resizeHandles.clear();
0526     emitSelectionChanged(0, DefaultWidgetSelectionFlags);
0527     emitActionSignals();
0528 }
0529 
0530 void Form::setInsertionPoint(const QPoint &p)
0531 {
0532     d->insertionPoint = p;
0533 }
0534 
0535 QAction* Form::action(const QString& name)
0536 {
0537     if (name == KStandardAction::name(KStandardAction::Undo)) {
0538         QAction *a = d->internalCollection.action( name );
0539         if (!a) {
0540             a = d->undoStack.createUndoAction(&d->internalCollection);
0541             // connect this action to the form instead of stack
0542             disconnect(a, SIGNAL(triggered()), &d->undoStack, SLOT(undo()));
0543             connect(a, SIGNAL(triggered()), this, SLOT(undo()));
0544         }
0545         return a;
0546     }
0547     else if (name == KStandardAction::name(KStandardAction::Redo)) {
0548         QAction *a = d->internalCollection.action( name );
0549         if (!a) {
0550             a = d->undoStack.createRedoAction(&d->internalCollection);
0551             // connect this action to the form instead of stack
0552             disconnect(a, SIGNAL(triggered()), &d->undoStack, SLOT(redo()));
0553             connect(a, SIGNAL(triggered()), this, SLOT(redo()));
0554         }
0555         return a;
0556     }
0557     return d->collection->action(name);
0558 }
0559 
0560 void Form::emitActionSignals()
0561 {
0562     // Update menu and toolbar items
0563     if (selectedWidget()) {
0564       if (widget() == selectedWidget())
0565         emitFormWidgetSelected();
0566       else
0567         emitWidgetSelected( false );
0568     }
0569     else if (selectedWidgets()) {
0570       emitWidgetSelected( true );
0571     }
0572 }
0573 
0574 void Form::emitUndoActionSignals()
0575 {
0576 //! @todo pixmapcollection
0577 #ifdef KEXI_PIXMAP_COLLECTIONS_SUPPORT
0578     QAction *undoAction = d->collection->action(QLatin1String("edit_undo"));
0579     if (undoAction)
0580         emitUndoEnabled(undoAction->isEnabled(), undoAction->text());
0581 
0582     QAction *redoAction = d->collection->action(QLatin1String("edit_redo"));
0583     if (redoAction)
0584         emitRedoEnabled(redoAction->isEnabled(), redoAction->text());
0585 #endif
0586 }
0587 
0588 void
0589 Form::emitSelectionSignals()
0590 {
0591     if (!selectedWidgets()->isEmpty()) {
0592         emitSelectionChanged(selectedWidgets()->first(), DefaultWidgetSelectionFlags);
0593     }
0594     foreach (QWidget *w, *selectedWidgets()) {
0595         emitSelectionChanged(w, LastSelection);
0596     }
0597 }
0598 
0599 void Form::emitWidgetSelected(bool multiple)
0600 {
0601     enableFormActions();
0602     // Enable edit actions
0603     d->enableAction("edit_copy", true);
0604     d->enableAction("edit_cut", true);
0605     d->enableAction("edit_delete", true);
0606     d->enableAction("clear_contents", true);
0607 
0608     // 'Align Widgets' menu
0609     d->enableAction("align_menu", multiple);
0610     d->enableAction("align_to_left", multiple);
0611     d->enableAction("align_to_right", multiple);
0612     d->enableAction("align_to_top", multiple);
0613     d->enableAction("align_to_bottom", multiple);
0614 
0615     d->enableAction("adjust_size_menu", true);
0616     d->enableAction("adjust_width_small", multiple);
0617     d->enableAction("adjust_width_big", multiple);
0618     d->enableAction("adjust_height_small", multiple);
0619     d->enableAction("adjust_height_big", multiple);
0620 
0621     d->enableAction("format_raise", true);
0622     d->enableAction("format_lower", true);
0623 
0624     QWidgetList *wlist = selectedWidgets();
0625     bool fontEnabled = false;
0626     foreach (QWidget* w, *wlist) {
0627         if (-1 != w->metaObject()->indexOfProperty("font")) {
0628             fontEnabled = true;
0629             break;
0630         }
0631     }
0632     d->enableAction("format_font", fontEnabled);
0633 
0634     // If the widgets selected is a container, we enable layout actions
0635     if (!multiple) {
0636         if (!wlist->isEmpty()) {
0637             objectTree()->lookup(wlist->first()->objectName());
0638         }
0639     }
0640     emit widgetSelected(true);
0641 }
0642 
0643 void Form::emitFormWidgetSelected()
0644 {
0645     d->enableAction("edit_copy", false);
0646     d->enableAction("edit_cut", false);
0647     d->enableAction("edit_delete", false);
0648     d->enableAction("clear_contents", false);
0649 
0650     // Disable format functions
0651     d->enableAction("align_menu", false);
0652     d->enableAction("align_to_left", false);
0653     d->enableAction("align_to_right", false);
0654     d->enableAction("align_to_top", false);
0655     d->enableAction("align_to_bottom", false);
0656     d->enableAction("adjust_size_menu", false);
0657     d->enableAction("format_raise", false);
0658     d->enableAction("format_lower", false);
0659 
0660     d->enableAction("format_font", false);
0661 
0662     enableFormActions();
0663     emit formWidgetSelected();
0664 }
0665 
0666 void Form::emitNoFormSelected()
0667 {
0668     disableWidgetActions();
0669 
0670     // Disable 'Tools' actions
0671     d->enableAction("pixmap_collection", false);
0672 #ifdef KFD_SIGSLOTS
0673     if (d->features & EnableConnections) {
0674         d->enableAction("form_connections", false);
0675     }
0676 #endif
0677     d->enableAction("taborder", false);
0678     d->enableAction("change_style", true);
0679 
0680     // Disable items in 'File'
0681     if (d->features & EnableFileActions) {
0682         d->enableAction("file_save", false);
0683         d->enableAction("file_save_as", false);
0684         d->enableAction("preview_form", false);
0685     }
0686 
0687     emit noFormSelected();
0688 }
0689 
0690 void Form::enableFormActions()
0691 {
0692     // Enable 'Tools' actions
0693     d->enableAction("pixmap_collection", true);
0694 #ifdef KFD_SIGSLOTS
0695     if (d->features & EnableConnections) {
0696         d->enableAction("form_connections", true);
0697     }
0698 #endif
0699     d->enableAction("taborder", true);
0700     d->enableAction("change_style", true);
0701 
0702     // Enable items in 'File'
0703     if (d->features & EnableFileActions) {
0704         d->enableAction("file_save", true);
0705         d->enableAction("file_save_as", true);
0706         d->enableAction("preview_form", true);
0707     }
0708 
0709     d->enableAction("edit_paste", true); //?? isPasteEnabled());
0710     d->enableAction("edit_select_all", true);
0711 }
0712 
0713 void Form::disableWidgetActions()
0714 {
0715     // Disable edit actions
0716     d->enableAction("edit_copy", false);
0717     d->enableAction("edit_cut", false);
0718     d->enableAction("edit_delete", false);
0719     d->enableAction("clear_contents", false);
0720 
0721     // Disable format functions
0722     d->enableAction("align_menu", false);
0723     d->enableAction("align_to_left", false);
0724     d->enableAction("align_to_right", false);
0725     d->enableAction("align_to_top", false);
0726     d->enableAction("align_to_bottom", false);
0727     d->enableAction("adjust_size_menu", false);
0728     d->enableAction("format_raise", false);
0729     d->enableAction("format_lower", false);
0730 }
0731 
0732 ///////////////////////////  Various slots and signals /////////////////////
0733 void Form::formDeleted()
0734 {
0735     d->selected.clear();
0736     d->resizeHandles.clear();
0737     deleteLater();
0738 }
0739 
0740 void Form::changeName(const QByteArray &oldname, const QByteArray &newname)
0741 {
0742     if (oldname == newname)
0743         return;
0744 
0745     if (d->topTree->rename(oldname, newname)) {
0746 #ifdef KFD_SIGSLOTS
0747         d->connBuffer->fixName(oldname, newname);
0748 #endif
0749         ResizeHandleSet *temp = d->resizeHandles.take(oldname);
0750         d->resizeHandles.insert(newname, temp);
0751     }
0752     else { // rename failed
0753         KMessageBox::sorry(widget()->topLevelWidget(),
0754                            xi18nc("@info",
0755                                   "Renaming widget <resource>%1</resource> to <resource>%2</resource> failed.",
0756                                   QString::fromLatin1(oldname), QString::fromLatin1(newname)));
0757         qWarning() << "widget" << newname << "already exists, reverting rename";
0758         d->propertySet.changeProperty("objectName", oldname);
0759     }
0760 }
0761 
0762 void Form::emitChildAdded(ObjectTreeItem *item)
0763 {
0764     addWidgetToTabStops(item);
0765     emit childAdded(item);
0766 }
0767 
0768 void Form::emitChildRemoved(ObjectTreeItem *item)
0769 {
0770     d->tabstops.removeOne(item);
0771 #ifdef KFD_SIGSLOTS
0772     if (d->connBuffer)
0773         d->connBuffer->removeAllConnectionsForWidget(item->name());
0774 #endif
0775     emit childRemoved(item);
0776 }
0777 
0778 bool Form::addCommand(Command *command, AddCommandOption option)
0779 {
0780     setModified(true);
0781     if (option == DontExecuteCommand) {
0782         command->blockRedoOnce();
0783     }
0784     return d->undoStack.push(command);
0785 }
0786 
0787 void Form::emitUndoEnabled()
0788 {
0789 //! @todo pixmapcollection
0790 #ifdef KEXI_PIXMAP_COLLECTIONS_SUPPORT
0791     QAction *undoAction = d->collection->action(QLatin1String("edit_undo"));
0792     if (undoAction)
0793         emitUndoEnabled(undoAction->isEnabled(), undoAction->text());
0794 #endif
0795 }
0796 
0797 void Form::emitRedoEnabled()
0798 {
0799 //! @todo pixmapcollection
0800 #ifdef KEXI_PIXMAP_COLLECTIONS_SUPPORT
0801     QAction *redoAction = d->collection->action(QLatin1String("edit_redo"));
0802     if (redoAction)
0803         emitRedoEnabled(redoAction->isEnabled(), redoAction->text());
0804 #endif
0805 }
0806 
0807 void Form::slotFormRestored()
0808 {
0809     setModified(false);
0810 }
0811 
0812 
0813 ///////////////////////////  Tab stops ////////////////////////
0814 
0815 void Form::addWidgetToTabStops(ObjectTreeItem *it)
0816 {
0817     QWidget *w = it->widget();
0818     if (!w)
0819         return;
0820     if (!(w->focusPolicy() & Qt::TabFocus)) {
0821         // For composed widgets, we check if one of the child can have focus
0822         const QObjectList list(w->children());
0823         foreach(const QObject *obj, list) {
0824             if (obj->isWidgetType()) {//QWidget::TabFocus flag will be checked later!
0825                 if (!d->tabstops.contains(it)) {
0826                     //qDebug() << "adding child of" << w << ":" << obj;
0827                     d->tabstops.append(it);
0828                     return;
0829                 }
0830             }
0831         }
0832     }
0833     else if (!d->tabstops.contains(it)) { // not yet in the list
0834         //qDebug() << "adding" << w;
0835         d->tabstops.append(it);
0836     }
0837 }
0838 
0839 void Form::updateTabStopsOrder()
0840 {
0841     ObjectTreeList newList(d->tabstops);
0842     foreach (ObjectTreeItem *item, d->tabstops) {
0843         if (!(item->widget()->focusPolicy() & Qt::TabFocus)) {
0844             //qDebug() << "Widget removed because has no TabFocus:"
0845             //.        << item->widget()->objectName();
0846             newList.removeOne(item);
0847         }
0848     }
0849     d->tabstops = newList;
0850 }
0851 
0852 //! Collects all the containers reculsively. Used by Form::autoAssignTabStops().
0853 static void collectContainers(ObjectTreeItem* item, QSet<Container*>& containers)
0854 {
0855     if (!item->container())
0856         return;
0857     if (!containers.contains(item->container())) {
0858         //qDebug() << item->container()->objectTree()->className()
0859         //         << item->container()->objectTree()->name();
0860         containers.insert(item->container());
0861     }
0862     foreach (ObjectTreeItem *child, *item->children()) {
0863         collectContainers(child, containers);
0864     }
0865 }
0866 
0867 void Form::autoAssignTabStops()
0868 {
0869     VerticalWidgetList list(toplevelContainer()->widget());
0870     HorizontalWidgetList hlist(toplevelContainer()->widget());
0871 
0872     // 1. Collect all the containers, as we'll be sorting widgets groupped by containers
0873     QSet<Container*> containers;
0874 
0875     collectContainers(toplevelContainer()->objectTree(), containers);
0876 
0877     foreach (ObjectTreeItem *item, d->tabstops) {
0878         if (item->widget()) {
0879             //qDebug() << "Widget to sort:" << item->widget();
0880             list.append(item->widget());
0881         }
0882     }
0883 
0884     list.sort();
0885     //foreach (QWidget *w, list) {
0886     //    qDebug() << w->metaObject()->className() << w->objectName();
0887     //}
0888     d->tabstops.clear();
0889 
0890     /// We automatically sort widget from the top-left to bottom-right corner
0891     //! \todo Handle RTL layout (ie from top-right to bottom-left)
0892     for (QWidgetList::ConstIterator it(list.constBegin()); it!=list.constEnd(); ++it) {
0893         QWidget *w = *it;
0894         hlist.append(w);
0895 
0896         ++it;
0897         QWidget *nextw = it==list.constEnd() ? 0 : *it;
0898         Q_UNUSED(nextw);
0899         QObject *page_w = 0;
0900         KFormDesigner::TabWidget *tab_w
0901                 = KFormDesigner::findParent<KFormDesigner::TabWidget>(
0902                     w, "KFormDesigner::TabWidget", page_w);
0903 
0904         for (; it!=list.constEnd(); ++it) {
0905             QWidget *nextw = *it;
0906             if (KDbUtils::hasParent(w, nextw)) // do not group (sort) widgets where one is a child of another
0907                 break;
0908             if (nextw->y() >= (w->y() + 20))
0909                 break;
0910             if (tab_w) {
0911                 QObject *page_nextw = 0;
0912                 KFormDesigner::TabWidget *tab_nextw
0913                         = KFormDesigner::findParent<KFormDesigner::TabWidget>(
0914                             nextw, "KFormDesigner::TabWidget", page_nextw);
0915                 if (tab_w == tab_nextw) {
0916                     if (page_w != page_nextw) // 'nextw' widget within different tab page
0917                         break;
0918                 }
0919             }
0920             hlist.append(nextw);
0921         }
0922         hlist.sort();
0923 
0924         foreach (QWidget *w, hlist) {
0925             ObjectTreeItem *tree = d->topTree->lookup(w->objectName());
0926             if (tree) {
0927                 //qDebug() << "adding" << tree->name();
0928                 d->tabstops.append(tree);
0929             }
0930         }
0931 
0932         --it;
0933         hlist.clear();
0934     }
0935 }
0936 
0937 QString Form::formatVersion() const
0938 {
0939     return d->formatVersion;
0940 }
0941 
0942 void Form::setFormatVersion(const QString &ver)
0943 {
0944     d->formatVersion = ver;
0945 }
0946 
0947 QString Form::originalFormatVersion() const
0948 {
0949     return d->originalFormatVersion;
0950 }
0951 
0952 void Form::setOriginalFormatVersion(const QString &ver)
0953 {
0954     d->originalFormatVersion = ver;
0955 }
0956 
0957 void Form::setFormWidget(FormWidget* w)
0958 {
0959     d->formWidget = w;
0960     if (!d->formWidget)
0961         return;
0962     d->formWidget->setForm(this);
0963 }
0964 
0965 void Form::enterWidgetInsertingState(const QByteArray &classname)
0966 {
0967     if (d->state != WidgetInserting) {
0968         enterWidgetSelectingState();
0969     }
0970     d->state = WidgetInserting;
0971 
0972     if (toplevelContainer()) {
0973         widget()->setCursor(QCursor(Qt::CrossCursor));
0974     }
0975     const QList<QWidget*> list(widget()->findChildren<QWidget*>());
0976     foreach (QWidget *w, list) {
0977         d->cursors.insert(w, w->cursor());
0978         w->setCursor(QCursor(Qt::CrossCursor));
0979     }
0980 
0981     d->selectedClass = classname;
0982     QAction *pointer_action = d->collection->action(QLatin1String("edit_pointer"));
0983     if (pointer_action) {
0984         pointer_action->setChecked(false);
0985     }
0986 }
0987 
0988 QByteArray Form::selectedClass() const
0989 {
0990     return d->selectedClass;
0991 }
0992 
0993 void Form::abortWidgetInserting()
0994 {
0995     if (d->state != WidgetInserting)
0996         return;
0997 
0998     widget()->unsetCursor();
0999     const QList<QWidget*> list(widget()->findChildren<QWidget*>());
1000     foreach (QWidget *w, list) {
1001         w->unsetCursor();
1002     }
1003     d->state = WidgetSelecting;
1004     QAction *pointer_action = d->widgetActionGroup->action(QLatin1String("edit_pointer"));
1005     if (pointer_action) {
1006         pointer_action->setChecked(true);
1007     }
1008 }
1009 
1010 void Form::enterWidgetSelectingState()
1011 {
1012     switch (d->state) {
1013     case WidgetInserting:
1014         abortWidgetInserting();
1015         break;
1016 #ifdef KFD_SIGSLOTS
1017     case Connecting:
1018         abortCreatingConnection();
1019         break;
1020 #endif
1021     default:
1022         break;
1023     }
1024 }
1025 
1026 #ifdef KFD_SIGSLOTS
1027 void Form::enterConnectingState()
1028 {
1029     if (!(d->features & EnableConnections))
1030         return;
1031     enterWidgetSelectingState();
1032 
1033     // We set a Pointing hand cursor while drawing the connection
1034     d->mouseTrackers = new QStringList();
1035     if (toplevelContainer()) {
1036         widget()->setCursor(QCursor(Qt::PointingHandCursor));
1037         widget()->setMouseTracking(true);
1038     }
1039     const QList<QWidget*> list(widget()->findChildren<QWidget*>());
1040     foreach(QWidget *w, list) {
1041         d->cursors.insert(w, w->cursor());
1042         w->setCursor(QCursor(Qt::PointingHandCursor));
1043         if (w->hasMouseTracking())
1044             d->mouseTrackers->append(w->objectName());
1045         w->setMouseTracking(true);
1046     }
1047     delete m_connection;
1048     m_connection = new Connection();
1049     m_drawingSlot = true;
1050     if (m_dragConnection)
1051         m_dragConnection->setChecked(true);
1052 }
1053 
1054 void Form::resetSelectedConnection()
1055 {
1056 //! @todo
1057     if (!(d->features & EnableConnections))
1058         return;
1059 
1060     delete m_connection;
1061     m_connection = new Connection();
1062 
1063     if (formWidget()) {
1064         formWidget()->clearForm();
1065     }
1066     if (widget()) {
1067         widget()->repaint();
1068     }
1069 }
1070 
1071 void Form::abortCreatingConnection()
1072 {
1073 //! @todo
1074     if (!(d->features & EnableConnections))
1075         return;
1076     if (d->state != Connecting)
1077         return;
1078 
1079     if (formWidget()) {
1080         formWidget()->clearForm();
1081     }
1082 
1083     widget()->unsetCursor();
1084     widget()->setMouseTracking(false);
1085     const QList<QWidget*> list(widget()->findChildren<QWidget*>());
1086     foreach (QWidget *w, list) {
1087         QHash<QObject*, QCursor>::ConstIterator curIt(d->cursors.find(w));
1088         if (curIt != d->cursors.constEnd())
1089             w->setCursor(*curIt);
1090         w->setMouseTracking(d->mouseTrackers->contains(w->objectName()));
1091     }
1092     delete d->mouseTrackers;
1093     d->mouseTrackers = 0;
1094 
1095     if (m_connection->slot().isNull())
1096         emit connectionAborted(this);
1097     delete m_connection;
1098     m_connection = 0;
1099     m_drawingSlot = false;
1100     QAction *pointer_action = d->widgetActionGroup->action(QLatin1String("edit_pointer"));
1101     if (pointer_action) {
1102         pointer_action->setChecked(true);
1103     }
1104 }
1105 #endif
1106 
1107 Form::State Form::state() const
1108 {
1109     return d->state;
1110 }
1111 
1112 void Form::addPropertyCommand(const QByteArray &wname, const QVariant &oldValue,
1113                               const QVariant &value, const QByteArray &propertyName,
1114                               AddCommandOption addOption, int idOfPropertyCommand)
1115 {
1116     QHash<QByteArray, QVariant> oldValues;
1117     oldValues.insert(wname, oldValue);
1118     addPropertyCommand(oldValues, value, propertyName, addOption, idOfPropertyCommand);
1119 }
1120 
1121 void Form::addPropertyCommand(const QHash<QByteArray, QVariant> &oldValues,
1122                               const QVariant &value, const QByteArray &propertyName,
1123                               AddCommandOption addOption, int idOfPropertyCommand)
1124 {
1125 //! @todo add to merge in PropertyCommand...
1126 #if 0
1127     qDebug() << d->propertySet[propertyName];
1128     qDebug() << "oldValue:" << oldValues << "value:" << value;
1129     qDebug() << "idOfPropertyCommand:" << idOfPropertyCommand;
1130     d->insideAddPropertyCommand = true;
1131     PropertyCommand *presentCommand = dynamic_cast<PropertyCommand*>( d->commandHistory->presentCommand() );
1132     if (   presentCommand
1133         && d->lastCommand == presentCommand
1134         && idOfPropertyCommand > 0
1135         && d->idOfPropertyCommand == idOfPropertyCommand)
1136     {
1137         d->lastCommand->setValue(value); // just change the value,
1138                                          // to avoid multiple PropertyCommands that only differ by value
1139     }
1140     else {
1141         d->lastCommand = new PropertyCommand(*this, oldValues, value, propertyName);
1142         if (!addCommand(d->lastCommand, execute)) {
1143             d->lastCommand = 0;
1144         }
1145         d->idOfPropertyCommand = idOfPropertyCommand;
1146     }
1147     d->insideAddPropertyCommand = false;
1148 #endif
1149     d->insideAddPropertyCommand = true;
1150     d->lastCommand = new PropertyCommand(*this, oldValues, value, propertyName);
1151     d->lastCommand->setUniqueId(idOfPropertyCommand);
1152     //qDebug() << "ADD:" << *d->lastCommand;
1153     if (!addCommand(d->lastCommand, addOption)) {
1154         d->lastCommand = 0;
1155     }
1156     d->insideAddPropertyCommand = false;
1157 }
1158 
1159 void Form::addPropertyCommandGroup(PropertyCommandGroup *commandGroup,
1160                                    AddCommandOption addOption, int idOfPropertyCommand)
1161 {
1162 //! @todo add to merge in PropertyCommand...?
1163 #if 0
1164     if (!commandGroup || commandGroup->commands().isEmpty())
1165         return;
1166     qDebug() << "count:" << commandGroup->commands().count();
1167     qDebug() << "idOfPropertyCommand:" << idOfPropertyCommand;
1168     d->insideAddPropertyCommand = true;
1169     PropertyCommandGroup *presentCommand = dynamic_cast<PropertyCommandGroup*>( d->commandHistory->presentCommand() );
1170     if (   presentCommand
1171         && d->lastCommandGroup == presentCommand
1172         && idOfPropertyCommand > 0
1173         && d->idOfPropertyCommand == idOfPropertyCommand)
1174     {
1175         presentCommand->copyPropertyValuesFrom(*commandGroup); // just change the values,
1176                                                        // to avoid multiple CommandsGroups
1177                                                        // that only differ by values
1178         delete commandGroup;
1179     }
1180     else {
1181         d->lastCommandGroup = commandGroup;
1182         addCommand(d->lastCommandGroup, execute);
1183         d->idOfPropertyCommand = idOfPropertyCommand;
1184     }
1185 #endif
1186     d->insideAddPropertyCommand = true;
1187     d->lastCommandGroup = commandGroup;
1188     if (!addCommand(d->lastCommandGroup, addOption)) {
1189         d->lastCommandGroup = 0;
1190     }
1191     d->idOfPropertyCommand = idOfPropertyCommand;
1192     d->insideAddPropertyCommand = false;
1193 }
1194 
1195 void Form::slotPropertyChanged(KPropertySet& set, KProperty& p)
1196 {
1197     Q_UNUSED(set);
1198 
1199     if (!d->slotPropertyChangedEnabled || !objectTree())
1200         return;
1201 
1202     const QByteArray property( p.name() );
1203     if (property.startsWith("this:"))
1204         return; //starts with magical prefix: it's a "meta" prop.
1205 
1206     const QVariant value( p.value() );
1207 
1208     // check if the name is valid (ie is correct identifier) and there is no name conflict
1209     if (property == "objectName") {
1210         if (d->selected.count() != 1) {
1211             qWarning() << "changing objectName property only allowed for single selection";
1212             return;
1213         }
1214         if (!checkNameValidity(value.toString(), CheckValidityOnly)) {
1215             // Tricky: revert later so if there's selectWidget() before, we can cancel selectWidget()
1216             QTimer::singleShot(500, this, &Form::checkNameValidityForSelection);
1217             return;
1218         }
1219     }
1220     else if (property == "paletteBackgroundPixmap") {
1221         // a widget with a background pixmap should have its own origin
1222         // special types of properties handled separately
1223     }
1224     else if (property == "paletteBackgroundColor") {
1225         d->setColorProperty(p, &QWidget::backgroundRole, p.value());
1226         return;
1227     }
1228     else if (property == "paletteForegroundColor") {
1229         d->setColorProperty(p, &QWidget::foregroundRole, p.value());
1230         return;
1231     }
1232     else if (property == "autoFillBackground") {
1233         if (!p.value().toBool()) { // make background inherited
1234             d->setColorProperty(p, &QWidget::backgroundRole, QVariant());
1235         }
1236     }
1237     else if (property == "hAlign" || property == "vAlign") {
1238         saveAlignProperty(property);
1239         return;
1240     }
1241 
1242     // make sure we are not already undoing -> avoid recursion
1243     if (d->isUndoing && !d->isRedoing) {
1244         return;
1245     }
1246 
1247     if (d->selected.count() == 1) { // one widget selected
1248         // If the last command is the same, we just change its value
1249 //! @todo add to merge in PropertyCommand if needed
1250         if (d->slotPropertyChanged_addCommandEnabled && !d->isRedoing) {
1251             addPropertyCommand(d->selected.first()->objectName().toLatin1(),
1252                                p.oldValue(), value, property, DontExecuteCommand);
1253         }
1254 
1255         // If the property is changed, we add it in ObjectTreeItem modifProp
1256         ObjectTreeItem *tree = objectTree()->lookup(d->selected.first()->objectName());
1257         if (tree && p.isModified()) {
1258             tree->addModifiedProperty(property, d->selected.first()->property(property));
1259         }
1260 
1261         if (property == "objectName") {
1262             changeName(d->selected.first()->objectName().toLatin1(), p.value().toByteArray());
1263             emit widgetNameChanged(d->selected.first()->objectName().toLatin1(), p.value().toByteArray());
1264         }
1265         d->selected.first()->setProperty(property, value);
1266         handleWidgetPropertyChanged(d->selected.first(), property, value);
1267     }
1268     else {
1269 //! @todo add to merge in PropertyCommand if needed
1270         if (d->slotPropertyChanged_addCommandEnabled && !d->isRedoing) {
1271             // We store old values for each widget
1272             QHash<QByteArray, QVariant> oldValues;
1273             foreach(QWidget* widget, d->selected) {
1274                 oldValues.insert(widget->objectName().toLatin1(), widget->property(property));
1275             }
1276             addPropertyCommand(oldValues, value, property, DontExecuteCommand);
1277         }
1278         foreach(QWidget* widget, d->selected) {
1279             ObjectTreeItem *titem = objectTree()->lookup(widget->objectName());
1280             if (titem && p.isModified())
1281                 titem->addModifiedProperty(property, widget->property(property));
1282             widget->setProperty(property, value);
1283             handleWidgetPropertyChanged(widget, property, value);
1284         }
1285     }
1286 }
1287 
1288 void Form::slotPropertyReset(KPropertySet& set, KProperty& property)
1289 {
1290     Q_UNUSED(set);
1291 
1292     if (d->selected.count() < 2)
1293         return;
1294 
1295     // We use the old value in modifProp for each widget
1296     foreach(QWidget* widget, d->selected) {
1297         ObjectTreeItem *titem = objectTree()->lookup(widget->objectName());
1298         if (titem && titem->modifiedProperties()->contains(property.name()))
1299             widget->setProperty(
1300                 property.name(), titem->modifiedProperties()->find(property.name()).value());
1301     }
1302 }
1303 
1304 bool Form::checkNameValidity(const QString &name, CheckValidityMode mode) const
1305 {
1306     if (d->selected.isEmpty())
1307         return false;
1308 //! @todo add to the undo buffer
1309     QWidget *w = d->selected.first();
1310 
1311     if (name.isEmpty()) {
1312         if (mode == CheckValidityShowMessages) {
1313             KMessageBox::sorry(widget(), xi18n("Widget name could not be empty."));
1314         }
1315         return false;
1316     }
1317     if (!KDb::isIdentifier(name)) {
1318         if (mode == CheckValidityShowMessages) {
1319             KMessageBox::sorry(widget(),
1320                                xi18nc("@info",
1321                                       "Could not rename widget <resource>%1</resource> to "
1322                                       "<resource>%2</resource> because "
1323                                       "<resource>%3</resource> is not a valid name (identifier) for a widget.",
1324                                     w->objectName(), name, name));
1325         }
1326         return false;
1327     }
1328     if (name != w->objectName() && objectTree()->lookup(name)) {
1329         if (mode == CheckValidityShowMessages) {
1330             KMessageBox::sorry(widget(),
1331                                xi18nc("@info",
1332                                       "Could not rename widget <resource>%1</resource> to <resource>%2</resource> "
1333                                       "because a widget with the name <resource>%3</resource> already exists.",
1334                                       w->objectName(), name, name));
1335         }
1336         return false;
1337     }
1338 
1339     return true;
1340 }
1341 
1342 void Form::checkNameValidityForSelection()
1343 {
1344     if (!checkNameValidity(d->propertySet.propertyValue("objectName").toString(), CheckValidityShowMessages)) {
1345         KexiUtils::BoolBlocker blocker(&d->slotPropertyChangedEnabled, false);
1346         d->propertySet["objectName"].resetValue();
1347         KexiMainWindowIface::global()->updatePropertyEditorInfoLabel();
1348     }
1349 }
1350 
1351 void Form::undo()
1352 {
1353     if (!objectTree())
1354         return;
1355     if (!d->undoStack.canUndo()) {
1356         qWarning() << "cannot redo";
1357         return;
1358     }
1359     d->isUndoing = true;
1360     d->undoStack.undo();
1361     d->isUndoing = false;
1362 }
1363 
1364 void Form::redo()
1365 {
1366     if (!objectTree())
1367         return;
1368     if (!d->undoStack.canRedo()) {
1369         qWarning() << "cannot redo";
1370         return;
1371     }
1372     d->isRedoing = true;
1373     d->undoStack.redo();
1374     d->isRedoing = false;
1375 }
1376 
1377 bool Form::isRedoing() const
1378 {
1379     return d->isRedoing;
1380 }
1381 
1382 void Form::setUndoing(bool undoing)
1383 {
1384     d->isUndoing = undoing;
1385 }
1386 
1387 bool Form::isUndoing() const
1388 {
1389     return d->isUndoing;
1390 }
1391 
1392 int Form::commandsCount() const
1393 {
1394     return d->undoStack.count();
1395 }
1396 
1397 const KUndo2Command* Form::command(int index) const
1398 {
1399     return d->undoStack.command(index);
1400 }
1401 
1402 bool Form::isPropertyVisible(const QByteArray &property, bool isTopLevel,
1403                              const QByteArray &classname) const
1404 {
1405     const bool multiple = d->selected.count() >= 2;
1406     if (multiple && classname.isEmpty())
1407         return false;
1408 
1409     QWidget *w = d->selected.first();
1410     WidgetWithSubpropertiesInterface* subpropIface
1411         = dynamic_cast<WidgetWithSubpropertiesInterface*>(w);
1412     QWidget *subwidget;
1413     if (subpropIface && subpropIface->findMetaSubproperty(property).isValid()) // special case - subproperty
1414         subwidget = subpropIface->subwidget();
1415     else
1416         subwidget = w;
1417 
1418     return library()->isPropertyVisible(
1419                subwidget->metaObject()->className(), subwidget, property, multiple, isTopLevel);
1420 }
1421 
1422 void Form::addWidget(QWidget *w)
1423 {
1424     d->selected.append(w);
1425 
1426     // Reset some stuff
1427     d->lastCommand = 0;
1428     d->lastCommandGroup = 0;
1429 
1430     QByteArray classname;
1431     if (d->selected.first()->metaObject()->className() == w->metaObject()->className()) {
1432         classname = d->selected.first()->metaObject()->className();
1433     }
1434 
1435     // show only properties shared by widget (properties chosen by factory)
1436     bool isTopLevel = isTopLevelWidget(w);
1437 
1438     for (KPropertySetIterator it(d->propertySet); it.current(); ++it) {
1439         //qDebug() << it.current();
1440         if (!isPropertyVisible(it.current()->name(), isTopLevel, classname)) {
1441             it.current()->setVisible(false);
1442         }
1443     }
1444 
1445     if (d->selected.count() >= 2) {
1446         //second widget, update metainfo
1447         d->propertySet["this:className"].setValue("special:multiple");
1448         d->propertySet["this:classString"].setValue(
1449             xi18n("Multiple Widgets (%1)", d->selected.count()));
1450         d->propertySet["this:iconName"].setValue(KexiIconName("multiple-objects"));
1451         //name doesn't make sense for now
1452         d->propertySet["objectName"].setValue("");
1453     }
1454 }
1455 
1456 void Form::createPropertiesForWidget(QWidget *w)
1457 {
1458     d->propertySet.clear();
1459 
1460     if (!objectTree()) {
1461         qWarning() << "no object tree!";
1462         return;
1463     }
1464     ObjectTreeItem *tree = objectTree()->lookup(w->objectName());
1465     if (!tree)
1466         return;
1467 
1468     const QHash<QString, QVariant>* modifiedProperties = tree->modifiedProperties();
1469     QHash<QString, QVariant>::ConstIterator modifiedPropertiesIt;
1470     bool isTopLevel = isTopLevelWidget(w);
1471     KProperty *newProp = 0;
1472     WidgetInfo *winfo = library()->widgetInfoForClassName(w->metaObject()->className());
1473     if (!winfo) {
1474         qWarning() << "no widget info for class" << w->metaObject()->className();
1475         return;
1476     }
1477 
1478 //! @todo ineffective, get property names directly
1479     const QList<QMetaProperty> propList(
1480         KexiUtils::propertiesForMetaObjectWithInherited(w->metaObject()));
1481     //qDebug() << "propList.count() ==" << propList.count();
1482     QSet<QByteArray> propNames;
1483     foreach(const QMetaProperty& mp, propList) {
1484         propNames.insert(mp.name());
1485     }
1486 
1487     // add subproperties if available
1488     WidgetWithSubpropertiesInterface* subpropIface
1489         = dynamic_cast<WidgetWithSubpropertiesInterface*>(w);
1490     if (subpropIface) {
1491         const QSet<QByteArray> subproperties(subpropIface->subproperties());
1492         foreach(const QByteArray& propName, subproperties) {
1493             propNames.insert(propName);
1494             //qDebug() << "Added subproperty:" << propName;
1495         }
1496     }
1497 
1498     // iterate over the property list, and create Property objects
1499     foreach(const QByteArray& propName, propNames) {
1500         //qDebug() << ">>" << propName;
1501         const QMetaProperty subMeta = // special case - subproperty
1502             subpropIface ? subpropIface->findMetaSubproperty(propName) : QMetaProperty();
1503         const QMetaProperty meta = subMeta.isValid() ? subMeta
1504                                    : KexiUtils::findPropertyWithSuperclasses(w, propName.constData());
1505         if (!meta.isValid()) {
1506             //qDebug() << "!meta.isValid()";
1507             continue;
1508         }
1509         const char* propertyName = meta.name();
1510         QWidget *subwidget = subMeta.isValid()//subpropIface
1511                              ? (subpropIface ? subpropIface->subwidget() : 0)
1512                              : w;
1513         WidgetInfo *subwinfo = subwidget ? library()->widgetInfoForClassName(
1514                                    subwidget->metaObject()->className()) : 0;
1515 //  qDebug() << "$$$" << subwidget->className();
1516 
1517         if (   subwinfo
1518             && meta.isDesignable(subwidget)
1519             && meta.isWritable()
1520             && meta.isReadable() && !d->propertySet.contains(propertyName)
1521            )
1522         {
1523             //! \todo add another list for property description
1524             QString desc(d->propCaption.value(meta.name()));
1525             //! \todo change i18n
1526             if (desc.isEmpty()) { //try to get property description from factory
1527                 desc = library()->propertyDescForName(subwinfo, propertyName);
1528             }
1529 
1530             modifiedPropertiesIt = modifiedProperties->find(propertyName);
1531             const bool oldValueExists = modifiedPropertiesIt != modifiedProperties->constEnd();
1532 
1533             if (meta.isEnumType()) {
1534                 if (qstrcmp(propertyName, "alignment") == 0)  {
1535                     createAlignProperty(meta, w, subwidget);
1536                     continue;
1537                 }
1538 
1539                 QStringList keys(KexiUtils::enumKeysForProperty(meta));
1540                 newProp = new KProperty(
1541                     propertyName, d->createValueList(subwinfo, keys),
1542                     // assign current or older value
1543                     meta.enumerator().valueToKey(
1544                         oldValueExists ? modifiedPropertiesIt.value().toInt()
1545                         : subwidget->property(propertyName).toInt()),
1546                     desc, desc);
1547                 //now set current value, so the old one is stored as old
1548                 if (oldValueExists) {
1549                     newProp->setValue(
1550                         meta.enumerator().valueToKey(subwidget->property(propertyName).toInt()));
1551                 }
1552             }
1553             else {
1554                 int realType = subwinfo->customTypeForProperty(propertyName);
1555                 if (realType == KProperty::Invalid || realType == KProperty::Auto) {
1556                     realType = int(meta.type());
1557                 }
1558                 newProp = new KProperty(
1559                     propertyName,
1560                     // assign current or older value
1561                     oldValueExists ? modifiedPropertiesIt.value() : subwidget->property(propertyName),
1562                     desc, desc, realType
1563                 );
1564                 //now set current value, so the old one is stored as old
1565                 if (oldValueExists) {
1566                     newProp->setValue(subwidget->property(propertyName));
1567                 }
1568             }
1569 
1570             if (!isPropertyVisible(propertyName, isTopLevel))
1571                 newProp->setVisible(false);
1572 //! @todo
1573             if (newProp->type() == KProperty::Invalid) {
1574                 newProp->setType(KProperty::String);
1575             }
1576 
1577             d->propertySet.addProperty(newProp);
1578         }
1579 
1580         // update the Property.oldValue() and isModified() using the value stored in the ObjectTreeItem
1581         updatePropertyValue(tree, propertyName, meta);
1582     }
1583 
1584     const QString paletteBackgroundColorDesc(d->propCaption.value("paletteBackgroundColor"));
1585     newProp = new KProperty("paletteBackgroundColor",
1586                                        w->palette().color(w->backgroundRole()),
1587                                        paletteBackgroundColorDesc,
1588                                        paletteBackgroundColorDesc);
1589 
1590     const QString paletteForegroundColorDesc(d->propCaption.value("paletteForegroundColor"));
1591     d->propertySet.addProperty(newProp);
1592     newProp = new KProperty("paletteForegroundColor",
1593                                        w->palette().color(w->foregroundRole()),
1594                                        paletteForegroundColorDesc,
1595                                        paletteForegroundColorDesc);
1596     d->propertySet.addProperty(newProp);
1597 
1598     // name should be updated only when pressing Enter
1599     d->propertySet["objectName"].setValueSyncPolicy(KProperty::ValueSyncPolicy::FocusOut);
1600 
1601     if (winfo) {
1602         library()->setPropertyOptions(d->propertySet, *winfo, w);
1603         d->propertySet.addProperty(newProp = new KProperty("this:classString", winfo->name()));
1604         newProp->setVisible(false);
1605         d->propertySet.addProperty(newProp = new KProperty("this:iconName", winfo->iconName()));
1606         newProp->setVisible(false);
1607     }
1608     d->propertySet.addProperty(newProp = new KProperty("this:className",
1609             w->metaObject()->className()));
1610     newProp->setVisible(false);
1611 }
1612 
1613 void Form::updatePropertyValue(ObjectTreeItem *tree, const char *property, const QMetaProperty &meta)
1614 {
1615     Q_UNUSED(tree);
1616     Q_UNUSED(property);
1617     Q_UNUSED(meta);
1618     return;
1619 //! @todo ????
1620 #if 0
1621     const char *propertyName = meta.isValid() ? meta.name() : property;
1622     if (!d->propertySet.contains(propertyName))
1623         return;
1624     KProperty &p = d->propertySet[propertyName];
1625 
1626 //! \todo what about set properties, and lists properties
1627     const QHash<QString, QVariant>::ConstIterator it(tree->modifiedProperties()->find(propertyName));
1628     if (it != tree->modifiedProperties()->constEnd()) {
1629         blockSignals(true);
1630         if (meta.isValid() && meta.isEnumType()) {
1631             p.setValue(meta.enumerator().valueToKey(it.value().toInt()), false);
1632         }
1633         else {
1634             p.setValue(it.value(), false);
1635         }
1636         p.setValue(p.value(), true);
1637         blockSignals(false);
1638     }
1639 #endif
1640 }
1641 
1642 //! @todo what about 'forceReload' arg? It's not passed to updatePropertiesForSelection() now...
1643 void Form::emitSelectionChanged(QWidget *w, WidgetSelectionFlags flags)
1644 {
1645     updatePropertiesForSelection(w, flags);
1646     emit selectionChanged(w, flags);
1647 }
1648 
1649 void Form::updatePropertiesForSelection(QWidget *w, WidgetSelectionFlags flags)
1650 {
1651     if (!w) {
1652 //! @todo clearSet()?
1653         return;
1654     }
1655     KexiMainWindowIface::global()->beginPropertyPaneUpdate();
1656 
1657     // if our list is empty,don't use add parameter value
1658     if (d->selected.isEmpty() == 0) {
1659         flags |= ReplacePreviousSelection;
1660     }
1661 
1662     if (flags & ReplacePreviousSelection) {
1663         createPropertiesForWidget(w);
1664         w->installEventFilter(this);
1665         connect(w, SIGNAL(destroyed()), this, SLOT(widgetDestroyed()));
1666     }
1667     else {
1668         addWidget(w);
1669     }
1670 
1671     if (flags & LastSelection) {
1672         emit propertySetSwitched();
1673     }
1674     KexiMainWindowIface::global()->endPropertyPaneUpdate();
1675 }
1676 
1677 KPropertySet* Form::propertySet()
1678 {
1679     return &d->propertySet;
1680 }
1681 
1682 bool Form::isSnapToGridEnabled() const
1683 {
1684     return d->snapToGrid;
1685 }
1686 
1687 void Form::setSnapToGridEnabled(bool enabled)
1688 {
1689     d->snapToGrid = enabled;
1690 }
1691 
1692 void Form::createContextMenu(QWidget *w, Container *container, const QPoint& menuPos,
1693                              ContextMenuTarget target)
1694 {
1695     if (!widget())
1696         return;
1697     const bool toplevelWidgetSelected = widget() == w;
1698     const int widgetsCount = container->form()->selectedWidgets()->count();
1699     const bool multiple = widgetsCount > 1;
1700 
1701     //set title
1702     QString n( container->form()->library()->displayName(w->metaObject()->className()) );
1703     QIcon icon;
1704     QString titleText;
1705     if (!multiple) {
1706         if (w == container->form()->widget()) {
1707             icon = KexiIcon("form");
1708             titleText = xi18n("%1 : Form", w->objectName());
1709         }
1710         else {
1711             icon = QIcon::fromTheme(
1712                        container->form()->library()->iconName(w->metaObject()->className()));
1713             titleText = QString(w->objectName()) + " : " + n;
1714         }
1715     }
1716     else {
1717         icon = KexiIcon("multiple-objects");
1718         titleText = xi18n("Multiple Widgets (%1)", widgetsCount);
1719     }
1720 
1721     QMenu menu;
1722     menu.addSection(icon, titleText);
1723 
1724     QAction *a;
1725 #define PLUG_ACTION(_name, forceVisible) \
1726     { a = d->collection->action(_name); \
1727         if (a && (forceVisible || a->isEnabled())) { \
1728             if (separatorNeeded) \
1729                 menu.addSeparator(); \
1730             separatorNeeded = false; \
1731             menu.addAction(a); \
1732         } \
1733     }
1734 
1735     bool separatorNeeded = false;
1736 
1737     PLUG_ACTION("edit_cut", !toplevelWidgetSelected);
1738     PLUG_ACTION("edit_copy", !toplevelWidgetSelected);
1739     PLUG_ACTION("edit_paste", true);
1740     PLUG_ACTION("edit_delete", !toplevelWidgetSelected);
1741     separatorNeeded = true;
1742     PLUG_ACTION("align_menu", !toplevelWidgetSelected);
1743     PLUG_ACTION("adjust_size_menu", !toplevelWidgetSelected);
1744     separatorNeeded = true;
1745 
1746     // We create the buddy menu
1747     QAction *noBuddyAction = 0;
1748     QLabel *buddyLabelWidget = 0;
1749     QStringList sortedItemNames;
1750     if (!multiple) {
1751         buddyLabelWidget = qobject_cast<QLabel*>(w);
1752         if (buddyLabelWidget) {
1753             if (!buddyLabelWidget->text().contains("&")
1754                 || buddyLabelWidget->textFormat() == Qt::RichText)
1755             {
1756                 buddyLabelWidget = 0;
1757             }
1758         }
1759     }
1760     if (buddyLabelWidget) { // setup menu
1761         if (separatorNeeded)
1762             menu.addSeparator();
1763 
1764         QMenu *sub = new QMenu(w);
1765         QWidget *buddy = buddyLabelWidget->buddy();
1766 
1767         noBuddyAction = sub->addAction(xi18n("No Buddy"));
1768         if (!buddy)
1769             noBuddyAction->setChecked(true);
1770         sub->addSeparator();
1771 
1772         // Add all the widgets that can have focus
1773         // 1. Sort by name
1774         QHash<QString, ObjectTreeItem*> items;
1775         foreach (ObjectTreeItem *item, *container->form()->tabStops()) {
1776             items.insert(item->name().toLatin1(), item);
1777         }
1778         sortedItemNames = items.keys();
1779         sortedItemNames.sort();
1780         foreach (const QString& name, sortedItemNames) {
1781             ObjectTreeItem *item = items.value(name);
1782             QAction* action = sub->addAction(
1783                 QIcon::fromTheme(
1784                     container->form()->library()->iconName(item->className().toLatin1())),
1785                 item->name()
1786             );
1787             if (item->widget() == buddy)
1788                 action->setChecked(true);
1789         }
1790         separatorNeeded = true;
1791     }
1792 
1793 #ifdef KFD_SIGSLOTS
1794     if (!multiple && (d->features & EnableEvents)) {
1795         if (separatorNeeded)
1796             menu.addSeparator();
1797 
1798         // We create the signals menu
1799         QMenu *sigMenu = new QMenu();
1800         const QList<QMetaMethod> list(
1801             KexiUtils::methodsForMetaObjectWithParents(w->metaObject(), QMetaMethod::Signal,
1802                     QMetaMethod::Public));
1803         foreach(const QMetaMethod& m, list) {
1804             sigMenu->addAction(m.signature());
1805         }
1806         QAction *eventsSubMenuAction = menu.addMenu(sigMenu);
1807         eventsSubMenuAction->setText(futureI18n("Events"));
1808         if (list.isEmpty())
1809             eventsSubMenuAction->setEnabled(false);
1810         connect(sigMenu, SIGNAL(triggered(QAction*)),
1811                 this, SLOT(menuSignalChosen(QAction*)));
1812         separatorNeeded = true;
1813     }
1814 #endif
1815 
1816     // Other items
1817     if (!multiple) {
1818         QAction* lastAction = 0;
1819         if (separatorNeeded) {
1820             lastAction = menu.addSeparator();
1821         }
1822         const int oldIndex = menu.actions().count() - 1;
1823         container->form()->library()
1824             ->createMenuActions(w->metaObject()->className(), w, &menu, container);
1825         if (oldIndex == (menu.actions().count() - 1)) {
1826             //nothing added
1827             if (separatorNeeded) {
1828                 menu.removeAction(lastAction);
1829             }
1830         }
1831     }
1832 
1833     //show the menu at the selected widget
1834     QPoint pos;
1835     switch (target) {
1836     case FormContextMenuTarget: {
1837         pos = w->mapToGlobal(menuPos);
1838         d->insertionPoint = menuPos;
1839         break;
1840     }
1841     case WidgetTreeContextMenuTarget: {
1842         pos = QCursor::pos();
1843         d->insertionPoint = container->widget()->mapToGlobal(w->pos() + QPoint(10, 10)); // user may still want to paste
1844         break;
1845     }
1846     }
1847 
1848     //qDebug() << w << container->widget() << "menuPos=" << menuPos << "pos=" << pos;
1849     QAction *result = menu.exec(pos);
1850     if (!result) {
1851         // nothing to do
1852     }
1853     else if (noBuddyAction && buddyLabelWidget && result == noBuddyAction) {
1854         buddyLabelWidget->setBuddy(0);
1855     }
1856     else if (sortedItemNames.contains(result->text())) {
1857         ObjectTreeItem *item = objectTree()->lookup(result->text());
1858         if (item && item->widget()) {
1859             buddyLabelWidget->setBuddy(item->widget());
1860         }
1861     }
1862     d->insertionPoint = QPoint();
1863 }
1864 
1865 void Form::deleteWidget()
1866 {
1867     if (!objectTree()) {
1868         return;
1869     }
1870 
1871     QWidgetList *list = selectedWidgets();
1872     if (list->isEmpty()) {
1873         return;
1874     }
1875 
1876     if (widget() == list->first()) {
1877         //toplevel form is selected, cannot delete it
1878         return;
1879     }
1880 
1881     Command *com = new DeleteWidgetCommand(*this, *list);
1882     addCommand(com);
1883 }
1884 
1885 void Form::copyWidget()
1886 {
1887     if (!objectTree() || isFormWidgetSelected()) {
1888         return;
1889     }
1890 
1891     QWidgetList *list = selectedWidgets();
1892     if (list->isEmpty()) {
1893         return;
1894     }
1895 
1896     QDomDocument doc;
1897     QHash<QByteArray, QByteArray> containers;
1898     QHash<QByteArray, QByteArray> parents;
1899     KFormDesigner::widgetsToXML(doc,
1900         containers, parents, *this, *list);
1901     KFormDesigner::copyToClipboard(doc.toString());
1902     emitActionSignals(); // to update 'Paste' item state
1903     emitUndoActionSignals();
1904 }
1905 
1906 bool Form::isFormWidgetSelected() const
1907 {
1908     return selectedWidget() && selectedWidget() == widget();
1909 }
1910 
1911 void Form::cutWidget()
1912 {
1913     if (!objectTree() || isFormWidgetSelected()) {
1914         return;
1915     }
1916 
1917     QWidgetList *list = selectedWidgets();
1918     if (list->isEmpty()) {
1919         return;
1920     }
1921 
1922     Command *com = new CutWidgetCommand(*this, *list);
1923     addCommand(com);
1924 }
1925 
1926 void Form::pasteWidget()
1927 {
1928     if (!objectTree()) {
1929         return;
1930     }
1931     const QMimeData *mimeData = QApplication::clipboard()->mimeData();
1932     const bool mimeDataHasXmlUiFormat = mimeData->hasFormat( KFormDesigner::mimeType() );
1933     if (!mimeDataHasXmlUiFormat && !mimeData->hasText()) {
1934         return;
1935     }
1936     QDomDocument doc;
1937     if (!doc.setContent( mimeDataHasXmlUiFormat
1938         ? QString::fromUtf8( mimeData->data(KFormDesigner::mimeType())) : mimeData->text() ))
1939     {
1940         return;
1941     }
1942     if (!doc.firstChildElement("UI").hasChildNodes()) {
1943         return;
1944     }
1945 
1946     Command *com = new PasteWidgetCommand(doc, *activeContainer(), d->insertionPoint);
1947     addCommand(com);
1948 }
1949 
1950 void Form::editTabOrder()
1951 {
1952     if (!objectTree()) {
1953         return;
1954     }
1955     QWidget *topLevel = widget()->topLevelWidget();
1956     TabStopDialog dlg(topLevel);
1957     if (dlg.exec(this) == QDialog::Accepted) {
1958         d->propertySet.changePropertyIfExists("autoTabStops", dlg.autoTabStops());
1959         //force set dirty
1960         setModified(true);
1961     }
1962 }
1963 
1964 void Form::editFormPixmapCollection()
1965 {
1966     if (!objectTree()) {
1967         return;
1968     }
1969 //! @todo pixmapcollection
1970 #ifdef KEXI_PIXMAP_COLLECTIONS_SUPPORT
1971     PixmapCollectionEditor dialog(pixmapCollection(), widget()->topLevelWidget());
1972     dialog.exec();
1973 #endif
1974 }
1975 
1976 void Form::editConnections()
1977 {
1978 #ifdef KFD_SIGSLOTS
1979     if (!(d->features & EnableConnections)) {
1980         return;
1981     }
1982     if (!objectTree()) {
1983         return;
1984     }
1985 
1986     ConnectionDialog dialog(this, widget()->topLevelWidget());
1987     dialog.exec();
1988 #endif
1989 }
1990 
1991 void Form::alignWidgets(WidgetAlignment alignment)
1992 {
1993     QWidgetList* selected = selectedWidgets();
1994     if (!objectTree() || selected->count() < 2) {
1995         return;
1996     }
1997 
1998     QWidget *parentWidget = selected->first()->parentWidget();
1999 
2000     foreach (QWidget *w, *selected) {
2001         if (w->parentWidget() != parentWidget) {
2002             //qDebug() << "alignment ==" << alignment <<  "widgets don't have the same parent widget";
2003             return;
2004         }
2005     }
2006 
2007     Command *com = new AlignWidgetsCommand(*this, alignment, *selected);
2008     addCommand(com);
2009 }
2010 
2011 void Form::alignWidgetsToLeft()
2012 {
2013     alignWidgets(AlignToLeft);
2014 }
2015 
2016 void Form::alignWidgetsToRight()
2017 {
2018     alignWidgets(AlignToRight);
2019 }
2020 
2021 void Form::alignWidgetsToTop()
2022 {
2023     alignWidgets(AlignToTop);
2024 }
2025 
2026 void Form::alignWidgetsToBottom()
2027 {
2028     alignWidgets(AlignToBottom);
2029 }
2030 
2031 void Form::adjustWidgetSize()
2032 {
2033     if (!objectTree()) {
2034         return;
2035     }
2036     Command *com = new AdjustSizeCommand(*this, AdjustSizeCommand::SizeToFit, *selectedWidgets());
2037     addCommand(com);
2038 }
2039 
2040 void Form::alignWidgetsToGrid()
2041 {
2042     if (!objectTree()) {
2043         return;
2044     }
2045     Command *com = new AlignWidgetsCommand(*this, AlignToGrid, *selectedWidgets());
2046     addCommand(com);
2047 }
2048 
2049 void Form::adjustSizeToGrid()
2050 {
2051     if (!objectTree()) {
2052         return;
2053     }
2054     Command *com = new AdjustSizeCommand(*this, AdjustSizeCommand::SizeToGrid, *selectedWidgets());
2055     addCommand(com);
2056 }
2057 
2058 void Form::adjustWidthToSmall()
2059 {
2060     if (!objectTree()) {
2061         return;
2062     }
2063     Command *com = new AdjustSizeCommand(*this, AdjustSizeCommand::SizeToSmallWidth, *selectedWidgets());
2064     addCommand(com);
2065 }
2066 
2067 void Form::adjustWidthToBig()
2068 {
2069     if (!objectTree()) {
2070         return;
2071     }
2072     Command *com = new AdjustSizeCommand(*this, AdjustSizeCommand::SizeToBigWidth, *selectedWidgets());
2073     addCommand(com);
2074 }
2075 
2076 void Form::adjustHeightToSmall()
2077 {
2078     if (!objectTree()) {
2079         return;
2080     }
2081     Command *com = new AdjustSizeCommand(*this, AdjustSizeCommand::SizeToSmallHeight, *selectedWidgets());
2082     addCommand(com);
2083 }
2084 
2085 void Form::adjustHeightToBig()
2086 {
2087     if (!objectTree()) {
2088         return;
2089     }
2090     Command *com = new AdjustSizeCommand(*this, AdjustSizeCommand::SizeToBigHeight, *selectedWidgets());
2091     addCommand(com);
2092 }
2093 
2094 void Form::bringWidgetToFront()
2095 {
2096     if (!objectTree()) {
2097         return;
2098     }
2099     foreach (QWidget *w, *selectedWidgets()) {
2100         w->raise();
2101     }
2102 }
2103 
2104 void Form::sendWidgetToBack()
2105 {
2106     if (!objectTree()) {
2107         return;
2108     }
2109 
2110     foreach (QWidget *w, *selectedWidgets()) {
2111         w->lower();
2112     }
2113 }
2114 
2115 void Form::selectAll()
2116 {
2117     if (!objectTree()) {
2118         return;
2119     }
2120     selectFormWidget();
2121     int count = objectTree()->children()->count();
2122     foreach (ObjectTreeItem *titem, *objectTree()->children()) {
2123         selectWidget(
2124             titem->widget(),
2125             AddToPreviousSelection | ((count > 1) ? MoreWillBeSelected : LastSelection)
2126         );
2127         count--;
2128     }
2129 }
2130 
2131 void Form::clearWidgetContent()
2132 {
2133     if (!objectTree()) {
2134         return;
2135     }
2136     foreach (QWidget *w, *selectedWidgets()) {
2137         library()->clearWidgetContent(w->metaObject()->className(), w);
2138     }
2139 }
2140 
2141 // Alignment-related functions /////////////////////////////
2142 
2143 void Form::createAlignProperty(const QMetaProperty& meta, QWidget *widget, QWidget *subwidget)
2144 {
2145     if (!objectTree())
2146         return;
2147 
2148     const Qt::Alignment alignment = Qt::Alignment(subwidget->property("alignment").toInt());
2149     WidgetInfo *winfo = library()->widgetInfoForClassName(subwidget->metaObject()->className());
2150     const Qt::Alignment supportedAlignmentFlags = winfo
2151         ? winfo->supportedAlignmentFlags()
2152         : Qt::Alignment(Qt::AlignHorizontal_Mask | Qt::AlignVertical_Mask);
2153     ObjectTreeItem *tree = objectTree()->lookup(widget->objectName());
2154     const bool isTopLevel = isTopLevelWidget(widget);
2155 
2156     const Qt::Alignment supportedHorizontalAlignmentFlags
2157         = supportedAlignmentFlags & Qt::AlignHorizontal_Mask;
2158     if (supportedHorizontalAlignmentFlags) {
2159         // Create the horizontal alignment property
2160         QStringList list(KexiUtils::enumKeysForProperty(meta, supportedHorizontalAlignmentFlags));
2161         if (list.removeOne(QStringLiteral("AlignHCenter"))) { // fix order
2162             list.prepend(QStringLiteral("AlignHCenter"));
2163         }
2164         if (list.removeOne(QStringLiteral("AlignLeft"))) {
2165             list.prepend(QStringLiteral("AlignLeft"));
2166         }
2167         const Qt::Alignment selectedHorizontalAlignmentFlags
2168             = alignment & supportedHorizontalAlignmentFlags;
2169         const QStringList selectedKeys(
2170             KexiUtils::enumKeysForProperty(meta, selectedHorizontalAlignmentFlags));
2171         QString selectedKey;
2172         if (selectedKeys.isEmpty()) { // for sanity
2173             selectedKey = list.first();
2174         } else {
2175             selectedKey = selectedKeys.first();
2176         }
2177         KProperty *p = new KProperty(
2178             "hAlign", d->createValueList(0, list), selectedKey,
2179             xi18nc("Translators: please keep this string short (less than 20 chars)", "Hor. Alignment"),
2180             xi18n("Horizontal Alignment"));
2181         d->propertySet.addProperty(p);
2182         if (!isPropertyVisible(p->name(), isTopLevel)) {
2183             p->setVisible(false);
2184         }
2185         updatePropertyValue(tree, "hAlign");
2186     }
2187 
2188     const Qt::Alignment supportedVerticalAlignmentFlags
2189         = supportedAlignmentFlags & Qt::AlignVertical_Mask;
2190     if (supportedVerticalAlignmentFlags) {
2191         QStringList list(KexiUtils::enumKeysForProperty(meta, supportedVerticalAlignmentFlags));
2192         if (list.removeOne("AlignVCenter")) { // fix order
2193             list.prepend("AlignVCenter");
2194         }
2195         if (list.removeOne("AlignTop")) {
2196             list.prepend("AlignTop");
2197         }
2198         const Qt::Alignment selectedVerticalAlignmentFlags
2199             = alignment & supportedVerticalAlignmentFlags;
2200         const QStringList selectedKeys(
2201             KexiUtils::enumKeysForProperty(meta, selectedVerticalAlignmentFlags));
2202         QString selectedKey;
2203         if (selectedKeys.isEmpty()) { // for sanity
2204             selectedKey = list.first();
2205         } else {
2206             selectedKey = selectedKeys.first();
2207         }
2208         KProperty *p = new KProperty(
2209             "vAlign", d->createValueList(0, list), selectedKey,
2210             xi18nc("Translators: please keep this string short (less than 20 chars)", "Ver. Alignment"),
2211             xi18n("Vertical Alignment"));
2212         d->propertySet.addProperty(p);
2213         if (!isPropertyVisible(p->name(), isTopLevel)) {
2214             p->setVisible(false);
2215         }
2216         updatePropertyValue(tree, "vAlign");
2217     }
2218 }
2219 
2220 void Form::saveAlignProperty(const QString &property)
2221 {
2222     QStringList list;
2223     if (d->propertySet.contains("hAlign"))
2224         list.append(d->propertySet["hAlign"].value().toString());
2225     if (d->propertySet.contains("vAlign"))
2226         list.append(d->propertySet["vAlign"].value().toString());
2227 
2228     WidgetWithSubpropertiesInterface* subpropIface
2229         = dynamic_cast<WidgetWithSubpropertiesInterface*>(d->selected.first());
2230     QWidget *subwidget = (subpropIface && subpropIface->subwidget())
2231                          ? subpropIface->subwidget() : static_cast<QWidget*>(d->selected.first());
2232     int count = subwidget->metaObject()->indexOfProperty("alignment");
2233     const QMetaProperty meta( subwidget->metaObject()->property(count) );
2234     const int valueForKeys = meta.enumerator().keysToValue(list.join("|").toLatin1());
2235     const int oldValue = subwidget->property("alignment").toInt();
2236     subwidget->setProperty("alignment", valueForKeys);
2237 
2238     ObjectTreeItem *tree = objectTree()->lookup(d->selected.first()->objectName());
2239     if (tree && d->propertySet[ property.toLatin1()].isModified()) {
2240         tree->addModifiedProperty(
2241             property.toLatin1(), d->propertySet[property.toLatin1()].oldValue());
2242     }
2243 
2244     if (d->isUndoing) {
2245         return;
2246     }
2247 
2248     d->lastCommand = new PropertyCommand(*this, d->selected.first()->objectName().toLatin1(),
2249                                          oldValue, valueForKeys, "alignment");
2250     if (!addCommand(d->lastCommand, DontExecuteCommand)) {
2251         d->lastCommand = 0;
2252     }
2253 }
2254 
2255 void Form::createPropertyCommandsInDesignMode(QWidget* widget,
2256         const QHash<QByteArray, QVariant> &propValues, Command *parentCommand, bool addToActiveForm)
2257 {
2258     if (!widget || propValues.isEmpty())
2259         return;
2260 
2261     //is this widget selected? (if so, use property system)
2262     const bool widgetIsSelected = selectedWidget() == widget;
2263 
2264     d->slotPropertyChanged_addCommandEnabled = false;
2265     QHash<QByteArray, QVariant>::ConstIterator endIt = propValues.constEnd();
2266     for (QHash<QByteArray, QVariant>::ConstIterator it = propValues.constBegin(); it != endIt; ++it) {
2267         if (!d->propertySet.contains(it.key())) {
2268             qWarning() << "\"" << it.key() << "\" property not found";
2269             continue;
2270         }
2271         (void)new PropertyCommand(*this, widget->objectName().toLatin1(),
2272                                   widget->property(it.key()), it.value(), it.key(), parentCommand);
2273         if (widgetIsSelected) {
2274             d->propertySet.changeProperty(it.key(), it.value());
2275         }
2276         else {
2277             WidgetWithSubpropertiesInterface* subpropIface
2278                 = dynamic_cast<WidgetWithSubpropertiesInterface*>(widget);
2279             QWidget *subwidget
2280                 = (subpropIface && subpropIface->subwidget()) ? subpropIface->subwidget() : widget;
2281             if (subwidget && -1 != subwidget->metaObject()->indexOfProperty(it.key())
2282                     && subwidget->property(it.key()) != it.value()) {
2283                 ObjectTreeItem *tree = objectTree()->lookup(widget->objectName());
2284                 if (tree) {
2285                     tree->addModifiedProperty(it.key(), subwidget->property(it.key()));
2286                 }
2287                 subwidget->setProperty(it.key(), it.value());
2288                 handleWidgetPropertyChanged(widget, it.key(), it.value());
2289             }
2290         }
2291     }
2292     d->lastCommand = 0;
2293     d->lastCommandGroup = 0;
2294     if (addToActiveForm) {
2295         addCommand(parentCommand, DontExecuteCommand);
2296     }
2297     d->slotPropertyChanged_addCommandEnabled = true;
2298 }
2299 
2300 void Form::handleWidgetPropertyChanged(QWidget *w, const QByteArray &name, const QVariant &value)
2301 {
2302     Q_UNUSED(w);
2303 
2304     if (name == "autoTabStops") {
2305         //update autoTabStops setting at KFD::Form level
2306         setAutoTabStops(value.toBool());
2307     }
2308     else if (name == "geometry" && widget()) {
2309         //fall back to sizeInternal property....
2310         d->propertySet.changePropertyIfExists("sizeInternal", value.toRect().size());
2311     }
2312 }
2313 
2314 void Form::changeFont()
2315 {
2316     QWidgetList *wlist = selectedWidgets();
2317     QWidgetList widgetsWithFontProperty;
2318     QFont font;
2319     bool oneFontSelected = true;
2320     foreach (QWidget* widget, *wlist) {
2321         if (library()->isPropertyVisible(widget->metaObject()->className(), widget, "font")) {
2322             widgetsWithFontProperty.append(widget);
2323             if (oneFontSelected) {
2324                 if (widgetsWithFontProperty.count() == 1)
2325                     font = widget->font();
2326                 else if (font != widget->font())
2327                     oneFontSelected = false;
2328             }
2329         }
2330     }
2331     if (widgetsWithFontProperty.isEmpty())
2332         return;
2333     if (!oneFontSelected) //many different fonts selected: pick a font from toplevel conatiner
2334         font = widget()->font();
2335 
2336     if (1 == widgetsWithFontProperty.count()) {
2337         //single widget's settings
2338         bool ok;
2339         font = QFontDialog::getFont(&ok, widget());
2340         if (!ok) {
2341             return;
2342         }
2343         d->propertySet.changeProperty("font", font);
2344         return;
2345     }
2346     //multiple widgets
2347 //! @todo KEXI3 port KFontDialog::getFontDiff()
2348 #if 0
2349     QFlags<KFontChooser::FontDiff> diffFlags = KFontChooser::NoFontDiffFlags;
2350     if (QDialog::Accepted != KFontDialog::getFontDiff(
2351                 font, diffFlags, KFontChooser::NoDisplayFlags, widget())
2352             || 0 == diffFlags) {
2353         return;
2354     }
2355     //update font
2356     foreach (QWidget* widget, widgetsWithFontProperty) {
2357         QFont prevFont(widget->font());
2358         if (diffFlags & KFontChooser::FontDiffFamily)
2359             prevFont.setFamily(font.family());
2360         if (diffFlags & KFontChooser::FontDiffStyle) {
2361             prevFont.setBold(font.bold());
2362             prevFont.setItalic(font.italic());
2363         }
2364         if (diffFlags & KFontChooser::FontDiffSize) {
2365             prevFont.setPointSize(font.pointSize());
2366         }
2367         //! @todo this modification is not added to UNDO BUFFER:
2368         //!      do it when KPropertySet supports multiple selections
2369         widget->setFont(prevFont);
2370     }
2371 //! @todo temporary fix for dirty flag
2372     setModified(true);
2373 #endif
2374 }
2375 
2376 void Form::setSlotPropertyChangedEnabled(bool set)
2377 {
2378     d->slotPropertyChangedEnabled = set;
2379 }
2380 
2381 void Form::createInlineEditor(const KFormDesigner::WidgetFactory::InlineEditorCreationArguments& args)
2382 {
2383     if (!args.execute)
2384         return;
2385     if (args.multiLine) {
2386         KTextEdit *textedit = new KTextEdit(args.widget->parentWidget());
2387         textedit->setPlainText(args.text);
2388         textedit->setAlignment(args.alignment);
2389         if (qobject_cast<QTextEdit*>(args.widget)) {
2390             textedit->setWordWrapMode(qobject_cast<QTextEdit*>(args.widget)->wordWrapMode());
2391             textedit->setLineWrapMode(qobject_cast<QTextEdit*>(args.widget)->lineWrapMode());
2392         }
2393         textedit->moveCursor(QTextCursor::End);
2394         textedit->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
2395         textedit->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); //ok?
2396         textedit->setFrameShape(args.useFrame ? QFrame::StyledPanel : QFrame::NoFrame);
2397         textedit->show();
2398         textedit->setFocus();
2399         textedit->selectAll();
2400         d->inlineEditor = textedit;
2401 
2402         connect(textedit, SIGNAL(textChanged()), this, SLOT(slotInlineTextChanged()));
2403         connect(args.widget, SIGNAL(destroyed()), this, SLOT(widgetDestroyed()));
2404         connect(textedit, SIGNAL(destroyed()), this, SLOT(inlineEditorDeleted()));
2405     } else {
2406         QLineEdit *editor = new QLineEdit(args.widget->parentWidget());
2407         d->inlineEditor = editor;
2408         editor->setText(args.text);
2409         editor->setAlignment(args.alignment);
2410         editor->setFrame(args.useFrame);
2411         editor->show();
2412         editor->setFocus();
2413         editor->selectAll();
2414         connect(editor, SIGNAL(textChanged(QString)), this, SLOT(changeInlineTextInternal(QString)));
2415         connect(args.widget, SIGNAL(destroyed()), this, SLOT(widgetDestroyed()));
2416         connect(editor, SIGNAL(destroyed()), this, SLOT(inlineEditorDeleted()));
2417     }
2418     d->inlineEditor->installEventFilter(this);
2419     d->inlineEditor->setFont(args.widget->font());
2420     d->inlineEditor->setGeometry(args.geometry);
2421     // setup palette
2422     d->inlineEditor->setBackgroundRole(args.widget->backgroundRole());
2423     QPalette pal(args.widget->palette());
2424     QBrush baseBrush;
2425     if (args.transparentBackground) {
2426         baseBrush = QBrush(Qt::transparent);
2427     }
2428     else {
2429         baseBrush = pal.base();
2430         QColor baseColor(baseBrush.color());
2431         if (!args.widget->inherits("KexiCommandLinkButton")) { //! @todo HACK! any idea??
2432             baseColor.setAlpha(120);
2433         }
2434         baseBrush.setColor(baseColor);
2435     }
2436     pal.setBrush(QPalette::Base, baseBrush);
2437     pal.setBrush(d->inlineEditor->backgroundRole(), pal.brush(args.widget->backgroundRole()));
2438     pal.setBrush(d->inlineEditor->foregroundRole(), pal.brush(args.widget->foregroundRole()));
2439     d->inlineEditor->setPalette(pal);
2440 
2441     //copy properties if available
2442     WidgetWithSubpropertiesInterface* subpropIface
2443         = dynamic_cast<WidgetWithSubpropertiesInterface*>(args.widget);
2444     QWidget *subwidget = (subpropIface && subpropIface->subwidget())
2445                          ? subpropIface->subwidget() : args.widget;
2446     if (   -1 != d->inlineEditor->metaObject()->indexOfProperty("margin")
2447         && -1 != subwidget->metaObject()->indexOfProperty("margin"))
2448     {
2449         d->inlineEditor->setProperty("margin", subwidget->property("margin"));
2450     }
2451     ResizeHandleSet *handles = resizeHandlesForWidget(args.widget);
2452     if (handles) {
2453         handles->setEditingMode(true);
2454         handles->raise();
2455     }
2456 
2457     ObjectTreeItem *tree = args.container->form()->objectTree()->lookup(args.widget->objectName());
2458     if (!tree)
2459         return;
2460     tree->eventEater()->setContainer(this);
2461 
2462     d->inlineEditorContainer = args.container;
2463     d->editedWidgetClass = args.classname;
2464     d->originalInlineText = args.text;
2465 
2466     d->slotPropertyChangedEnabled = false;
2467     InlineTextEditingCommand command( // to update size of the widget
2468         *this, selectedWidget(), d->editedWidgetClass, args.text);
2469     command.execute();
2470     d->slotPropertyChangedEnabled = true;
2471 }
2472 
2473 void Form::changeInlineTextInternal(const QString& text)
2474 {
2475     if (d->editedWidgetClass.isEmpty())
2476         return;
2477     d->slotPropertyChangedEnabled = false;
2478     InlineTextEditingCommand *command = new InlineTextEditingCommand(
2479         *this, selectedWidget(), d->editedWidgetClass, text);
2480     addCommand(command);
2481     d->slotPropertyChangedEnabled = true;
2482 }
2483 
2484 bool Form::eventFilter(QObject *obj, QEvent *ev)
2485 {
2486     if (   (ev->type() == QEvent::Resize || ev->type() == QEvent::Move)
2487         && obj == selectedWidget() && d->inlineEditor)
2488     {
2489         // resize widget using resize handles
2490         WidgetInfo *winfo = library()->widgetInfoForClassName(obj->metaObject()->className());
2491         if (winfo) {
2492             winfo->factory()->resizeEditor(
2493                 d->inlineEditor, selectedWidget(),
2494                 selectedWidget()->metaObject()->className());
2495         }
2496     }
2497     else if (   ev->type() == QEvent::Paint && obj == selectedWidget()
2498              && d->inlineEditor && d->inlineEditorContainer)
2499     {
2500         // paint event for container edited (eg button group)
2501         return d->inlineEditorContainer->eventFilter(obj, ev);
2502     }
2503     else if (   ev->type() == QEvent::MouseButtonPress && obj == selectedWidget()
2504              && d->inlineEditor && d->inlineEditorContainer)
2505     {
2506         // click outside editor --> cancel editing
2507         resetInlineEditor();
2508         return d->inlineEditorContainer->eventFilter(obj, ev);
2509     }
2510 
2511     if (ev->type() == QEvent::FocusOut && d->inlineEditor) {
2512         QWidget *w = d->inlineEditor;
2513         if (obj != w)
2514             return false;
2515 
2516         QWidget *focus = w->topLevelWidget()->focusWidget();
2517         if (   focus
2518             && w != focus
2519             && !KexiUtils::findFirstChild<QWidget*>(w, focus->objectName().toLatin1(),
2520                                                     focus->metaObject()->className())
2521            )
2522         {
2523             resetInlineEditor();
2524         }
2525     }
2526     else if (ev->type() == QEvent::KeyPress) {
2527         QWidget *w = d->inlineEditor;
2528         if (obj != w)
2529             return false;
2530 
2531         QKeyEvent *e = static_cast<QKeyEvent*>(ev);
2532         if (   (e->key() == Qt::Key_Return || e->key() == Qt::Key_Enter)
2533             && e->modifiers() != Qt::AltModifier)
2534         {
2535             resetInlineEditor();
2536         }
2537         if (e->key() == Qt::Key_Escape) {
2538             setInlineEditorText(d->originalInlineText);
2539             resetInlineEditor();
2540         }
2541     }
2542     else if (ev->type() == QEvent::ContextMenu) {
2543         QWidget *w = d->inlineEditor;
2544         if (obj != w)
2545             return false;
2546 
2547         return true;
2548     }
2549     return false;
2550 }
2551 
2552 void Form::slotInlineTextChanged()
2553 {
2554     changeInlineTextInternal(inlineEditorText());
2555 }
2556 
2557 QString Form::inlineEditorText() const
2558 {
2559     QWidget *ed = d->inlineEditor;
2560     if (!ed)
2561         return QString();
2562     return qobject_cast<KTextEdit*>(ed)
2563            ? qobject_cast<KTextEdit*>(ed)->toPlainText() : qobject_cast<QLineEdit*>(ed)->text();
2564 }
2565 
2566 void Form::setInlineEditorText(const QString& text)
2567 {
2568     QWidget *ed = d->inlineEditor;
2569     if (!ed)
2570         return;
2571 
2572     if (qobject_cast<KTextEdit*>(ed))
2573         qobject_cast<KTextEdit*>(ed)->setPlainText(text);
2574     else if (qobject_cast<QLineEdit*>(ed))
2575         qobject_cast<QLineEdit*>(ed)->setText(text);
2576     else
2577         qWarning() << "Inline editor is neither KTextEdit nor QLineEdit";
2578 }
2579 
2580 void Form::disableFilter(QWidget *w, Container *container)
2581 {
2582     Q_UNUSED(container);
2583     ObjectTreeItem *tree = objectTree()->lookup(w->objectName());
2584     if (!tree)
2585         return;
2586     tree->eventEater()->setContainer(this);
2587 
2588     w->setFocus();
2589     ResizeHandleSet *handles = resizeHandlesForWidget(w);
2590     if (handles) {
2591         handles->setEditingMode(true);
2592         handles->raise();
2593     }
2594     d->inlineEditor = 0;
2595     d->inlineEditorContainer = 0;
2596     d->editedWidgetClass.clear();
2597 
2598     if (!tree->isEnabled()) {
2599         //! @todo widget is disabled, so we re-enable it while editing
2600     }
2601     connect(w, SIGNAL(destroyed()), this, SLOT(widgetDestroyed()));
2602 }
2603 
2604 void Form::resetInlineEditor()
2605 {
2606     if (!d->inlineEditorContainer) {
2607         return;
2608     }
2609     d->inlineEditorContainer->stopInlineEditing();
2610 
2611     QWidget *ed = d->inlineEditor;
2612     QWidget *widget = selectedWidget();
2613     if (widget) {
2614         FormWidgetInterface* fwiface = dynamic_cast<FormWidgetInterface*>(widget);
2615         if (fwiface)
2616             fwiface->setEditingMode(false);
2617 
2618         ObjectTreeItem *tree = objectTree()->lookup(widget->objectName());
2619         if (!tree) {
2620             qWarning() << "Cannot find tree item for widget" << widget->objectName();
2621             return;
2622         }
2623         tree->eventEater()->setContainer(d->inlineEditorContainer);
2624 
2625         // "disable" the widget if needed
2626         if (!ed && !tree->isEnabled()) {
2627             widget->setPalette(KexiUtils::paletteForReadOnly(widget->palette()));
2628         }
2629     }
2630     if (ed) {
2631         d->slotPropertyChangedEnabled = false;
2632         InlineTextEditingCommand command(
2633             *this, selectedWidget(), d->editedWidgetClass, inlineEditorText());
2634         command.execute();
2635         d->slotPropertyChangedEnabled = true;
2636     }
2637     d->inlineEditor = 0;
2638     d->inlineEditorContainer = 0;
2639     if (ed) {
2640         disconnect(ed, 0, this, 0);
2641         ed->deleteLater();
2642     }
2643 
2644     if (widget) {
2645         disconnect(widget, 0, this, 0);
2646         widget->update();
2647     }
2648 
2649     ResizeHandleSet *handles = resizeHandlesForWidget(widget);
2650     if (handles) {
2651         handles->setEditingMode(false);
2652     }
2653     d->editedWidgetClass.clear();
2654 }
2655 
2656 void Form::widgetDestroyed()
2657 {
2658     if (d->inlineEditor) {
2659         d->inlineEditor->deleteLater();
2660         d->inlineEditor = 0;
2661     }
2662 
2663     ResizeHandleSet *handles = resizeHandlesForWidget(static_cast<QWidget*>(sender()));
2664     if (handles) {
2665         handles->setEditingMode(false);
2666     }
2667     d->inlineEditorContainer = 0;
2668     d->editedWidgetClass.clear();
2669 }
2670 
2671 void Form::inlineEditorDeleted()
2672 {
2673     ResizeHandleSet *handles = resizeHandlesForWidget(static_cast<QWidget*>(sender()));
2674     if (handles) {
2675         handles->setEditingMode(false);
2676     }
2677     d->inlineEditor = 0;
2678     d->inlineEditorContainer = 0;
2679     d->editedWidgetClass.clear();
2680 }
2681 
2682 QByteArray Form::editedWidgetClass() const
2683 {
2684     return d->editedWidgetClass;
2685 }
2686