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-2005 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 
00040 #ifdef QT_ONLY
00041 #else
00042 #include <kdebug.h>
00043 #include <kiconloader.h>
00044 #include <klocale.h>
00045 #include <kdeversion.h>
00046 #include <kapplication.h>
00047 #endif
00048 
00049 namespace KoProperty {
00050 
00052 static bool kofficeAppDirAdded = false;
00053 
00056 inline bool hasParent(QObject* par, QObject* o)
00057 {
00058     if (!o || !par)
00059         return false;
00060     while (o && o != par)
00061         o = o->parent();
00062     return o == par;
00063 }
00064 
00065 class EditorPrivate
00066 {
00067     public:
00068         EditorPrivate(Editor *editor)
00069         : itemDict(101, false), justClickedItem(false)
00070         {
00071             currentItem = 0;
00072             undoButton = 0;
00073             topItem = 0;
00074             if (!kofficeAppDirAdded) {
00075                 kofficeAppDirAdded = true;
00076                 KGlobal::iconLoader()->addAppDir("koffice");
00077             }
00078             previouslyCollapsedGroupItem = 0;
00079             childFormPreviouslyCollapsedGroupItem = 0;
00080             slotPropertyChanged_enabled = true;
00081             QObject::connect(&changeSetLaterTimer, SIGNAL(timeout()), 
00082                 editor, SLOT(changeSetLater()));
00083         }
00084         ~EditorPrivate()
00085         {
00086         }
00087 
00088         QGuardedPtr<Set> set;
00090         QMap<Property*, Widget* >  widgetCache;
00091         QGuardedPtr<Widget> currentWidget;
00092         EditorItem *currentItem;
00093         EditorItem *topItem; 
00094         QPushButton *undoButton; 
00095         EditorItem::Dict itemDict;
00096 
00097         int baseRowHeight;
00098         bool sync : 1;
00099         bool insideSlotValueChanged : 1;
00100 
00102         QTimer changeSetLaterTimer;
00103         bool setListLater_set : 1;
00104         bool preservePrevSelection_preservePrevSelection : 1;
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(sectionHandleDoubleClicked (int)), this, SLOT(slotColumnSizeChanged(int)));
00174 }
00175 
00176 Editor::~Editor()
00177 {
00178     clearWidgetCache();
00179     delete d;
00180 }
00181 
00182 void
00183 Editor::fill()
00184 {
00185     setUpdatesEnabled(false);
00186     qApp->eventLoop()->processEvents(QEventLoop::AllEvents);
00187     hideEditor();
00188     KListView::clear();
00189     d->itemDict.clear();
00190     clearWidgetCache();
00191     if(!d->set) {
00192         d->topItem = 0;
00193         setUpdatesEnabled(true);
00194         triggerUpdate();
00195         return;
00196     }
00197 
00198     d->topItem = new EditorDummyItem(this);
00199 
00200 //  int i = 0;
00201     StringListMap map = d->set->groups();
00202 //  kopropertydbg << "Editor::fill(): groups = " << map.count() << endl;
00203     if(map.count() == 1) { // one group (default one), so don't show groups
00204 //      EditorGroupItem *hiddenGroupItem = new EditorGroupItem(d->topItem, "");
00205 
00206         QValueList<QCString> props = map.begin().data();
00207         QValueList<QCString>::ConstIterator it = props.constBegin();
00208         for( ; it != props.constEnd(); ++it)
00209             addItem(*it, d->topItem);
00210 
00211     } else { // else create a groupItem for each group
00212         StringListMap::ConstIterator it = map.constBegin();
00213         for( ; it != map.constEnd(); ++it) {
00214             EditorGroupItem *groupItem = 0;
00215 //          kopropertydbg << "'" << it.key() << "' -> " << it.data().count() << endl;
00216             if(!it.key().isEmpty() && !it.data().isEmpty() && map.count() > 1)
00217                 groupItem = new EditorGroupItem(d->topItem, d->set->groupDescription(it.key()) );
00218 
00219             QValueList<QCString>::ConstIterator it2 = it.data().constBegin();
00220             for( ; it2 != it.data().constEnd(); ++it2)
00221                 addItem(*it2, groupItem);
00222         }
00223 
00224     }
00225 
00226 //  repaint();
00227 
00228     if (firstChild())
00229     {
00230         setCurrentItem(firstChild());
00231         setSelected(firstChild(), true);
00232         slotClicked(firstChild());
00233     }
00234     setUpdatesEnabled(true);
00235     // aaah, call this instead of update() as explained here http://lists.trolltech.com/qt-interest/2000-06/thread00337-0.html
00236     triggerUpdate();
00237 }
00238 
00239 void
00240 Editor::addItem(const QCString &name, EditorItem *parent)
00241 {
00242     if(!d->set || !d->set->contains(name))
00243         return;
00244 
00245     Property *property = &(d->set->property(name));
00246     if(!property || !property->isVisible()) {
00247 //      kopropertydbg << "Property is not visible: " << name << endl;
00248         return;
00249     }
00250     QListViewItem *last = parent ? parent->firstChild() : d->topItem->firstChild();
00251     while(last && last->nextSibling())
00252         last = last->nextSibling();
00253 
00254     EditorItem *item=0;
00255     if(parent)
00256         item = new EditorItem(this, parent, property, last);
00257     else
00258         item = new EditorItem(this, d->topItem, property, last);
00259     d->itemDict.insert(name, item);
00260 
00261     // Create child items
00262     item->setOpen(true);
00263     if(!property->children())
00264         return;
00265 
00266     last = 0;
00267     QValueList<Property*>::ConstIterator endIt = property->children()->constEnd();
00268     for(QValueList<Property*>::ConstIterator it = property->children()->constBegin(); it != endIt; ++it) {
00270         if( *it && (*it)->isVisible() )
00271             last = new EditorItem(this, item, *it, last);
00272     }
00273 }
00274 
00275 void
00276 Editor::changeSet(Set *set, bool preservePrevSelection)
00277 {
00278     if (d->insideSlotValueChanged) {
00279         //changeSet() called from inside of slotValueChanged()
00280         //this is dangerous, because there can be pending events,
00281         //especially for the GUI stuff, so let's do delayed work
00282         d->setListLater_list = set;
00283         d->preservePrevSelection_preservePrevSelection = preservePrevSelection;
00284         qApp->eventLoop()->processEvents(QEventLoop::AllEvents);
00285         if (!d->setListLater_set) {
00286             d->setListLater_set = true;
00287             d->changeSetLaterTimer.start(10, true);
00288         }
00289         return;
00290     }
00291 
00292     if (d->set) {
00293         slotWidgetAcceptInput(d->currentWidget);
00294         //store prev. selection for this prop set
00295         if (d->currentItem)
00296             d->set->setPrevSelection( d->currentItem->property()->name() );
00297         d->set->disconnect(this);
00298     }
00299 
00300     QCString selectedPropertyName1, selectedPropertyName2;
00301     if (preservePrevSelection) {
00302         //try to find prev. selection:
00303         //1. in new list's prev. selection
00304         if(set)
00305             selectedPropertyName1 = set->prevSelection();
00306         //2. in prev. list's current selection
00307         if(d->set)
00308             selectedPropertyName2 = d->set->prevSelection();
00309     }
00310 
00311     d->set = set;
00312     if (d->set) {
00313         //receive property changes
00314         connect(d->set, SIGNAL(propertyChanged(KoProperty::Set&, KoProperty::Property&)),
00315             this, SLOT(slotPropertyChanged(KoProperty::Set&, KoProperty::Property&)));
00316         connect(d->set, SIGNAL(propertyReset(KoProperty::Set&, KoProperty::Property&)),
00317             this, SLOT(slotPropertyReset(KoProperty::Set&, KoProperty::Property&)));
00318         connect(d->set,SIGNAL(aboutToBeCleared()), this, SLOT(slotSetWillBeCleared()));
00319         connect(d->set,SIGNAL(aboutToBeDeleted()), this, SLOT(slotSetWillBeDeleted()));
00320     }
00321 
00322     fill();
00323 
00324     emit propertySetChanged(d->set);
00325 
00326     if (d->set) {
00327         //select prev. selected item
00328         EditorItem * item = 0;
00329         if (!selectedPropertyName2.isEmpty()) //try other one for old prop set
00330             item = d->itemDict[selectedPropertyName2];
00331         if (!item && !selectedPropertyName1.isEmpty()) //try old one for current prop set
00332             item = d->itemDict[selectedPropertyName1];
00333 
00334         if (item) {
00335             d->itemToSelectLater = item;
00336             QTimer::singleShot(10, this, SLOT(selectItemLater()));
00337             //d->doNotSetFocusOnSelection = !hasParent(this, focusWidget());
00338             //setSelected(item, true);
00339             //d->doNotSetFocusOnSelection = false;
00340 //          ensureItemVisible(item);
00341         }
00342     }
00343 }
00344 
00346 void Editor::selectItemLater()
00347 {
00348     if (!d->itemToSelectLater)
00349         return;
00350     EditorItem *item = d->itemToSelectLater;
00351     d->itemToSelectLater = 0;
00352     setSelected(item, true);
00353     ensureItemVisible(item);
00354 }
00355 
00357 void
00358 Editor::changeSetLater()
00359 {
00360     qApp->eventLoop()->processEvents(QEventLoop::AllEvents);
00361     if (kapp->hasPendingEvents()) {
00362         d->changeSetLaterTimer.start(10, true); //try again...
00363         return;
00364     }
00365     d->setListLater_set = false;
00366     if (!d->setListLater_list)
00367         return;
00368 
00369     bool b = d->insideSlotValueChanged;
00370     d->insideSlotValueChanged = false;
00371     changeSet(d->setListLater_list, d->preservePrevSelection_preservePrevSelection);
00372     d->insideSlotValueChanged = b;
00373 }
00374 
00375 void
00376 Editor::clear(bool editorOnly)
00377 {
00378     d->itemToSelectLater = 0;
00379     hideEditor();
00380 
00381     if(!editorOnly) {
00382         qApp->eventLoop()->processEvents(QEventLoop::AllEvents);
00383         if(d->set)
00384             d->set->disconnect(this);
00385         clearWidgetCache();
00386         KListView::clear();
00387         d->itemDict.clear();
00388         d->topItem = 0;
00389     }
00390 }
00391 
00392 void
00393 Editor::undo()
00394 {
00395     if(!d->currentWidget || !d->currentItem || (d->set && d->set->isReadOnly()) || (d->currentWidget && d->currentWidget->isReadOnly()))
00396         return;
00397 
00398     int propertySync = d->currentWidget->property()->autoSync();
00399     bool sync = (propertySync != 0 && propertySync != 1) ?
00400                  d->sync : (propertySync!=0);
00401 
00402     if(sync)
00403         d->currentItem->property()->resetValue();
00404     if (d->currentWidget && d->currentItem) {//(check because current widget could be removed by resetValue())
00405         d->currentWidget->setValue( d->currentItem->property()->value());
00406         repaintItem(d->currentItem);
00407     }
00408 }
00409 
00410 void
00411 Editor::slotPropertyChanged(Set& set, Property& property)
00412 {
00413     if (!d->slotPropertyChanged_enabled)
00414         return;
00415     if(&set != d->set)
00416         return;
00417 
00418     if (d->currentItem && d->currentItem->property() == &property) {
00419         d->currentWidget->setValue(property.value(), false);
00420         for(QListViewItem *item = d->currentItem->firstChild(); item; item = item->nextSibling())
00421             repaintItem(item);
00422     }
00423     else  {
00424         // prop not in the dict, might be a child property:
00425         EditorItem *item = d->itemDict[property.name()];
00426         if(!item && property.parent())
00427             item = d->itemDict[property.parent()->name()];
00428         if (item) {
00429             repaintItem(item);
00430             for(QListViewItem *it = item->firstChild(); it; it = it->nextSibling())
00431                 repaintItem(it);
00432         }
00433     }
00434 
00436 #if 0
00437     if (property.parent() && property.parent()->type()==Rect) {
00438         const int delta = property.value().toInt()-previousValue.toInt();
00439         if (property.type()==Rect_X) { //|| property.type()==Rect_Y)
00440             property.parent()->child("width")->setValue(delta, false);
00441         }
00442 
00443 /*  if (widget->property() && (QWidget*)d->currentWidget==widget && d->currentItem->parent()) {
00444         EditorItem *parentItem = static_cast<EditorItem*>(d->currentItem->parent());
00445         const int thisType = ;
00446             && parentItem->property()->type()==Rect) {
00447             //changing x or y components of Rect type shouldn't change width or height, respectively
00448             if (thisType==Rect_X) {
00449                 EditorItem *rectWidthItem = static_cast<EditorItem*>(d->currentItem->nextSibling()->nextSibling());
00450                 if (delta!=0) {
00451                     rectWidthItem->property()->setValue(rectWidthItem->property()->value().toInt()+delta, false);
00452                 }
00453             }
00454         }*/
00455     }
00456 #endif
00457     showUndoButton( property.isModified() );
00458 }
00459 
00460 void
00461 Editor::slotPropertyReset(Set& set, Property& property)
00462 {
00463     if(&set != d->set)
00464         return;
00465 
00466     if (d->currentItem && d->currentItem->property() == &property) {
00467         d->currentWidget->setValue(property.value(), false);
00468         for(QListViewItem *item = d->currentItem->firstChild(); item; item = item->nextSibling())
00469             repaintItem(item);
00470     }
00471     else  {
00472         EditorItem *item = d->itemDict[property.name()];
00473         // prop not in the dict, might be a child prop.
00474         if(!item && property.parent())
00475             item = d->itemDict[property.parent()->name()];
00476         if (item) {
00477             repaintItem(item);
00478             for(QListViewItem *it = item->firstChild(); it; it = it->nextSibling())
00479                 repaintItem(it);
00480         }
00481     }
00482 
00483     showUndoButton( false );
00484 }
00485 
00486 void
00487 Editor::slotWidgetValueChanged(Widget *widget)
00488 {
00489     if(!widget || !d->set || (d->set && d->set->isReadOnly()) || (widget && widget->isReadOnly()) || !widget->property())
00490         return;
00491 
00492     d->insideSlotValueChanged = true;
00493 
00494     QVariant value = widget->value();
00495     int propertySync = widget->property()->autoSync();
00496     bool sync = (propertySync != 0 && propertySync != 1) ?
00497                  d->sync : (propertySync!=0);
00498 
00499     if(sync) {
00500         d->slotPropertyChanged_enabled = false;
00501         QGuardedPtr<Widget> pWidget = widget; //safe, widget can be destroyed in the meantime
00502         widget->property()->setValue(value);
00503         if (pWidget)
00504           showUndoButton( pWidget->property()->isModified() );
00505         d->slotPropertyChanged_enabled = true;
00506     }
00507 
00508     d->insideSlotValueChanged = false;
00509 }
00510 
00511 void
00512 Editor::acceptInput()
00513 {
00514     slotWidgetAcceptInput(d->currentWidget);
00515 }
00516 
00517 void
00518 Editor::slotWidgetAcceptInput(Widget *widget)
00519 {
00520     if(!widget || !d->set || !widget->property() || (d->set && d->set->isReadOnly()) || (widget && widget->isReadOnly()))
00521         return;
00522 
00523     widget->property()->setValue(widget->value());
00524 }
00525 
00526 void
00527 Editor::slotWidgetRejectInput(Widget *widget)
00528 {
00529     if(!widget || !d->set)
00530         return;
00531 
00532     undo();
00533 }
00534 
00535 void
00536 Editor::slotClicked(QListViewItem *it)
00537 {
00538     d->previouslyCollapsedGroupItem = 0;
00539     d->childFormPreviouslyCollapsedGroupItem = 0;
00540 
00541     acceptInput();
00542 
00543     hideEditor();
00544     if(!it)
00545         return;
00546 
00547     EditorItem *item = static_cast<EditorItem*>(it);
00548     Property *p = item ? item->property() : 0;
00549     if(!p)
00550         return;
00551 
00552     d->currentItem = item;
00553     d->currentWidget = createWidgetForProperty(p);
00554 
00555     //moved up updateEditorGeometry();
00556     showUndoButton( p->isModified() );
00557     if (d->currentWidget) {
00558         if (d->currentWidget->visibleFlag()) {
00559             d->currentWidget->show();
00560             if (hasParent( this, kapp->focusWidget() ))
00561                 d->currentWidget->setFocus();
00562         }
00563     }
00564 
00565     d->justClickedItem = true;
00566 }
00567 
00568 void
00569 Editor::slotCurrentChanged(QListViewItem *item)
00570 {
00571     if (item == firstChild()) {
00572         QListViewItem *oldItem = item;
00573         while (item && (!item->isSelectable() || !item->isVisible()))
00574             item = item->itemBelow();
00575         if (item && item != oldItem) {
00576             setSelected(item,true);
00577             return;
00578         }
00579     }
00580 }
00581 
00582 void
00583 Editor::slotSetWillBeCleared()
00584 {
00585     d->itemToSelectLater = 0;
00586     if (d->currentWidget) {
00587         acceptInput();
00588         d->currentWidget->setProperty(0);
00589     }
00590     clear();
00591 }
00592 
00593 void
00594 Editor::slotSetWillBeDeleted()
00595 {
00596     clear();
00597     d->set = 0;
00598 }
00599 
00600 Widget*
00601 Editor::createWidgetForProperty(Property *property, bool changeWidgetProperty)
00602 {
00603 //  int type = property->type();
00604     QGuardedPtr<Widget> widget = d->widgetCache[property];
00605 
00606     if(!widget) {
00607         widget = FactoryManager::self()->createWidgetForProperty(property);
00608         if (!widget)
00609             return 0;
00610         widget->setReadOnly( (d->set && d->set->isReadOnly()) || property->isReadOnly() );
00611         d->widgetCache[property] = widget;
00612         widget->setProperty(0); // to force reloading property later
00613         widget->hide();
00614         connect(widget, SIGNAL(valueChanged(Widget*)),
00615             this, SLOT(slotWidgetValueChanged(Widget*)) );
00616         connect(widget, SIGNAL(acceptInput(Widget*)),
00617             this, SLOT(slotWidgetAcceptInput(Widget*)) );
00618         connect(widget, SIGNAL(rejectInput(Widget*)),
00619             this, SLOT(slotWidgetRejectInput(Widget*)) );
00620     }
00621 
00622     //update geometry earlier, because Widget::setValue() can depend on widget's geometry
00623     updateEditorGeometry(d->currentItem, widget);
00624 
00625     if(widget && (!widget->property() || changeWidgetProperty))
00626         widget->setProperty(property);
00627 
00628 //  if (!d->doNotSetFocusOnSelection) {
00629 //      widget->setFocus();
00630 //  }
00631 
00632     return widget;
00633 }
00634 
00635 
00636 void
00637 Editor::clearWidgetCache()
00638 {
00639     for(QMap<Property*, Widget*>::iterator it = d->widgetCache.begin(); it != d->widgetCache.end(); ++it)
00640         it.data()->deleteLater();
00641 //      delete it.data();
00642     d->widgetCache.clear();
00643 }
00644 
00645 void
00646 Editor::updateEditorGeometry(bool forceUndoButtonSettings, bool undoButtonVisible)
00647 {
00648     updateEditorGeometry(d->currentItem, d->currentWidget, 
00649         forceUndoButtonSettings, undoButtonVisible);
00650 }
00651 
00652 void
00653 Editor::updateEditorGeometry(EditorItem *item, Widget* widget, 
00654   bool forceUndoButtonSettings, bool undoButtonVisible)
00655 {
00656     if(!item || !widget)
00657         return;
00658 
00659     int placeForUndoButton;
00660     if (forceUndoButtonSettings ? undoButtonVisible : d->undoButton->isVisible())
00661         placeForUndoButton = d->undoButton->width();
00662     else
00663         placeForUndoButton = widget->leavesTheSpaceForRevertButton() ? d->undoButton->width() : 0;
00664 
00665     QRect r;
00666     int y = itemPos(item);
00667     r.setX(header()->sectionPos(1)-(widget->hasBorders()?1:0)); //-1, to align to horizontal line
00668     r.setY(y-(widget->hasBorders()?1:0));
00669     r.setWidth(header()->sectionSize(1)+(widget->hasBorders()?1:0) //+1 because we subtracted 1 from X
00670         - placeForUndoButton);
00671     r.setHeight(item->height()+(widget->hasBorders()?1:-1));
00672 
00673     // check if the column is fully visible
00674     if (visibleWidth() < r.right())
00675         r.setRight(visibleWidth());
00676 
00677     moveChild(widget, r.x(), r.y());
00678     widget->resize(r.size());
00679     qApp->eventLoop()->processEvents(QEventLoop::AllEvents);
00680 }
00681 
00682 void
00683 Editor::hideEditor()
00684 {
00685     d->currentItem = 0;
00686     QWidget *cw = d->currentWidget;
00687     if(cw) {
00688         d->currentWidget = 0;
00689         cw->hide();
00690     }
00691     d->undoButton->hide();
00692 }
00693 
00694 void
00695 Editor::showUndoButton( bool show )
00696 {
00697     if (!d->currentItem || !d->currentWidget || (d->currentWidget && d->currentWidget->isReadOnly()))
00698         return;
00699 
00700     int y = viewportToContents(QPoint(0, itemRect(d->currentItem).y())).y();
00701     QRect geometry(columnWidth(0), y, columnWidth(1) + 1, d->currentItem->height());
00702     d->undoButton->resize(d->baseRowHeight, d->baseRowHeight);
00703 
00704     updateEditorGeometry(true, show);
00705 
00706     if (!show) {
00707 /*    if (d->currentWidget) {
00708             if (d->currentWidget->leavesTheSpaceForRevertButton()) {
00709                 geometry.setWidth(geometry.width()-d->undoButton->width());
00710             }
00711             d->currentWidget->resize(geometry.width(), geometry.height());
00712         }*/
00713         d->undoButton->hide();
00714         return;
00715     }
00716 
00717     QPoint p = contentsToViewport(QPoint(0, geometry.y()));
00718     d->undoButton->move(geometry.x() + geometry.width() 
00719         -((d->currentWidget && d->currentWidget->hasBorders())?1:0)/*editor is moved by 1 to left*/
00720         - d->undoButton->width(), p.y());
00721 //  if (d->currentWidget) {
00722 //    d->currentWidget->move(d->currentWidget->x(), p.y());
00723 //    d->currentWidget->resize(geometry.width()-d->undoButton->width(), geometry.height());
00724 //  }
00725     d->undoButton->show();
00726 }
00727 
00728 void
00729 Editor::slotExpanded(QListViewItem *item)
00730 {
00731     if (!item)
00732         return;
00733 
00734     //select child item again if a group item has been expanded
00735     if (!selectedItem() && dynamic_cast<EditorGroupItem*>(item) && d->previouslyCollapsedGroupItem == item
00736         && d->childFormPreviouslyCollapsedGroupItem) {
00737             setSelected(d->childFormPreviouslyCollapsedGroupItem, true);
00738             setCurrentItem(selectedItem());
00739             slotClicked(selectedItem());
00740     }
00741     updateEditorGeometry();
00742 }
00743 
00744 void
00745 Editor::slotCollapsed(QListViewItem *item)
00746 {
00747     if (!item)
00748         return;
00749     //unselect child item and hide editor if a group item has been collapsed
00750     if (dynamic_cast<EditorGroupItem*>(item)) {
00751         for (QListViewItem *i = selectedItem(); i; i = i->parent()) {
00752             if (i->parent()==item) {
00753                 d->previouslyCollapsedGroupItem = item;
00754                 d->childFormPreviouslyCollapsedGroupItem = selectedItem();
00755                 hideEditor();
00756                 setSelected(selectedItem(), false);
00757                 setSelected(item->nextSibling(), true);
00758                 break;
00759             }
00760         }
00761     }
00762     updateEditorGeometry();
00763 }
00764 
00765 void
00766 Editor::slotColumnSizeChanged(int section, int oldSize, int newSize)
00767 {
00768     Q_UNUSED(section);
00769     Q_UNUSED(oldSize);
00770     Q_UNUSED(newSize);
00771     updateEditorGeometry();
00772     for (QListViewItemIterator it(this); it.current(); ++it) {
00773 //      if (section == 0 && dynamic_cast<EditorGroupItem*>(it.current())) {
00774 //          it.current()->repaint();
00775 //  }
00776     }
00777 /*
00778     if(d->currentWidget) {
00779         if(section == 0)
00780             d->currentWidget->move(newS, d->currentWidget->y());
00781         else  {
00782             if(d->undoButton->isVisible())
00783                 d->currentWidget->resize(newS - d->undoButton->width(), d->currentWidget->height());
00784             else
00785                 d->currentWidget->resize(
00786                     newS-(d->currentWidget->leavesTheSpaceForRevertButton() ? d->undoButton->width() : 0),
00787                     d->currentWidget->height());
00788         }
00789     }*/
00790     update();
00791 }
00792 
00793 void
00794 Editor::slotColumnSizeChanged(int section)
00795 {
00796     setColumnWidth(1, viewport()->width() - columnWidth(0));
00797     slotColumnSizeChanged(section, 0, header()->sectionSize(section));
00798 
00799 /*  if(d->currentWidget) {
00800         if(d->undoButton->isVisible())
00801             d->currentWidget->resize(columnWidth(1) - d->undoButton->width(), d->currentWidget->height());
00802         else
00803             d->currentWidget->resize(
00804                 columnWidth(1)-(d->currentWidget->leavesTheSpaceForRevertButton() ? d->undoButton->width() : 0),
00805                 d->currentWidget->height());
00806     }*/
00807     if(d->undoButton->isVisible())
00808         showUndoButton(true);
00809     else
00810         updateEditorGeometry();
00811 }
00812 
00813 QSize
00814 Editor::sizeHint() const
00815 {
00816     return QSize( QFontMetrics(font()).width(columnText(0)+columnText(1)+"   "),
00817         KListView::sizeHint().height());
00818 }
00819 
00820 void
00821 Editor::setFocus()
00822 {
00823     EditorItem *item = static_cast<EditorItem *>(selectedItem());
00824     if (item) {
00825         if (!d->justClickedItem)
00826             ensureItemVisible(item);
00827         d->justClickedItem = false;
00828     }
00829     else {
00830         //select an item before focusing
00831         item = static_cast<EditorItem *>(itemAt(QPoint(10,1)));
00832         if (item) {
00833             ensureItemVisible(item);
00834             setSelected(item, true);
00835         }
00836     }
00837     if (d->currentWidget) {
00838 //      kopropertydbg << "d->currentWidget->setFocus()" << endl;
00839         d->currentWidget->setFocus();
00840     }
00841     else {
00842 //      kopropertydbg << "KListView::setFocus()" << endl;
00843         KListView::setFocus();
00844     }
00845 }
00846 
00847 void
00848 Editor::resizeEvent(QResizeEvent *ev)
00849 {
00850     KListView::resizeEvent(ev);
00851     if(d->undoButton->isVisible())
00852         showUndoButton(true);
00853     update();
00854 }
00855 
00856 bool
00857 Editor::eventFilter( QObject * watched, QEvent * e )
00858 {
00859     if ((watched==this || watched==viewport()) && e->type()==QEvent::KeyPress) {
00860         if (handleKeyPress(static_cast<QKeyEvent*>(e)))
00861             return true;
00862     }
00863     return KListView::eventFilter(watched, e);
00864 }
00865 
00866 bool
00867 Editor::handleKeyPress(QKeyEvent* ev)
00868 {
00869     const int k = ev->key();
00870     const Qt::ButtonState s = ev->state();
00871 
00872     //selection moving
00873     QListViewItem *item = 0;
00874 
00875     if ( ((s == NoButton) && (k == Key_Up)) || (k==Key_BackTab) ) {
00876         //find prev visible
00877         item = selectedItem() ? selectedItem()->itemAbove() : 0;
00878         while (item && (!item->isSelectable() || !item->isVisible()))
00879             item = item->itemAbove();
00880         if (!item)
00881             return true;
00882     }
00883     else if( (s == NoButton) && ((k == Key_Down) || (k == Key_Tab)) ) {
00884         //find next visible
00885         item = selectedItem() ? selectedItem()->itemBelow() : 0;
00886         while (item && (!item->isSelectable() || !item->isVisible()))
00887             item = item->itemBelow();
00888         if (!item)
00889             return true;
00890     }
00891     else if( (s==NoButton) && (k==Key_Home) ) {
00892         if (d->currentWidget && d->currentWidget->hasFocus())
00893             return false;
00894         //find 1st visible
00895         item = firstChild();
00896         while (item && (!item->isSelectable() || !item->isVisible()))
00897             item = item->itemBelow();
00898     }
00899     else if( (s==NoButton) && (k==Key_End) ) {
00900         if (d->currentWidget && d->currentWidget->hasFocus())
00901             return false;
00902         //find last visible
00903         item = selectedItem();
00904         QListViewItem *lastVisible = item;
00905         while (item) { // && (!item->isSelectable() || !item->isVisible()))
00906             item = item->itemBelow();
00907             if (item && item->isSelectable() && item->isVisible())
00908                 lastVisible = item;
00909         }
00910         item = lastVisible;
00911     }
00912 
00913     if(item) {
00914         ev->accept();
00915         ensureItemVisible(item);
00916         setSelected(item, true);
00917         return true;
00918     }
00919     return false;
00920 }
00921 
00922 void
00923 Editor::updateFont()
00924 {
00925     setFont(parentWidget()->font());
00926     d->baseRowHeight = QFontMetrics(parentWidget()->font()).height() + itemMargin() * 2;
00927     if (!d->currentItem)
00928         d->undoButton->resize(d->baseRowHeight, d->baseRowHeight);
00929     else {
00930         showUndoButton(d->undoButton->isVisible());
00931         updateEditorGeometry();
00932     }
00933 }
00934 
00935 bool
00936 Editor::event( QEvent * e )
00937 {
00938     if (e->type()==QEvent::ParentFontChange) {
00939         updateFont();
00940     }
00941     return KListView::event(e);
00942 }
00943 
00944 void
00945 Editor::contentsMousePressEvent( QMouseEvent * e )
00946 {
00947     QListViewItem *item = itemAt(e->pos());
00948     if (dynamic_cast<EditorGroupItem*>(item)) {
00949         setOpen( item, !isOpen(item) );
00950         return;
00951     }
00952     KListView::contentsMousePressEvent(e);
00953 }
00954 
00955 #include "editor.moc"
KDE Home | KDE Accessibility Home | Description of Access Keys