File indexing completed on 2024-05-12 16:39:47

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