kexi

kexidbform.cpp

00001 /* This file is part of the KDE project
00002    Copyright (C) 2004 Lucijan Busch <lucijan@kde.org>
00003    Copyright (C) 2004 Cedric Pasteur <cedric.pasteur@free.fr>
00004    Copyright (C) 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 <qobjectlist.h>
00023 #include <qpainter.h>
00024 #include <qcursor.h>
00025 #include <qapplication.h>
00026 #include <qfocusdata.h>
00027 
00028 #include <kdebug.h>
00029 
00030 #include "kexidbform.h"
00031 #include "kexiformscrollview.h"
00032 
00033 #include <formeditor/objecttree.h>
00034 #include <formeditor/formmanager.h> 
00035 #include <widget/tableview/kexidataawareobjectiface.h>
00036 #include <widget/kexiscrollview.h>
00037 
00039 class KexiDBForm::Private
00040 {
00041     public:
00042         Private()
00043          : dataAwareObject(0)
00044          , orderedFocusWidgetsIterator(orderedFocusWidgets)
00045          , autoTabStops(false)
00046         {
00047         }
00048         KexiDataAwareObjectInterface* dataAwareObject;
00050         QPtrList<QWidget> orderedFocusWidgets; 
00052         QPtrList<QWidget> orderedDataAwareWidgets;
00053         QMap<KexiDataItemInterface*, uint> indicesForDataAwareWidgets; 
00054         QPtrListIterator<QWidget> orderedFocusWidgetsIterator;
00055         QPixmap buffer; 
00056         QRect prev_rect; 
00057         bool autoTabStops : 1;
00058 };
00059 
00060 //========================
00061 
00062 KexiDBForm::KexiDBForm(QWidget *parent, KexiDataAwareObjectInterface* dataAwareObject, 
00063     const char *name/*, KexiDB::Connection *conn*/)
00064  : KexiDBFormBase(parent, name)
00065  , KexiFormDataItemInterface()
00066  , d(new Private())
00067 {
00068     installEventFilter(this);
00069 //test  setDisplayMode( KexiGradientWidget::SimpleGradient );
00070     editedItem = 0;
00071     d->dataAwareObject = dataAwareObject;
00072     m_hasFocusableWidget = false;
00073 
00074 //  setFocusPolicy(NoFocus);
00075 
00076     //m_conn = conn;
00077     kexipluginsdbg << "KexiDBForm::KexiDBForm(): " << endl;
00078     setCursor(QCursor(Qt::ArrowCursor)); //to avoid keeping Size cursor when moving from form's boundaries
00079     setAcceptDrops( true );
00080 }
00081 
00082 KexiDBForm::~KexiDBForm()
00083 {
00084     kexipluginsdbg << "KexiDBForm::~KexiDBForm(): close" << endl;
00085     delete d;
00086 }
00087 
00088 KexiDataAwareObjectInterface* KexiDBForm::dataAwareObject() const { return d->dataAwareObject; }
00089 
00090 //repaint all children widgets
00091 static void repaintAll(QWidget *w)
00092 {
00093     QObjectList *list = w->queryList("QWidget");
00094     QObjectListIt it(*list);
00095     for (QObject *obj; (obj=it.current()); ++it ) {
00096         static_cast<QWidget*>(obj)->repaint();
00097     }
00098     delete list;
00099 }
00100 
00101 void
00102 KexiDBForm::drawRect(const QRect& r, int type)
00103 {
00104     QValueList<QRect> l;
00105     l.append(r);
00106     drawRects(l, type);
00107 }
00108 
00109 void
00110 KexiDBForm::drawRects(const QValueList<QRect> &list, int type)
00111 {
00112     QPainter p;
00113     p.begin(this, true);
00114     bool unclipped = testWFlags( WPaintUnclipped );
00115     setWFlags( WPaintUnclipped );
00116 
00117     if (d->prev_rect.isValid()) {
00118         //redraw prev. selection's rectangle
00119         p.drawPixmap( QPoint(d->prev_rect.x()-2, d->prev_rect.y()-2), d->buffer, 
00120             QRect(d->prev_rect.x()-2, d->prev_rect.y()-2, d->prev_rect.width()+4, d->prev_rect.height()+4));
00121     }
00122     p.setBrush(QBrush::NoBrush);
00123     if(type == 1) // selection rect
00124         p.setPen(QPen(white, 1, Qt::DotLine));
00125     else if(type == 2) // insert rect
00126         p.setPen(QPen(white, 2));
00127     p.setRasterOp(XorROP);
00128 
00129     d->prev_rect = QRect();
00130     QValueList<QRect>::ConstIterator endIt = list.constEnd();
00131     for(QValueList<QRect>::ConstIterator it = list.constBegin(); it != endIt; ++it) {
00132         p.drawRect(*it);
00133         if (d->prev_rect.isValid())
00134             d->prev_rect = d->prev_rect.unite(*it);
00135         else
00136             d->prev_rect = *it;
00137     }
00138 
00139     if (!unclipped)
00140         clearWFlags( WPaintUnclipped );
00141     p.end();
00142 }
00143 
00144 void
00145 KexiDBForm::initBuffer()
00146 {
00147     repaintAll(this);
00148     d->buffer.resize( width(), height() );
00149     d->buffer = QPixmap::grabWindow( winId() );
00150     d->prev_rect = QRect();
00151 }
00152 
00153 void
00154 KexiDBForm::clearForm()
00155 {
00156     QPainter p;
00157     p.begin(this, true);
00158     bool unclipped = testWFlags( WPaintUnclipped );
00159     setWFlags( WPaintUnclipped );
00160 
00161     //redraw entire form surface
00162     p.drawPixmap( QPoint(0,0), d->buffer, QRect(0,0,d->buffer.width(), d->buffer.height()) );
00163 
00164     if (!unclipped)
00165         clearWFlags( WPaintUnclipped );
00166     p.end();
00167 
00168     repaintAll(this);
00169 }
00170 
00171 void
00172 KexiDBForm::highlightWidgets(QWidget *from, QWidget *to)//, const QPoint &point)
00173 {
00174     QPoint fromPoint, toPoint;
00175     if(from && from->parentWidget() && (from != this))
00176         fromPoint = from->parentWidget()->mapTo(this, from->pos());
00177     if(to && to->parentWidget() && (to != this))
00178         toPoint = to->parentWidget()->mapTo(this, to->pos());
00179 
00180     QPainter p;
00181     p.begin(this, true);
00182     bool unclipped = testWFlags( WPaintUnclipped );
00183     setWFlags( WPaintUnclipped );
00184 
00185     if (d->prev_rect.isValid()) {
00186         //redraw prev. selection's rectangle
00187         p.drawPixmap( QPoint(d->prev_rect.x(), d->prev_rect.y()), d->buffer, 
00188             QRect(d->prev_rect.x(), d->prev_rect.y(), d->prev_rect.width(), d->prev_rect.height()));
00189     }
00190 
00191     p.setPen( QPen(Qt::red, 2) );
00192 
00193     if(to)
00194     {
00195         QPixmap pix1 = QPixmap::grabWidget(from);
00196         QPixmap pix2 = QPixmap::grabWidget(to);
00197 
00198         if((from != this) && (to != this))
00199             p.drawLine( from->parentWidget()->mapTo(this, from->geometry().center()), to->parentWidget()->mapTo(this, to->geometry().center()) );
00200 
00201         p.drawPixmap(fromPoint.x(), fromPoint.y(), pix1);
00202         p.drawPixmap(toPoint.x(), toPoint.y(), pix2);
00203 
00204         if(to == this)
00205             p.drawRoundRect(2, 2, width()-4, height()-4, 4, 4);
00206         else
00207             p.drawRoundRect(toPoint.x(), toPoint.y(), to->width(), to->height(), 5, 5);
00208     }
00209 
00210     if(from == this)
00211         p.drawRoundRect(2, 2, width()-4, height()-4, 4, 4);
00212     else
00213         p.drawRoundRect(fromPoint.x(),  fromPoint.y(), from->width(), from->height(), 5, 5);
00214 
00215     if((to == this) || (from == this))
00216         d->prev_rect = QRect(0, 0, d->buffer.width(), d->buffer.height());
00217     else if(to)
00218     {
00219         d->prev_rect.setX( (fromPoint.x() < toPoint.x()) ? (fromPoint.x() - 5) : (toPoint.x() - 5) );
00220         d->prev_rect.setY( (fromPoint.y() < toPoint.y()) ? (fromPoint.y() - 5) : (toPoint.y() - 5) );
00221         d->prev_rect.setRight( (fromPoint.x() < toPoint.x()) ? (toPoint.x() + to->width() + 10) : (fromPoint.x() + from->width() + 10) );
00222         d->prev_rect.setBottom( (fromPoint.y() < toPoint.y()) ? (toPoint.y() + to->height() + 10) : (fromPoint.y() + from->height() + 10) ) ;
00223     }
00224     else
00225         d->prev_rect = QRect(fromPoint.x()- 5,  fromPoint.y() -5, from->width() + 10, from->height() + 10);
00226 
00227     if (!unclipped)
00228         clearWFlags( WPaintUnclipped );
00229     p.end();
00230 }
00231 
00232 QSize
00233 KexiDBForm::sizeHint() const
00234 {
00235     //todo: find better size (user configured?)
00236     return QSize(400,300);
00237 }
00238 
00239 void KexiDBForm::setInvalidState( const QString& displayText )
00240 {
00241     Q_UNUSED( displayText );
00242 
00244 }
00245 
00246 bool KexiDBForm::autoTabStops() const
00247 {
00248     return d->autoTabStops;
00249 }
00250 
00251 void KexiDBForm::setAutoTabStops(bool set)
00252 {
00253     d->autoTabStops = set;
00254 }
00255 
00256 QPtrList<QWidget>* KexiDBForm::orderedFocusWidgets() const
00257 {
00258     return &d->orderedFocusWidgets;
00259 }
00260 
00261 QPtrList<QWidget>* KexiDBForm::orderedDataAwareWidgets() const
00262 {
00263     return &d->orderedDataAwareWidgets;
00264 }
00265 
00266 void KexiDBForm::updateTabStopsOrder(KFormDesigner::Form* form)
00267 {
00268     QWidget *fromWidget = 0;
00269     //QWidget *topLevelWidget = form->widget()->topLevelWidget();
00270 //js    form->updateTabStopsOrder(); //certain widgets can have now updated focusPolicy properties, fix this
00271     uint numberOfDataAwareWidgets = 0;
00272 //  if (d->orderedFocusWidgets.isEmpty()) {
00273         //generate a new list
00274         for (KFormDesigner::ObjectTreeListIterator it(form->tabStopsIterator()); it.current(); ++it) {
00275             if (it.current()->widget()->focusPolicy() & QWidget::TabFocus) {
00276                 //this widget has tab focus:
00277                 it.current()->widget()->installEventFilter(this);
00278                 //also filter events for data-aware children of this widget (i.e. KexiDBAutoField's editors)
00279                 QObjectList *children = it.current()->widget()->queryList("QWidget");
00280                 for (QObjectListIt childrenIt(*children); childrenIt.current(); ++childrenIt) {
00281                     if (dynamic_cast<KexiFormDataItemInterface*>(childrenIt.current())) {
00282                         kexipluginsdbg << "KexiDBForm::updateTabStopsOrder(): also adding '" << childrenIt.current()->className() << " " << childrenIt.current()->name()  << "' child to filtered widgets" << endl;
00283                         it.current()->widget()->installEventFilter(static_cast<QWidget*>(childrenIt.current()));
00284                     }
00285                 }
00286                 delete children;
00287                 if (fromWidget) {
00288                     kexipluginsdbg << "KexiDBForm::updateTabStopsOrder() tab order: " << fromWidget->name() 
00289                         << " -> " << it.current()->widget()->name() << endl;
00290     //              setTabOrder( fromWidget, it.current()->widget() );
00291                 }
00292                 fromWidget = it.current()->widget();
00293                 d->orderedFocusWidgets.append( it.current()->widget() );
00294             }
00295 
00296             KexiFormDataItemInterface* dataItem = dynamic_cast<KexiFormDataItemInterface*>( it.current()->widget() );
00297             if (dataItem && !dataItem->dataSource().isEmpty()) {
00298                 kexipluginsdbg << "#" << numberOfDataAwareWidgets << ": " 
00299                     << dataItem->dataSource() << " (" << it.current()->widget()->name() << ")" << endl;
00300 //  /*! @todo d->indicesForDataAwareWidgets SHOULDNT BE UPDATED HERE BECAUSE
00301 //  THERE CAN BE ALSO NON-TABSTOP DATA WIDGETS!
00302 //  */
00303                 d->indicesForDataAwareWidgets.replace( 
00304                     dataItem, 
00305                     numberOfDataAwareWidgets );
00306                 numberOfDataAwareWidgets++;
00307 
00308                 d->orderedDataAwareWidgets.append( it.current()->widget() );
00309             }
00310         }//for
00311 //  }
00312 /*  else {
00313         //restore ordering
00314         for (QPtrListIterator<QWidget> it(d->orderedFocusWidgets); it.current(); ++it) {
00315             if (fromWidget) {
00316                 kdDebug() << "KexiDBForm::updateTabStopsOrder() tab order: " << fromWidget->name() 
00317                     << " -> " << it.current()->name() << endl;
00318                 setTabOrder( fromWidget, it.current() );
00319             }
00320             fromWidget = it.current();
00321         }
00322 //      SET_FOCUS_USING_REASON(focusWidget(), QFocusEvent::Tab);
00323     }*/
00324 }
00325 
00326 void KexiDBForm::updateTabStopsOrder()
00327 {
00328     for (QPtrListIterator<QWidget> it( d->orderedFocusWidgets ); it.current();) {
00329         if (! (it.current()->focusPolicy() & QWidget::TabFocus))
00330             d->orderedFocusWidgets.remove( it.current() );
00331         else
00332             ++it;
00333     }
00334 }
00335 
00336 bool KexiDBForm::eventFilter( QObject * watched, QEvent * e )
00337 {
00338     if (e->type()==QEvent::Resize && watched == this)
00339         kexipluginsdbg << "RESIZE" << endl;
00340     if (e->type()==QEvent::KeyPress) {
00341         if (preview()) {
00342             QKeyEvent *ke = static_cast<QKeyEvent*>(e);
00343             if (ke->state() == Qt::NoButton && ke->key() == Qt::Key_Escape) {
00344                 //cancel field editing/row editing
00345                 if (d->dataAwareObject->editor()) {
00346                     d->dataAwareObject->cancelEditor();
00347                 }
00348                 else if (d->dataAwareObject->rowEditing()) {
00349                     d->dataAwareObject->cancelRowEdit();
00350                 }
00351                 ke->accept();
00352                 return true;
00353             }
00354             // jstaniek: Fix for Qt bug (handling e.g. Alt+2, Ctrl+2 keys on every platform)
00355             //           It's important because we're using alt+2 short cut by default
00356             //           Damn! I've reported this to Trolltech in November 2004 - still not fixed.
00357             if (ke->isAccepted() && (ke->state() & Qt::AltButton) && ke->text()>="0" && ke->text()<="9")
00358                 return true;
00359             const bool tab = ke->state() == Qt::NoButton && ke->key() == Qt::Key_Tab;
00360             const bool backtab = ((ke->state() == Qt::NoButton || ke->state() == Qt::ShiftButton) && ke->key() == Qt::Key_Backtab)
00361                 || (ke->state() == Qt::ShiftButton && ke->key() == Qt::Key_Tab);
00362 
00363             if (tab || backtab) {
00364                 if (d->orderedFocusWidgetsIterator.current() != static_cast<QWidget*>(watched)) {
00365                     d->orderedFocusWidgetsIterator.toFirst();
00366                     while (d->orderedFocusWidgetsIterator.current() && d->orderedFocusWidgetsIterator.current()!=static_cast<QWidget*>(watched)) {
00367                         //QWidget *ww = d->orderedFocusWidgetsIterator.current();
00368                         ++d->orderedFocusWidgetsIterator;
00369                     }
00370                 }
00371                 kexipluginsdbg << watched->name() << endl;
00372                 if (tab) {
00373                     if (d->orderedFocusWidgets.first() && watched == d->orderedFocusWidgets.last()) {
00374                         d->orderedFocusWidgetsIterator.toFirst();
00375                     }
00376                     else if (watched == d->orderedFocusWidgetsIterator.current()) {
00377 /*  QEvent fe( QEvent::FocusOut );
00378     QFocusEvent::setReason(QFocusEvent::Tab);
00379     QApplication::sendEvent( d->orderedFocusWidgetsIterator.current(), &fe );
00380     QFocusEvent::resetReason();*/
00381                         ++d->orderedFocusWidgetsIterator; //next
00382                     }
00383                     else
00384                         return true; //ignore
00385                     //set focus, but don't use just setFocus() because certain widgets
00386                     //behaves differently (e.g. QLineEdit calls selectAll()) when 
00387                     //focus event's reason is QFocusEvent::Tab
00388                     SET_FOCUS_USING_REASON(d->orderedFocusWidgetsIterator.current(), QFocusEvent::Tab);
00389                     kexipluginsdbg << "focusing " << d->orderedFocusWidgetsIterator.current()->name() << endl;
00390                     return true;
00391                 } else if (backtab) {
00392                     if (d->orderedFocusWidgets.last() && watched == d->orderedFocusWidgets.first()) {
00393                         d->orderedFocusWidgetsIterator.toLast();
00394                     }
00395                     else if (watched == d->orderedFocusWidgetsIterator.current()) {
00396                         --d->orderedFocusWidgetsIterator; //prev
00397                     }
00398                     else
00399                         return true; //ignore
00400                     //set focus, see above note
00401                     SET_FOCUS_USING_REASON(d->orderedFocusWidgetsIterator.current(), QFocusEvent::Backtab);
00402                     kexipluginsdbg << "focusing " << d->orderedFocusWidgetsIterator.current()->name() << endl;
00403                     return true;
00404                 }
00405             }
00406         }
00407     }
00408     else if (e->type()==QEvent::FocusIn) {
00409         if (preview()) {
00410             if (dynamic_cast<KexiDataItemInterface*>(watched) && d->dataAwareObject) {
00411                 uint index = d->indicesForDataAwareWidgets[ dynamic_cast<KexiDataItemInterface*>(watched) ];
00412                 kexipluginsdbg << "KexiDBForm: moving cursor to column #" << index << endl;
00413                 editedItem = 0;
00414                 if ((int)index!=d->dataAwareObject->currentColumn()) {
00415                     d->dataAwareObject->setCursorPosition( d->dataAwareObject->currentRow(), index /*column*/ );
00416                 }
00417             }
00418         }
00419     }
00420     return KexiDBFormBase::eventFilter(watched, e);
00421 }
00422 
00423 bool KexiDBForm::valueIsNull()
00424 {
00425     return true;
00426 }
00427 
00428 bool KexiDBForm::valueIsEmpty()
00429 {
00430     return true;
00431 }
00432 
00433 bool KexiDBForm::isReadOnly() const
00434 {
00435     if (d->dataAwareObject)
00436         return d->dataAwareObject->isReadOnly();
00438     return false;
00439 }
00440 
00441 QWidget* KexiDBForm::widget()
00442 {
00443     return this;
00444 }
00445 
00446 bool KexiDBForm::cursorAtStart()
00447 {
00448     return false;
00449 }
00450 
00451 bool KexiDBForm::cursorAtEnd()
00452 {
00453     return false;
00454 }
00455 
00456 void KexiDBForm::clear()
00457 {
00459 }
00460 
00461 bool KexiDBForm::preview() const {
00462     return dynamic_cast<KexiScrollView*>(d->dataAwareObject)
00463         ? dynamic_cast<KexiScrollView*>(d->dataAwareObject)->preview() : false;
00464 }
00465 
00466 int KexiDBForm::indexForDataItem( KexiDataItemInterface* item ) const
00467 {
00468     if (!item)
00469         return -1;
00470     QMapConstIterator<KexiDataItemInterface*, uint> it = d->indicesForDataAwareWidgets.find( item );
00471     if (it==d->indicesForDataAwareWidgets.constEnd())
00472         return -1;
00473     return it.data();
00474 }
00475 
00476 void KexiDBForm::dragMoveEvent( QDragMoveEvent *e )
00477 {
00478     emit handleDragMoveEvent(e);
00479 }
00480 
00481 void KexiDBForm::dropEvent( QDropEvent *e ) 
00482 {
00483     emit handleDropEvent(e);
00484 }
00485 
00486 void KexiDBForm::setCursor( const QCursor & cursor )
00487 {
00488     //js: empty, to avoid fscking problems with random cursors!
00490 
00491     if (KFormDesigner::FormManager::self()->isInserting()) //exception
00492         KexiDBFormBase::setCursor(cursor);
00493 }
00494 
00496 /*
00497 void KexiDBForm::paintEvent( QPaintEvent *e )
00498 {
00499     QPainter p;
00500     p.begin(this, true);
00501     bool unclipped = testWFlags( WPaintUnclipped );
00502     setWFlags( WPaintUnclipped );
00503 
00504     p.setPen(white);
00505     p.setRasterOp(XorROP);
00506     p.drawLine(e->rect().topLeft(), e->rect().bottomRight());
00507 
00508     if (!unclipped)
00509         clearWFlags( WPaintUnclipped );
00510     p.end();
00511     KexiDBFormBase::paintEvent(e);
00512 }
00513 */
00514 
00515 #include "kexidbform.moc"
KDE Home | KDE Accessibility Home | Description of Access Keys