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