File indexing completed on 2024-04-21 15:30:31

0001 /* This file is part of the KDE project
0002    Copyright (C) 2008-2018 Jarosław Staniek <staniek@kde.org>
0003 
0004    This library is free software; you can redistribute it and/or
0005    modify it under the terms of the GNU Library General Public
0006    License as published by the Free Software Foundation; either
0007    version 2 of the License, or (at your option) any later version.
0008 
0009    This library is distributed in the hope that it will be useful,
0010    but WITHOUT ANY WARRANTY; without even the implied warranty of
0011    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0012    Library General Public License for more details.
0013 
0014    You should have received a copy of the GNU Library General Public License
0015    along with this library; see the file COPYING.LIB.  If not, write to
0016    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0017  * Boston, MA 02110-1301, USA.
0018 */
0019 
0020 #include "KPropertyEditorView.h"
0021 #include "KPropertyEditorDataModel_p.h"
0022 #include "KProperty.h"
0023 #include "KPropertySet.h"
0024 #include "KPropertyWidgetsFactory.h"
0025 #include "KPropertyWidgetsPluginManager.h"
0026 #include "kproperty_debug.h"
0027 #include "KPropertyUtils.h"
0028 #include "KPropertyUtils_p.h"
0029 
0030 #include <QIcon>
0031 #include <QPointer>
0032 #include <QItemDelegate>
0033 #include <QPainter>
0034 #include <QMouseEvent>
0035 #include <QToolTip>
0036 #include <QApplication>
0037 #include <QHeaderView>
0038 #include <QLineEdit>
0039 
0040 #if 0 // not sure if we should use it, better to fix Oxygen?
0041 #include <kexiutils/styleproxy.h>
0042 
0043 //! Used to alter the widget's style at design time
0044 class EditorViewStyle : public KexiUtils::StyleProxy
0045 {
0046 public:
0047     explicit EditorViewStyle(QStyle* parentStyle) : KexiUtils::StyleProxy(parentStyle)
0048     {
0049     }
0050 
0051     virtual void drawPrimitive(PrimitiveElement elem, const QStyleOption* option,
0052         QPainter* painter, const QWidget* widget) const
0053     {
0054 /*        if (elem == PE_PanelLineEdit) {
0055             const QStyleOptionFrame *panel = qstyleoption_cast<const QStyleOptionFrame*>(option);
0056             if (panel) {
0057                 QStyleOptionFrame alteredOption(*panel);
0058                 alteredOption.lineWidth = 0;
0059                 KexiUtils::StyleProxy::drawPrimitive(elem, &alteredOption,
0060                     painter, widget);
0061                 return;
0062             }
0063         }*/
0064         KexiUtils::StyleProxy::drawPrimitive(elem, option,
0065             painter, widget);
0066     }
0067 };
0068 #endif
0069 
0070 static bool effectiveValueSyncPolicy(const KProperty *property, bool defaultValue)
0071 {
0072     if (property->valueSyncPolicy() == KProperty::ValueSyncPolicy::Editor) {
0073         return defaultValue;
0074     }
0075     return property->valueSyncPolicy() == KProperty::ValueSyncPolicy::Auto;
0076 }
0077 
0078 //----------
0079 
0080 class ItemDelegate : public QItemDelegate
0081 {
0082 public:
0083     explicit ItemDelegate(KPropertyEditorView *parent);
0084     ~ItemDelegate() override;
0085     void paint(QPainter *painter,
0086         const QStyleOptionViewItem &option, const QModelIndex &index) const override;
0087     QSize sizeHint(const QStyleOptionViewItem &option,
0088         const QModelIndex &index) const override;
0089     QWidget * createEditor(QWidget *parent,
0090         const QStyleOptionViewItem &option, const QModelIndex &index) const override;
0091     mutable QPointer<QWidget> m_currentEditor;
0092 };
0093 
0094 ItemDelegate::ItemDelegate(KPropertyEditorView *parent)
0095 : QItemDelegate(parent)
0096 {
0097 }
0098 
0099 ItemDelegate::~ItemDelegate()
0100 {
0101 }
0102 
0103 static int getIconSize(int fontPixelSize)
0104 {
0105     return fontPixelSize * 0.85;
0106 }
0107 
0108 static int typeForProperty(const KProperty* prop)
0109 {
0110     if (prop->listData())
0111         return KProperty::ValueFromList;
0112     else
0113         return prop->type();
0114 }
0115 
0116 void ItemDelegate::paint(QPainter *painter,
0117                          const QStyleOptionViewItem &option,
0118                          const QModelIndex &index) const
0119 {
0120     QStyleOptionViewItem alteredOption(option);
0121     const KPropertyUtilsPrivate::PainterSaver saver(painter);
0122     const KPropertyEditorDataModel *editorModel = qobject_cast<const KPropertyEditorDataModel*>(index.model());
0123     if (!editorModel) {
0124         return;
0125     }
0126 
0127     QRect r(option.rect);
0128     bool modified = false;
0129     const QColor gridLineColor(qobject_cast<KPropertyEditorView*>(parent())->gridLineColor());
0130     if (gridLineColor.isValid()) {
0131         alteredOption.rect.setTop(alteredOption.rect.top() + 1);
0132     }
0133     if (index.column()==0) {
0134         r.setWidth(r.width() - 1);
0135         r.setLeft(-1); // to avoid displaying double left border
0136 
0137         QVariant modifiedVariant( editorModel->data(index, KPropertyEditorDataModel::PropertyModifiedRole) );
0138         if (modifiedVariant.isValid() && modifiedVariant.toBool()) {
0139             modified = true;
0140             QFont font(alteredOption.font);
0141             font.setBold(true);
0142             alteredOption.font = font;
0143         }
0144     }
0145     else {
0146         r.setLeft(r.left()-1);
0147     }
0148     const int x2 = alteredOption.rect.right();
0149     const int y2 = alteredOption.rect.bottom();
0150     const int iconSize = getIconSize( alteredOption.font.pixelSize() );
0151     if (modified) {
0152         alteredOption.rect.setRight( alteredOption.rect.right() - iconSize * 1 );
0153     }
0154 
0155     const bool isGroupHeader(editorModel->data(index, KPropertyEditorDataModel::PropertyGroupRole).toBool());
0156     if (!isGroupHeader) {
0157         KProperty *property = editorModel->propertyForIndex(index);
0158         const int t = typeForProperty( property );
0159         bool useQItemDelegatePaint = true; // ValueDisplayInterface is used by default
0160         if (index.column() == 1 && KPropertyWidgetsPluginManager::self()->paint(t, painter, alteredOption, index)) {
0161             useQItemDelegatePaint = false;
0162         }
0163         if (useQItemDelegatePaint) {
0164             QItemDelegate::paint(painter, alteredOption, index);
0165         }
0166 
0167         if (modified) {
0168             alteredOption.rect.setRight( alteredOption.rect.right() - iconSize * 3 / 2 );
0169             int y1 = alteredOption.rect.top();
0170             QLinearGradient grad(x2 - iconSize * 2, y1, x2 - iconSize / 2, y1);
0171             QColor color(
0172                 alteredOption.palette.color(
0173                     (alteredOption.state & QStyle::State_Selected) ? QPalette::Highlight : QPalette::Base ));
0174             color.setAlpha(0);
0175             grad.setColorAt(0.0, color);
0176             color.setAlpha(255);
0177             grad.setColorAt(0.5, color);
0178             QBrush gradBrush(grad);
0179             painter->fillRect(x2 - iconSize * 2, y1, iconSize * 2, y2 - y1 + 1, gradBrush);
0180 
0181             //!TODO
0182             //QPixmap revertIcon(QIcon::fromTheme(QLatin1String("edit-undo")).pixmap(iconSize, iconSize));
0183             //revertIcon = KIconEffect().apply(revertIcon, KIconEffect::Colorize, 1.0,
0184             //    alteredOption.palette.color(
0185             //        (alteredOption.state & QStyle::State_Selected) ? QPalette::HighlightedText : QPalette::Text ), false);
0186             //painter->drawPixmap( x2 - iconSize - 2,
0187             //   y1 + 1 + (alteredOption.rect.height() - revertIcon.height()) / 2, revertIcon);
0188         }
0189     }
0190 
0191     if (gridLineColor.isValid()) {
0192         QPen pen(gridLineColor);
0193         painter->setPen(pen);
0194         painter->drawLine(r.topLeft(), r.topRight() + QPoint(1, 0));
0195         painter->drawLine(r.bottomLeft() + QPoint(0, 1), r.bottomRight() + QPoint(1, 1));
0196         if (!isGroupHeader) {
0197             painter->drawLine(r.topRight() + QPoint(1, 0), r.bottomRight() + QPoint(1, 1));
0198             painter->drawLine(r.topLeft(), r.bottomLeft() + QPoint(0, 1));
0199         }
0200     }
0201     else {
0202         QPen pen(alteredOption.palette.color(QPalette::AlternateBase));
0203         painter->setPen(pen);
0204         painter->drawLine(r.topLeft(), r.topRight());
0205     }
0206     //kprDebug()<<"rect:" << r << "viewport:" << painter->viewport() << "window:"<<painter->window();
0207 }
0208 
0209 QSize ItemDelegate::sizeHint(const QStyleOptionViewItem &option,
0210                              const QModelIndex &index) const
0211 {
0212     QStyleOptionViewItem realOption(option);
0213     if (index.column() == 0) {
0214         // Measure for bold font because it might be used at any time if the property value is modified
0215         realOption.font.setBold(true);
0216     }
0217     return QItemDelegate::sizeHint(realOption, index) + QSize(0, 2);
0218 }
0219 
0220 QWidget * ItemDelegate::createEditor(QWidget * parent,
0221     const QStyleOptionViewItem & option, const QModelIndex & index ) const
0222 {
0223     if (!index.isValid())
0224         return nullptr;
0225     const KProperty *property = KPropertyUtils::propertyForIndex(index);
0226     if (property && property->isReadOnly()) {
0227         return nullptr;
0228     }
0229     const int t = property ? typeForProperty(property) : KProperty::String;
0230     QStyleOptionViewItem alteredOption(option);
0231     alteredOption.rect.setHeight(alteredOption.rect.height()+3);
0232     QWidget *w = KPropertyWidgetsPluginManager::self()->createEditor(t, parent, alteredOption, index);
0233     if (!w) {
0234         // fall back to String type
0235         w = KPropertyWidgetsPluginManager::self()->createEditor(KProperty::String, parent, alteredOption, index);
0236     }
0237     if (w) {
0238         if (-1 != w->metaObject()->indexOfSignal(QMetaObject::normalizedSignature("commitData(QWidget*)").constData())
0239             && property && !property->children())
0240         {
0241         }
0242     }
0243     else {
0244         w = QItemDelegate::createEditor(parent, alteredOption, index);
0245     }
0246     QObject::disconnect(w, SIGNAL(commitData(QWidget*)),
0247         this, SIGNAL(commitData(QWidget*)));
0248     if (property && effectiveValueSyncPolicy(property,
0249             qobject_cast<KPropertyEditorView*>(this->parent())->isValueSyncEnabled()))
0250     {
0251         QObject::connect(w, SIGNAL(commitData(QWidget*)),
0252             this, SIGNAL(commitData(QWidget*)), Qt::UniqueConnection);
0253     }
0254     m_currentEditor = w;
0255     return w;
0256 }
0257 
0258 //----------
0259 
0260 class Q_DECL_HIDDEN KPropertyEditorView::Private
0261 {
0262 public:
0263     explicit Private(KPropertyEditorView *view)
0264      : model(nullptr)
0265      , gridLineColor( KPropertyEditorView::defaultGridLineColor() )
0266      , valueSync(true)
0267      , slotPropertyChangedEnabled(true)
0268      , q(view)
0269     {
0270     }
0271 
0272     //! Expands group and parent property items if needed (based on settings)
0273     void expandIfNeeded() {
0274         if (!model) {
0275             return;
0276         }
0277         const int rowCount = model->rowCount();
0278         for (int row = 0; row < rowCount; row++) {
0279             expandChildItemsIfNeeded(model->index(row, 0));
0280         }
0281     }
0282 
0283     //! Expands property child items in a subtree recursively if needed (based on settings)
0284     void expandChildItemsIfNeeded(const QModelIndex &parent) {
0285         if (!model) {
0286             return;
0287         }
0288         const bool isGroupHeader(model->data(parent, KPropertyEditorDataModel::PropertyGroupRole).toBool());
0289         if (isGroupHeader) {
0290             if (groupItemsExpanded) {
0291                 q->expand(parent);
0292             }
0293         } else {
0294             if (childPropertyItemsExpanded) {
0295                 q->expand(parent);
0296             }
0297         }
0298         const int rowCount = model->rowCount(parent);
0299         for (int row = 0; row < rowCount; row++) {
0300             const QModelIndex child(model->index(row, 0, parent));
0301             expandChildItemsIfNeeded(child);
0302         }
0303     }
0304 
0305     QPointer<KPropertySet> set;
0306     KPropertyEditorDataModel *model;
0307     ItemDelegate *itemDelegate;
0308     QColor gridLineColor;
0309     bool valueSync;
0310     bool slotPropertyChangedEnabled;
0311     bool childPropertyItemsExpanded = true;
0312     bool groupItemsExpanded = true;
0313     bool groupsVisible = true;
0314     bool toolTipsVisible = false;
0315 
0316 private:
0317     KPropertyEditorView * const q;
0318 };
0319 
0320 KPropertyEditorView::KPropertyEditorView(QWidget* parent)
0321         : QTreeView(parent)
0322         , d(new Private(this))
0323 {
0324     setObjectName(QLatin1String("KPropertyEditorView"));
0325     setAlternatingRowColors(true);
0326     setSelectionBehavior(QAbstractItemView::SelectRows);
0327     setSelectionMode(QAbstractItemView::SingleSelection);
0328     setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel);
0329     setAnimated(false);
0330     setAllColumnsShowFocus(true);
0331     header()->setSectionsMovable(false);
0332 
0333     setEditTriggers(
0334           QAbstractItemView::CurrentChanged
0335         | QAbstractItemView::DoubleClicked
0336         | QAbstractItemView::EditKeyPressed
0337         | QAbstractItemView::AnyKeyPressed
0338         | QAbstractItemView::AllEditTriggers);
0339 
0340     setItemDelegate(d->itemDelegate = new ItemDelegate(this));
0341 }
0342 
0343 KPropertyEditorView::~KPropertyEditorView()
0344 {
0345     delete d;
0346 }
0347 
0348 void KPropertyEditorView::changeSet(KPropertySet *set, SetOptions options)
0349 {
0350     changeSetInternal(set, options, QByteArray());
0351 }
0352 
0353 void KPropertyEditorView::changeSet(KPropertySet *set, const QByteArray& propertyToSelect, SetOptions options)
0354 {
0355     changeSetInternal(set, options, propertyToSelect);
0356 }
0357 
0358 void KPropertyEditorView::changeSetInternal(KPropertySet *set, SetOptions options,
0359     const QByteArray& propertyToSelect)
0360 {
0361 //! @todo port??
0362 #if 0
0363     if (d->insideSlotValueChanged) {
0364         //changeSet() called from inside of slotValueChanged()
0365         //this is dangerous, because there can be pending events,
0366         //especially for the GUI stuff, so let's do delayed work
0367         d->setListLater_list = set;
0368         d->preservePrevSelection_preservePrevSelection = preservePrevSelection;
0369         d->preservePrevSelection_propertyToSelect = propertyToSelect;
0370         qApp->processEvents(QEventLoop::AllEvents);
0371         if (d->set) {
0372             //store prev. selection for this prop set
0373             if (d->currentItem)
0374                 d->set->setPrevSelection(d->currentItem->property()->name());
0375             kprDebug() << d->set->prevSelection();
0376         }
0377         if (!d->setListLater_set) {
0378             d->setListLater_set = true;
0379             d->changeSetLaterTimer.setSingleShot(true);
0380             d->changeSetLaterTimer.start(10);
0381         }
0382         return;
0383     }
0384 #endif
0385 
0386     const bool setChanged = d->set != set;
0387     if (d->set) {
0388         acceptInput();
0389         //store prev. selection for this prop set
0390         QModelIndex index = currentIndex();
0391         if (index.isValid()) {
0392 //! @todo This crashes when changing the interpreter type in the script plugin
0393 #if 0
0394             KProperty *property = d->model->propertyForIndex(index);
0395             //if (property->isNull())
0396             //    kprDebug() << "WTF? a NULL property?";
0397             //else
0398                 //d->set->setPreviousSelection(property->name());
0399 #endif
0400         }
0401         else {
0402             d->set->setPreviousSelection(QByteArray());
0403         }
0404         if (setChanged) {
0405             d->set->disconnect(this);
0406         }
0407     }
0408 
0409     QByteArray selectedPropertyName1 = propertyToSelect;
0410     QByteArray selectedPropertyName2 = propertyToSelect;
0411     if (options & SetOption::PreservePreviousSelection) {
0412         //try to find prev. selection:
0413         //1. in new list's prev. selection
0414         if (set)
0415             selectedPropertyName1 = set->previousSelection();
0416         //2. in prev. list's current selection
0417         if (d->set)
0418             selectedPropertyName2 = d->set->previousSelection();
0419     }
0420 
0421     if (setChanged) {
0422         d->set = set;
0423     }
0424     if (d->set && setChanged) {
0425         //receive property changes
0426         connect(d->set, SIGNAL(propertyChangedInternal(KPropertySet&,KProperty&)),
0427                 this, SLOT(slotPropertyChanged(KPropertySet&,KProperty&)));
0428         connect(d->set, SIGNAL(propertyReset(KPropertySet&,KProperty&)),
0429                 this, SLOT(slotPropertyReset(KPropertySet&,KProperty&)));
0430         connect(d->set, SIGNAL(aboutToBeCleared()), this, SLOT(slotSetWillBeCleared()));
0431         connect(d->set, SIGNAL(aboutToBeDeleted()), this, SLOT(slotSetWillBeDeleted()));
0432         connect(d->set, &KPropertySet::readOnlyFlagChanged,
0433                 this, &KPropertyEditorView::slotReadOnlyFlagChanged);
0434     }
0435 
0436     KPropertyEditorDataModel *oldModel = d->model;
0437     const KPropertySetIterator::Order setOrder
0438         = (options & SetOption::AlphabeticalOrder)
0439             ? KPropertySetIterator::Order::Alphabetical
0440             : KPropertySetIterator::Order::Insertion;
0441     d->model = d->set ? new KPropertyEditorDataModel(this, setOrder) : nullptr;
0442     setModel( d->model );
0443     delete oldModel;
0444 
0445     if (d->model && d->set && !d->set->isEmpty()) {
0446         d->expandIfNeeded();
0447     }
0448 
0449     emit propertySetChanged(d->set);
0450 
0451     if (d->set) {
0452         //select prev. selected item
0453         QModelIndex index;
0454         if (!selectedPropertyName2.isEmpty()) //try other one for old prop set
0455             index = d->model->indexForPropertyName( selectedPropertyName2 );
0456         if (!index.isValid() && !selectedPropertyName1.isEmpty()) //try old one for current prop set
0457             index = d->model->indexForPropertyName( selectedPropertyName1 );
0458 
0459         if (index.isValid()) {
0460             setCurrentIndex(index);
0461             scrollTo(index);
0462         }
0463     }
0464 }
0465 
0466 void KPropertyEditorView::slotSetWillBeCleared()
0467 {
0468     changeSet(nullptr, QByteArray());
0469 }
0470 
0471 void KPropertyEditorView::slotSetWillBeDeleted()
0472 {
0473     changeSet(nullptr, QByteArray());
0474 }
0475 
0476 void KPropertyEditorView::slotReadOnlyFlagChanged()
0477 {
0478     const QModelIndex index = currentIndex();
0479     setCurrentIndex(QModelIndex());
0480     if (index.isValid()) {
0481         selectionModel()->select(index, QItemSelectionModel::Select);
0482         setCurrentIndex(index);
0483     }
0484 }
0485 
0486 void KPropertyEditorView::setValueSyncEnabled(bool set)
0487 {
0488     d->valueSync = set;
0489 }
0490 
0491 bool KPropertyEditorView::isValueSyncEnabled() const
0492 {
0493     return d->valueSync;
0494 }
0495 
0496 void KPropertyEditorView::setChildPropertyItemsExpanded(bool set)
0497 {
0498     d->childPropertyItemsExpanded = set;
0499 }
0500 
0501 bool KPropertyEditorView::childPropertyItemsExpanded() const
0502 {
0503     return d->childPropertyItemsExpanded;
0504 }
0505 
0506 void KPropertyEditorView::setGroupItemsExpanded(bool set)
0507 {
0508     d->groupItemsExpanded = set;
0509 }
0510 
0511 bool KPropertyEditorView::groupItemsExpanded() const
0512 {
0513     return d->groupItemsExpanded;
0514 }
0515 
0516 bool KPropertyEditorView::groupsVisible() const
0517 {
0518     return d->groupsVisible;
0519 }
0520 
0521 void KPropertyEditorView::setGroupsVisible(bool set)
0522 {
0523     if (d->groupsVisible == set) {
0524         return;
0525     }
0526     if (d->model) {
0527         d->model->updateGroupsVisibility();
0528         d->expandIfNeeded();
0529     }
0530     viewport()->update();
0531 }
0532 
0533 void KPropertyEditorView::currentChanged( const QModelIndex & current, const QModelIndex & previous )
0534 {
0535     QTreeView::currentChanged( current, previous );
0536 }
0537 
0538 bool KPropertyEditorView::edit( const QModelIndex & index, EditTrigger trigger, QEvent * event )
0539 {
0540     bool result;
0541     if (!d->set || d->set->isReadOnly()) {
0542         result = false;
0543     } else {
0544         result = QTreeView::edit(index, trigger, event);
0545     }
0546     if (result) {
0547       QLineEdit *lineEditEditor = qobject_cast<QLineEdit*>(d->itemDelegate->m_currentEditor.data());
0548       if (lineEditEditor) {
0549         lineEditEditor->deselect();
0550         lineEditEditor->end(false);
0551       }
0552     }
0553     return result;
0554 }
0555 
0556 void KPropertyEditorView::drawBranches( QPainter * painter, const QRect & rect, const QModelIndex & index ) const
0557 {
0558     QTreeView::drawBranches( painter, rect, index );
0559 }
0560 
0561 void KPropertyEditorView::drawRow(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
0562 {
0563     if (!d->model) {
0564         return;
0565     }
0566     const KPropertyUtilsPrivate::PainterSaver saver(painter);
0567     const bool isGroupHeader(d->model->data(index, KPropertyEditorDataModel::PropertyGroupRole).toBool());
0568     QStyleOptionViewItem alteredOption(option);
0569     QTreeView::drawRow(painter, alteredOption, index);
0570     if (isGroupHeader) {
0571         // Special case: group header should be displayed over both columns. There's an issue with
0572         // alternate background which is painted over text in the 2nd column, so draw the text here
0573         // by hand.
0574         QFont font(alteredOption.font);
0575         font.setBold(true);
0576         alteredOption.font = font;
0577         painter->setFont(font);
0578         painter->drawText(
0579             alteredOption.rect.adjusted(style()->pixelMetric(QStyle::PM_TreeViewIndentation), 0, 0, 0),
0580             index.data(Qt::DisplayRole).toString(), Qt::AlignLeft | Qt::AlignVCenter);
0581     }
0582 }
0583 
0584 QRect KPropertyEditorView::revertButtonArea( const QModelIndex& index ) const
0585 {
0586     if (index.column() != 0 || !d->model)
0587         return QRect();
0588     QVariant modifiedVariant( d->model->data(index, KPropertyEditorDataModel::PropertyModifiedRole) );
0589     if (!modifiedVariant.isValid() || !modifiedVariant.toBool())
0590         return QRect();
0591     const int iconSize = getIconSize( fontInfo().pixelSize() );
0592     int x2 = columnWidth(0);
0593     int x1 = x2 - iconSize - 2;
0594     QRect r(visualRect(index));
0595     r.setLeft(x1);
0596     r.setRight(x2);
0597     return r;
0598 }
0599 
0600 bool KPropertyEditorView::withinRevertButtonArea( int x, const QModelIndex& index ) const
0601 {
0602     QRect r(revertButtonArea( index ));
0603     if (!r.isValid())
0604         return false;
0605     return r.left() < x && x < r.right();
0606 }
0607 
0608 void KPropertyEditorView::mousePressEvent ( QMouseEvent * event )
0609 {
0610     QTreeView::mousePressEvent( event );
0611     QModelIndex index = indexAt( event->pos() );
0612     setCurrentIndex(index);
0613     if (withinRevertButtonArea( event->x(), index )) {
0614         undo();
0615     }
0616 }
0617 
0618 void KPropertyEditorView::undo()
0619 {
0620     if (!d->set || d->set->isReadOnly() || !d->model)
0621         return;
0622 
0623     KProperty *property = d->model->propertyForIndex(currentIndex());
0624     if (effectiveValueSyncPolicy(property, d->valueSync)) {
0625         property->resetValue();
0626     }
0627 }
0628 
0629 void KPropertyEditorView::acceptInput()
0630 {
0631 //! @todo
0632 }
0633 
0634 void KPropertyEditorView::commitData( QWidget * editor )
0635 {
0636     QAbstractItemView::commitData( editor );
0637 }
0638 
0639 bool KPropertyEditorView::viewportEvent( QEvent * event )
0640 {
0641     if (event->type() == QEvent::ToolTip) {
0642         QHelpEvent *hevent = static_cast<QHelpEvent*>(event);
0643         const QModelIndex index = indexAt(hevent->pos());
0644         if (index.column() == 0 && withinRevertButtonArea( hevent->x(), index )) {
0645             QRect r(revertButtonArea( index ));
0646             QToolTip::showText(hevent->globalPos(), tr("Undo changes"), this, r);
0647         }
0648         else {
0649             QToolTip::hideText();
0650         }
0651     }
0652     return QTreeView::viewportEvent(event);
0653 }
0654 
0655 QSize KPropertyEditorView::sizeHint() const
0656 {
0657     return viewportSizeHint();
0658 }
0659 
0660 KPropertySet* KPropertyEditorView::propertySet() const
0661 {
0662     return d->set;
0663 }
0664 
0665 QColor KPropertyEditorView::gridLineColor() const
0666 {
0667     return d->gridLineColor;
0668 }
0669 
0670 void KPropertyEditorView::setGridLineColor(const QColor& color)
0671 {
0672     d->gridLineColor = color;
0673     viewport()->update();
0674 }
0675 
0676 static QModelIndex findChildItem(const KProperty& property, const QModelIndex &parent)
0677 {
0678     if (parent.model() && KPropertyUtils::propertyForIndex(parent) == &property) {
0679         return parent;
0680     }
0681     int row = 0;
0682     while (true) {
0683         QModelIndex childItem = parent.child(row, 0);
0684         if (childItem.isValid()) {
0685             QModelIndex subchild = findChildItem(property, childItem);
0686             if (subchild.isValid()) {
0687                 return subchild;
0688             }
0689         }
0690         else {
0691             return QModelIndex();
0692         }
0693         row++;
0694     }
0695 }
0696 
0697 void KPropertyEditorView::slotPropertyChanged(KPropertySet& set, KProperty& property)
0698 {
0699     Q_UNUSED(set);
0700     if (!d->slotPropertyChangedEnabled || !d->model)
0701         return;
0702     d->slotPropertyChangedEnabled = false;
0703     KProperty *realProperty = &property;
0704     while (realProperty->parent()) { // find top-level property
0705         realProperty = realProperty->parent();
0706     }
0707     const QModelIndex parentIndex( d->model->indexForPropertyName(realProperty->name()) );
0708     if (parentIndex.isValid()) {
0709         QModelIndex index = findChildItem(property, parentIndex);
0710         updateSubtree(index);
0711     }
0712     d->slotPropertyChangedEnabled = true;
0713 }
0714 
0715 void KPropertyEditorView::updateSubtree(const QModelIndex &index)
0716 {
0717     if (!index.isValid() || !d->model) {
0718         return;
0719     }
0720     update(index);
0721     update(index.parent());
0722     update(d->model->indexForColumn(index, 1));
0723     update(d->model->indexForColumn(index.parent(), 1));
0724 
0725     KProperty *property = static_cast<KProperty*>(index.internalPointer());
0726     if (property->children()) {
0727         int row = 0;
0728         foreach (KProperty* p, *property->children()) {
0729             updateSubtree(d->model->createIndex(row, 0, p));
0730             ++row;
0731         }
0732     }
0733 }
0734 
0735 void KPropertyEditorView::slotPropertyReset(KPropertySet& set, KProperty& property)
0736 {
0737 //! @todo OK?
0738     slotPropertyChanged(set, property);
0739 }
0740 
0741 
0742 bool KPropertyEditorView::toolTipsVisible() const
0743 {
0744     return d->toolTipsVisible;
0745 }
0746 
0747 void KPropertyEditorView::setToolTipsVisible(bool set)
0748 {
0749     d->toolTipsVisible = set;
0750 }