|
1 /**************************************************************************** |
|
2 ** |
|
3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). |
|
4 ** All rights reserved. |
|
5 ** Contact: Nokia Corporation (qt-info@nokia.com) |
|
6 ** |
|
7 ** This file is part of the Qt3Support module of the Qt Toolkit. |
|
8 ** |
|
9 ** $QT_BEGIN_LICENSE:LGPL$ |
|
10 ** No Commercial Usage |
|
11 ** This file contains pre-release code and may not be distributed. |
|
12 ** You may use this file in accordance with the terms and conditions |
|
13 ** contained in the Technology Preview License Agreement accompanying |
|
14 ** this package. |
|
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 ** If you have questions regarding the use of this file, please contact |
|
29 ** Nokia at qt-info@nokia.com. |
|
30 ** |
|
31 ** |
|
32 ** |
|
33 ** |
|
34 ** |
|
35 ** |
|
36 ** |
|
37 ** |
|
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 |
|
62 QT_BEGIN_NAMESPACE |
|
63 |
|
64 //#define QT_DEBUG_DATATABLE |
|
65 |
|
66 class Q3DataTablePrivate |
|
67 { |
|
68 public: |
|
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 |
|
121 void 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 |
|
248 Q3DataTable::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 |
|
268 Q3DataTable::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 |
|
279 void 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 |
|
297 Q3DataTable::~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 |
|
315 void 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 |
|
339 void 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 |
|
358 void 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 */ |
|
377 void 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 */ |
|
392 void 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 */ |
|
413 void 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 |
|
423 QString 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 |
|
442 void 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 |
|
468 void 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 |
|
484 void Q3DataTable::setSort( const QSqlIndex& sort ) |
|
485 { |
|
486 d->cur.setSort( sort ); |
|
487 } |
|
488 |
|
489 QStringList Q3DataTable::sort() const |
|
490 { |
|
491 return d->cur.sort(); |
|
492 } |
|
493 |
|
494 /*! |
|
495 Returns the cursor used by the data table. |
|
496 */ |
|
497 |
|
498 Q3SqlCursor* Q3DataTable::sqlCursor() const |
|
499 { |
|
500 return d->cur.cursor(); |
|
501 } |
|
502 |
|
503 void Q3DataTable::setConfirmEdits( bool confirm ) |
|
504 { |
|
505 d->dat.setConfirmEdits( confirm ); |
|
506 } |
|
507 |
|
508 void Q3DataTable::setConfirmInsert( bool confirm ) |
|
509 { |
|
510 d->dat.setConfirmInsert( confirm ); |
|
511 } |
|
512 |
|
513 void Q3DataTable::setConfirmUpdate( bool confirm ) |
|
514 { |
|
515 d->dat.setConfirmUpdate( confirm ); |
|
516 } |
|
517 |
|
518 void 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 |
|
535 bool 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 |
|
552 bool 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 |
|
569 bool 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 |
|
586 bool 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 |
|
603 void Q3DataTable::setConfirmCancels( bool confirm ) |
|
604 { |
|
605 d->dat.setConfirmCancels( confirm ); |
|
606 } |
|
607 |
|
608 bool 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 |
|
626 QWidget * 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 */ |
|
647 bool 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 */ |
|
764 void 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 */ |
|
774 void 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 */ |
|
817 void Q3DataTable::contentsMousePressEvent( QMouseEvent* e ) |
|
818 { |
|
819 Q3Table::contentsMousePressEvent( e ); |
|
820 } |
|
821 |
|
822 /*! \reimp */ |
|
823 QWidget* 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 */ |
|
849 void 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 */ |
|
891 void Q3DataTable::doInsertCurrent() |
|
892 { |
|
893 insertCurrent(); |
|
894 } |
|
895 |
|
896 /*! \internal */ |
|
897 void Q3DataTable::doUpdateCurrent() |
|
898 { |
|
899 updateCurrent(); |
|
900 if ( d->dat.mode() == QSql::None ) { |
|
901 viewport()->setFocus(); |
|
902 } |
|
903 } |
|
904 |
|
905 /*! \reimp */ |
|
906 void Q3DataTable::activateNextCell() |
|
907 { |
|
908 // if ( d->dat.mode() == QSql::None ) |
|
909 // Q3Table::activateNextCell(); |
|
910 } |
|
911 |
|
912 /*! \internal |
|
913 */ |
|
914 |
|
915 void 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 */ |
|
934 void 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 */ |
|
957 bool 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 |
|
1014 QWidget* 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 |
|
1042 bool 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 |
|
1098 void 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 |
|
1116 bool 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 |
|
1190 bool 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 |
|
1250 QSql::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 |
|
1263 QSql::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 */ |
|
1282 void 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 |
|
1357 void 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 |
|
1397 int 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 |
|
1410 bool 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 |
|
1422 void 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 |
|
1441 void Q3DataTable::setAutoEdit( bool autoEdit ) |
|
1442 { |
|
1443 d->dat.setAutoEdit( autoEdit ); |
|
1444 } |
|
1445 |
|
1446 bool 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 |
|
1459 void Q3DataTable::setNullText( const QString& nullText ) |
|
1460 { |
|
1461 d->nullTxt = nullText; |
|
1462 d->nullTxtChanged = true; |
|
1463 } |
|
1464 |
|
1465 QString 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 |
|
1478 void Q3DataTable::setTrueText( const QString& trueText ) |
|
1479 { |
|
1480 d->trueTxt = trueText; |
|
1481 } |
|
1482 |
|
1483 QString 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 |
|
1496 void Q3DataTable::setFalseText( const QString& falseText ) |
|
1497 { |
|
1498 d->falseTxt = falseText; |
|
1499 } |
|
1500 |
|
1501 QString 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 |
|
1514 void Q3DataTable::setDateFormat( const Qt::DateFormat f ) |
|
1515 { |
|
1516 d->datefmt = f; |
|
1517 } |
|
1518 |
|
1519 Qt::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 |
|
1530 int 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 |
|
1543 void 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 |
|
1556 void 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 |
|
1567 int 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 |
|
1579 QString 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 |
|
1596 QVariant 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 */ |
|
1613 void 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 */ |
|
1651 void Q3DataTable::sliderPressed() |
|
1652 { |
|
1653 disconnect( verticalScrollBar(), SIGNAL(valueChanged(int)), |
|
1654 this, SLOT(loadNextPage()) ); |
|
1655 } |
|
1656 |
|
1657 /*! \internal */ |
|
1658 void 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 |
|
1673 void 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 */ |
|
1694 void 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 */ |
|
1711 void 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 |
|
1731 void 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 |
|
1775 void 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 |
|
1787 int 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 |
|
1801 void 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 |
|
1840 void 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 */ |
|
1875 void Q3DataTable::handleError( const QSqlError& e ) |
|
1876 { |
|
1877 d->dat.handleError( this, e ); |
|
1878 } |
|
1879 |
|
1880 /*! \reimp |
|
1881 */ |
|
1882 |
|
1883 void 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 |
|
1906 void Q3DataTable::resizeData ( int ) |
|
1907 { |
|
1908 |
|
1909 } |
|
1910 |
|
1911 /*! \reimp |
|
1912 */ |
|
1913 |
|
1914 Q3TableItem * Q3DataTable::item ( int, int ) const |
|
1915 { |
|
1916 return 0; |
|
1917 } |
|
1918 |
|
1919 /*! \reimp |
|
1920 */ |
|
1921 |
|
1922 void Q3DataTable::setItem ( int , int , Q3TableItem * ) |
|
1923 { |
|
1924 |
|
1925 } |
|
1926 |
|
1927 /*! \reimp |
|
1928 */ |
|
1929 |
|
1930 void Q3DataTable::clearCell ( int , int ) |
|
1931 { |
|
1932 |
|
1933 } |
|
1934 |
|
1935 /*! \reimp |
|
1936 */ |
|
1937 |
|
1938 void Q3DataTable::setPixmap ( int , int , const QPixmap & ) |
|
1939 { |
|
1940 |
|
1941 } |
|
1942 |
|
1943 /*! \reimp */ |
|
1944 void 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 |
|
1959 void 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 |
|
1977 void 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 |
|
1990 void 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 |
|
2002 void 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 |
|
2013 QSqlRecord* 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 |
|
2028 void 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 |
|
2039 void 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 |
|
2054 void 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 |
|
2130 void 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 |
|
2144 bool 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 */ |
|
2158 QString 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 |
|
2190 void 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 |
|
2216 void 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 */ |
|
2226 void Q3DataTable::drawContents(QPainter *) |
|
2227 { |
|
2228 } |
|
2229 |
|
2230 /*! |
|
2231 \reimp |
|
2232 */ |
|
2233 |
|
2234 void Q3DataTable::hideColumn( int col ) |
|
2235 { |
|
2236 d->fldHidden[col] = true; |
|
2237 refresh( RefreshColumns ); |
|
2238 } |
|
2239 |
|
2240 /*! |
|
2241 \reimp |
|
2242 */ |
|
2243 |
|
2244 void Q3DataTable::showColumn( int col ) |
|
2245 { |
|
2246 d->fldHidden[col] = false; |
|
2247 refresh( RefreshColumns ); |
|
2248 } |
|
2249 |
|
2250 /*! |
|
2251 \reimp |
|
2252 */ |
|
2253 void 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 |
|
2333 QT_END_NAMESPACE |