lib

editor.cpp

00001 /* This file is part of the KDE project
00002    Copyright (C) 2004 Cedric Pasteur <cedric.pasteur@free.fr>
00003    Copyright (C) 2004 Alexander Dymo <cloudtemple@mskat.net>
00004    Copyright (C) 2004-2006 Jaroslaw Staniek <js@iidea.pl>
00005 
00006    This library is free software; you can redistribute it and/or
00007    modify it under the terms of the GNU Library General Public
00008    License as published by the Free Software Foundation; either
00009    version 2 of the License, or (at your option) any later version.
00010 
00011    This library is distributed in the hope that it will be useful,
00012    but WITHOUT ANY WARRANTY; without even the implied warranty of
00013    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00014    Library General Public License for more details.
00015 
00016    You should have received a copy of the GNU Library General Public License
00017    along with this library; see the file COPYING.LIB.  If not, write to
00018    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00019  * Boston, MA 02110-1301, USA.
00020 */
00021 
00022 #include "editor.h"
00023 #include "editoritem.h"
00024 #include "set.h"
00025 #include "factory.h"
00026 #include "property.h"
00027 #include "widget.h"
00028 
00029 #include <qpushbutton.h>
00030 #include <qlayout.h>
00031 #include <qmap.h>
00032 #include <qguardedptr.h>
00033 #include <qheader.h>
00034 #include <qasciidict.h>
00035 #include <qtooltip.h>
00036 #include <qapplication.h>
00037 #include <qeventloop.h>
00038 #include <qtimer.h>
00039 #include <qlabel.h>
00040 
00041 #include <kdebug.h>
00042 #include <kiconloader.h>
00043 #include <klocale.h>
00044 #include <kdeversion.h>
00045 #include <kapplication.h>
00046 
00047 namespace KoProperty {
00048 
00050 static bool kofficeAppDirAdded = false;
00051 
00054 inline bool hasParent(QObject* par, QObject* o)
00055 {
00056     if (!o || !par)
00057         return false;
00058     while (o && o != par)
00059         o = o->parent();
00060     return o == par;
00061 }
00062 
00063 class EditorPrivate
00064 {
00065     public:
00066         EditorPrivate(Editor *editor)
00067         : itemDict(101, false), justClickedItem(false)
00068         {
00069             currentItem = 0;
00070             undoButton = 0;
00071             topItem = 0;
00072             itemToSelectLater = 0;
00073             if (!kofficeAppDirAdded) {
00074                 kofficeAppDirAdded = true;
00075                 KGlobal::iconLoader()->addAppDir("koffice");
00076             }
00077             previouslyCollapsedGroupItem = 0;
00078             childFormPreviouslyCollapsedGroupItem = 0;
00079             slotPropertyChanged_enabled = true;
00080             QObject::connect(&changeSetLaterTimer, SIGNAL(timeout()),
00081                 editor, SLOT(changeSetLater()));
00082         }
00083         ~EditorPrivate()
00084         {
00085         }
00086 
00087         QGuardedPtr<Set> set;
00089         QMap<Property*, Widget* >  widgetCache;
00090         QGuardedPtr<Widget> currentWidget;
00091         EditorItem *currentItem;
00092         EditorItem *topItem; 
00093         QPushButton *undoButton; 
00094         EditorItem::Dict itemDict;
00095 
00096         int baseRowHeight;
00097         bool sync : 1;
00098         bool insideSlotValueChanged : 1;
00099 
00101         QTimer changeSetLaterTimer;
00102         bool setListLater_set : 1;
00103         bool preservePrevSelection_preservePrevSelection : 1;
00104         QCString preservePrevSelection_propertyToSelect;
00105         //bool doNotSetFocusOnSelection : 1;
00107         bool justClickedItem : 1;
00109         bool slotPropertyChanged_enabled : 1;
00111         Set* setListLater_list;
00113         EditorItem *itemToSelectLater;
00114 
00115         QListViewItem *previouslyCollapsedGroupItem;
00116         QListViewItem *childFormPreviouslyCollapsedGroupItem;
00117 };
00118 }
00119 
00120 using namespace KoProperty;
00121 
00122 Editor::Editor(QWidget *parent, bool autoSync, const char *name)
00123  : KListView(parent, name)
00124 {
00125     d = new EditorPrivate(this);
00126     d->itemDict.setAutoDelete(false);
00127 
00128     d->set = 0;
00129     d->topItem = 0;
00130     d->currentItem = 0;
00131     d->sync = autoSync;
00132     d->insideSlotValueChanged = false;
00133     d->setListLater_set = false;
00134     d->preservePrevSelection_preservePrevSelection = false;
00135     d->setListLater_list = 0;
00136 
00137     d->undoButton = new QPushButton(viewport());
00138     d->undoButton->setFocusPolicy(QWidget::NoFocus);
00139     setFocusPolicy(QWidget::ClickFocus);
00140     d->undoButton->setMinimumSize(QSize(5,5)); // allow to resize undoButton even below pixmap size
00141     d->undoButton->setPixmap(SmallIcon("undo"));
00142     QToolTip::add(d->undoButton, i18n("Undo changes"));
00143     d->undoButton->hide();
00144     connect(d->undoButton, SIGNAL(clicked()), this, SLOT(undo()));
00145 
00146     installEventFilter(this);
00147     viewport()->installEventFilter(this);
00148 
00149     addColumn(i18n("Name"));
00150     addColumn(i18n("Value"));
00151     setAllColumnsShowFocus(true);
00152     setColumnWidthMode(0, QListView::Maximum);
00153     setFullWidth(true);
00154     setShowSortIndicator(false);
00155 #if KDE_IS_VERSION(3,3,9)
00156     setShadeSortColumn(false);
00157 #endif
00158     setTooltipColumn(0);
00159     setSorting(0);
00160     setItemMargin(KPROPEDITOR_ITEM_MARGIN);
00161     header()->setMovingEnabled( false );
00162     setTreeStepSize(16 + 2/*left*/ + 1/*right*/);
00163 
00164     updateFont();
00165 //  d->baseRowHeight = QFontMetrics(font()).height() + itemMargin()*2;
00166 
00167     connect(this, SIGNAL(selectionChanged(QListViewItem *)), this, SLOT(slotClicked(QListViewItem *)));
00168     connect(this, SIGNAL(currentChanged(QListViewItem *)), this, SLOT(slotCurrentChanged(QListViewItem *)));
00169     connect(this, SIGNAL(expanded(QListViewItem *)), this, SLOT(slotExpanded(QListViewItem *)));
00170     connect(this, SIGNAL(collapsed(QListViewItem *)), this, SLOT(slotCollapsed(QListViewItem *)));
00171     connect(header(), SIGNAL(sizeChange(int, int, int)), this, SLOT(slotColumnSizeChanged(int, int, int)));
00172 //  connect(header(), SIGNAL(clicked(int)), this, SLOT(updateEditorGeometry()));
00173 //  connect(header(), SIGNAL(clicked(int)), this, SLOT(updateEditorGeometryAndGroupLabels()));
00174     connect(header(), SIGNAL(sectionHandleDoubleClicked (int)), this, SLOT(slotColumnSizeChanged(int)));
00175     updateGroupLabelsPosition();
00176 }
00177 
00178 Editor::~Editor()
00179 {
00180     clearWidgetCache();
00181     delete d;
00182     d = 0;
00183 }
00184 
00185 void
00186 Editor::fill()
00187 {
00188     setUpdatesEnabled(false);
00189     d->itemToSelectLater = 0;
00190     qApp->eventLoop()->processEvents(QEventLoop::AllEvents);
00191     hideEditor();
00192     KListView::clear();
00193     d->itemDict.clear();
00194     clearWidgetCache();
00195     if(!d->set) {
00196         d->topItem = 0;
00197         setUpdatesEnabled(true);
00198         triggerUpdate();
00199         return;
00200     }
00201 
00202     d->topItem = new EditorDummyItem(this);
00203 
00204     const QValueList<QCString> groupNames = d->set->groupNames();
00205 //  kopropertydbg << "Editor::fill(): group names = " << groupNames.count() << endl;
00206     if(groupNames.count() == 1) { // one group (default one), so don't show groups
00207         //add flat set of properties
00208         const QValueList<QCString>& propertyNames = d->set->propertyNamesForGroup( groupNames.first() );
00209         QValueListConstIterator<QCString> it = propertyNames.constBegin();
00210         for( ; it != propertyNames.constEnd(); ++it)
00211             addItem(*it, d->topItem);
00212     }
00213     else { // create a groupItem for each group
00214         EditorGroupItem *prevGroupItem = 0;
00215         int sortOrder = 0;
00216         for (QValueListConstIterator<QCString> it = groupNames.constBegin(); it!=groupNames.constEnd(); 
00217             ++it, sortOrder++) 
00218         {
00219             const QValueList<QCString>& propertyNames = d->set->propertyNamesForGroup(*it);
00220             EditorGroupItem *groupItem;
00221             if (prevGroupItem)
00222                 groupItem = new EditorGroupItem(d->topItem, prevGroupItem, 
00223                     d->set->groupDescription(*it), d->set->groupIcon(*it), sortOrder );
00224             else
00225                 groupItem = new EditorGroupItem(d->topItem, 
00226                     d->set->groupDescription(*it), d->set->groupIcon(*it), sortOrder );
00227 
00228             QValueList<QCString>::ConstIterator it2 = propertyNames.constBegin();
00229             for( ; it2 != propertyNames.constEnd(); ++it2)
00230                 addItem(*it2, groupItem);
00231 
00232             prevGroupItem = groupItem;
00233         }
00234     }
00235 
00236 //  repaint();
00237 
00238     if (firstChild())
00239     {
00240         setCurrentItem(firstChild());
00241         setSelected(firstChild(), true);
00242         slotClicked(firstChild());
00243         updateGroupLabelsPosition();
00244     }
00245     setUpdatesEnabled(true);
00246     // aaah, call this instead of update() as explained here http://lists.trolltech.com/qt-interest/2000-06/thread00337-0.html
00247     triggerUpdate();
00248 }
00249 
00250 void
00251 Editor::addItem(const QCString &name, EditorItem *parent)
00252 {
00253     if(!d->set || !d->set->contains(name))
00254         return;
00255 
00256     Property *property = &(d->set->property(name));
00257     if(!property || !property->isVisible()) {
00258 //      kopropertydbg << "Property is not visible: " << name << endl;
00259         return;
00260     }
00261     QListViewItem *last = parent ? parent->firstChild() : d->topItem->firstChild();
00262     while(last && last->nextSibling())
00263         last = last->nextSibling();
00264 
00265     EditorItem *item=0;
00266     if(parent)
00267         item = new EditorItem(this, parent, property, last);
00268     else
00269         item = new EditorItem(this, d->topItem, property, last);
00270     d->itemDict.insert(name, item);
00271 
00272     // Create child items
00273     item->setOpen(true);
00274     if(!property->children())
00275         return;
00276 
00277     last = 0;
00278     QValueList<Property*>::ConstIterator endIt = property->children()->constEnd();
00279     for(QValueList<Property*>::ConstIterator it = property->children()->constBegin(); it != endIt; ++it) {
00281         if( *it && (*it)->isVisible() )
00282             last = new EditorItem(this, item, *it, last);
00283     }
00284 }
00285 
00286 void
00287 Editor::changeSet(Set *set, bool preservePrevSelection)
00288 {
00289     changeSetInternal(set, preservePrevSelection, "");
00290 }
00291 
00292 void
00293 Editor::changeSet(Set *set, const QCString& propertyToSelect)
00294 {
00295     changeSetInternal(set, !propertyToSelect.isEmpty(), propertyToSelect);
00296 }
00297 
00298 void
00299 Editor::changeSetInternal(Set *set, bool preservePrevSelection, const QCString& propertyToSelect)
00300 {
00301     if (d->insideSlotValueChanged) {
00302         //changeSet() called from inside of slotValueChanged()
00303         //this is dangerous, because there can be pending events,
00304         //especially for the GUI stuff, so let's do delayed work
00305         d->setListLater_list = set;
00306         d->preservePrevSelection_preservePrevSelection = preservePrevSelection;
00307         d->preservePrevSelection_propertyToSelect = propertyToSelect;
00308         qApp->eventLoop()->processEvents(QEventLoop::AllEvents);
00309         if (d->set) {
00310             //store prev. selection for this prop set
00311             if (d->currentItem)
00312                 d->set->setPrevSelection( d->currentItem->property()->name() );
00313             kdDebug() << d->set->prevSelection() << endl;
00314         }
00315         if (!d->setListLater_set) {
00316             d->setListLater_set = true;
00317             d->changeSetLaterTimer.start(10, true);
00318         }
00319         return;
00320     }
00321 
00322     if (d->set) {
00323         slotWidgetAcceptInput(d->currentWidget);
00324         //store prev. selection for this prop set
00325         if (d->currentItem)
00326             d->set->setPrevSelection( d->currentItem->property()->name() );
00327         else
00328             d->set->setPrevSelection( "" );
00329         d->set->disconnect(this);
00330     }
00331 
00332     QCString selectedPropertyName1 = propertyToSelect, selectedPropertyName2 = propertyToSelect;
00333     if (preservePrevSelection) {
00334         //try to find prev. selection:
00335         //1. in new list's prev. selection
00336         if(set)
00337             selectedPropertyName1 = set->prevSelection();
00338         //2. in prev. list's current selection
00339         if(d->set)
00340             selectedPropertyName2 = d->set->prevSelection();
00341     }
00342 
00343     d->set = set;
00344     if (d->set) {
00345         //receive property changes
00346         connect(d->set, SIGNAL(propertyChangedInternal(KoProperty::Set&, KoProperty::Property&)),
00347             this, SLOT(slotPropertyChanged(KoProperty::Set&, KoProperty::Property&)));
00348         connect(d->set, SIGNAL(propertyReset(KoProperty::Set&, KoProperty::Property&)),
00349             this, SLOT(slotPropertyReset(KoProperty::Set&, KoProperty::Property&)));
00350         connect(d->set,SIGNAL(aboutToBeCleared()), this, SLOT(slotSetWillBeCleared()));
00351         connect(d->set,SIGNAL(aboutToBeDeleted()), this, SLOT(slotSetWillBeDeleted()));
00352     }
00353 
00354     fill();
00355 
00356     emit propertySetChanged(d->set);
00357 
00358     if (d->set) {
00359         //select prev. selected item
00360         EditorItem * item = 0;
00361         if (!selectedPropertyName2.isEmpty()) //try other one for old prop set
00362             item = d->itemDict[selectedPropertyName2];
00363         if (!item && !selectedPropertyName1.isEmpty()) //try old one for current prop set
00364             item = d->itemDict[selectedPropertyName1];
00365 
00366         if (item) {
00367             d->itemToSelectLater = item;
00368             QTimer::singleShot(10, this, SLOT(selectItemLater()));
00369             //d->doNotSetFocusOnSelection = !hasParent(this, focusWidget());
00370             //setSelected(item, true);
00371             //d->doNotSetFocusOnSelection = false;
00372 //          ensureItemVisible(item);
00373         }
00374     }
00375 }
00376 
00378 void Editor::selectItemLater()
00379 {
00380     if (!d->itemToSelectLater)
00381         return;
00382     EditorItem *item = d->itemToSelectLater;
00383     d->itemToSelectLater = 0;
00384     setSelected(item, true);
00385     ensureItemVisible(item);
00386 }
00387 
00389 void
00390 Editor::changeSetLater()
00391 {
00392     qApp->eventLoop()->processEvents(QEventLoop::AllEvents);
00393     if (kapp->hasPendingEvents()) {
00394         d->changeSetLaterTimer.start(10, true); //try again...
00395         return;
00396     }
00397     d->setListLater_set = false;
00398     if (!d->setListLater_list)
00399         return;
00400 
00401     bool b = d->insideSlotValueChanged;
00402     d->insideSlotValueChanged = false;
00403     changeSetInternal(d->setListLater_list, d->preservePrevSelection_preservePrevSelection, 
00404         d->preservePrevSelection_propertyToSelect);
00405     d->insideSlotValueChanged = b;
00406 }
00407 
00408 void
00409 Editor::clear(bool editorOnly)
00410 {
00411     d->itemToSelectLater = 0;
00412     hideEditor();
00413 
00414     if(!editorOnly) {
00415         qApp->eventLoop()->processEvents(QEventLoop::AllEvents);
00416         if(d->set)
00417             d->set->disconnect(this);
00418         clearWidgetCache(); 
00419         KListView::clear();
00420         d->itemDict.clear();
00421         d->topItem = 0;
00422     }
00423 }
00424 
00425 void
00426 Editor::undo()
00427 {
00428     if(!d->currentWidget || !d->currentItem || (d->set && d->set->isReadOnly()) || (d->currentWidget && d->currentWidget->isReadOnly()))
00429         return;
00430 
00431     int propertySync = d->currentWidget->property()->autoSync();
00432     bool sync = (propertySync != 0 && propertySync != 1) ?
00433                  d->sync : (propertySync!=0);
00434 
00435     if(sync)
00436         d->currentItem->property()->resetValue();
00437     if (d->currentWidget && d->currentItem) {//(check because current widget could be removed by resetValue())
00438         d->currentWidget->setValue( d->currentItem->property()->value());
00439         repaintItem(d->currentItem);
00440     }
00441 }
00442 
00443 void
00444 Editor::slotPropertyChanged(Set& set, Property& property)
00445 {
00446     if (!d->slotPropertyChanged_enabled)
00447         return;
00448     if(&set != d->set)
00449         return;
00450 
00451     if (d->currentItem && d->currentItem->property() == &property) {
00452         d->currentWidget->setValue(property.value(), false);
00453         for(QListViewItem *item = d->currentItem->firstChild(); item; item = item->nextSibling())
00454             repaintItem(item);
00455     }
00456     else  {
00457         // prop not in the dict, might be a child property:
00458         EditorItem *item = d->itemDict[property.name()];
00459         if(!item && property.parent())
00460             item = d->itemDict[property.parent()->name()];
00461         if (item) {
00462             repaintItem(item);
00463             for(QListViewItem *it = item->firstChild(); it; it = it->nextSibling())
00464                 repaintItem(it);
00465         }
00466     }
00467 
00469 #if 0
00470     if (property.parent() && property.parent()->type()==Rect) {
00471         const int delta = property.value().toInt()-previousValue.toInt();
00472         if (property.type()==Rect_X) { //|| property.type()==Rect_Y)
00473             property.parent()->child("width")->setValue(delta, false);
00474         }
00475 
00476 /*  if (widget->property() && (QWidget*)d->currentWidget==widget && d->currentItem->parent()) {
00477         EditorItem *parentItem = static_cast<EditorItem*>(d->currentItem->parent());
00478         const int thisType = ;
00479             && parentItem->property()->type()==Rect) {
00480             //changing x or y components of Rect type shouldn't change width or height, respectively
00481             if (thisType==Rect_X) {
00482                 EditorItem *rectWidthItem = static_cast<EditorItem*>(d->currentItem->nextSibling()->nextSibling());
00483                 if (delta!=0) {
00484                     rectWidthItem->property()->setValue(rectWidthItem->property()->value().toInt()+delta, false);
00485                 }
00486             }
00487         }*/
00488     }
00489 #endif
00490     showUndoButton( property.isModified() );
00491 }
00492 
00493 void
00494 Editor::slotPropertyReset(Set& set, Property& property)
00495 {
00496     if(&set != d->set)
00497         return;
00498 
00499     if (d->currentItem && d->currentItem->property() == &property) {
00500         d->currentWidget->setValue(property.value(), false);
00501         for(QListViewItem *item = d->currentItem->firstChild(); item; item = item->nextSibling())
00502             repaintItem(item);
00503     }
00504     else  {
00505         EditorItem *item = d->itemDict[property.name()];
00506         // prop not in the dict, might be a child prop.
00507         if(!item && property.parent())
00508             item = d->itemDict[property.parent()->name()];
00509         if (item) {
00510             repaintItem(item);
00511             for(QListViewItem *it = item->firstChild(); it; it = it->nextSibling())
00512                 repaintItem(it);
00513         }
00514     }
00515 
00516     showUndoButton( false );
00517 }
00518 
00519 void
00520 Editor::slotWidgetValueChanged(Widget *widget)
00521 {
00522     if(!widget || !d->set || (d->set && d->set->isReadOnly()) || (widget && widget->isReadOnly()) || !widget->property())
00523         return;
00524 
00525     d->insideSlotValueChanged = true;
00526 
00527     QVariant value = widget->value();
00528     int propertySync = widget->property()->autoSync();
00529     bool sync = (propertySync != 0 && propertySync != 1) ?
00530                  d->sync : (propertySync!=0);
00531 
00532     if(sync) {
00533         d->slotPropertyChanged_enabled = false;
00534         QGuardedPtr<Widget> pWidget = widget; //safe, widget can be destroyed in the meantime
00535         widget->property()->setValue(value);
00536         if (pWidget)
00537           showUndoButton( pWidget->property()->isModified() );
00538         d->slotPropertyChanged_enabled = true;
00539     }
00540 
00541     d->insideSlotValueChanged = false;
00542 }
00543 
00544 void
00545 Editor::acceptInput()
00546 {
00547     slotWidgetAcceptInput(d->currentWidget);
00548 }
00549 
00550 void
00551 Editor::slotWidgetAcceptInput(Widget *widget)
00552 {
00553     if(!widget || !d->set || !widget->property() || (d->set && d->set->isReadOnly()) || (widget && widget->isReadOnly()))
00554         return;
00555 
00556     widget->property()->setValue(widget->value());
00557 }
00558 
00559 void
00560 Editor::slotWidgetRejectInput(Widget *widget)
00561 {
00562     if(!widget || !d->set)
00563         return;
00564 
00565     undo();
00566 }
00567 
00568 void
00569 Editor::slotClicked(QListViewItem *it)
00570 {
00571     d->previouslyCollapsedGroupItem = 0;
00572     d->childFormPreviouslyCollapsedGroupItem = 0;
00573 
00574     acceptInput();
00575 
00576     hideEditor();
00577     if(!it)
00578         return;
00579 
00580     EditorItem *item = static_cast<EditorItem*>(it);
00581     Property *p = item ? item->property() : 0;
00582     if(!p)
00583         return;
00584 
00585     d->currentItem = item;
00586     d->currentWidget = createWidgetForProperty(p);
00587 
00588     //moved up updateEditorGeometry();
00589     showUndoButton( p->isModified() );
00590     if (d->currentWidget) {
00591         if (d->currentWidget->visibleFlag()) {
00592             d->currentWidget->show();
00593             if (hasParent( this, kapp->focusWidget() ))
00594                 d->currentWidget->setFocus();
00595         }
00596     }
00597 
00598     d->justClickedItem = true;
00599 }
00600 
00601 void
00602 Editor::slotCurrentChanged(QListViewItem *item)
00603 {
00604     if (item == firstChild()) {
00605         QListViewItem *oldItem = item;
00606         while (item && (!item->isSelectable() || !item->isVisible()))
00607             item = item->itemBelow();
00608         if (item && item != oldItem) {
00609             setSelected(item,true);
00610             return;
00611         }
00612     }
00613 }
00614 
00615 void
00616 Editor::slotSetWillBeCleared()
00617 {
00618     d->itemToSelectLater = 0;
00619     if (d->currentWidget) {
00620         acceptInput();
00621         d->currentWidget->setProperty(0);
00622     }
00623     clear();
00624 }
00625 
00626 void
00627 Editor::slotSetWillBeDeleted()
00628 {
00629     clear();
00630     d->set = 0;
00631 }
00632 
00633 Widget*
00634 Editor::createWidgetForProperty(Property *property, bool changeWidgetProperty)
00635 {
00636 //  int type = property->type();
00637     QGuardedPtr<Widget> widget = d->widgetCache[property];
00638 
00639     if(!widget) {
00640         widget = FactoryManager::self()->createWidgetForProperty(property);
00641         if (!widget)
00642             return 0;
00643         widget->setReadOnly( (d->set && d->set->isReadOnly()) || property->isReadOnly() );
00644         d->widgetCache[property] = widget;
00645         widget->setProperty(0); // to force reloading property later
00646         widget->hide();
00647         connect(widget, SIGNAL(valueChanged(Widget*)),
00648             this, SLOT(slotWidgetValueChanged(Widget*)) );
00649         connect(widget, SIGNAL(acceptInput(Widget*)),
00650             this, SLOT(slotWidgetAcceptInput(Widget*)) );
00651         connect(widget, SIGNAL(rejectInput(Widget*)),
00652             this, SLOT(slotWidgetRejectInput(Widget*)) );
00653     }
00654 
00655     //update geometry earlier, because Widget::setValue() can depend on widget's geometry
00656     updateEditorGeometry(d->currentItem, widget);
00657 
00658     if(widget && (!widget->property() || changeWidgetProperty))
00659         widget->setProperty(property);
00660 
00661 //  if (!d->doNotSetFocusOnSelection) {
00662 //      widget->setFocus();
00663 //  }
00664 
00665     return widget;
00666 }
00667 
00668 
00669 void
00670 Editor::clearWidgetCache()
00671 {
00672     for(QMap<Property*, Widget*>::iterator it = d->widgetCache.begin(); it != d->widgetCache.end(); ++it)
00673         it.data()->deleteLater();
00674 //      delete it.data();
00675     d->widgetCache.clear();
00676 }
00677 
00678 void
00679 Editor::updateEditorGeometry(bool forceUndoButtonSettings, bool undoButtonVisible)
00680 {
00681     updateEditorGeometry(d->currentItem, d->currentWidget,
00682         forceUndoButtonSettings, undoButtonVisible);
00683 }
00684 
00685 void
00686 Editor::updateEditorGeometry(EditorItem *item, Widget* widget,
00687   bool forceUndoButtonSettings, bool undoButtonVisible)
00688 {
00689     if(!item || !widget)
00690         return;
00691 
00692     int placeForUndoButton;
00693     if (forceUndoButtonSettings ? undoButtonVisible : d->undoButton->isVisible())
00694         placeForUndoButton = d->undoButton->width();
00695     else
00696         placeForUndoButton = widget->leavesTheSpaceForRevertButton() ? d->undoButton->width() : 0;
00697 
00698     QRect r;
00699     int y = itemPos(item);
00700     r.setX(header()->sectionPos(1)-(widget->hasBorders()?1:0)); //-1, to align to horizontal line
00701     r.setY(y-(widget->hasBorders()?1:0));
00702     r.setWidth(header()->sectionSize(1)+(widget->hasBorders()?1:0) //+1 because we subtracted 1 from X
00703         - placeForUndoButton);
00704     r.setHeight(item->height()+(widget->hasBorders()?1:-1));
00705 
00706     // check if the column is fully visible
00707     if (visibleWidth() < r.right())
00708         r.setRight(visibleWidth());
00709 
00710     moveChild(widget, r.x(), r.y());
00711     widget->resize(r.size());
00712     qApp->eventLoop()->processEvents(QEventLoop::AllEvents);
00713 }
00714 
00715 void
00716 Editor::updateGroupLabelsPosition()
00717 {
00718     if(!d->topItem || d->itemDict.isEmpty())
00719         return;
00720 
00721     EditorGroupItem *group = dynamic_cast<EditorGroupItem*>(d->topItem->firstChild());
00722     while(group) {
00723         QRect r = itemRect((QListViewItem*) group);
00724         if(group->label()) {
00725             group->label()->setGeometry(r);
00726             group->label()->repaint();
00727         }
00728         group = dynamic_cast<EditorGroupItem*>(group->nextSibling());
00729     }
00730 }
00731 
00732 void
00733 Editor::hideEditor()
00734 {
00735     d->currentItem = 0;
00736     QWidget *cw = d->currentWidget;
00737     if(cw) {
00738         d->currentWidget = 0;
00739         cw->hide();
00740     }
00741     d->undoButton->hide();
00742 }
00743 
00744 void
00745 Editor::showUndoButton( bool show )
00746 {
00747     if (!d->currentItem || !d->currentWidget || (d->currentWidget && d->currentWidget->isReadOnly()))
00748         return;
00749 
00750     int y = viewportToContents(QPoint(0, itemRect(d->currentItem).y())).y();
00751     QRect geometry(columnWidth(0), y, columnWidth(1) + 1, d->currentItem->height());
00752     d->undoButton->resize(d->baseRowHeight, d->currentItem->height());
00753 
00754     updateEditorGeometry(true, show);
00755 
00756     if (!show) {
00757 /*    if (d->currentWidget) {
00758             if (d->currentWidget->leavesTheSpaceForRevertButton()) {
00759                 geometry.setWidth(geometry.width()-d->undoButton->width());
00760             }
00761             d->currentWidget->resize(geometry.width(), geometry.height());
00762         }*/
00763         d->undoButton->hide();
00764         return;
00765     }
00766 
00767     QPoint p = contentsToViewport(QPoint(0, geometry.y()));
00768     d->undoButton->move(geometry.x() + geometry.width()
00769         -((d->currentWidget && d->currentWidget->hasBorders())?1:0)/*editor is moved by 1 to left*/
00770         - d->undoButton->width(), p.y());
00771 //  if (d->currentWidget) {
00772 //    d->currentWidget->move(d->currentWidget->x(), p.y());
00773 //    d->currentWidget->resize(geometry.width()-d->undoButton->width(), geometry.height());
00774 //  }
00775     d->undoButton->show();
00776 }
00777 
00778 void
00779 Editor::slotExpanded(QListViewItem *item)
00780 {
00781     if (!item)
00782         return;
00783 
00784     //select child item again if a group item has been expanded
00785     if (!selectedItem() && dynamic_cast<EditorGroupItem*>(item) && d->previouslyCollapsedGroupItem == item
00786         && d->childFormPreviouslyCollapsedGroupItem) {
00787             setSelected(d->childFormPreviouslyCollapsedGroupItem, true);
00788             setCurrentItem(selectedItem());
00789             slotClicked(selectedItem());
00790     }
00791     updateEditorGeometry();
00792     updateGroupLabelsPosition();
00793     repaintContents();
00794     repaint();
00795 }
00796 
00797 void
00798 Editor::slotCollapsed(QListViewItem *item)
00799 {
00800     if (!item)
00801         return;
00802     //unselect child item and hide editor if a group item has been collapsed
00803     if (dynamic_cast<EditorGroupItem*>(item)) {
00804         for (QListViewItem *i = selectedItem(); i; i = i->parent()) {
00805             if (i->parent()==item) {
00806                 d->previouslyCollapsedGroupItem = item;
00807                 d->childFormPreviouslyCollapsedGroupItem = selectedItem();
00808                 hideEditor();
00809                 setSelected(selectedItem(), false);
00810                 setSelected(item->nextSibling(), true);
00811                 break;
00812             }
00813         }
00814     }
00815     updateEditorGeometry();
00816     updateGroupLabelsPosition();
00817     repaintContents();
00818     repaint();
00819 }
00820 
00821 void
00822 Editor::slotColumnSizeChanged(int section, int oldSize, int newSize)
00823 {
00824     Q_UNUSED(section);
00825     Q_UNUSED(oldSize);
00826     Q_UNUSED(newSize);
00827     /*for (QListViewItemIterator it(this); it.current(); ++it) {
00828         if (section == 0 && dynamic_cast<EditorGroupItem*>(it.current())) {
00829             it.current()->repaint();
00830     }
00831     }*/
00832 /*
00833     if(d->currentWidget) {
00834         if(section == 0)
00835             d->currentWidget->move(newS, d->currentWidget->y());
00836         else  {
00837             if(d->undoButton->isVisible())
00838                 d->currentWidget->resize(newS - d->undoButton->width(), d->currentWidget->height());
00839             else
00840                 d->currentWidget->resize(
00841                     newS-(d->currentWidget->leavesTheSpaceForRevertButton() ? d->undoButton->width() : 0),
00842                     d->currentWidget->height());
00843         }
00844     }*/
00845 //  repaintContents();
00846 //  repaint();
00847     updateEditorGeometry();
00848     update();
00849 }
00850 
00851 void
00852 Editor::slotColumnSizeChanged(int section)
00853 {
00854     setColumnWidth(1, viewport()->width() - columnWidth(0));
00855     slotColumnSizeChanged(section, 0, header()->sectionSize(section));
00856 
00857 /*  if(d->currentWidget) {
00858         if(d->undoButton->isVisible())
00859             d->currentWidget->resize(columnWidth(1) - d->undoButton->width(), d->currentWidget->height());
00860         else
00861             d->currentWidget->resize(
00862                 columnWidth(1)-(d->currentWidget->leavesTheSpaceForRevertButton() ? d->undoButton->width() : 0),
00863                 d->currentWidget->height());
00864     }*/
00865     if(d->undoButton->isVisible())
00866         showUndoButton(true);
00867     else
00868         updateEditorGeometry();
00869 }
00870 
00871 QSize
00872 Editor::sizeHint() const
00873 {
00874     return QSize( QFontMetrics(font()).width(columnText(0)+columnText(1)+"   "),
00875         KListView::sizeHint().height());
00876 }
00877 
00878 void
00879 Editor::setFocus()
00880 {
00881     EditorItem *item = static_cast<EditorItem *>(selectedItem());
00882     if (item) {
00883         if (!d->justClickedItem)
00884             ensureItemVisible(item);
00885         d->justClickedItem = false;
00886     }
00887     else {
00888         //select an item before focusing
00889         item = static_cast<EditorItem *>(itemAt(QPoint(10,1)));
00890         if (item) {
00891             ensureItemVisible(item);
00892             setSelected(item, true);
00893         }
00894     }
00895     if (d->currentWidget) {
00896 //      kopropertydbg << "d->currentWidget->setFocus()" << endl;
00897         d->currentWidget->setFocus();
00898     }
00899     else {
00900 //      kopropertydbg << "KListView::setFocus()" << endl;
00901         KListView::setFocus();
00902     }
00903 }
00904 
00905 void
00906 Editor::resizeEvent(QResizeEvent *ev)
00907 {
00908     KListView::resizeEvent(ev);
00909     if(d->undoButton->isVisible())
00910         showUndoButton(true);
00911     update();
00912     updateGroupLabelsPosition();
00913 }
00914 
00915 bool
00916 Editor::eventFilter( QObject * watched, QEvent * e )
00917 {
00918     if ((watched==this || watched==viewport()) && e->type()==QEvent::KeyPress) {
00919         if (handleKeyPress(static_cast<QKeyEvent*>(e)))
00920             return true;
00921     }
00922     return KListView::eventFilter(watched, e);
00923 }
00924 
00925 bool
00926 Editor::handleKeyPress(QKeyEvent* ev)
00927 {
00928     const int k = ev->key();
00929     const Qt::ButtonState s = ev->state();
00930 
00931     //selection moving
00932     QListViewItem *item = 0;
00933 
00934     if ( ((s == NoButton) && (k == Key_Up)) || (k==Key_BackTab) ) {
00935         //find prev visible
00936         item = selectedItem() ? selectedItem()->itemAbove() : 0;
00937         while (item && (!item->isSelectable() || !item->isVisible()))
00938             item = item->itemAbove();
00939         if (!item)
00940             return true;
00941     }
00942     else if( (s == NoButton) && ((k == Key_Down) || (k == Key_Tab)) ) {
00943         //find next visible
00944         item = selectedItem() ? selectedItem()->itemBelow() : 0;
00945         while (item && (!item->isSelectable() || !item->isVisible()))
00946             item = item->itemBelow();
00947         if (!item)
00948             return true;
00949     }
00950     else if( (s==NoButton) && (k==Key_Home) ) {
00951         if (d->currentWidget && d->currentWidget->hasFocus())
00952             return false;
00953         //find 1st visible
00954         item = firstChild();
00955         while (item && (!item->isSelectable() || !item->isVisible()))
00956             item = item->itemBelow();
00957     }
00958     else if( (s==NoButton) && (k==Key_End) ) {
00959         if (d->currentWidget && d->currentWidget->hasFocus())
00960             return false;
00961         //find last visible
00962         item = selectedItem();
00963         QListViewItem *lastVisible = item;
00964         while (item) { // && (!item->isSelectable() || !item->isVisible()))
00965             item = item->itemBelow();
00966             if (item && item->isSelectable() && item->isVisible())
00967                 lastVisible = item;
00968         }
00969         item = lastVisible;
00970     }
00971 
00972     if(item) {
00973         ev->accept();
00974         ensureItemVisible(item);
00975         setSelected(item, true);
00976         return true;
00977     }
00978     return false;
00979 }
00980 
00981 void
00982 Editor::updateFont()
00983 {
00984     setFont(parentWidget()->font());
00985     d->baseRowHeight = QFontMetrics(parentWidget()->font()).height() + itemMargin() * 2;
00986     if (!d->currentItem)
00987         d->undoButton->resize(d->baseRowHeight, d->baseRowHeight);
00988     else {
00989         showUndoButton(d->undoButton->isVisible());
00990         updateEditorGeometry();
00991     }
00992     updateGroupLabelsPosition();
00993 }
00994 
00995 bool
00996 Editor::event( QEvent * e )
00997 {
00998     if (e->type()==QEvent::ParentFontChange) {
00999         updateFont();
01000     }
01001     return KListView::event(e);
01002 }
01003 
01004 void
01005 Editor::contentsMousePressEvent( QMouseEvent * e )
01006 {
01007     QListViewItem *item = itemAt(e->pos());
01008     if (dynamic_cast<EditorGroupItem*>(item)) {
01009         setOpen( item, !isOpen(item) );
01010         return;
01011     }
01012     KListView::contentsMousePressEvent(e);
01013 }
01014 
01015 void
01016 Editor::setSorting( int column, bool ascending )
01017 {
01018     if (d->set && d->set->groupNames().count()>1) //do not sort when groups are present (maybe reenable this later?)
01019         return;
01020     KListView::setSorting( column, ascending );
01021     updateEditorGeometry();
01022     updateGroupLabelsPosition();
01023     repaintContents();
01024     repaint();
01025 }
01026 
01027 #include "editor.moc"
KDE Home | KDE Accessibility Home | Description of Access Keys