source: trunk/src/qt3support/sql/q3datatable.cpp@ 561

Last change on this file since 561 was 561, checked in by Dmitry A. Kuminov, 15 years ago

trunk: Merged in qt 4.6.1 sources.

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