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