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

Last change on this file since 504 was 2, checked in by Dmitry A. Kuminov, 16 years ago

Initially imported qt-all-opensource-src-4.5.1 from Trolltech.

File size: 63.5 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4** Contact: Qt Software Information ([email protected])
5**
6** This file is part of the Qt3Support module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial Usage
10** Licensees holding valid Qt Commercial licenses may use this file in
11** accordance with the Qt Commercial License Agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and Nokia.
14**
15** GNU Lesser General Public License Usage
16** Alternatively, this file may be used under the terms of the GNU Lesser
17** General Public License version 2.1 as published by the Free Software
18** Foundation and appearing in the file LICENSE.LGPL included in the
19** packaging of this file. Please review the following information to
20** ensure the GNU Lesser General Public License version 2.1 requirements
21** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
22**
23** In addition, as a special exception, Nokia gives you certain
24** additional rights. These rights are described in the Nokia Qt LGPL
25** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
26** 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 are unsure which license is appropriate for your use, please
37** contact the sales department 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 \sa QSql::isNull()
1731*/
1732
1733void Q3DataTable::paintCell( QPainter * p, int row, int col, const QRect & cr,
1734 bool selected, const QColorGroup &cg )
1735{
1736 Q3Table::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->fieldPtr( 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()->fieldPtr( indexOf( col ) ), cr,
1749 selected );
1750 } else {
1751 if ( sqlCursor()->seek( row ) )
1752 paintField( p, sqlCursor()->fieldPtr( indexOf( col ) ), cr,
1753 selected );
1754 }
1755 } else {
1756 if ( sqlCursor()->seek( row ) )
1757 paintField( p, sqlCursor()->fieldPtr( 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 Q3DataTable::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 Q3DataTable::fieldAlignment( const QSqlField* /*field*/ )
1790{
1791 return Qt::AlignLeft | Qt::AlignVCenter; //## Reggie: add alignment to Q3Table
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 Q3DataTable::setSize( Q3SqlCursor* 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 Q3DataTable::setSqlCursor( Q3SqlCursor* 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 ( int i = 0; i < sqlCursor()->count(); ++i ) {
1855 addColumn( sqlCursor()->fieldPtr( i )->name(), sqlCursor()->fieldPtr( i )->name() );
1856 setColumnReadOnly( i, sqlCursor()->fieldPtr( 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 Q3DataTable::handleError( const QSqlError& e )
1878{
1879 d->dat.handleError( this, e );
1880}
1881
1882/*! \reimp
1883 */
1884
1885void Q3DataTable::keyPressEvent( QKeyEvent* e )
1886{
1887 switch( e->key() ) {
1888 case Qt::Key_Left:
1889 case Qt::Key_Right:
1890 case Qt::Key_Up:
1891 case Qt::Key_Down:
1892 case Qt::Key_Prior:
1893 case Qt::Key_Next:
1894 case Qt::Key_Home:
1895 case Qt::Key_End:
1896 case Qt::Key_F2:
1897 case Qt::Key_Enter: case Qt::Key_Return:
1898 case Qt::Key_Tab: case Qt::Key_BackTab:
1899 Q3Table::keyPressEvent( e );
1900 default:
1901 return;
1902 }
1903}
1904
1905/*! \reimp
1906*/
1907
1908void Q3DataTable::resizeData ( int )
1909{
1910
1911}
1912
1913/*! \reimp
1914*/
1915
1916Q3TableItem * Q3DataTable::item ( int, int ) const
1917{
1918 return 0;
1919}
1920
1921/*! \reimp
1922*/
1923
1924void Q3DataTable::setItem ( int , int , Q3TableItem * )
1925{
1926
1927}
1928
1929/*! \reimp
1930*/
1931
1932void Q3DataTable::clearCell ( int , int )
1933{
1934
1935}
1936
1937/*! \reimp
1938*/
1939
1940void Q3DataTable::setPixmap ( int , int , const QPixmap & )
1941{
1942
1943}
1944
1945/*! \reimp */
1946void Q3DataTable::takeItem ( Q3TableItem * )
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 Q3DataTable 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 Q3SqlEditorFactory
1959*/
1960
1961void Q3DataTable::installEditorFactory( Q3SqlEditorFactory * 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 Q3DataTable 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 Q3SqlPropertyMap
1977*/
1978
1979void Q3DataTable::installPropertyMap( Q3SqlPropertyMap* 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 Q3DataTable::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 Q3DataTable::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* Q3DataTable::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 Q3DataTable::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 Q3DataTable::sortDescending( int col )
2042{
2043 sortColumn( col, false );
2044}
2045
2046/*!
2047 \fn void Q3DataTable::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 Q3DataTable::refresh( Q3DataTable::Refresh mode )
2057{
2058 Q3SqlCursor* 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 const QSqlField* field = 0;
2082 int i;
2083 int fpos = -1;
2084 for ( i = 0; i < (int)d->fld.count(); ++i ) {
2085 if ( cur->fieldPtr( i ) && cur->fieldPtr( 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->fieldPtr( 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 Q3Table::showColumn( i ); // ugly but necessary
2102 Q3Table::hideColumn( i );
2103 } else {
2104 Q3Table::showColumn( i );
2105 }
2106 if ( d->fldWidth[ i ] > -1 )
2107 Q3Table::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( Q3DataTable::RefreshData ).
2130*/
2131
2132void Q3DataTable::refresh()
2133{
2134 refresh( RefreshData );
2135}
2136
2137/*!
2138 \internal
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 Q3DataTable::findBuffer( const QSqlIndex& idx, int atHint )
2147{
2148 Q3SqlCursor* 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 Q3DataTable::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 Q3DataTable::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 Q3DataTable::drawContents( QPainter * p, int cx, int cy, int cw, int ch )
2219{
2220 Q3Table::drawContents( p, cx, cy, cw, ch );
2221 if ( sqlCursor() && currentRow() >= 0 )
2222 sqlCursor()->seek( currentRow() );
2223}
2224
2225/*!
2226 \reimp
2227 */
2228void Q3DataTable::drawContents(QPainter *)
2229{
2230}
2231
2232/*!
2233 \reimp
2234*/
2235
2236void Q3DataTable::hideColumn( int col )
2237{
2238 d->fldHidden[col] = true;
2239 refresh( RefreshColumns );
2240}
2241
2242/*!
2243 \reimp
2244*/
2245
2246void Q3DataTable::showColumn( int col )
2247{
2248 d->fldHidden[col] = false;
2249 refresh( RefreshColumns );
2250}
2251
2252/*!
2253 \reimp
2254*/
2255void Q3DataTable::selectRow(int row)
2256{
2257 setCurrentCell(row, currentColumn());
2258}
2259
2260/*!
2261 \fn void Q3DataTable::currentChanged( QSqlRecord* record )
2262
2263 This signal is emitted whenever a new row is selected in the
2264 table. The \a record parameter points to the contents of the newly
2265 selected record.
2266*/
2267
2268/*!
2269 \fn void Q3DataTable::primeInsert( QSqlRecord* buf )
2270
2271 This signal is emitted after the cursor is primed for insert by
2272 the table, when an insert action is beginning on the table. The \a
2273 buf parameter points to the edit buffer being inserted. Connect to
2274 this signal in order to, for example, prime the record buffer with
2275 default data values.
2276*/
2277
2278/*!
2279 \fn void Q3DataTable::primeUpdate( QSqlRecord* buf )
2280
2281 This signal is emitted after the cursor is primed for update by
2282 the table, when an update action is beginning on the table. The \a
2283 buf parameter points to the edit buffer being updated. Connect to
2284 this signal in order to, for example, provide some visual feedback
2285 that the user is in 'edit mode'.
2286*/
2287
2288/*!
2289 \fn void Q3DataTable::primeDelete( QSqlRecord* buf )
2290
2291 This signal is emitted after the cursor is primed for delete by
2292 the table, when a delete action is beginning on the table. The \a
2293 buf parameter points to the edit buffer being deleted. Connect to
2294 this signal in order to, for example, record auditing information
2295 on deletions.
2296*/
2297
2298/*!
2299 \fn void Q3DataTable::beforeInsert( QSqlRecord* buf )
2300
2301 This signal is emitted just before the cursor's edit buffer is
2302 inserted into the database. The \a buf parameter points to the
2303 edit buffer being inserted. Connect to this signal to, for
2304 example, populate a key field with a unique sequence number.
2305*/
2306
2307/*!
2308 \fn void Q3DataTable::beforeUpdate( QSqlRecord* buf )
2309
2310 This signal is emitted just before the cursor's edit buffer is
2311 updated in the database. The \a buf parameter points to the edit
2312 buffer being updated. Connect to this signal when you want to
2313 transform the user's data behind-the-scenes.
2314*/
2315
2316/*!
2317 \fn void Q3DataTable::beforeDelete( QSqlRecord* buf )
2318
2319 This signal is emitted just before the currently selected record
2320 is deleted from the database. The \a buf parameter points to the
2321 edit buffer being deleted. Connect to this signal to, for example,
2322 copy some of the fields for later use.
2323*/
2324
2325/*!
2326 \fn void Q3DataTable::cursorChanged( QSql::Op mode )
2327
2328 This signal is emitted whenever the cursor record was changed due
2329 to an edit. The \a mode parameter is the type of edit that just
2330 took place.
2331*/
2332
2333#endif
2334
2335QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.