source: trunk/src/sql/qsqlmanager_p.cpp

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

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

File size: 20.0 KB
RevLine 
[196]1/****************************************************************************
2**
3** Implementation of sql manager classes
4**
5** Created : 2000-11-03
6**
7** Copyright (C) 2005-2007 Trolltech ASA. All rights reserved.
8**
9** This file is part of the sql module of the Qt GUI Toolkit.
10**
11** This file may be distributed under the terms of the Q Public License
12** as defined by Trolltech ASA of Norway and appearing in the file
13** LICENSE.QPL included in the packaging of this file.
14**
15** This file may be distributed and/or modified under the terms of the
16** GNU General Public License version 2 as published by the Free Software
17** Foundation and appearing in the file LICENSE.GPL included in the
18** packaging of this file.
19**
20** Licensees holding valid Qt Enterprise Edition licenses may use this
21** file in accordance with the Qt Commercial License Agreement provided
22** with the Software.
23**
24** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
25** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
26**
27** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for
28** information about Qt Commercial License Agreements.
29** See http://www.trolltech.com/qpl/ for QPL licensing information.
30** See http://www.trolltech.com/gpl/ for GPL licensing information.
31**
32** Contact info@trolltech.com if any conditions of this licensing are
33** not clear to you.
34**
35**********************************************************************/
36
37#include "qsqlmanager_p.h"
38
39#ifndef QT_NO_SQL
40
41#include "qapplication.h"
42#include "qwidget.h"
43#include "qsqlcursor.h"
44#include "qsqlform.h"
45#include "qsqldriver.h"
46#include "qstring.h"
47#include "qmessagebox.h"
48#include "qbitarray.h"
49
50//#define QT_DEBUG_DATAMANAGER
51
52class QSqlCursorManagerPrivate
53{
54public:
55 QSqlCursorManagerPrivate()
56 : cur( 0 ), autoDelete( FALSE )
57 {}
58
59 QString ftr;
60 QStringList srt;
61 QSqlCursor* cur;
62 bool autoDelete;
63};
64
65/*!
66 \class QSqlCursorManager qsqlmanager_p.h
67 \brief The QSqlCursorManager class manages a database cursor.
68
69 \module sql
70
71 \internal
72
73 This class provides common cursor management functionality. This
74 includes saving and applying sorts and filters, refreshing (i.e.,
75 re-selecting) the cursor and searching for records within the
76 cursor.
77
78*/
79
80/*! \internal
81
82 Constructs a cursor manager.
83
84*/
85
86QSqlCursorManager::QSqlCursorManager()
87{
88 d = new QSqlCursorManagerPrivate;
89}
90
91
92/*! \internal
93
94 Destroys the object and frees any allocated resources.
95
96*/
97
98QSqlCursorManager::~QSqlCursorManager()
99{
100 if ( d->autoDelete )
101 delete d->cur;
102 delete d;
103}
104
105/*! \internal
106
107 Sets the manager's sort to the index \a sort. To apply the new
108 sort, use refresh().
109
110 */
111
112void QSqlCursorManager::setSort( const QSqlIndex& sort )
113{
114 setSort( sort.toStringList() );
115}
116
117/*! \internal
118
119 Sets the manager's sort to the stringlist \a sort. To apply the
120 new sort, use refresh().
121
122 */
123
124void QSqlCursorManager::setSort( const QStringList& sort )
125{
126 d->srt = sort;
127}
128
129/*! \internal
130
131 Returns the current sort, or an empty stringlist if there is none.
132
133*/
134
135QStringList QSqlCursorManager::sort() const
136{
137 return d->srt;
138}
139
140/*! \internal
141
142 Sets the manager's filter to the string \a filter. To apply the
143 new filter, use refresh().
144
145*/
146
147void QSqlCursorManager::setFilter( const QString& filter )
148{
149 d->ftr = filter;
150}
151
152/*! \internal
153
154 Returns the current filter, or an empty string if there is none.
155
156*/
157
158QString QSqlCursorManager::filter() const
159{
160 return d->ftr;
161}
162
163/*! \internal
164
165 Sets auto-delete to \a enable. If TRUE, the default cursor will
166 be deleted when necessary.
167
168 \sa autoDelete()
169*/
170
171void QSqlCursorManager::setAutoDelete( bool enable )
172{
173 d->autoDelete = enable;
174}
175
176
177/*! \internal
178
179 Returns TRUE if auto-deletion is enabled, otherwise FALSE.
180
181 \sa setAutoDelete()
182
183*/
184
185bool QSqlCursorManager::autoDelete() const
186{
187 return d->autoDelete;
188}
189
190/*! \internal
191
192 Sets the default cursor used by the manager to \a cursor. If \a
193 autoDelete is TRUE (the default is FALSE), the manager takes
194 ownership of the \a cursor pointer, which will be deleted when the
195 manager is destroyed, or when setCursor() is called again. To
196 activate the \a cursor use refresh().
197
198 \sa cursor()
199
200*/
201
202void QSqlCursorManager::setCursor( QSqlCursor* cursor, bool autoDelete )
203{
204 if ( d->autoDelete )
205 delete d->cur;
206 d->cur = cursor;
207 d->autoDelete = autoDelete;
208}
209
210/*! \internal
211
212 Returns a pointer to the default cursor used for navigation, or 0
213 if there is no default cursor.
214
215 \sa setCursor()
216
217*/
218
219QSqlCursor* QSqlCursorManager::cursor() const
220{
221 return d->cur;
222}
223
224
225/*! \internal
226
227 Refreshes the manager using the default cursor. The manager's
228 filter and sort are applied. Returns TRUE on success, FALSE if an
229 error occurred or there is no current cursor.
230
231 \sa setFilter() setSort()
232
233*/
234
235bool QSqlCursorManager::refresh()
236{
237 QSqlCursor* cur = cursor();
238 if ( !cur )
239 return FALSE;
240 QString currentFilter = d->ftr;
241 QStringList currentSort = d->srt;
242 QSqlIndex newSort = QSqlIndex::fromStringList( currentSort, cur );
243 return cur->select( currentFilter, newSort );
244}
245
246/* \internal
247
248 Returns TRUE if the \a buf field values that correspond to \a idx
249 match the field values in \a cur that correspond to \a idx.
250*/
251
252static bool index_matches( const QSqlCursor* cur, const QSqlRecord* buf,
253 const QSqlIndex& idx )
254{
255 bool indexEquals = FALSE;
256 for ( uint i = 0; i < idx.count(); ++i ) {
257 const QString fn( idx.field(i)->name() );
258 if ( cur->value( fn ) == buf->value( fn ) )
259 indexEquals = TRUE;
260 else {
261 indexEquals = FALSE;
262 break;
263 }
264 }
265 return indexEquals;
266}
267
268/*
269 Return less than, equal to or greater than 0 if buf1 is less than,
270 equal to or greater than buf2 according to fields described in idx.
271 (### Currently only uses first field.)
272*/
273
274static int compare_recs( const QSqlRecord* buf1, const QSqlRecord* buf2,
275 const QSqlIndex& idx )
276{
277 int cmp = 0;
278
279 int i = 0;
280 const QString fn( idx.field(i)->name() );
281 const QSqlField* f1 = buf1->field( fn );
282
283 if ( f1 ) {
284 switch ( f1->type() ) { // ### more types?
285 case QVariant::String:
286 case QVariant::CString:
287 cmp = f1->value().toString().simplifyWhiteSpace().compare(
288 buf2->value(fn).toString().simplifyWhiteSpace() );
289 break;
290 default:
291 if ( f1->value().toDouble() < buf2->value( fn ).toDouble() )
292 cmp = -1;
293 else if ( f1->value().toDouble() > buf2->value( fn ).toDouble() )
294 cmp = 1;
295 }
296 }
297
298 if ( idx.isDescending(i) )
299 cmp = -cmp;
300 return cmp;
301}
302
303#ifdef QT_DEBUG_DATAMANAGER
304static void debug_datamanager_buffer( const QString& msg, QSqlRecord* cursor )
305{
306 qDebug("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++");
307 qDebug( "%s", msg.latin1() );
308 for ( uint j = 0; j < cursor->count(); ++j ) {
309 qDebug( "%s", (cursor->field(j)->name() + " type:"
310 + QString(cursor->field(j)->value().typeName())
311 + " value:" + cursor->field(j)->value().toString())
312 .latin1() );
313 }
314}
315#endif
316
317
318/*! \internal
319
320 Relocates the default cursor to the record matching the cursor's
321edit buffer. Only the field names specified by \a idx are used to
322determine an exact match of the cursor to the edit buffer. However,
323other fields in the edit buffer are also used during the search,
324therefore all fields in the edit buffer should be primed with desired
325values for the record being sought. This function is typically used
326to relocate a cursor to the correct position after an insert or
327update. For example:
328
329\code
330 QSqlCursor* myCursor = myManager.cursor();
331 ...
332 QSqlRecord* buf = myCursor->primeUpdate();
333 buf->setValue( "name", "Ola" );
334 buf->setValue( "city", "Oslo" );
335 ...
336 myCursor->update(); // update current record
337 myCursor->select(); // refresh the cursor
338 myManager.findBuffer( myCursor->primaryIndex() ); // go to the updated record
339\endcode
340
341*/
342
343//## possibly add sizeHint parameter
344bool QSqlCursorManager::findBuffer( const QSqlIndex& idx, int atHint )
345{
346#ifdef QT_DEBUG_DATAMANAGER
347 qDebug("QSqlCursorManager::findBuffer:");
348#endif
349 QSqlCursor* cur = cursor();
350 if ( !cur )
351 return FALSE;
352 if ( !cur->isActive() )
353 return FALSE;
354 if ( !idx.count() ) {
355 if ( cur->at() == QSql::BeforeFirst )
356 cur->next();
357 return FALSE;
358 }
359 QSqlRecord* buf = cur->editBuffer();
360 bool indexEquals = FALSE;
361#ifdef QT_DEBUG_DATAMANAGER
362 qDebug(" Checking hint...");
363#endif
364 /* check the hint */
365 if ( cur->seek( atHint ) )
366 indexEquals = index_matches( cur, buf, idx );
367
368 if ( !indexEquals ) {
369#ifdef QT_DEBUG_DATAMANAGER
370 qDebug(" Checking current page...");
371#endif
372 /* check current page */
373 int pageSize = 20;
374 int startIdx = QMAX( atHint - pageSize, 0 );
375 int endIdx = atHint + pageSize;
376 for ( int j = startIdx; j <= endIdx; ++j ) {
377 if ( cur->seek( j ) ) {
378 indexEquals = index_matches( cur, buf, idx );
379 if ( indexEquals )
380 break;
381 }
382 }
383 }
384
385 if ( !indexEquals && cur->driver()->hasFeature( QSqlDriver::QuerySize )
386 && cur->sort().count() ) {
387#ifdef QT_DEBUG_DATAMANAGER
388 qDebug(" Using binary search...");
389#endif
390 // binary search based on record buffer and current sort fields
391 int lo = 0;
392 int hi = cur->size();
393 int mid;
394 if ( compare_recs( buf, cur, cur->sort() ) >= 0 )
395 lo = cur->at();
396 while ( lo != hi ) {
397 mid = lo + (hi - lo) / 2;
398 if ( !cur->seek( mid ) )
399 break;
400 if ( index_matches( cur, buf, idx ) ) {
401 indexEquals = TRUE;
402 break;
403 }
404 int c = compare_recs( buf, cur, cur->sort() );
405 if ( c < 0 ) {
406 hi = mid;
407 } else if ( c == 0 ) {
408 // found it, but there may be duplicates
409 int at = mid;
410 do {
411 mid--;
412 if ( !cur->seek( mid ) )
413 break;
414 if ( index_matches( cur, buf, idx ) ) {
415 indexEquals = TRUE;
416 break;
417 }
418 } while ( compare_recs( buf, cur, cur->sort() ) == 0 );
419
420 if ( !indexEquals ) {
421 mid = at;
422 do {
423 mid++;
424 if ( !cur->seek( mid ) )
425 break;
426 if ( index_matches( cur, buf, idx ) ) {
427 indexEquals = TRUE;
428 break;
429 }
430 } while ( compare_recs( buf, cur, cur->sort() ) == 0 );
431 }
432 break;
433 } else if ( c > 0 ) {
434 lo = mid + 1;
435 }
436 }
437 }
438
439 if ( !indexEquals ) {
440#ifdef QT_DEBUG_DATAMANAGER
441 qDebug(" Using brute search...");
442#endif
443#ifndef QT_NO_CURSOR
444 QApplication::setOverrideCursor( Qt::waitCursor );
445#endif
446 /* give up, use brute force */
447 int startIdx = 0;
448 if ( cur->at() != startIdx ) {
449 cur->seek( startIdx );
450 }
451 for ( ;; ) {
452 indexEquals = FALSE;
453 indexEquals = index_matches( cur, buf, idx );
454 if ( indexEquals )
455 break;
456 if ( !cur->next() )
457 break;
458 }
459#ifndef QT_NO_CURSOR
460 QApplication::restoreOverrideCursor();
461#endif
462 }
463#ifdef QT_DEBUG_DATAMANAGER
464 qDebug(" Done, result:" + QString::number( indexEquals ) );
465#endif
466 return indexEquals;
467}
468
469#ifndef QT_NO_SQL_FORM
470
471class QSqlFormManagerPrivate
472{
473public:
474 QSqlFormManagerPrivate() : frm(0), rcd(0) {}
475 QSqlForm* frm;
476 QSqlRecord* rcd;
477};
478
479
480/*! \internal
481
482 Creates a form manager.
483
484*/
485
486QSqlFormManager::QSqlFormManager()
487{
488 d = new QSqlFormManagerPrivate();
489}
490
491/*! \internal
492
493 Destroys the object and frees any allocated resources.
494
495*/
496
497QSqlFormManager::~QSqlFormManager()
498{
499 delete d;
500}
501
502/*! \internal
503
504 Clears the default form values. If there is no default form,
505 nothing happens,
506
507*/
508
509void QSqlFormManager::clearValues()
510{
511 if ( form() )
512 form()->clearValues();
513}
514
515/*! \internal
516
517 Sets the form used by the form manager to \a form. If a record has
518 already been assigned to the form manager, that record is also used by
519 the \a form to display data.
520
521 \sa form()
522
523*/
524
525void QSqlFormManager::setForm( QSqlForm* form )
526{
527 d->frm = form;
528 if ( d->rcd && d->frm )
529 d->frm->setRecord( d->rcd );
530}
531
532
533/*! \internal
534
535 Returns the default form used by the form manager, or 0 if there is
536 none.
537
538 \sa setForm()
539
540*/
541
542QSqlForm* QSqlFormManager::form()
543{
544 return d->frm;
545}
546
547
548/*! \internal
549
550 Sets the record used by the form manager to \a record. If a form has
551 already been assigned to the form manager, \a record is also used by
552 the default form to display data.
553
554 \sa record()
555
556*/
557
558void QSqlFormManager::setRecord( QSqlRecord* record )
559{
560 d->rcd = record;
561 if ( d->frm ) {
562 d->frm->setRecord( d->rcd );
563 }
564}
565
566
567/*! \internal
568
569 Returns the default record used by the form manager, or 0 if there is
570 none.
571
572 \sa setRecord()
573*/
574
575QSqlRecord* QSqlFormManager::record()
576{
577 return d->rcd;
578}
579
580
581/*! \internal
582
583 Causes the default form to read its fields . If there is no
584 default form, nothing happens.
585
586 \sa setForm()
587
588*/
589
590void QSqlFormManager::readFields()
591{
592 if ( d->frm ) {
593 d->frm->readFields();
594 }
595}
596
597/*! \internal
598
599 Causes the default form to write its fields . If there is no
600 default form, nothing happens.
601
602 \sa setForm()
603
604*/
605
606void QSqlFormManager::writeFields()
607{
608 if ( d->frm ) {
609 d->frm->writeFields();
610 }
611}
612
613#endif // QT_NO_SQL_FORM
614
615class QDataManagerPrivate
616{
617public:
618 QDataManagerPrivate()
619 : mode( QSql::None ), autoEd( TRUE ), confEdits( 3 ),
620 confCancs( FALSE ) {}
621 QSql::Op mode;
622 bool autoEd;
623 QBitArray confEdits;
624 bool confCancs;
625
626};
627
628/*!
629 \class QDataManager qsqlmanager_p.h
630 \ingroup database
631
632 \brief The QDataManager class is an internal class for implementing
633 the data-aware widgets.
634
635 \internal
636
637 QDataManager is a strictly internal class that acts as a base class
638 for other data-aware widgets.
639
640*/
641
642
643/*! \internal
644
645 Constructs an empty data handler.
646
647*/
648
649QDataManager::QDataManager()
650{
651 d = new QDataManagerPrivate();
652}
653
654
655/*! \internal
656
657 Destroys the object and frees any allocated resources.
658
659*/
660
661QDataManager::~QDataManager()
662{
663 delete d;
664}
665
666
667/*! \internal
668
669 Virtual function which is called when an error has occurred The
670 default implementation displays a warning message to the user with
671 information about the error.
672
673*/
674void QDataManager::handleError( QWidget* parent, const QSqlError& e )
675{
676#ifndef QT_NO_MESSAGEBOX
677 if (e.driverText().isEmpty() && e.databaseText().isEmpty()) {
678 QMessageBox::warning ( parent, "Warning", "An error occurred while accessing the database");
679 } else {
680 QMessageBox::warning ( parent, "Warning", e.driverText() + "\n" + e.databaseText(),
681 0, 0 );
682 }
683#endif // QT_NO_MESSAGEBOX
684}
685
686
687/*! \internal
688
689 Sets the internal mode to \a m.
690
691*/
692
693void QDataManager::setMode( QSql::Op m )
694{
695 d->mode = m;
696}
697
698
699/*! \internal
700
701 Returns the current mode.
702
703*/
704
705QSql::Op QDataManager::mode() const
706{
707 return d->mode;
708}
709
710
711/*! \internal
712
713 Sets the auto-edit mode to \a auto.
714
715*/
716
717void QDataManager::setAutoEdit( bool autoEdit )
718{
719 d->autoEd = autoEdit;
720}
721
722
723
724/*! \internal
725
726 Returns TRUE if auto-edit mode is enabled; otherwise returns FALSE.
727
728*/
729
730bool QDataManager::autoEdit() const
731{
732 return d->autoEd;
733}
734
735/*! \internal
736
737 If \a confirm is TRUE, all edit operations (inserts, updates and
738 deletes) will be confirmed by the user. If \a confirm is FALSE (the
739 default), all edits are posted to the database immediately.
740
741*/
742void QDataManager::setConfirmEdits( bool confirm )
743{
744 d->confEdits.fill( confirm );
745}
746
747/*! \internal
748
749 If \a confirm is TRUE, all inserts will be confirmed by the user.
750 If \a confirm is FALSE (the default), all edits are posted to the
751 database immediately.
752
753*/
754
755void QDataManager::setConfirmInsert( bool confirm )
756{
757 d->confEdits[ QSql::Insert ] = confirm;
758}
759
760/*! \internal
761
762 If \a confirm is TRUE, all updates will be confirmed by the user.
763 If \a confirm is FALSE (the default), all edits are posted to the
764 database immediately.
765
766*/
767
768void QDataManager::setConfirmUpdate( bool confirm )
769{
770 d->confEdits[ QSql::Update ] = confirm;
771}
772
773/*! \internal
774
775 If \a confirm is TRUE, all deletes will be confirmed by the user.
776 If \a confirm is FALSE (the default), all edits are posted to the
777 database immediately.
778
779*/
780
781void QDataManager::setConfirmDelete( bool confirm )
782{
783 d->confEdits[ QSql::Delete ] = confirm;
784}
785
786/*! \internal
787
788 Returns TRUE if the table confirms all edit operations (inserts,
789 updates and deletes), otherwise returns FALSE.
790*/
791
792bool QDataManager::confirmEdits() const
793{
794 return ( confirmInsert() && confirmUpdate() && confirmDelete() );
795}
796
797/*! \internal
798
799 Returns TRUE if the table confirms inserts, otherwise returns
800 FALSE.
801*/
802
803bool QDataManager::confirmInsert() const
804{
805 return d->confEdits[ QSql::Insert ];
806}
807
808/*! \internal
809
810 Returns TRUE if the table confirms updates, otherwise returns
811 FALSE.
812*/
813
814bool QDataManager::confirmUpdate() const
815{
816 return d->confEdits[ QSql::Update ];
817}
818
819/*! \internal
820
821 Returns TRUE if the table confirms deletes, otherwise returns
822 FALSE.
823*/
824
825bool QDataManager::confirmDelete() const
826{
827 return d->confEdits[ QSql::Delete ];
828}
829
830/*! \internal
831
832 If \a confirm is TRUE, all cancels will be confirmed by the user
833 through a message box. If \a confirm is FALSE (the default), all
834 cancels occur immediately.
835*/
836
837void QDataManager::setConfirmCancels( bool confirm )
838{
839 d->confCancs = confirm;
840}
841
842/*! \internal
843
844 Returns TRUE if the table confirms cancels, otherwise returns FALSE.
845*/
846
847bool QDataManager::confirmCancels() const
848{
849 return d->confCancs;
850}
851
852/*! \internal
853
854 Virtual function which returns a confirmation for an edit of mode \a
855 m. Derived classes can reimplement this function and provide their
856 own confirmation dialog. The default implementation uses a message
857 box which prompts the user to confirm the edit action. The dialog
858 is centered over \a parent.
859
860*/
861
862QSql::Confirm QDataManager::confirmEdit( QWidget* parent, QSql::Op m )
863{
864 int ans = 2;
865 if ( m == QSql::Delete ) {
866#ifndef QT_NO_MESSAGEBOX
867 ans = QMessageBox::information( parent,
868 qApp->translate( "QSql", "Delete" ),
869 qApp->translate( "QSql", "Delete this record?" ),
870 qApp->translate( "QSql", "Yes" ),
871 qApp->translate( "QSql", "No" ),
872 QString::null, 0, 1 );
873#else
874 ans = QSql::No;
875#endif // QT_NO_MESSAGEBOX
876 } else if ( m != QSql::None ) {
877 QString caption;
878 if ( m == QSql::Insert ) {
879 caption = qApp->translate( "QSql", "Insert" );
880 } else { // QSql::Update
881 caption = qApp->translate( "QSql", "Update" );
882 }
883#ifndef QT_NO_MESSAGEBOX
884 ans = QMessageBox::information( parent, caption,
885 qApp->translate( "QSql", "Save edits?" ),
886 qApp->translate( "QSql", "Yes" ),
887 qApp->translate( "QSql", "No" ),
888 qApp->translate( "QSql", "Cancel" ),
889 0, 2 );
890#else
891 ans = QSql::No;
892#endif // QT_NO_MESSAGEBOX
893 }
894
895 switch ( ans ) {
896 case 0:
897 return QSql::Yes;
898 case 1:
899 return QSql::No;
900 default:
901 return QSql::Cancel;
902 }
903}
904
905/*! \internal
906
907 Virtual function which returns a confirmation for cancelling an edit
908 mode \a m. Derived classes can reimplement this function and
909 provide their own confirmation dialog. The default implementation
910 uses a message box which prompts the user to confirm the edit
911 action. The dialog is centered over \a parent.
912
913
914*/
915
916QSql::Confirm QDataManager::confirmCancel( QWidget* parent, QSql::Op )
917{
918#ifndef QT_NO_MESSAGEBOX
919 switch ( QMessageBox::information( parent,
920 qApp->translate( "QSql", "Confirm" ),
921 qApp->translate( "QSql", "Cancel your edits?" ),
922 qApp->translate( "QSql", "Yes" ),
923 qApp->translate( "QSql", "No" ),
924 QString::null, 0, 1 ) ) {
925 case 0:
926 return QSql::Yes;
927 case 1:
928 return QSql::No;
929 default:
930 return QSql::Cancel;
931 }
932#else
933 return QSql::Yes;
934#endif // QT_NO_MESSAGEBOX
935}
936
937#endif
Note: See TracBrowser for help on using the repository browser.