source: trunk/src/sql/qdatatable.cpp

Last change on this file was 196, checked in by rudi, 14 years ago

Add SQL module (currently it isn't build by default, however it's needed for QtDesigner)

File size: 64.6 KB
Line 
1/****************************************************************************
2**
3** Implementation of QDataTable class
4**
5** Created : 2000-11-03
6**
7** Copyright (C) 2005-2007 Trolltech ASA. All rights reserved.
8**
9** This file is part of the sql module of the Qt GUI Toolkit.
10**
11** This file may be distributed under the terms of the Q Public License
12** as defined by Trolltech ASA of Norway and appearing in the file
13** LICENSE.QPL included in the packaging of this file.
14**
15** This file may be distributed and/or modified under the terms of the
16** GNU General Public License version 2 as published by the Free Software
17** Foundation and appearing in the file LICENSE.GPL included in the
18** packaging of this file.
19**
20** Licensees holding valid Qt Enterprise Edition licenses may use this
21** file in accordance with the Qt Commercial License Agreement provided
22** with the Software.
23**
24** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
25** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
26**
27** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for
28** information about Qt Commercial License Agreements.
29** See http://www.trolltech.com/qpl/ for QPL licensing information.
30** See http://www.trolltech.com/gpl/ for GPL licensing information.
31**
32** Contact info@trolltech.com if any conditions of this licensing are
33** not clear to you.
34**
35**********************************************************************/
36
37#include "qdatatable.h"
38
39#ifndef QT_NO_SQL_VIEW_WIDGETS
40
41#include "qsqldriver.h"
42#include "qsqleditorfactory.h"
43#include "qsqlpropertymap.h"
44#include "qapplication.h"
45#include "qlayout.h"
46#include "qpainter.h"
47#include "qpopupmenu.h"
48#include "qvaluelist.h"
49#include "qsqlmanager_p.h"
50#include "qdatetime.h"
51#include "qcursor.h"
52#include "qtimer.h"
53
54//#define QT_DEBUG_DATATABLE
55
56class QDataTablePrivate
57{
58public:
59 QDataTablePrivate()
60 : nullTxtChanged( FALSE ),
61 haveAllRows( FALSE ),
62 continuousEdit( FALSE ),
63 editorFactory( 0 ),
64 propertyMap( 0 ),
65 editRow( -1 ),
66 editCol( -1 ),
67 insertRowLast( -1 ),
68 insertPreRows( -1 ),
69 editBuffer( 0 ),
70 cancelMode( FALSE ),
71 cancelInsert( FALSE ),
72 cancelUpdate( FALSE )
73 {}
74 ~QDataTablePrivate() { if ( propertyMap ) delete propertyMap; }
75
76 QString nullTxt;
77 bool nullTxtChanged;
78 typedef QValueList< uint > ColIndex;
79 ColIndex colIndex;
80 bool haveAllRows;
81 bool continuousEdit;
82 QSqlEditorFactory* editorFactory;
83 QSqlPropertyMap* propertyMap;
84 QString trueTxt;
85 Qt::DateFormat datefmt;
86 QString falseTxt;
87 int editRow;
88 int editCol;
89 int insertRowLast;
90 QString insertHeaderLabelLast;
91 int insertPreRows;
92 QSqlRecord* editBuffer;
93 bool cancelMode;
94 bool cancelInsert;
95 bool cancelUpdate;
96 int lastAt;
97 QString ftr;
98 QStringList srt;
99 QStringList fld;
100 QStringList fldLabel;
101 QValueList<int> fldWidth;
102 QValueList<QIconSet> fldIcon;
103 QValueList<bool> fldHidden;
104 QSqlCursorManager cur;
105 QDataManager dat;
106};
107
108#ifdef QT_DEBUG_DATATABLE
109void qt_debug_buffer( const QString& msg, QSqlRecord* cursor )
110{
111 qDebug("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++");
112 qDebug(msg);
113 for ( uint j = 0; j < cursor->count(); ++j ) {
114 qDebug(cursor->field(j)->name() + " type:" + QString(cursor->field(j)->value().typeName()) + " value:" + cursor->field(j)->value().toString() );
115 }
116}
117#endif
118
119/*!
120 \enum QDataTable::Refresh
121
122 This enum describes the refresh options.
123
124 \value RefreshData refresh the data, i.e. read it from the database
125 \value RefreshColumns refresh the list of fields, e.g. the column headings
126 \value RefreshAll refresh both the data and the list of fields
127*/
128
129
130/*!
131 \class QDataTable qdatatable.h
132 \brief The QDataTable class provides a flexible SQL table widget that supports browsing and editing.
133
134 \ingroup database
135 \mainclass
136 \module sql
137
138 QDataTable supports various functions for presenting and editing
139 SQL data from a \l QSqlCursor in a table.
140
141 If you want a to present your data in a form use QDataBrowser, or
142 for read-only forms, QDataView.
143
144 When displaying data, QDataTable only retrieves data for visible
145 rows. If the driver supports the 'query size' property the
146 QDataTable will have the correct number of rows and the vertical
147 scrollbar will accurately reflect the number of rows displayed in
148 proportion to the number of rows in the dataset. If the driver
149 does not support the 'query size' property, rows are dynamically
150 fetched from the database on an as-needed basis with the scrollbar
151 becoming more accurate as the user scrolls down through the
152 records. This allows extremely large queries to be displayed as
153 quickly as possible, with minimum memory usage.
154
155 QDataTable inherits QTable's API and extends it with functions to
156 sort and filter the data and sort columns. See setSqlCursor(),
157 setFilter(), setSort(), setSorting(), sortColumn() and refresh().
158
159 When displaying editable cursors, cell editing will be enabled.
160 (For more information on editable cursors, see \l QSqlCursor).
161 QDataTable can be used to modify existing data and to add new
162 records. When a user makes changes to a field in the table, the
163 cursor's edit buffer is used. The table will not send changes in
164 the edit buffer to the database until the user moves to a
165 different record in the grid or presses Enter. Cell editing is
166 initiated by pressing F2 (or right clicking and then clicking the
167 appropriate popup menu item) and canceled by pressing Esc. If
168 there is a problem updating or adding data, errors are handled
169 automatically (see handleError() to change this behavior). Note
170 that if autoEdit() is FALSE navigating to another record will
171 cancel the insert or update.
172
173 The user can be asked to confirm all edits with setConfirmEdits().
174 For more precise control use setConfirmInsert(),
175 setConfirmUpdate(), setConfirmDelete() and setConfirmCancels().
176 Use setAutoEdit() to control the behaviour of the table when the
177 user edits a record and then navigates. (Note that setAutoDelete()
178 is unrelated; it is used to set whether the QSqlCursor is deleted
179 when the table is deleted.)
180
181 Since the data table can perform edits, it must be able to
182 uniquely identify every record so that edits are correctly
183 applied. Because of this the underlying cursor must have a valid
184 primary index to ensure that a unique record is inserted, updated
185 or deleted within the database otherwise the database may be
186 changed to an inconsistent state.
187
188 QDataTable creates editors using the default \l QSqlEditorFactory.
189 Different editor factories can be used by calling
190 installEditorFactory(). A property map is used to map between the
191 cell's value and the editor. You can use your own property map
192 with installPropertyMap().
193
194 The contents of a cell is available as a QString with text() or as
195 a QVariant with value(). The current record is returned by
196 currentRecord(). Use the find() function to search for a string in
197 the table.
198
199 Editing actions can be applied programatically. For example, the
200 insertCurrent() function reads the fields from the current record
201 into the cursor and performs the insert. The updateCurrent() and
202 deleteCurrent() functions perform similarly to update and delete
203 the current record respectively.
204
205 Columns in the table can be created automatically based on the
206 cursor (see setSqlCursor()). Columns can be manipulated manually
207 using addColumn(), removeColumn() and setColumn().
208
209 The table automatically copies many of the properties of the
210 cursor to format the display of data within cells (alignment,
211 visibility, etc.). The cursor can be changed with setSqlCursor().
212 The filter (see setFilter()) and sort defined within the table are
213 used instead of the filter and sort set on the cursor. For sorting
214 options see setSort(), sortColumn(), sortAscending() and
215 sortDescending(). Note that sorting operations will not behave as
216 expected if you are using a QSqlSelectCursor because it uses
217 user-defined SQL queries to obtain data.
218
219 The text used to represent NULL, TRUE and FALSE values can be
220 changed with setNullText(), setTrueText() and setFalseText()
221 respectively. You can change the appearance of cells by
222 reimplementing paintField().
223
224 Whenever a new row is selected in the table the currentChanged()
225 signal is emitted. The primeInsert() signal is emitted when an
226 insert is initiated. The primeUpdate() and primeDelete() signals
227 are emitted when update and deletion are initiated respectively.
228 Just before the database is updated a signal is emitted;
229 beforeInsert(), beforeUpdate() or beforeDelete() as appropriate.
230
231*/
232
233/*!
234 Constructs a data table which is a child of \a parent, called
235 name \a name.
236*/
237
238QDataTable::QDataTable ( QWidget * parent, const char * name )
239 : QTable( parent, name )
240{
241 init();
242}
243
244/*!
245 Constructs a data table which is a child of \a parent, called name
246 \a name using the cursor \a cursor.
247
248 If \a autoPopulate is TRUE (the default is FALSE), columns are
249 automatically created based upon the fields in the \a cursor
250 record. Note that \a autoPopulate only governs the creation of
251 columns; to load the cursor's data into the table use refresh().
252
253 If the \a cursor is read-only, the table also becomes read-only.
254 In addition, the table adopts the cursor's driver's definition for
255 representing NULL values as strings.
256*/
257
258QDataTable::QDataTable ( QSqlCursor* cursor, bool autoPopulate, QWidget * parent, const char * name )
259 : QTable( parent, name )
260{
261 init();
262 setSqlCursor( cursor, autoPopulate );
263}
264
265/*! \internal
266*/
267
268
269void QDataTable::init()
270{
271 d = new QDataTablePrivate();
272 setAutoEdit( TRUE );
273 setSelectionMode( SingleRow );
274 setFocusStyle( FollowStyle );
275 d->trueTxt = tr( "True" );
276 d->falseTxt = tr( "False" );
277 d->datefmt = Qt::LocalDate;
278 reset();
279 connect( this, SIGNAL( selectionChanged() ),
280 SLOT( updateCurrentSelection()));
281}
282
283/*!
284 Destroys the object and frees any allocated resources.
285*/
286
287QDataTable::~QDataTable()
288{
289 delete d;
290}
291
292
293/*!
294 Adds the next column to be displayed using the field \a fieldName,
295 column label \a label, width \a width and iconset \a iconset.
296
297 If \a label is specified, it is used as the column's header label,
298 otherwise the field's display label is used when setSqlCursor() is
299 called. The \a iconset is used to set the icon used by the column
300 header; by default there is no icon.
301
302 \sa setSqlCursor() refresh()
303*/
304
305void QDataTable::addColumn( const QString& fieldName,
306 const QString& label,
307 int width,
308 const QIconSet& iconset )
309{
310 d->fld += fieldName;
311 d->fldLabel += label;
312 d->fldIcon += iconset;
313 d->fldWidth += width;
314 d->fldHidden += FALSE;
315}
316
317/*!
318 Sets the \a col column to display using the field \a fieldName,
319 column label \a label, width \a width and iconset \a iconset.
320
321 If \a label is specified, it is used as the column's header label,
322 otherwise the field's display label is used when setSqlCursor() is
323 called. The \a iconset is used to set the icon used by the column
324 header; by default there is no icon.
325
326 \sa setSqlCursor() refresh()
327*/
328
329void QDataTable::setColumn( uint col, const QString& fieldName,
330 const QString& label,
331 int width,
332 const QIconSet& iconset )
333{
334 d->fld[col]= fieldName;
335 d->fldLabel[col] = label;
336 d->fldIcon[col] = iconset;
337 d->fldWidth[col] = width;
338 d->fldHidden[col] = FALSE;
339}
340
341/*!
342 Removes column \a col from the list of columns to be displayed. If
343 \a col does not exist, nothing happens.
344
345 \sa QSqlField
346*/
347
348void QDataTable::removeColumn( uint col )
349{
350 if ( d->fld.at( col ) != d->fld.end() ) {
351 d->fld.remove( d->fld.at( col ) );
352 d->fldLabel.remove( d->fldLabel.at( col ) );
353 d->fldIcon.remove( d->fldIcon.at( col ) );
354 d->fldWidth.remove( d->fldWidth.at( col ) );
355 d->fldHidden.remove( d->fldHidden.at( col ) );
356 }
357}
358
359/*!
360 Sets the column \a col to the width \a w. Note that unlike QTable
361 the QDataTable is not immediately redrawn, you must call
362 refresh(QDataTable::RefreshColumns)
363 yourself.
364
365 \sa refresh()
366*/
367void QDataTable::setColumnWidth( int col, int w )
368{
369 if ( d->fldWidth.at( col ) != d->fldWidth.end() ) {
370 d->fldWidth[col] = w;
371 }
372}
373
374/*!
375 Resizes column \a col so that the column width is wide enough to
376 display the widest item the column contains (including the column
377 label). If the table's QSqlCursor is not currently active, the
378 cursor will be refreshed before the column width is calculated. Be
379 aware that this function may be slow on tables that contain large
380 result sets.
381*/
382void QDataTable::adjustColumn( int col )
383{
384 QSqlCursor * cur = sqlCursor();
385 if ( !cur || cur->count() <= (uint)col )
386 return;
387 if ( !cur->isActive() ) {
388 d->cur.refresh();
389 }
390 int oldRow = currentRow();
391 int w = fontMetrics().width( horizontalHeader()->label( col ) + "W" );
392 cur->seek( QSql::BeforeFirst );
393 while ( cur->next() ) {
394 w = QMAX( w, fontMetrics().width( fieldToString( cur->field( indexOf( col ) ) ) ) + 10 );
395 }
396 setColumnWidth( col, w );
397 cur->seek( oldRow );
398 refresh( RefreshColumns );
399}
400
401/*! \reimp
402*/
403void QDataTable::setColumnStretchable( int col, bool s )
404{
405 if ( numCols() == 0 ) {
406 refresh( RefreshColumns );
407 }
408 if ( numCols() > col ) {
409 QTable::setColumnStretchable( col, s );
410 }
411}
412
413QString QDataTable::filter() const
414{
415 return d->cur.filter();
416}
417
418/*!
419 \property QDataTable::filter
420 \brief the data filter for the data table
421
422 The filter applies to the data shown in the table. To view data
423 with a new filter, use refresh(). A filter string is an SQL WHERE
424 clause without the WHERE keyword.
425
426 There is no default filter.
427
428 \sa sort()
429
430*/
431
432void QDataTable::setFilter( const QString& filter )
433{
434 d->cur.setFilter( filter );
435}
436
437
438/*!
439 \property QDataTable::sort
440 \brief the data table's sort
441
442 The table's sort affects the order in which data records are
443 displayed in the table. To apply a sort, use refresh().
444
445 When examining the sort property, a string list is returned with
446 each item having the form 'fieldname order' (e.g., 'id ASC',
447 'surname DESC').
448
449 There is no default sort.
450
451 Note that if you want to iterate over the sort list, you should
452 iterate over a copy, e.g.
453 \code
454 QStringList list = myDataTable.sort();
455 QStringList::Iterator it = list.begin();
456 while( it != list.end() ) {
457 myProcessing( *it );
458 ++it;
459 }
460 \endcode
461
462 \sa filter() refresh()
463*/
464
465void QDataTable::setSort( const QStringList& sort )
466{
467 d->cur.setSort( sort );
468}
469
470/*!
471 \overload
472
473 Sets the sort to be applied to the displayed data to \a sort. If
474 there is no current cursor, nothing happens. A QSqlIndex contains
475 field names and their ordering (ASC or DESC); these are used to
476 compose the ORDER BY clause.
477
478 \sa sort()
479*/
480
481void QDataTable::setSort( const QSqlIndex& sort )
482{
483 d->cur.setSort( sort );
484}
485
486QStringList QDataTable::sort() const
487{
488 return d->cur.sort();
489}
490
491/*!
492 Returns the cursor used by the data table.
493*/
494
495QSqlCursor* QDataTable::sqlCursor() const
496{
497 return d->cur.cursor();
498}
499
500void QDataTable::setConfirmEdits( bool confirm )
501{
502 d->dat.setConfirmEdits( confirm );
503}
504
505void QDataTable::setConfirmInsert( bool confirm )
506{
507 d->dat.setConfirmInsert( confirm );
508}
509
510void QDataTable::setConfirmUpdate( bool confirm )
511{
512 d->dat.setConfirmUpdate( confirm );
513}
514
515void QDataTable::setConfirmDelete( bool confirm )
516{
517 d->dat.setConfirmDelete( confirm );
518}
519
520/*!
521 \property QDataTable::confirmEdits
522 \brief whether the data table confirms edit operations
523
524 If the confirmEdits property is TRUE, the data table confirms all
525 edit operations (inserts, updates and deletes). Finer control of
526 edit confirmation can be achieved using \l confirmCancels, \l
527 confirmInsert, \l confirmUpdate and \l confirmDelete.
528
529 \sa confirmCancels() confirmInsert() confirmUpdate() confirmDelete()
530*/
531
532bool QDataTable::confirmEdits() const
533{
534 return ( d->dat.confirmEdits() );
535}
536
537/*!
538 \property QDataTable::confirmInsert
539 \brief whether the data table confirms insert operations
540
541 If the confirmInsert property is TRUE, all insertions must be
542 confirmed by the user through a message box (this behaviour can be
543 changed by overriding the confirmEdit() function), otherwise all
544 insert operations occur immediately.
545
546 \sa confirmCancels() confirmEdits() confirmUpdate() confirmDelete()
547*/
548
549bool QDataTable::confirmInsert() const
550{
551 return ( d->dat.confirmInsert() );
552}
553
554/*!
555 \property QDataTable::confirmUpdate
556 \brief whether the data table confirms update operations
557
558 If the confirmUpdate property is TRUE, all updates must be
559 confirmed by the user through a message box (this behaviour can be
560 changed by overriding the confirmEdit() function), otherwise all
561 update operations occur immediately.
562
563 \sa confirmCancels() confirmEdits() confirmInsert() confirmDelete()
564*/
565
566bool QDataTable::confirmUpdate() const
567{
568 return ( d->dat.confirmUpdate() );
569}
570
571/*!
572 \property QDataTable::confirmDelete
573 \brief whether the data table confirms delete operations
574
575 If the confirmDelete property is TRUE, all deletions must be
576 confirmed by the user through a message box (this behaviour can be
577 changed by overriding the confirmEdit() function), otherwise all
578 delete operations occur immediately.
579
580 \sa confirmCancels() confirmEdits() confirmUpdate() confirmInsert()
581*/
582
583bool QDataTable::confirmDelete() const
584{
585 return ( d->dat.confirmDelete() );
586}
587
588/*!
589 \property QDataTable::confirmCancels
590 \brief whether the data table confirms cancel operations
591
592 If the confirmCancel property is TRUE, all cancels must be
593 confirmed by the user through a message box (this behavior can be
594 changed by overriding the confirmCancel() function), otherwise all
595 cancels occur immediately. The default is FALSE.
596
597 \sa confirmEdits() confirmCancel()
598*/
599
600void QDataTable::setConfirmCancels( bool confirm )
601{
602 d->dat.setConfirmCancels( confirm );
603}
604
605bool QDataTable::confirmCancels() const
606{
607 return d->dat.confirmCancels();
608}
609
610/*!
611 \reimp
612
613 For an editable table, creates an editor suitable for the field in
614 column \a col. The editor is created using the default editor
615 factory, unless a different editor factory was installed with
616 installEditorFactory(). The editor is primed with the value of the
617 field in \a col using a property map. The property map used is the
618 default property map, unless a new property map was installed with
619 installPropertMap(). If \a initFromCell is TRUE then the editor is
620 primed with the value in the QDataTable cell.
621*/
622
623QWidget * QDataTable::createEditor( int , int col, bool initFromCell ) const
624{
625 if ( d->dat.mode() == QSql::None )
626 return 0;
627
628 QSqlEditorFactory * f = (d->editorFactory == 0) ?
629 QSqlEditorFactory::defaultFactory() : d->editorFactory;
630
631 QSqlPropertyMap * m = (d->propertyMap == 0) ?
632 QSqlPropertyMap::defaultMap() : d->propertyMap;
633
634 QWidget * w = 0;
635 if( initFromCell && d->editBuffer ){
636 w = f->createEditor( viewport(), d->editBuffer->field( indexOf( col ) ) );
637 if ( w )
638 m->setProperty( w, d->editBuffer->value( indexOf( col ) ) );
639 }
640 return w;
641}
642
643/*! \reimp */
644bool QDataTable::eventFilter( QObject *o, QEvent *e )
645{
646 if ( d->cancelMode )
647 return TRUE;
648
649 int r = currentRow();
650 int c = currentColumn();
651
652 if ( d->dat.mode() != QSql::None ) {
653 r = d->editRow;
654 c = d->editCol;
655 }
656
657 d->cancelInsert = FALSE;
658 d->cancelUpdate = FALSE;
659 switch ( e->type() ) {
660 case QEvent::KeyPress: {
661 int conf = QSql::Yes;
662 QKeyEvent *ke = (QKeyEvent*)e;
663 if ( ( ke->key() == Key_Tab || ke->key() == Qt::Key_BackTab )
664 && ke->state() & Qt::ControlButton )
665 return FALSE;
666
667 if ( ke->key() == Key_Escape && d->dat.mode() == QSql::Insert ){
668 if ( confirmCancels() && !d->cancelMode ) {
669 d->cancelMode = TRUE;
670 conf = confirmCancel( QSql::Insert );
671 d->cancelMode = FALSE;
672 }
673 if ( conf == QSql::Yes ) {
674 d->cancelInsert = TRUE;
675 } else {
676 QWidget *editorWidget = cellWidget( r, c );
677 if ( editorWidget ) {
678 editorWidget->setActiveWindow();
679 editorWidget->setFocus();
680 }
681 return TRUE;
682 }
683 }
684 if ( ke->key() == Key_Escape && d->dat.mode() == QSql::Update ) {
685 if ( confirmCancels() && !d->cancelMode ) {
686 d->cancelMode = TRUE;
687 conf = confirmCancel( QSql::Update );
688 d->cancelMode = FALSE;
689 }
690 if ( conf == QSql::Yes ){
691 d->cancelUpdate = TRUE;
692 } else {
693 QWidget *editorWidget = cellWidget( r, c );
694 if ( editorWidget ) {
695 editorWidget->setActiveWindow();
696 editorWidget->setFocus();
697 }
698 return TRUE;
699 }
700 }
701 if ( ke->key() == Key_Insert && d->dat.mode() == QSql::None ) {
702 beginInsert();
703 return TRUE;
704 }
705 if ( ke->key() == Key_Delete && d->dat.mode() == QSql::None ) {
706 deleteCurrent();
707 return TRUE;
708 }
709 if ( d->dat.mode() != QSql::None ) {
710 if ( (ke->key() == Key_Tab) && (c < numCols() - 1) && (!isColumnReadOnly( c+1 ) || d->dat.mode() == QSql::Insert) )
711 d->continuousEdit = TRUE;
712 else if ( (ke->key() == Key_BackTab) && (c > 0) && (!isColumnReadOnly( c-1 ) || d->dat.mode() == QSql::Insert) )
713 d->continuousEdit = TRUE;
714 else
715 d->continuousEdit = FALSE;
716 }
717 QSqlCursor * sql = sqlCursor();
718 if ( sql && sql->driver() &&
719 !sql->driver()->hasFeature( QSqlDriver::QuerySize ) &&
720 ke->key() == Key_End && d->dat.mode() == QSql::None ) {
721#ifndef QT_NO_CURSOR
722 QApplication::setOverrideCursor( Qt::WaitCursor );
723#endif
724 int i = sql->at();
725 if ( i < 0 ) {
726 i = 0;
727 sql->seek(0);
728 }
729 while ( sql->next() )
730 i++;
731 setNumRows( i+1 );
732 setCurrentCell( i+1, currentColumn() );
733#ifndef QT_NO_CURSOR
734 QApplication::restoreOverrideCursor();
735#endif
736 return TRUE;
737 }
738 break;
739 }
740 case QEvent::FocusOut: {
741 QWidget *editorWidget = cellWidget( r, c );
742 repaintCell( currentRow(), currentColumn() );
743 if ( !d->cancelMode && editorWidget && o == editorWidget &&
744 ( d->dat.mode() == QSql::Insert) && !d->continuousEdit) {
745 setCurrentCell( r, c );
746 d->cancelInsert = TRUE;
747 }
748 d->continuousEdit = FALSE;
749 break;
750 }
751 case QEvent::FocusIn:
752 repaintCell( currentRow(), currentColumn() );
753 break;
754 default:
755 break;
756 }
757 return QTable::eventFilter( o, e );
758}
759
760/*! \reimp */
761void QDataTable::resizeEvent ( QResizeEvent * e )
762{
763 if ( sqlCursor() &&
764 sqlCursor()->driver() &&
765 !sqlCursor()->driver()->hasFeature( QSqlDriver::QuerySize ) )
766 loadNextPage();
767 QTable::resizeEvent( e );
768}
769
770/*! \reimp */
771void QDataTable::contentsContextMenuEvent( QContextMenuEvent* e )
772{
773 QTable::contentsContextMenuEvent( e );
774 if ( isEditing() && d->dat.mode() != QSql::None )
775 endEdit( d->editRow, d->editCol, autoEdit(), FALSE );
776 if ( !sqlCursor() )
777 return;
778 if ( d->dat.mode() == QSql::None ) {
779 if ( isReadOnly() )
780 return;
781 enum {
782 IdInsert,
783 IdUpdate,
784 IdDelete
785 };
786 QGuardedPtr<QPopupMenu> popup = new QPopupMenu( this, "qt_datatable_menu" );
787 int id[ 3 ];
788 id[ IdInsert ] = popup->insertItem( tr( "Insert" ) );
789 id[ IdUpdate ] = popup->insertItem( tr( "Update" ) );
790 id[ IdDelete ] = popup->insertItem( tr( "Delete" ) );
791 bool enableInsert = sqlCursor()->canInsert();
792 popup->setItemEnabled( id[ IdInsert ], enableInsert );
793 bool enableUpdate = currentRow() > -1 && sqlCursor()->canUpdate() && !isColumnReadOnly( currentColumn() );
794 popup->setItemEnabled( id[ IdUpdate ], enableUpdate );
795 bool enableDelete = currentRow() > -1 && sqlCursor()->canDelete();
796 popup->setItemEnabled( id[ IdDelete ], enableDelete );
797 int r = popup->exec( e->globalPos() );
798 delete (QPopupMenu*) popup;
799 if ( r == id[ IdInsert ] )
800 beginInsert();
801 else if ( r == id[ IdUpdate ] ) {
802 if ( beginEdit( currentRow(), currentColumn(), FALSE ) )
803 setEditMode( Editing, currentRow(), currentColumn() );
804 else
805 endUpdate();
806 }
807 else if ( r == id[ IdDelete ] )
808 deleteCurrent();
809 e->accept();
810 }
811}
812
813/*! \reimp */
814void QDataTable::contentsMousePressEvent( QMouseEvent* e )
815{
816 QTable::contentsMousePressEvent( e );
817}
818
819/*! \reimp */
820QWidget* QDataTable::beginEdit ( int row, int col, bool replace )
821{
822 d->editRow = -1;
823 d->editCol = -1;
824 if ( !sqlCursor() )
825 return 0;
826 if ( d->dat.mode() == QSql::Insert && !sqlCursor()->canInsert() )
827 return 0;
828 if ( d->dat.mode() == QSql::Update && !sqlCursor()->canUpdate() )
829 return 0;
830 d->editRow = row;
831 d->editCol = col;
832 if ( d->continuousEdit ) {
833 // see comment in beginInsert()
834 bool fakeReadOnly = isColumnReadOnly( col );
835 setColumnReadOnly( col, FALSE );
836 QWidget* w = QTable::beginEdit( row, col, replace );
837 setColumnReadOnly( col, fakeReadOnly );
838 return w;
839 }
840 if ( d->dat.mode() == QSql::None && sqlCursor()->canUpdate() && sqlCursor()->primaryIndex().count() > 0 )
841 return beginUpdate( row, col, replace );
842 return 0;
843}
844
845/*! \reimp */
846void QDataTable::endEdit( int row, int col, bool, bool )
847{
848 bool accept = autoEdit() && !d->cancelInsert && !d->cancelUpdate;
849
850 QWidget *editor = cellWidget( row, col );
851 if ( !editor )
852 return;
853 if ( d->cancelMode )
854 return;
855 if ( d->dat.mode() != QSql::None && d->editBuffer ) {
856 QSqlPropertyMap * m = (d->propertyMap == 0) ?
857 QSqlPropertyMap::defaultMap() : d->propertyMap;
858 d->editBuffer->setValue( indexOf( col ), m->property( editor ) );
859 clearCellWidget( row, col );
860 if ( !d->continuousEdit ) {
861 switch ( d->dat.mode() ) {
862 case QSql::Insert:
863 if ( accept )
864 QTimer::singleShot( 0, this, SLOT( doInsertCurrent() ) );
865 else
866 endInsert();
867 break;
868 case QSql::Update:
869 if ( accept )
870 QTimer::singleShot( 0, this, SLOT( doUpdateCurrent() ) );
871 else
872 endUpdate();
873 break;
874 default:
875 break;
876 }
877 }
878 } else {
879 setEditMode( NotEditing, -1, -1 );
880 }
881 if ( d->dat.mode() == QSql::None )
882 viewport()->setFocus();
883 updateCell( row, col );
884 emit valueChanged( row, col );
885}
886
887/*! \internal */
888void QDataTable::doInsertCurrent()
889{
890 insertCurrent();
891}
892
893/*! \internal */
894void QDataTable::doUpdateCurrent()
895{
896 updateCurrent();
897 if ( d->dat.mode() == QSql::None ) {
898 viewport()->setFocus();
899 }
900}
901
902/*! \reimp */
903void QDataTable::activateNextCell()
904{
905// if ( d->dat.mode() == QSql::None )
906// QTable::activateNextCell();
907}
908
909/*! \internal
910*/
911
912void QDataTable::endInsert()
913{
914 if ( d->dat.mode() != QSql::Insert )
915 return;
916 d->dat.setMode( QSql::None );
917 d->editBuffer = 0;
918 verticalHeader()->setLabel( d->editRow, QString::number( d->editRow +1 ) );
919 d->editRow = -1;
920 d->editCol = -1;
921 d->insertRowLast = -1;
922 d->insertHeaderLabelLast = QString::null;
923 setEditMode( NotEditing, -1, -1 );
924 setNumRows( d->insertPreRows );
925 d->insertPreRows = -1;
926 viewport()->setFocus();
927}
928
929/*! \internal
930*/
931
932void QDataTable::endUpdate()
933{
934 d->dat.setMode( QSql::None );
935 d->editBuffer = 0;
936 updateRow( d->editRow );
937 d->editRow = -1;
938 d->editCol = -1;
939 setEditMode( NotEditing, -1, -1 );
940}
941
942/*!
943 Protected virtual function called when editing is about to begin
944 on a new record. If the table is read-only, or if there's no
945 cursor or the cursor does not allow inserts, nothing happens.
946
947 Editing takes place using the cursor's edit buffer(see
948 QSqlCursor::editBuffer()).
949
950 When editing begins, a new row is created in the table marked with
951 an asterisk '*' in the row's vertical header column, i.e. at the
952 left of the row.
953*/
954
955bool QDataTable::beginInsert()
956{
957 if ( !sqlCursor() || isReadOnly() || !numCols() )
958 return FALSE;
959 if ( !sqlCursor()->canInsert() )
960 return FALSE;
961 int i = 0;
962 int row = currentRow();
963
964 d->insertPreRows = numRows();
965 if ( row < 0 || numRows() < 1 )
966 row = 0;
967 setNumRows( d->insertPreRows + 1 );
968 setCurrentCell( row, 0 );
969 d->editBuffer = sqlCursor()->primeInsert();
970 emit primeInsert( d->editBuffer );
971 d->dat.setMode( QSql::Insert );
972 int lastRow = row;
973 int lastY = contentsY() + visibleHeight();
974 for ( i = row; i < numRows() ; ++i ) {
975 QRect cg = cellGeometry( i, 0 );
976 if ( (cg.y()+cg.height()) > lastY ) {
977 lastRow = i;
978 break;
979 }
980 }
981 if ( lastRow == row && ( numRows()-1 > row ) )
982 lastRow = numRows() - 1;
983 d->insertRowLast = lastRow;
984 d->insertHeaderLabelLast = verticalHeader()->label( d->insertRowLast );
985 verticalHeader()->setLabel( row, "*" );
986 d->editRow = row;
987 // in the db world it's common to allow inserting new records
988 // into a table that has read-only columns - temporarily
989 // switch off read-only mode for such columns
990 bool fakeReadOnly = isColumnReadOnly( 0 );
991 setColumnReadOnly( 0, FALSE );
992 if ( QTable::beginEdit( row, 0, FALSE ) )
993 setEditMode( Editing, row, 0 );
994 setColumnReadOnly( 0, fakeReadOnly );
995 return TRUE;
996}
997
998/*!
999 Protected virtual function called when editing is about to begin
1000 on an existing row. If the table is read-only, or if there's no
1001 cursor, nothing happens.
1002
1003 Editing takes place using the cursor's edit buffer (see
1004 QSqlCursor::editBuffer()).
1005
1006 \a row and \a col refer to the row and column in the QDataTable.
1007
1008 (\a replace is provided for reimplementors and reflects the API of
1009 QTable::beginEdit().)
1010*/
1011
1012QWidget* QDataTable::beginUpdate ( int row, int col, bool replace )
1013{
1014 if ( !sqlCursor() || isReadOnly() || isColumnReadOnly( col ) )
1015 return 0;
1016 setCurrentCell( row, col );
1017 d->dat.setMode( QSql::Update );
1018 if ( sqlCursor()->seek( row ) ) {
1019 d->editBuffer = sqlCursor()->primeUpdate();
1020 sqlCursor()->seek( currentRow() );
1021 emit primeUpdate( d->editBuffer );
1022 return QTable::beginEdit( row, col, replace );
1023 }
1024 return 0;
1025}
1026
1027/*!
1028 For an editable table, issues an insert on the current cursor
1029 using the values in the cursor's edit buffer. If there is no
1030 current cursor or there is no current "insert" row, nothing
1031 happens. If confirmEdits() or confirmInsert() is TRUE,
1032 confirmEdit() is called to confirm the insert. Returns TRUE if the
1033 insert succeeded; otherwise returns FALSE.
1034
1035 The underlying cursor must have a valid primary index to ensure
1036 that a unique record is inserted within the database otherwise the
1037 database may be changed to an inconsistent state.
1038*/
1039
1040bool QDataTable::insertCurrent()
1041{
1042 if ( d->dat.mode() != QSql::Insert || ! numCols() )
1043 return FALSE;
1044 if ( !sqlCursor()->canInsert() ) {
1045#ifdef QT_CHECK_RANGE
1046 qWarning("QDataTable::insertCurrent: insert not allowed for " +
1047 sqlCursor()->name() );
1048#endif
1049 endInsert();
1050 return FALSE;
1051 }
1052 int b = 0;
1053 int conf = QSql::Yes;
1054 if ( confirmEdits() || confirmInsert() )
1055 conf = confirmEdit( QSql::Insert );
1056 switch ( conf ) {
1057 case QSql::Yes: {
1058#ifndef QT_NO_CURSOR
1059 QApplication::setOverrideCursor( Qt::waitCursor );
1060#endif
1061 emit beforeInsert( d->editBuffer );
1062 b = sqlCursor()->insert();
1063#ifndef QT_NO_CURSOR
1064 QApplication::restoreOverrideCursor();
1065#endif
1066 if ( ( !b && !sqlCursor()->isActive() ) || !sqlCursor()->isActive() ) {
1067 handleError( sqlCursor()->lastError() );
1068 endInsert(); // cancel the insert if anything goes wrong
1069 refresh();
1070 } else {
1071 endInsert();
1072 refresh();
1073 QSqlIndex idx = sqlCursor()->primaryIndex();
1074 findBuffer( idx, d->lastAt );
1075 repaintContents( contentsX(), contentsY(), visibleWidth(), visibleHeight(), FALSE );
1076 emit cursorChanged( QSql::Insert );
1077 }
1078 break;
1079 }
1080 case QSql::No:
1081 endInsert();
1082 break;
1083 case QSql::Cancel:
1084 if ( QTable::beginEdit( currentRow(), currentColumn(), FALSE ) )
1085 setEditMode( Editing, currentRow(), currentColumn() );
1086 break;
1087 }
1088 return ( b > 0 );
1089}
1090
1091/*! \internal
1092
1093 Updates the row \a row.
1094*/
1095
1096void QDataTable::updateRow( int row )
1097{
1098 for ( int i = 0; i < numCols(); ++i )
1099 updateCell( row, i );
1100}
1101
1102/*!
1103 For an editable table, issues an update using the cursor's edit
1104 buffer. If there is no current cursor or there is no current
1105 selection, nothing happens. If confirmEdits() or confirmUpdate()
1106 is TRUE, confirmEdit() is called to confirm the update. Returns
1107 TRUE if the update succeeded; otherwise returns FALSE.
1108
1109 The underlying cursor must have a valid primary index to ensure
1110 that a unique record is updated within the database otherwise the
1111 database may be changed to an inconsistent state.
1112*/
1113
1114bool QDataTable::updateCurrent()
1115{
1116 if ( d->dat.mode() != QSql::Update )
1117 return FALSE;
1118 if ( sqlCursor()->primaryIndex().count() == 0 ) {
1119#ifdef QT_CHECK_RANGE
1120 qWarning("QDataTable::updateCurrent: no primary index for " +
1121 sqlCursor()->name() );
1122#endif
1123 endUpdate();
1124 return FALSE;
1125 }
1126 if ( !sqlCursor()->canUpdate() ) {
1127#ifdef QT_CHECK_RANGE
1128 qWarning("QDataTable::updateCurrent: updates not allowed for " +
1129 sqlCursor()->name() );
1130#endif
1131 endUpdate();
1132 return FALSE;
1133 }
1134 int b = 0;
1135 int conf = QSql::Yes;
1136 if ( confirmEdits() || confirmUpdate() )
1137 conf = confirmEdit( QSql::Update );
1138 switch ( conf ) {
1139 case QSql::Yes: {
1140#ifndef QT_NO_CURSOR
1141 QApplication::setOverrideCursor( Qt::waitCursor );
1142#endif
1143 emit beforeUpdate( d->editBuffer );
1144 b = sqlCursor()->update();
1145#ifndef QT_NO_CURSOR
1146 QApplication::restoreOverrideCursor();
1147#endif
1148 if ( ( !b && !sqlCursor()->isActive() ) || !sqlCursor()->isActive() ) {
1149 handleError( sqlCursor()->lastError() );
1150 endUpdate();
1151 refresh();
1152 setCurrentCell( d->editRow, d->editCol );
1153 if ( QTable::beginEdit( d->editRow, d->editCol, FALSE ) )
1154 setEditMode( Editing, d->editRow, d->editCol );
1155 } else {
1156 emit cursorChanged( QSql::Update );
1157 refresh();
1158 endUpdate();
1159 }
1160 break;
1161 }
1162 case QSql::No:
1163 endUpdate();
1164 setEditMode( NotEditing, -1, -1 );
1165 break;
1166 case QSql::Cancel:
1167 setCurrentCell( d->editRow, d->editCol );
1168 if ( QTable::beginEdit( d->editRow, d->editCol, FALSE ) )
1169 setEditMode( Editing, d->editRow, d->editCol );
1170 break;
1171 }
1172 return ( b > 0 );
1173}
1174
1175/*!
1176 For an editable table, issues a delete on the current cursor's
1177 primary index using the values of the currently selected row. If
1178 there is no current cursor or there is no current selection,
1179 nothing happens. If confirmEdits() or confirmDelete() is TRUE,
1180 confirmEdit() is called to confirm the delete. Returns TRUE if the
1181 delete succeeded; otherwise FALSE.
1182
1183 The underlying cursor must have a valid primary index to ensure
1184 that a unique record is deleted within the database otherwise the
1185 database may be changed to an inconsistent state.
1186*/
1187
1188bool QDataTable::deleteCurrent()
1189{
1190 if ( !sqlCursor() || isReadOnly() )
1191 return FALSE;
1192 if ( sqlCursor()->primaryIndex().count() == 0 ) {
1193#ifdef QT_CHECK_RANGE
1194 qWarning("QDataTable::deleteCurrent: no primary index " +
1195 sqlCursor()->name() );
1196#endif
1197 return FALSE;
1198 }
1199 if ( !sqlCursor()->canDelete() )
1200 return FALSE;
1201
1202 int b = 0;
1203 int conf = QSql::Yes;
1204 if ( confirmEdits() || confirmDelete() )
1205 conf = confirmEdit( QSql::Delete );
1206
1207 // Have to have this here - the confirmEdit() might pop up a
1208 // dialog that causes a repaint which the cursor to the
1209 // record it has to repaint.
1210 if ( !sqlCursor()->seek( currentRow() ) )
1211 return FALSE;
1212 switch ( conf ) {
1213 case QSql::Yes:{
1214#ifndef QT_NO_CURSOR
1215 QApplication::setOverrideCursor( Qt::waitCursor );
1216#endif
1217 sqlCursor()->primeDelete();
1218 emit primeDelete( sqlCursor()->editBuffer() );
1219 emit beforeDelete( sqlCursor()->editBuffer() );
1220 b = sqlCursor()->del();
1221#ifndef QT_NO_CURSOR
1222 QApplication::restoreOverrideCursor();
1223#endif
1224 if ( !b )
1225 handleError( sqlCursor()->lastError() );
1226 refresh();
1227 emit cursorChanged( QSql::Delete );
1228 setCurrentCell( currentRow(), currentColumn() );
1229 repaintContents( contentsX(), contentsY(), visibleWidth(), visibleHeight(), FALSE );
1230 verticalHeader()->repaint(); // get rid of trailing garbage
1231 }
1232 break;
1233 case QSql::No:
1234 setEditMode( NotEditing, -1, -1 );
1235 break;
1236 }
1237 return ( b > 0 );
1238}
1239
1240/*!
1241 Protected virtual function which returns a confirmation for an
1242 edit of mode \a m. Derived classes can reimplement this function
1243 to provide their own confirmation dialog. The default
1244 implementation uses a message box which prompts the user to
1245 confirm the edit action.
1246*/
1247
1248QSql::Confirm QDataTable::confirmEdit( QSql::Op m )
1249{
1250 return d->dat.confirmEdit( this, m );
1251}
1252
1253/*!
1254 Protected virtual function which returns a confirmation for
1255 cancelling an edit mode of \a m. Derived classes can reimplement
1256 this function to provide their own cancel dialog. The default
1257 implementation uses a message box which prompts the user to
1258 confirm the cancel.
1259*/
1260
1261QSql::Confirm QDataTable::confirmCancel( QSql::Op m )
1262{
1263 return d->dat.confirmCancel( this, m );
1264}
1265
1266
1267/*!
1268 Searches the current cursor for a cell containing the string \a
1269 str starting at the current cell and working forwards (or
1270 backwards if \a backwards is TRUE). If the string is found, the
1271 cell containing the string is set as the current cell. If \a
1272 caseSensitive is FALSE the case of \a str will be ignored.
1273
1274 The search will wrap, i.e. if the first (or if backwards is TRUE,
1275 last) cell is reached without finding \a str the search will
1276 continue until it reaches the starting cell. If \a str is not
1277 found the search will fail and the current cell will remain
1278 unchanged.
1279*/
1280void QDataTable::find( const QString & str, bool caseSensitive, bool backwards )
1281{
1282 if ( !sqlCursor() )
1283 return;
1284
1285 QSqlCursor * r = sqlCursor();
1286 QString tmp, text;
1287 uint row = currentRow(), startRow = row,
1288 col = backwards ? currentColumn() - 1 : currentColumn() + 1;
1289 bool wrap = TRUE, found = FALSE;
1290
1291 if( str.isEmpty() || str.isNull() )
1292 return;
1293
1294 if( !caseSensitive )
1295 tmp = str.lower();
1296 else
1297 tmp = str;
1298
1299#ifndef QT_NO_CURSOR
1300 QApplication::setOverrideCursor( Qt::waitCursor );
1301#endif
1302 while( wrap ){
1303 while( !found && r->seek( row ) ){
1304 for( int i = col; backwards ? (i >= 0) : (i < (int) numCols());
1305 backwards ? i-- : i++ )
1306 {
1307 text = r->value( indexOf( i ) ).toString();
1308 if( !caseSensitive ){
1309 text = text.lower();
1310 }
1311 if( text.contains( tmp ) ){
1312 setCurrentCell( row, i );
1313 col = i;
1314 found = TRUE;
1315 }
1316 }
1317 if( !backwards ){
1318 col = 0;
1319 row++;
1320 } else {
1321 col = numCols() - 1;
1322 row--;
1323 }
1324 }
1325 if( !backwards ){
1326 if( startRow != 0 ){
1327 startRow = 0;
1328 } else {
1329 wrap = FALSE;
1330 }
1331 r->first();
1332 row = 0;
1333 } else {
1334 if( startRow != (uint) (numRows() - 1) ){
1335 startRow = numRows() - 1;
1336 } else {
1337 wrap = FALSE;
1338 }
1339 r->last();
1340 row = numRows() - 1;
1341 }
1342 }
1343#ifndef QT_NO_CURSOR
1344 QApplication::restoreOverrideCursor();
1345#endif
1346}
1347
1348
1349/*!
1350 Resets the table so that it displays no data.
1351
1352 \sa setSqlCursor()
1353*/
1354
1355void QDataTable::reset()
1356{
1357 clearCellWidget( currentRow(), currentColumn() );
1358 switch ( d->dat.mode() ) {
1359 case QSql::Insert:
1360 endInsert();
1361 break;
1362 case QSql::Update:
1363 endUpdate();
1364 break;
1365 default:
1366 break;
1367 }
1368 ensureVisible( 0, 0 );
1369 verticalScrollBar()->setValue(0);
1370 setNumRows(0);
1371
1372 d->haveAllRows = FALSE;
1373 d->continuousEdit = FALSE;
1374 d->dat.setMode( QSql::None );
1375 d->editRow = -1;
1376 d->editCol = -1;
1377 d->insertRowLast = -1;
1378 d->insertHeaderLabelLast = QString::null;
1379 d->cancelMode = FALSE;
1380 d->lastAt = -1;
1381 d->fld.clear();
1382 d->fldLabel.clear();
1383 d->fldWidth.clear();
1384 d->fldIcon.clear();
1385 d->fldHidden.clear();
1386 if ( sorting() )
1387 horizontalHeader()->setSortIndicator( -1 );
1388}
1389
1390/*!
1391 Returns the index of the field within the current SQL query that
1392 is displayed in column \a i.
1393*/
1394
1395int QDataTable::indexOf( uint i ) const
1396{
1397 QDataTablePrivate::ColIndex::ConstIterator it = d->colIndex.at( i );
1398 if ( it != d->colIndex.end() )
1399 return *it;
1400 return -1;
1401}
1402
1403/*!
1404 Returns TRUE if the table will automatically delete the cursor
1405 specified by setSqlCursor(); otherwise returns FALSE.
1406*/
1407
1408bool QDataTable::autoDelete() const
1409{
1410 return d->cur.autoDelete();
1411}
1412
1413/*!
1414 Sets the cursor auto-delete flag to \a enable. If \a enable is
1415 TRUE, the table will automatically delete the cursor specified by
1416 setSqlCursor(). If \a enable is FALSE (the default), the cursor
1417 will not be deleted.
1418*/
1419
1420void QDataTable::setAutoDelete( bool enable )
1421{
1422 d->cur.setAutoDelete( enable );
1423}
1424
1425/*!
1426 \property QDataTable::autoEdit
1427 \brief whether the data table automatically applies edits
1428
1429 The default value for this property is TRUE. When the user begins
1430 an insert or update in the table there are two possible outcomes
1431 when they navigate to another record:
1432
1433 \list 1
1434 \i the insert or update is is performed -- this occurs if autoEdit is TRUE
1435 \i the insert or update is abandoned -- this occurs if autoEdit is FALSE
1436 \endlist
1437*/
1438
1439void QDataTable::setAutoEdit( bool autoEdit )
1440{
1441 d->dat.setAutoEdit( autoEdit );
1442}
1443
1444bool QDataTable::autoEdit() const
1445{
1446 return d->dat.autoEdit();
1447}
1448
1449/*!
1450 \property QDataTable::nullText
1451 \brief the text used to represent NULL values
1452
1453 The nullText property will be used to represent NULL values in the
1454 table. The default value is provided by the cursor's driver.
1455*/
1456
1457void QDataTable::setNullText( const QString& nullText )
1458{
1459 d->nullTxt = nullText;
1460 d->nullTxtChanged = TRUE;
1461}
1462
1463QString QDataTable::nullText() const
1464{
1465 return d->nullTxt;
1466}
1467
1468/*!
1469 \property QDataTable::trueText
1470 \brief the text used to represent true values
1471
1472 The trueText property will be used to represent NULL values in the
1473 table. The default value is "True".
1474*/
1475
1476void QDataTable::setTrueText( const QString& trueText )
1477{
1478 d->trueTxt = trueText;
1479}
1480
1481QString QDataTable::trueText() const
1482{
1483 return d->trueTxt;
1484}
1485
1486/*!
1487 \property QDataTable::falseText
1488 \brief the text used to represent false values
1489
1490 The falseText property will be used to represent NULL values in
1491 the table. The default value is "False".
1492*/
1493
1494void QDataTable::setFalseText( const QString& falseText )
1495{
1496 d->falseTxt = falseText;
1497}
1498
1499QString QDataTable::falseText() const
1500{
1501 return d->falseTxt;
1502}
1503
1504/*!
1505 \property QDataTable::dateFormat
1506 \brief the format used for displaying date/time values
1507
1508 The dateFormat property is used for displaying date/time values in
1509 the table. The default value is \c Qt::LocalDate.
1510*/
1511
1512void QDataTable::setDateFormat( const DateFormat f )
1513{
1514 d->datefmt = f;
1515}
1516
1517Qt::DateFormat QDataTable::dateFormat() const
1518{
1519 return d->datefmt;
1520}
1521
1522/*!
1523 \property QDataTable::numRows
1524
1525 \brief the number of rows in the table
1526*/
1527
1528int QDataTable::numRows() const
1529{
1530 return QTable::numRows();
1531}
1532
1533/*!
1534 \reimp
1535
1536 The number of rows in the table will be determined by the cursor
1537 (see setSqlCursor()), so normally this function should never be
1538 called. It is included for completeness.
1539*/
1540
1541void QDataTable::setNumRows ( int r )
1542{
1543 QTable::setNumRows( r );
1544}
1545
1546/*!
1547 \reimp
1548
1549 The number of columns in the table will be determined
1550 automatically (see addColumn()), so normally this function should
1551 never be called. It is included for completeness.
1552*/
1553
1554void QDataTable::setNumCols ( int r )
1555{
1556 QTable::setNumCols( r );
1557}
1558
1559/*!
1560 \property QDataTable::numCols
1561
1562 \brief the number of columns in the table
1563*/
1564
1565int QDataTable::numCols() const
1566{
1567 return QTable::numCols();
1568}
1569
1570/*!
1571 Returns the text in cell \a row, \a col, or an empty string if the
1572 cell is empty. If the cell's value is NULL then nullText() will be
1573 returned. If the cell does not exist then QString::null is
1574 returned.
1575*/
1576
1577QString QDataTable::text ( int row, int col ) const
1578{
1579 if ( !sqlCursor() )
1580 return QString::null;
1581
1582 QString s;
1583 if ( sqlCursor()->seek( row ) )
1584 s = sqlCursor()->value( indexOf( col ) ).toString();
1585 sqlCursor()->seek( currentRow() );
1586 return s;
1587}
1588
1589/*!
1590 Returns the value in cell \a row, \a col, or an invalid value if
1591 the cell does not exist or has no value.
1592*/
1593
1594QVariant QDataTable::value ( int row, int col ) const
1595{
1596 if ( !sqlCursor() )
1597 return QVariant();
1598
1599 QVariant v;
1600 if ( sqlCursor()->seek( row ) )
1601 v = sqlCursor()->value( indexOf( col ) );
1602 sqlCursor()->seek( currentRow() );
1603 return v;
1604}
1605
1606/*! \internal
1607 Used to update the table when the size of the result set cannot be
1608 determined - divide the result set into pages and load the pages as
1609 the user moves around in the table.
1610*/
1611void QDataTable::loadNextPage()
1612{
1613 if ( d->haveAllRows )
1614 return;
1615 if ( !sqlCursor() )
1616 return;
1617 int pageSize = 0;
1618 int lookAhead = 0;
1619 if ( height() ) {
1620 pageSize = (int)( height() * 2 / 20 );
1621 lookAhead = pageSize / 2;
1622 }
1623 int startIdx = verticalScrollBar()->value() / 20;
1624 int endIdx = startIdx + pageSize + lookAhead;
1625 if ( endIdx < numRows() || endIdx < 0 )
1626 return;
1627
1628 // check for empty result set
1629 if ( sqlCursor()->at() == QSql::BeforeFirst && !sqlCursor()->next() ) {
1630 d->haveAllRows = TRUE;
1631 return;
1632 }
1633
1634 while ( endIdx > 0 && !sqlCursor()->seek( endIdx ) )
1635 endIdx--;
1636 if ( endIdx != ( startIdx + pageSize + lookAhead ) )
1637 d->haveAllRows = TRUE;
1638 // small hack to prevent QTable from moving the view when a row
1639 // is selected and the contents is resized
1640 SelectionMode m = selectionMode();
1641 clearSelection();
1642 setSelectionMode( NoSelection );
1643 setNumRows( endIdx + 1 );
1644 sqlCursor()->seek( currentRow() );
1645 setSelectionMode( m );
1646}
1647
1648/*! \internal */
1649void QDataTable::sliderPressed()
1650{
1651 disconnect( verticalScrollBar(), SIGNAL( valueChanged(int) ),
1652 this, SLOT( loadNextPage() ) );
1653}
1654
1655/*! \internal */
1656void QDataTable::sliderReleased()
1657{
1658 loadNextPage();
1659 connect( verticalScrollBar(), SIGNAL( valueChanged(int) ),
1660 this, SLOT( loadNextPage() ) );
1661}
1662
1663/*!
1664 Sorts column \a col in ascending order if \a ascending is TRUE
1665 (the default); otherwise sorts in descending order.
1666
1667 The \a wholeRows parameter is ignored; QDataTable always sorts
1668 whole rows by the specified column.
1669*/
1670
1671void QDataTable::sortColumn ( int col, bool ascending,
1672 bool )
1673{
1674 if ( sorting() ) {
1675 if ( isEditing() && d->dat.mode() != QSql::None )
1676 endEdit( d->editRow, d->editCol, autoEdit(), FALSE );
1677 if ( !sqlCursor() )
1678 return;
1679 QSqlIndex lastSort = sqlCursor()->sort();
1680 QSqlIndex newSort( lastSort.cursorName(), "newSort" );
1681 QSqlField *field = sqlCursor()->field( indexOf( col ) );
1682 if ( field )
1683 newSort.append( *field );
1684 newSort.setDescending( 0, !ascending );
1685 horizontalHeader()->setSortIndicator( col, ascending );
1686 setSort( newSort );
1687 refresh();
1688 }
1689}
1690
1691/*! \reimp */
1692void QDataTable::columnClicked ( int col )
1693{
1694 if ( sorting() ) {
1695 if ( !sqlCursor() )
1696 return;
1697 QSqlIndex lastSort = sqlCursor()->sort();
1698 bool asc = TRUE;
1699 if ( lastSort.count() && lastSort.field( 0 )->name() == sqlCursor()->field( indexOf( col ) )->name() )
1700 asc = lastSort.isDescending( 0 );
1701 sortColumn( col, asc );
1702 emit currentChanged( sqlCursor() );
1703 }
1704}
1705
1706/*!
1707 \reimp
1708
1709 Repaints the cell at \a row, \a col.
1710*/
1711void QDataTable::repaintCell( int row, int col )
1712{
1713 QRect cg = cellGeometry( row, col );
1714 QRect re( QPoint( cg.x() - 2, cg.y() - 2 ),
1715 QSize( cg.width() + 4, cg.height() + 4 ) );
1716 repaintContents( re, FALSE );
1717}
1718
1719/*!
1720 \reimp
1721
1722 This function renders the cell at \a row, \a col with the value of
1723 the corresponding cursor field on the painter \a p. Depending on
1724 the table's current edit mode, paintField() is called for the
1725 appropriate cursor field. \a cr describes the cell coordinates in
1726 the content coordinate system. If \a selected is TRUE the cell has
1727 been selected and would normally be rendered differently than an
1728 unselected cell.
1729
1730 \sa QSql::isNull()
1731*/
1732
1733void QDataTable::paintCell( QPainter * p, int row, int col, const QRect & cr,
1734 bool selected, const QColorGroup &cg )
1735{
1736 QTable::paintCell( p, row, col, cr, selected, cg ); // empty cell
1737
1738 if ( !sqlCursor() )
1739 return;
1740
1741 p->setPen( selected ? cg.highlightedText() : cg.text() );
1742 if ( d->dat.mode() != QSql::None ) {
1743 if ( row == d->editRow && d->editBuffer ) {
1744 paintField( p, d->editBuffer->field( indexOf( col ) ), cr,
1745 selected );
1746 } else if ( row > d->editRow && d->dat.mode() == QSql::Insert ) {
1747 if ( sqlCursor()->seek( row - 1 ) )
1748 paintField( p, sqlCursor()->field( indexOf( col ) ), cr,
1749 selected );
1750 } else {
1751 if ( sqlCursor()->seek( row ) )
1752 paintField( p, sqlCursor()->field( indexOf( col ) ), cr,
1753 selected );
1754 }
1755 } else {
1756 if ( sqlCursor()->seek( row ) )
1757 paintField( p, sqlCursor()->field( indexOf( col ) ), cr, selected );
1758
1759 }
1760}
1761
1762
1763/*!
1764 Paints the \a field on the painter \a p. The painter has already
1765 been translated to the appropriate cell's origin where the \a
1766 field is to be rendered. \a cr describes the cell coordinates in
1767 the content coordinate system. The \a selected parameter is
1768 ignored.
1769
1770 If you want to draw custom field content you must reimplement
1771 paintField() to do the custom drawing. The default implementation
1772 renders the \a field value as text. If the field is NULL,
1773 nullText() is displayed in the cell. If the field is Boolean,
1774 trueText() or falseText() is displayed as appropriate.
1775*/
1776
1777void QDataTable::paintField( QPainter * p, const QSqlField* field,
1778 const QRect & cr, bool )
1779{
1780 if ( !field )
1781 return;
1782 p->drawText( 2,2, cr.width()-4, cr.height()-4, fieldAlignment( field ), fieldToString( field ) );
1783}
1784
1785/*!
1786 Returns the alignment for \a field.
1787*/
1788
1789int QDataTable::fieldAlignment( const QSqlField* /*field*/ )
1790{
1791 return Qt::AlignLeft | Qt::AlignVCenter; //## Reggie: add alignment to QTable
1792}
1793
1794
1795/*!
1796 If the cursor's \a sql driver supports query sizes, the number of
1797 rows in the table is set to the size of the query. Otherwise, the
1798 table dynamically resizes itself as it is scrolled. If \a sql is
1799 not active, it is made active by issuing a select() on the cursor
1800 using the \a sql cursor's current filter and current sort.
1801*/
1802
1803void QDataTable::setSize( QSqlCursor* sql )
1804{
1805 // ### what are the connect/disconnect calls doing here!? move to refresh()
1806 if ( sql->driver() && sql->driver()->hasFeature( QSqlDriver::QuerySize ) ) {
1807 setVScrollBarMode( Auto );
1808 disconnect( verticalScrollBar(), SIGNAL( sliderPressed() ),
1809 this, SLOT( sliderPressed() ) );
1810 disconnect( verticalScrollBar(), SIGNAL( sliderReleased() ),
1811 this, SLOT( sliderReleased() ) );
1812 disconnect( verticalScrollBar(), SIGNAL( valueChanged(int) ),
1813 this, SLOT( loadNextPage() ) );
1814 if ( numRows() != sql->size() )
1815 setNumRows( sql->size() );
1816 } else {
1817 setVScrollBarMode( AlwaysOn );
1818 connect( verticalScrollBar(), SIGNAL( sliderPressed() ),
1819 this, SLOT( sliderPressed() ) );
1820 connect( verticalScrollBar(), SIGNAL( sliderReleased() ),
1821 this, SLOT( sliderReleased() ) );
1822 connect( verticalScrollBar(), SIGNAL( valueChanged(int) ),
1823 this, SLOT( loadNextPage() ) );
1824 setNumRows(0);
1825 loadNextPage();
1826 }
1827}
1828
1829/*!
1830 Sets \a cursor as the data source for the table. To force the
1831 display of the data from \a cursor, use refresh(). If \a
1832 autoPopulate is TRUE, columns are automatically created based upon
1833 the fields in the \a cursor record. If \a autoDelete is TRUE (the
1834 default is FALSE), the table will take ownership of the \a cursor
1835 and delete it when appropriate. If the \a cursor is read-only, the
1836 table becomes read-only. The table adopts the cursor's driver's
1837 definition for representing NULL values as strings.
1838
1839 \sa refresh() setReadOnly() setAutoDelete() QSqlDriver::nullText()
1840*/
1841
1842void QDataTable::setSqlCursor( QSqlCursor* cursor, bool autoPopulate, bool autoDelete )
1843{
1844 setUpdatesEnabled( FALSE );
1845 d->cur.setCursor( 0 );
1846 if ( cursor ) {
1847 d->cur.setCursor( cursor, autoDelete );
1848 if ( autoPopulate ) {
1849 d->fld.clear();
1850 d->fldLabel.clear();
1851 d->fldWidth.clear();
1852 d->fldIcon.clear();
1853 d->fldHidden.clear();
1854 for ( uint i = 0; i < sqlCursor()->count(); ++i ) {
1855 addColumn( sqlCursor()->field( i )->name(), sqlCursor()->field( i )->name() );
1856 setColumnReadOnly( i, sqlCursor()->field( i )->isReadOnly() );
1857 }
1858 }
1859 setReadOnly( sqlCursor()->isReadOnly() );
1860 if ( sqlCursor()->driver() && !d->nullTxtChanged )
1861 setNullText(sqlCursor()->driver()->nullText() );
1862 setAutoDelete( autoDelete );
1863 } else {
1864 setNumRows( 0 );
1865 setNumCols( 0 );
1866 }
1867 setUpdatesEnabled( TRUE );
1868}
1869
1870
1871/*!
1872 Protected virtual function which is called when an error \a e has
1873 occurred on the current cursor(). The default implementation
1874 displays a warning message to the user with information about the
1875 error.
1876*/
1877void QDataTable::handleError( const QSqlError& e )
1878{
1879 d->dat.handleError( this, e );
1880}
1881
1882/*! \reimp
1883 */
1884
1885void QDataTable::keyPressEvent( QKeyEvent* e )
1886{
1887 switch( e->key() ) {
1888 case Key_Left:
1889 case Key_Right:
1890 case Key_Up:
1891 case Key_Down:
1892 case Key_Prior:
1893 case Key_Next:
1894 case Key_Home:
1895 case Key_End:
1896 case Key_F2:
1897 case Key_Enter: case Key_Return:
1898 case Key_Tab: case Key_BackTab:
1899 QTable::keyPressEvent( e );
1900 default:
1901 return;
1902 }
1903}
1904
1905/*! \reimp
1906*/
1907
1908void QDataTable::resizeData ( int )
1909{
1910
1911}
1912
1913/*! \reimp
1914*/
1915
1916QTableItem * QDataTable::item ( int, int ) const
1917{
1918 return 0;
1919}
1920
1921/*! \reimp
1922*/
1923
1924void QDataTable::setItem ( int , int , QTableItem * )
1925{
1926
1927}
1928
1929/*! \reimp
1930*/
1931
1932void QDataTable::clearCell ( int , int )
1933{
1934
1935}
1936
1937/*! \reimp
1938*/
1939
1940void QDataTable::setPixmap ( int , int , const QPixmap & )
1941{
1942
1943}
1944
1945/*! \reimp */
1946void QDataTable::takeItem ( QTableItem * )
1947{
1948
1949}
1950
1951/*!
1952 Installs a new SQL editor factory \a f. This enables the user to
1953 create and instantiate their own editors for use in cell editing.
1954 Note that QDataTable takes ownership of this pointer, and will
1955 delete it when it is no longer needed or when
1956 installEditorFactory() is called again.
1957
1958 \sa QSqlEditorFactory
1959*/
1960
1961void QDataTable::installEditorFactory( QSqlEditorFactory * f )
1962{
1963 if( f ) {
1964 delete d->editorFactory;
1965 d->editorFactory = f;
1966 }
1967}
1968
1969/*!
1970 Installs a new property map \a m. This enables the user to create
1971 and instantiate their own property maps for use in cell editing.
1972 Note that QDataTable takes ownership of this pointer, and will
1973 delete it when it is no longer needed or when installPropertMap()
1974 is called again.
1975
1976 \sa QSqlPropertyMap
1977*/
1978
1979void QDataTable::installPropertyMap( QSqlPropertyMap* m )
1980{
1981 if ( m ) {
1982 delete d->propertyMap;
1983 d->propertyMap = m;
1984 }
1985}
1986
1987/*! \internal
1988
1989 Sets the current selection to \a row, \a col.
1990*/
1991
1992void QDataTable::setCurrentSelection( int row, int )
1993{
1994 if ( !sqlCursor() )
1995 return;
1996 if ( row == d->lastAt )
1997 return;
1998 if ( !sqlCursor()->seek( row ) )
1999 return;
2000 d->lastAt = row;
2001 emit currentChanged( sqlCursor() );
2002}
2003
2004void QDataTable::updateCurrentSelection()
2005{
2006 setCurrentSelection( currentRow(), -1 );
2007}
2008
2009/*!
2010 Returns the currently selected record, or 0 if there is no current
2011 selection. The table owns the pointer, so do \e not delete it or
2012 otherwise modify it or the cursor it points to.
2013*/
2014
2015QSqlRecord* QDataTable::currentRecord() const
2016{
2017 if ( !sqlCursor() || currentRow() < 0 )
2018 return 0;
2019 if ( !sqlCursor()->seek( currentRow() ) )
2020 return 0;
2021 return sqlCursor();
2022}
2023
2024/*!
2025 Sorts column \a col in ascending order.
2026
2027 \sa setSorting()
2028*/
2029
2030void QDataTable::sortAscending( int col )
2031{
2032 sortColumn( col, TRUE );
2033}
2034
2035/*!
2036 Sorts column \a col in descending order.
2037
2038 \sa setSorting()
2039*/
2040
2041void QDataTable::sortDescending( int col )
2042{
2043 sortColumn( col, FALSE );
2044}
2045
2046/*!
2047 \overload void QDataTable::refresh( Refresh mode )
2048
2049 Refreshes the table. If there is no currently defined cursor (see
2050 setSqlCursor()), nothing happens. The \a mode parameter determines
2051 which type of refresh will take place.
2052
2053 \sa Refresh setSqlCursor() addColumn()
2054*/
2055
2056void QDataTable::refresh( QDataTable::Refresh mode )
2057{
2058 QSqlCursor* cur = sqlCursor();
2059 if ( !cur )
2060 return;
2061 bool refreshData = ( (mode & RefreshData) == RefreshData );
2062 bool refreshCol = ( (mode & RefreshColumns) == RefreshColumns );
2063 if ( ( (mode & RefreshAll) == RefreshAll ) ) {
2064 refreshData = TRUE;
2065 refreshCol = TRUE;
2066 }
2067 if ( !refreshCol && d->fld.count() && numCols() == 0 )
2068 refreshCol = TRUE;
2069 viewport()->setUpdatesEnabled( FALSE );
2070 d->haveAllRows = FALSE;
2071 if ( refreshData ) {
2072 if ( !d->cur.refresh() && d->cur.cursor() ) {
2073 handleError( d->cur.cursor()->lastError() );
2074 }
2075 d->lastAt = -1;
2076 }
2077 if ( refreshCol ) {
2078 setNumCols( 0 );
2079 d->colIndex.clear();
2080 if ( d->fld.count() ) {
2081 QSqlField* field = 0;
2082 int i;
2083 int fpos = -1;
2084 for ( i = 0; i < (int)d->fld.count(); ++i ) {
2085 if ( cur->field( i ) && cur->field( i )->name() == d->fld[ i ] )
2086 // if there is a field with the desired name on the desired position
2087 // then we take that
2088 fpos = i;
2089 else
2090 // otherwise we take the first field that matches the desired name
2091 fpos = cur->position( d->fld[ i ] );
2092 field = cur->field( fpos );
2093 if ( field && ( cur->isGenerated( fpos ) ||
2094 cur->isCalculated( field->name() ) ) )
2095 {
2096 setNumCols( numCols() + 1 );
2097 d->colIndex.append( fpos );
2098 setColumnReadOnly( numCols()-1, field->isReadOnly() || isColumnReadOnly( numCols()-1 ) );
2099 horizontalHeader()->setLabel( numCols()-1, d->fldIcon[ i ], d->fldLabel[ i ] );
2100 if ( d->fldHidden[ i ] ) {
2101 QTable::showColumn( i ); // ugly but necessary
2102 QTable::hideColumn( i );
2103 } else {
2104 QTable::showColumn( i );
2105 }
2106 if ( d->fldWidth[ i ] > -1 )
2107 QTable::setColumnWidth( i, d->fldWidth[i] );
2108 }
2109 }
2110 }
2111 }
2112 viewport()->setUpdatesEnabled( TRUE );
2113 viewport()->repaint( FALSE );
2114 horizontalHeader()->repaint();
2115 verticalHeader()->repaint();
2116 setSize( cur );
2117 // keep others aware
2118 if ( d->lastAt == -1 )
2119 setCurrentSelection( -1, -1 );
2120 else if ( d->lastAt != currentRow() )
2121 setCurrentSelection( currentRow(), currentColumn() );
2122 if ( cur->isValid() )
2123 emit currentChanged( sqlCursor() );
2124}
2125
2126/*!
2127 Refreshes the table. The cursor is refreshed using the current
2128 filter, the current sort, and the currently defined columns.
2129 Equivalent to calling refresh( QDataTable::RefreshData ).
2130*/
2131
2132void QDataTable::refresh()
2133{
2134 refresh( RefreshData );
2135}
2136
2137/*!
2138 \reimp
2139
2140 Selects the record in the table using the current cursor edit
2141 buffer and the fields specified by the index \a idx. If \a atHint
2142 is specified, it will be used as a hint about where to begin
2143 searching.
2144*/
2145
2146bool QDataTable::findBuffer( const QSqlIndex& idx, int atHint )
2147{
2148 QSqlCursor* cur = sqlCursor();
2149 if ( !cur )
2150 return FALSE;
2151 bool found = d->cur.findBuffer( idx, atHint );
2152 if ( found )
2153 setCurrentCell( cur->at(), currentColumn() );
2154 return found;
2155}
2156
2157/*! \internal
2158 Returns the string representation of a database field.
2159*/
2160QString QDataTable::fieldToString( const QSqlField * field )
2161{
2162 QString text;
2163 if ( field->isNull() ) {
2164 text = nullText();
2165 } else {
2166 QVariant val = field->value();
2167 switch ( val.type() ) {
2168 case QVariant::Bool:
2169 text = val.toBool() ? d->trueTxt : d->falseTxt;
2170 break;
2171 case QVariant::Date:
2172 text = val.toDate().toString( d->datefmt );
2173 break;
2174 case QVariant::Time:
2175 text = val.toTime().toString( d->datefmt );
2176 break;
2177 case QVariant::DateTime:
2178 text = val.toDateTime().toString( d->datefmt );
2179 break;
2180 default:
2181 text = val.toString();
2182 break;
2183 }
2184 }
2185 return text;
2186}
2187
2188/*!
2189 \reimp
2190*/
2191
2192void QDataTable::swapColumns( int col1, int col2, bool )
2193{
2194 QString fld = d->fld[ col1 ];
2195 QString fldLabel = d->fldLabel[ col1 ];
2196 QIconSet fldIcon = d->fldIcon[ col1 ];
2197 int fldWidth = d->fldWidth[ col1 ];
2198
2199 d->fld[ col1 ] = d->fld[ col2 ];
2200 d->fldLabel[ col1 ] = d->fldLabel[ col2 ];
2201 d->fldIcon[ col1 ] = d->fldIcon[ col2 ];
2202 d->fldWidth[ col1 ] = d->fldWidth[ col2 ];
2203
2204 d->fld[ col2 ] = fld;
2205 d->fldLabel[ col2 ] = fldLabel;
2206 d->fldIcon[ col2 ] = fldIcon;
2207 d->fldWidth[ col2 ] = fldWidth;
2208
2209 int colIndex = d->colIndex[ col1 ];
2210 d->colIndex[ col1 ] = d->colIndex[ col2 ];
2211 d->colIndex[ col2 ] = colIndex;
2212}
2213
2214/*!
2215 \reimp
2216*/
2217
2218void QDataTable::drawContents( QPainter * p, int cx, int cy, int cw, int ch )
2219{
2220 QTable::drawContents( p, cx, cy, cw, ch );
2221 if ( sqlCursor() && currentRow() >= 0 )
2222 sqlCursor()->seek( currentRow() );
2223}
2224
2225/*!
2226 \reimp
2227*/
2228
2229void QDataTable::hideColumn( int col )
2230{
2231 d->fldHidden[col] = TRUE;
2232 refresh( RefreshColumns );
2233}
2234
2235/*!
2236 \reimp
2237*/
2238
2239void QDataTable::showColumn( int col )
2240{
2241 d->fldHidden[col] = FALSE;
2242 refresh( RefreshColumns );
2243}
2244
2245/*!
2246 \fn void QDataTable::currentChanged( QSqlRecord* record )
2247
2248 This signal is emitted whenever a new row is selected in the
2249 table. The \a record parameter points to the contents of the newly
2250 selected record.
2251*/
2252
2253/*!
2254 \fn void QDataTable::primeInsert( QSqlRecord* buf )
2255
2256 This signal is emitted after the cursor is primed for insert by
2257 the table, when an insert action is beginning on the table. The \a
2258 buf parameter points to the edit buffer being inserted. Connect to
2259 this signal in order to, for example, prime the record buffer with
2260 default data values.
2261*/
2262
2263/*!
2264 \fn void QDataTable::primeUpdate( QSqlRecord* buf )
2265
2266 This signal is emitted after the cursor is primed for update by
2267 the table, when an update action is beginning on the table. The \a
2268 buf parameter points to the edit buffer being updated. Connect to
2269 this signal in order to, for example, provide some visual feedback
2270 that the user is in 'edit mode'.
2271*/
2272
2273/*!
2274 \fn void QDataTable::primeDelete( QSqlRecord* buf )
2275
2276 This signal is emitted after the cursor is primed for delete by
2277 the table, when a delete action is beginning on the table. The \a
2278 buf parameter points to the edit buffer being deleted. Connect to
2279 this signal in order to, for example, record auditing information
2280 on deletions.
2281*/
2282
2283/*!
2284 \fn void QDataTable::beforeInsert( QSqlRecord* buf )
2285
2286 This signal is emitted just before the cursor's edit buffer is
2287 inserted into the database. The \a buf parameter points to the
2288 edit buffer being inserted. Connect to this signal to, for
2289 example, populate a key field with a unique sequence number.
2290*/
2291
2292/*!
2293 \fn void QDataTable::beforeUpdate( QSqlRecord* buf )
2294
2295 This signal is emitted just before the cursor's edit buffer is
2296 updated in the database. The \a buf parameter points to the edit
2297 buffer being updated. Connect to this signal when you want to
2298 transform the user's data behind-the-scenes.
2299*/
2300
2301/*!
2302 \fn void QDataTable::beforeDelete( QSqlRecord* buf )
2303
2304 This signal is emitted just before the currently selected record
2305 is deleted from the database. The \a buf parameter points to the
2306 edit buffer being deleted. Connect to this signal to, for example,
2307 copy some of the fields for later use.
2308*/
2309
2310/*!
2311 \fn void QDataTable::cursorChanged( QSql::Op mode )
2312
2313 This signal is emitted whenever the cursor record was changed due
2314 to an edit. The \a mode parameter is the type of edit that just
2315 took place.
2316*/
2317
2318#endif
Note: See TracBrowser for help on using the repository browser.