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