diff options
author | Alexis Menard <alexis.menard@nokia.com> | 2009-04-17 14:06:06 (GMT) |
---|---|---|
committer | Alexis Menard <alexis.menard@nokia.com> | 2009-04-17 14:06:06 (GMT) |
commit | f15b8a83e2e51955776a3f07cb85ebfc342dd8ef (patch) | |
tree | c5dc684986051654898db11ce73e03b9fec8db99 /src/qt3support/sql | |
download | Qt-f15b8a83e2e51955776a3f07cb85ebfc342dd8ef.zip Qt-f15b8a83e2e51955776a3f07cb85ebfc342dd8ef.tar.gz Qt-f15b8a83e2e51955776a3f07cb85ebfc342dd8ef.tar.bz2 |
Initial import of statemachine branch from the old kinetic repository
Diffstat (limited to 'src/qt3support/sql')
23 files changed, 9282 insertions, 0 deletions
diff --git a/src/qt3support/sql/q3databrowser.cpp b/src/qt3support/sql/q3databrowser.cpp new file mode 100644 index 0000000..fd20f66 --- /dev/null +++ b/src/qt3support/sql/q3databrowser.cpp @@ -0,0 +1,1281 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt3Support module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "q3databrowser.h" + +#ifndef QT_NO_SQL_VIEW_WIDGETS + +#include "q3sqlform.h" +#include "private/q3sqlmanager_p.h" +#include "qsqlresult.h" + +QT_BEGIN_NAMESPACE + +class Q3DataBrowserPrivate +{ +public: + Q3DataBrowserPrivate() : boundaryCheck(true), readOnly(false) {} + Q3SqlCursorManager cur; + Q3SqlFormManager frm; + Q3DataManager dat; + bool boundaryCheck; + bool readOnly; +}; + +/*! + \class Q3DataBrowser + \brief The Q3DataBrowser class provides data manipulation and + navigation for data entry forms. + + \compat + + A high-level API is provided for navigating through data records + in a cursor, for inserting, updating and deleting records, and for + refreshing data in the display. + + If you want a read-only form to present database data use + Q3DataView; if you want a table-based presentation of your data use + Q3DataTable. + + A Q3DataBrowser is used to associate a dataset with a form in much + the same way as a Q3DataTable associates a dataset with a table. + Once the data browser has been constructed it can be associated + with a dataset with setSqlCursor(), and with a form with + setForm(). Boundary checking, sorting and filtering can be set + with setBoundaryChecking(), setSort() and setFilter(), + respectively. + + The insertCurrent() function reads the fields from the default + form into the default cursor and performs the insert. The + updateCurrent() and deleteCurrent() functions perform similarly to + update and delete the current record respectively. + + The user can be asked to confirm all edits with setConfirmEdits(). + For more precise control use setConfirmInsert(), + setConfirmUpdate(), setConfirmDelete() and setConfirmCancels(). + Use setAutoEdit() to control the behavior of the form when the + user edits a record and then navigates. + + The record set is navigated using first(), next(), prev(), last() + and seek(). The form's display is updated with refresh(). When + navigation takes place the firstRecordAvailable(), + lastRecordAvailable(), nextRecordAvailable() and + prevRecordAvailable() signals are emitted. When the cursor record + is changed due to navigation the cursorChanged() signal is + emitted. + + If you want finer control of the insert, update and delete + processes then you can use the lower level functions to perform + these operations as described below. + + The form is populated with data from the database with + readFields(). If the user is allowed to edit, (see setReadOnly()), + write the form's data back to the cursor's edit buffer with + writeFields(). You can clear the values in the form with + clearValues(). Editing is performed as follows: + \list + \i \e insert When the data browser enters insertion mode it emits the + primeInsert() signal which you can connect to, for example to + pre-populate fields. Call writeFields() to write the user's edits to + the cursor's edit buffer then call insert() to insert the record + into the database. The beforeInsert() signal is emitted just before + the cursor's edit buffer is inserted into the database; connect to + this for example, to populate fields such as an auto-generated + primary key. + \i \e update For updates the primeUpdate() signal is emitted when + the data browser enters update mode. After calling writeFields() + call update() to update the record and connect to the beforeUpdate() + signal to manipulate the user's data before the update takes place. + \i \e delete For deletion the primeDelete() signal is emitted when + the data browser enters deletion mode. After calling writeFields() + call del() to delete the record and connect to the beforeDelete() + signal, for example to record an audit of the deleted record. + \endlist + +*/ + +/*! + \enum Q3DataBrowser::Boundary + + This enum describes where the data browser is positioned. + + \value Unknown the boundary cannot be determined (usually because + there is no default cursor, or the default cursor is not active). + + \value None the browser is not positioned on a boundary, but it is + positioned on a record somewhere in the middle. + + \value BeforeBeginning the browser is positioned before the + first available record. + + \value Beginning the browser is positioned at the first record. + + \value End the browser is positioned at the last + record. + + \value AfterEnd the browser is positioned after the last + available record. +*/ + +/*! + Constructs a data browser which is a child of \a parent, with the + name \a name and widget flags set to \a fl. +*/ + +Q3DataBrowser::Q3DataBrowser(QWidget *parent, const char *name, Qt::WindowFlags fl) + : QWidget(parent, name, fl) +{ + d = new Q3DataBrowserPrivate(); + d->dat.setMode(QSql::Update); +} + +/*! + Destroys the object and frees any allocated resources. +*/ + +Q3DataBrowser::~Q3DataBrowser() +{ + delete d; +} + + +/*! + Returns an enum indicating the boundary status of the browser. + + This is achieved by moving the default cursor and checking the + position, however the current default form values will not be + altered. After checking for the boundary, the cursor is moved back + to its former position. See \l Q3DataBrowser::Boundary. + + \sa Boundary +*/ + +Q3DataBrowser::Boundary Q3DataBrowser::boundary() +{ + Q3SqlCursor* cur = d->cur.cursor(); + if (!cur || !cur->isActive()) + return Unknown; + if (!cur->isValid()) { + if (cur->at() == QSql::BeforeFirst) + return BeforeBeginning; + if (cur->at() == QSql::AfterLast) + return AfterEnd; + return Unknown; + } + if (cur->at() == 0) + return Beginning; + int currentAt = cur->at(); + + Boundary b = None; + if (!cur->previous()) + b = Beginning; + else + cur->seek(currentAt); + if (b == None && !cur->next()) + b = End; + cur->seek(currentAt); + return b; +} + + +/*! + \property Q3DataBrowser::boundaryChecking + \brief whether boundary checking is active + + When boundary checking is active (the default), signals are + emitted indicating the current position of the default cursor. + + \sa boundary() +*/ + +void Q3DataBrowser::setBoundaryChecking(bool active) +{ + d->boundaryCheck = active; +} + +bool Q3DataBrowser::boundaryChecking() const +{ + return d->boundaryCheck; +} + +/*! + \property Q3DataBrowser::sort + \brief the data browser's sort + + The data browser's sort affects the order in which records are + viewed in the browser. Call refresh() to apply the new sort. + + When retrieving the sort property, a string list is returned in + the form 'fieldname order', e.g. 'id ASC', 'surname DESC'. + + There is no default sort. + + Note that if you want to iterate over the list, you should iterate + over a copy, e.g. + \snippet doc/src/snippets/code/src_qt3support_sql_q3databrowser.cpp 0 +*/ + +void Q3DataBrowser::setSort(const QStringList& sort) +{ + d->cur.setSort(sort); +} + +/*! + \overload + + Sets the data browser's sort to the QSqlIndex \a sort. To apply + the new sort, use refresh(). + +*/ +void Q3DataBrowser::setSort(const QSqlIndex& sort) +{ + d->cur.setSort(sort); +} + +QStringList Q3DataBrowser::sort() const +{ + return d->cur.sort(); +} + + +/*! + \property Q3DataBrowser::filter + \brief the data browser's filter + + The filter applies to the data shown in the browser. Call + refresh() to apply the new filter. A filter is a string containing + a SQL WHERE clause without the WHERE keyword, e.g. "id>1000", + "name LIKE 'A%'", etc. + + There is no default filter. + + \sa sort() +*/ + +void Q3DataBrowser::setFilter(const QString& filter) +{ + d->cur.setFilter(filter); +} + + +QString Q3DataBrowser::filter() const +{ + return d->cur.filter(); +} + + +/*! + Sets the default cursor used by the data browser to \a cursor. If + \a autoDelete is true (the default is false), the data browser + takes ownership of the \a cursor pointer, which will be deleted + when the browser is destroyed, or when setSqlCursor() is called + again. To activate the \a cursor use refresh(). The cursor's edit + buffer is used in the default form to browse and edit records. + + \sa sqlCursor() form() setForm() +*/ + +void Q3DataBrowser::setSqlCursor(Q3SqlCursor* cursor, bool autoDelete) +{ + if (!cursor) + return; + d->cur.setCursor(cursor, autoDelete); + d->frm.setRecord(cursor->editBuffer()); + if (cursor->isReadOnly()) + setReadOnly(true); +} + + +/*! + Returns the default cursor used for navigation, or 0 if there is + no default cursor. + + \sa setSqlCursor() +*/ + +Q3SqlCursor* Q3DataBrowser::sqlCursor() const +{ + return d->cur.cursor(); +} + + +/*! + Sets the browser's default form to \a form. The cursor and all + navigation and data manipulation functions that the browser + provides become available to the \a form. +*/ + +void Q3DataBrowser::setForm(Q3SqlForm* form) +{ + d->frm.setForm(form); +} + + +/*! + Returns the data browser's default form or 0 if no form has been + set. +*/ + +Q3SqlForm* Q3DataBrowser::form() +{ + return d->frm.form(); +} + +/*! + \property Q3DataBrowser::readOnly + \brief whether the browser is read-only + + The default is false, i.e. data can be edited. If the data browser + is read-only, no database edits will be allowed. +*/ + +void Q3DataBrowser::setReadOnly(bool active) +{ + d->readOnly = active; +} + +bool Q3DataBrowser::isReadOnly() const +{ + return d->readOnly; +} + +void Q3DataBrowser::setConfirmEdits(bool confirm) +{ + d->dat.setConfirmEdits(confirm); +} + +/*! + \property Q3DataBrowser::confirmInsert + \brief whether the data browser confirms insertions + + If this property is true, the browser confirms insertions, + otherwise insertions happen immediately. + + \sa confirmCancels() confirmEdits() confirmUpdate() confirmDelete() confirmEdit() +*/ + +void Q3DataBrowser::setConfirmInsert(bool confirm) +{ + d->dat.setConfirmInsert(confirm); +} + +/*! + \property Q3DataBrowser::confirmUpdate + \brief whether the browser confirms updates + + If this property is true, the browser confirms updates, otherwise + updates happen immediately. + + \sa confirmCancels() confirmEdits() confirmInsert() confirmDelete() confirmEdit() +*/ + +void Q3DataBrowser::setConfirmUpdate(bool confirm) +{ + d->dat.setConfirmUpdate(confirm); +} + +/*! + \property Q3DataBrowser::confirmDelete + \brief whether the browser confirms deletions + + If this property is true, the browser confirms deletions, + otherwise deletions happen immediately. + + \sa confirmCancels() confirmEdits() confirmUpdate() confirmInsert() confirmEdit() +*/ + +void Q3DataBrowser::setConfirmDelete(bool confirm) +{ + d->dat.setConfirmDelete(confirm); +} + +/*! + \property Q3DataBrowser::confirmEdits + \brief whether the browser confirms edits + + If this property is true, the browser confirms all edit operations + (insertions, updates and deletions), otherwise all edit operations + happen immediately. Confirmation is achieved by presenting the + user with a message box -- this behavior can be changed by + reimplementing the confirmEdit() function, + + \sa confirmEdit() confirmCancels() confirmInsert() confirmUpdate() confirmDelete() +*/ + +bool Q3DataBrowser::confirmEdits() const +{ + return (d->dat.confirmEdits()); +} + +bool Q3DataBrowser::confirmInsert() const +{ + return (d->dat.confirmInsert()); +} + +bool Q3DataBrowser::confirmUpdate() const +{ + return (d->dat.confirmUpdate()); +} + +bool Q3DataBrowser::confirmDelete() const +{ + return (d->dat.confirmDelete()); +} + +/*! + \property Q3DataBrowser::confirmCancels + \brief whether the browser confirms cancel operations + + If this property is true, all cancels must be confirmed by the + user through a message box (this behavior can be changed by + overriding the confirmCancel() function), otherwise all cancels + occur immediately. The default is false. + + \sa confirmEdits() confirmCancel() +*/ + +void Q3DataBrowser::setConfirmCancels(bool confirm) +{ + d->dat.setConfirmCancels(confirm); +} + +bool Q3DataBrowser::confirmCancels() const +{ + return d->dat.confirmCancels(); +} + +/*! + \property Q3DataBrowser::autoEdit + \brief whether the browser automatically applies edits + + The default value for this property is true. When the user begins + an insertion or an update on a form there are two possible + outcomes when they navigate to another record: + + \list + \i the insert or update is is performed -- this occurs if autoEdit is true + \i the insert or update is discarded -- this occurs if autoEdit is false + \endlist +*/ + +void Q3DataBrowser::setAutoEdit(bool autoEdit) +{ + d->dat.setAutoEdit(autoEdit); +} + +bool Q3DataBrowser::autoEdit() const +{ + return d->dat.autoEdit(); +} + +/*! + \fn void Q3DataBrowser::firstRecordAvailable(bool available) + + This signal is emitted whenever the position of the cursor + changes. The \a available parameter indicates whether or not the + first record in the default cursor is available. +*/ + +/*! + \fn void Q3DataBrowser::lastRecordAvailable(bool available) + + This signal is emitted whenever the position of the cursor + changes. The \a available parameter indicates whether or not the + last record in the default cursor is available. +*/ + +/*! + \fn void Q3DataBrowser::nextRecordAvailable(bool available) + + This signal is emitted whenever the position of the cursor + changes. The \a available parameter indicates whether or not the + next record in the default cursor is available. +*/ + + +/*! + \fn void Q3DataBrowser::prevRecordAvailable(bool available) + + This signal is emitted whenever the position of the cursor + changes. The \a available parameter indicates whether or not the + previous record in the default cursor is available. +*/ + + +/*! + \fn void Q3DataBrowser::currentChanged(const QSqlRecord* record) + + This signal is emitted whenever the current cursor position + changes. The \a record parameter points to the contents of the + current cursor's record. +*/ + + +/*! + \fn void Q3DataBrowser::primeInsert(QSqlRecord* buf) + + This signal is emitted when the data browser enters insertion + mode. The \a buf parameter points to the record buffer that is to + be inserted. Connect to this signal to, for example, prime the + record buffer with default data values, auto-numbered fields etc. + (Note that Q3SqlCursor::primeInsert() is \e not called on the + default cursor, as this would corrupt values in the form.) + + \sa insert() +*/ + + +/*! + \fn void Q3DataBrowser::primeUpdate(QSqlRecord* buf) + + This signal is emitted when the data browser enters update mode. + Note that during navigation (first(), last(), next(), prev()), + each record that is shown in the default form is primed for + update. The \a buf parameter points to the record buffer being + updated. (Note that Q3SqlCursor::primeUpdate() is \e not called on + the default cursor, as this would corrupt values in the form.) + Connect to this signal in order to, for example, keep track of + which records have been updated, perhaps for auditing purposes. + + \sa update() +*/ + +/*! + \fn void Q3DataBrowser::primeDelete(QSqlRecord* buf) + + This signal is emitted when the data browser enters deletion mode. + The \a buf parameter points to the record buffer being deleted. + (Note that Q3SqlCursor::primeDelete() is \e not called on the + default cursor, as this would corrupt values in the form.) + Connect to this signal in order to, for example, save a copy of + the deleted record for auditing purposes. + + \sa del() +*/ + + +/*! + \fn void Q3DataBrowser::cursorChanged(Q3SqlCursor::Mode mode) + + This signal is emitted whenever the cursor record was changed due + to navigation. The \a mode parameter is the edit that just took + place, e.g. Insert, Update or Delete. See \l Q3SqlCursor::Mode. +*/ + + +/*! + Refreshes the data browser's data using the default cursor. The + browser's current filter and sort are applied if they have been + set. + + \sa setFilter() setSort() +*/ + +void Q3DataBrowser::refresh() +{ + d->cur.refresh(); +} + + +/*! + Performs an insert operation on the data browser's cursor. If + there is no default cursor or no default form, nothing happens. + + If auto-editing is on (see setAutoEdit()), the following happens: + + \list + \i If the browser is already actively inserting a record, + the current form's data is inserted into the database. + \i If the browser is not inserting a record, but the current record + was changed by the user, the record is updated in the database with + the current form's data (i.e. with the changes). + \endlist + + If there is an error handling any of the above auto-edit actions, + handleError() is called and no insert or update is performed. + + If no error occurred, or auto-editing is not enabled, the data browser + begins actively inserting a record into the database by performing the + following actions: + + \list + \i The default cursor is primed for insert using Q3SqlCursor::primeInsert(). + \i The primeInsert() signal is emitted. + \i The form is updated with the values in the default cursor's. + edit buffer so that the user can fill in the values to be inserted. + \endlist + +*/ + +void Q3DataBrowser::insert() +{ + QSqlRecord* buf = d->frm.record(); + Q3SqlCursor* cur = d->cur.cursor(); + if (!buf || !cur) + return; + bool doIns = true; + QSql::Confirm conf = QSql::Yes; + switch (d->dat.mode()) { + case QSql::Insert: + if (autoEdit()) { + if (confirmInsert()) + conf = confirmEdit(QSql::Insert); + switch (conf) { + case QSql::Yes: + insertCurrent(); + break; + case QSql::No: + break; + case QSql::Cancel: + doIns = false; + break; + } + } + break; + default: + if (autoEdit() && currentEdited()) { + if (confirmUpdate()) + conf = confirmEdit(QSql::Update); + switch (conf) { + case QSql::Yes: + updateCurrent(); + break; + case QSql::No: + break; + case QSql::Cancel: + doIns = false; + break; + } + } + break; + } + if (doIns) { + d->dat.setMode(QSql::Insert); + sqlCursor()->primeInsert(); + emit primeInsert(d->frm.record()); + readFields(); + } +} + + +/*! + Performs an update operation on the data browser's cursor. + + If there is no default cursor or no default form, nothing happens. + Otherwise, the following happens: + + If the data browser is actively inserting a record (see insert()), + that record is inserted into the database using insertCurrent(). + Otherwise, the database is updated with the current form's data + using updateCurrent(). If there is an error handling either + action, handleError() is called. +*/ + +void Q3DataBrowser::update() +{ + QSqlRecord* buf = d->frm.record(); + Q3SqlCursor* cur = d->cur.cursor(); + if (!buf || !cur) + return; + QSql::Confirm conf = QSql::Yes; + switch (d->dat.mode()){ + case QSql::Insert: + if (confirmInsert()) + conf = confirmEdit(QSql::Insert); + switch (conf) { + case QSql::Yes: + if (insertCurrent()) + d->dat.setMode(QSql::Update); + break; + case QSql::No: + d->dat.setMode(QSql::Update); + cur->editBuffer(true); + readFields(); + break; + case QSql::Cancel: + break; + } + break; + default: + d->dat.setMode(QSql::Update); + if (confirmUpdate()) + conf = confirmEdit(QSql::Update); + switch (conf) { + case QSql::Yes: + updateCurrent(); + break; + case QSql::No: + case QSql::Cancel: + break; + } + break; + } +} + + +/*! + Performs a delete operation on the data browser's cursor. If there + is no default cursor or no default form, nothing happens. + + Otherwise, the following happens: + + The current form's record is deleted from the database, providing + that the data browser is not in insert mode. If the data browser + is actively inserting a record (see insert()), the insert action + is canceled, and the browser navigates to the last valid record + that was current. If there is an error, handleError() is called. +*/ + +void Q3DataBrowser::del() +{ + QSqlRecord* buf = d->frm.record(); + Q3SqlCursor* cur = d->cur.cursor(); + if (!buf || !cur) + return; + QSql::Confirm conf = QSql::Yes; + switch (d->dat.mode()){ + case QSql::Insert: + if (confirmCancels()) + conf = confirmCancel(QSql::Insert); + if (conf == QSql::Yes) { + cur->editBuffer(true); /* restore from cursor */ + readFields(); + d->dat.setMode(QSql::Update); + } else + d->dat.setMode(QSql::Insert); + break; + default: + if (confirmDelete()) + conf = confirmEdit(QSql::Delete); + switch (conf) { + case QSql::Yes: + emit primeDelete(buf); + deleteCurrent(); + break; + case QSql::No: + case QSql::Cancel: + break; + } + d->dat.setMode(QSql::Update); + break; + } +} + +/*! + Moves the default cursor to the record specified by index \a i + and refreshes the default form to display that record. If there is + no default form or no default cursor, nothing happens. If + \a relative is true (the default is false), the cursor is moved + relative to its current position. If the data browser successfully + navigated to the desired record, the default cursor is primed for + update and the primeUpdate() signal is emitted. + + If the browser is already positioned on the desired record nothing + happens. Returns false if there is no cursor. Otherwise returns + true. +*/ + +bool Q3DataBrowser::seek(int i, bool relative) +{ + int b = 0; + Q3SqlCursor* cur = d->cur.cursor(); + if (!cur) + return false; + if (preNav()) + b = cur->seek(i, relative); + postNav(b); + return b; +} + +/*! + Moves the default cursor to the first record and refreshes the + default form to display this record. If there is no default form + or no default cursor, nothing happens. If the data browser + successfully navigated to the first record, the default cursor is + primed for update and the primeUpdate() signal is emitted. + + If the browser is already positioned on the first record nothing + happens. + +*/ + +void Q3DataBrowser::first() +{ + nav(&Q3SqlCursor::first); +} + + +/*! + Moves the default cursor to the last record and refreshes the + default form to display this record. If there is no default form + or no default cursor, nothing happens. If the data browser + successfully navigated to the last record, the default cursor is + primed for update and the primeUpdate() signal is emitted. + + If the browser is already positioned on the last record nothing + happens. +*/ + +void Q3DataBrowser::last() +{ + nav(&Q3SqlCursor::last); +} + + +/*! + Moves the default cursor to the next record and refreshes the + default form to display this record. If there is no default form + or no default cursor, nothing happens. If the data browser + successfully navigated to the next record, the default cursor is + primed for update and the primeUpdate() signal is emitted. + + If the browser is positioned on the last record nothing happens. +*/ + +void Q3DataBrowser::next() +{ + nav(&Q3SqlCursor::next); +} + + +/*! + Moves the default cursor to the previous record and refreshes the + default form to display this record. If there is no default form + or no default cursor, nothing happens. If the data browser + successfully navigated to the previous record, the default cursor + is primed for update and the primeUpdate() signal is emitted. + + If the browser is positioned on the first record nothing happens. +*/ + +void Q3DataBrowser::prev() +{ + nav(&Q3SqlCursor::previous); +} + +/*! + Reads the fields from the default cursor's edit buffer and + displays them in the form. If there is no default cursor or no + default form, nothing happens. +*/ + +void Q3DataBrowser::readFields() +{ + d->frm.readFields(); +} + + +/*! + Writes the form's data to the default cursor's edit buffer. If + there is no default cursor or no default form, nothing happens. +*/ + +void Q3DataBrowser::writeFields() +{ + d->frm.writeFields(); +} + + +/*! + Clears all the values in the form. + + All the edit buffer field values are set to their 'zero state', + e.g. 0 for numeric fields and "" for string fields. Then the + widgets are updated using the property map. For example, a + combobox that is property-mapped to integers would scroll to the + first item. See the \l Q3SqlPropertyMap constructor for the default + mappings of widgets to properties. +*/ + +void Q3DataBrowser::clearValues() +{ + d->frm.clearValues(); +} + +/*! + Reads the fields from the default form into the default cursor and + performs an insert on the default cursor. If there is no default + form or no default cursor, nothing happens. If an error occurred + during the insert into the database, handleError() is called and + false is returned. If the insert was successful, the cursor is + refreshed and relocated to the newly inserted record, the + cursorChanged() signal is emitted, and true is returned. + + \sa cursorChanged() sqlCursor() form() handleError() +*/ + +bool Q3DataBrowser::insertCurrent() +{ + if (isReadOnly()) + return false; + QSqlRecord* buf = d->frm.record(); + Q3SqlCursor* cur = d->cur.cursor(); + if (!buf || !cur) + return false; + writeFields(); + emit beforeInsert(buf); + int ar = cur->insert(); + if (!ar || !cur->isActive()) { + handleError(cur->lastError()); + refresh(); + updateBoundary(); + } else { + refresh(); + d->cur.findBuffer(cur->primaryIndex()); + updateBoundary(); + cursorChanged(Q3SqlCursor::Insert); + return true; + } + return false; +} + + +/*! + Reads the fields from the default form into the default cursor and + performs an update on the default cursor. If there is no default + form or no default cursor, nothing happens. If an error occurred + during the update on the database, handleError() is called and + false is returned. If the update was successful, the cursor is + refreshed and relocated to the updated record, the cursorChanged() + signal is emitted, and true is returned. + + \sa cursor() form() handleError() +*/ + +bool Q3DataBrowser::updateCurrent() +{ + if (isReadOnly()) + return false; + QSqlRecord* buf = d->frm.record(); + Q3SqlCursor* cur = d->cur.cursor(); + if (!buf || !cur) + return false; + writeFields(); + emit beforeUpdate(buf); + int ar = cur->update(); + if (!ar || !cur->isActive()) { + handleError(cur->lastError()); + refresh(); + updateBoundary(); + } else { + refresh(); + d->cur.findBuffer(cur->primaryIndex()); + updateBoundary(); + cur->editBuffer(true); + cursorChanged(Q3SqlCursor::Update); + readFields(); + return true; + } + return false; +} + + +/*! + Performs a delete on the default cursor using the values from the + default form and updates the default form. If there is no default + form or no default cursor, nothing happens. If the deletion was + successful, the cursor is repositioned to the nearest record and + true is returned. The nearest record is the next record if there + is one otherwise the previous record if there is one. If an error + occurred during the deletion from the database, handleError() is + called and false is returned. + + \sa cursor() form() handleError() +*/ + +bool Q3DataBrowser::deleteCurrent() +{ + if (isReadOnly()) + return false; + QSqlRecord* buf = d->frm.record(); + Q3SqlCursor* cur = d->cur.cursor(); + if (!buf || !cur) + return false; + writeFields(); + int n = cur->at(); + emit beforeDelete(buf); + int ar = cur->del(); + if (ar) { + refresh(); + updateBoundary(); + cursorChanged(Q3SqlCursor::Delete); + if (!cur->seek(n)) + last(); + if (cur->isValid()) { + cur->editBuffer(true); + readFields(); + } else { + clearValues(); + } + return true; + } else { + if (!cur->isActive()) { + handleError(cur->lastError()); + refresh(); + updateBoundary(); + } + } + return false; +} + + +/*! + Returns true if the form's edit buffer differs from the current + cursor buffer; otherwise returns false. +*/ + +bool Q3DataBrowser::currentEdited() +{ + QSqlRecord* buf = d->frm.record(); + Q3SqlCursor* cur = d->cur.cursor(); + if (!buf || !cur) + return false; + if (!cur->isActive() || !cur->isValid()) + return false; + writeFields(); + for (int i = 0; i < cur->count(); ++i) { + if (cur->value(i) != buf->value(i)) + return true; + } + return false; +} + +/*! \internal + + Pre-navigation checking. +*/ + +bool Q3DataBrowser::preNav() +{ + QSqlRecord* buf = d->frm.record(); + Q3SqlCursor* cur = d->cur.cursor(); + if (!buf || !cur) + return false; + + if (!isReadOnly() && autoEdit() && currentEdited()) { + bool ok = true; + QSql::Confirm conf = QSql::Yes; + switch (d->dat.mode()){ + case QSql::Insert: + if (confirmInsert()) + conf = confirmEdit(QSql::Insert); + switch (conf) { + case QSql::Yes: + ok = insertCurrent(); + d->dat.setMode(QSql::Update); + break; + case QSql::No: + d->dat.setMode(QSql::Update); + break; + case QSql::Cancel: + return false; + } + break; + default: + if (confirmUpdate()) + conf = confirmEdit(QSql::Update); + switch (conf) { + case QSql::Yes: + ok = updateCurrent(); + break; + case QSql::No: + break; + case QSql::Cancel: + return false; + } + } + return ok; + } + return true; +} + +/*! \internal + + Handles post-navigation according to \a primeUpd. +*/ + +void Q3DataBrowser::postNav(bool primeUpd) +{ + if (primeUpd) { + QSqlRecord* buf = d->frm.record(); + Q3SqlCursor* cur = d->cur.cursor(); + if (!buf || !cur) + return; + currentChanged(cur); + cur->primeUpdate(); + emit primeUpdate(buf); + readFields(); + } + updateBoundary(); +} + +/*! \internal + + Navigate default cursor according to \a nav. Handles autoEdit. + +*/ +void Q3DataBrowser::nav(Nav nav) +{ + int b = 0; + Q3SqlCursor* cur = d->cur.cursor(); + if (!cur) + return; + if (preNav()) + b = (cur->*nav)(); + postNav(b); +} + +/*! + If boundaryChecking() is true, checks the boundary of the current + default cursor and emits signals which indicate the position of + the cursor. +*/ + +void Q3DataBrowser::updateBoundary() +{ + if (d->boundaryCheck) { + Boundary bound = boundary(); + switch (bound) { + case Unknown: + case None: + emit firstRecordAvailable(true); + emit prevRecordAvailable(true); + emit nextRecordAvailable(true); + emit lastRecordAvailable(true); + break; + + case BeforeBeginning: + emit firstRecordAvailable(false); + emit prevRecordAvailable(false); + emit nextRecordAvailable(true); + emit lastRecordAvailable(true); + break; + + case Beginning: + emit firstRecordAvailable(false); + emit prevRecordAvailable(false); + emit nextRecordAvailable(true); + emit lastRecordAvailable(true); + break; + + case End: + emit firstRecordAvailable(true); + emit prevRecordAvailable(true); + emit nextRecordAvailable(false); + emit lastRecordAvailable(false); + break; + + case AfterEnd: + emit firstRecordAvailable(true); + emit prevRecordAvailable(true); + emit nextRecordAvailable(false); + emit lastRecordAvailable(false); + break; + } + } +} + +/*! + Virtual function which handles the error \a error. The default + implementation warns the user with a message box. +*/ + +void Q3DataBrowser::handleError(const QSqlError& error) +{ + d->dat.handleError(this, error); +} + +/*! + Protected virtual function which returns a confirmation for an + edit of mode \a m. Derived classes can reimplement this function + and provide their own confirmation dialog. The default + implementation uses a message box which prompts the user to + confirm the edit action. +*/ + +QSql::Confirm Q3DataBrowser::confirmEdit(QSql::Op m) +{ + return d->dat.confirmEdit(this, m); +} + +/*! + Protected virtual function which returns a confirmation for + canceling an edit mode \a m. Derived classes can reimplement this + function and provide their own confirmation dialog. The default + implementation uses a message box which prompts the user to + confirm the edit action. +*/ + +QSql::Confirm Q3DataBrowser::confirmCancel(QSql::Op m) +{ + return d->dat.confirmCancel(this, m); +} + +/*! + \fn void Q3DataBrowser::beforeInsert(QSqlRecord* buf) + + This signal is emitted just before the cursor's edit buffer is + inserted into the database. The \a buf parameter points to the + edit buffer being inserted. You might connect to this signal to + populate a generated primary key for example. +*/ + +/*! + \fn void Q3DataBrowser::beforeUpdate(QSqlRecord* buf) + + This signal is emitted just before the cursor's edit buffer is + updated in the database. The \a buf parameter points to the edit + buffer being updated. You might connect to this signal to capture + some auditing information about the update. +*/ + +/*! + \fn void Q3DataBrowser::beforeDelete(QSqlRecord* buf) + + This signal is emitted just before the cursor's edit buffer is + deleted from the database. The \a buf parameter points to the edit + buffer being deleted. You might connect to this signal to capture + some auditing information about the deletion. +*/ + +QT_END_NAMESPACE + +#endif diff --git a/src/qt3support/sql/q3databrowser.h b/src/qt3support/sql/q3databrowser.h new file mode 100644 index 0000000..5727a63 --- /dev/null +++ b/src/qt3support/sql/q3databrowser.h @@ -0,0 +1,183 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt3Support module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef Q3DATABROWSER_H +#define Q3DATABROWSER_H + +#include <QtGui/qwidget.h> +#include <QtCore/qstring.h> +#include <QtCore/qstringlist.h> +#include <QtSql/qsql.h> +#include <QtSql/qsqlindex.h> +#include <Qt3Support/q3sqlcursor.h> +#include <QtSql/qsqlerror.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Qt3Support) + +#ifndef QT_NO_SQL_VIEW_WIDGETS + +class Q3SqlForm; +class Q3DataBrowserPrivate; + +class Q_COMPAT_EXPORT Q3DataBrowser : public QWidget +{ + Q_OBJECT + Q_PROPERTY(bool boundaryChecking READ boundaryChecking WRITE setBoundaryChecking) + Q_PROPERTY(QString filter READ filter WRITE setFilter) + Q_PROPERTY(QStringList sort READ sort WRITE setSort) + Q_PROPERTY(bool confirmEdits READ confirmEdits WRITE setConfirmEdits) + Q_PROPERTY(bool confirmInsert READ confirmInsert WRITE setConfirmInsert) + Q_PROPERTY(bool confirmUpdate READ confirmUpdate WRITE setConfirmUpdate) + Q_PROPERTY(bool confirmDelete READ confirmDelete WRITE setConfirmDelete) + Q_PROPERTY(bool confirmCancels READ confirmCancels WRITE setConfirmCancels) + Q_PROPERTY(bool readOnly READ isReadOnly WRITE setReadOnly) + Q_PROPERTY(bool autoEdit READ autoEdit WRITE setAutoEdit) + +public: + Q3DataBrowser(QWidget* parent=0, const char* name=0, Qt::WindowFlags fl = 0); + ~Q3DataBrowser(); + + enum Boundary { + Unknown, + None, + BeforeBeginning, + Beginning, + End, + AfterEnd + }; + + Boundary boundary(); + void setBoundaryChecking(bool active); + bool boundaryChecking() const; + + void setSort(const QSqlIndex& sort); + void setSort(const QStringList& sort); + QStringList sort() const; + void setFilter(const QString& filter); + QString filter() const; + virtual void setSqlCursor(Q3SqlCursor* cursor, bool autoDelete = false); + Q3SqlCursor* sqlCursor() const; + virtual void setForm(Q3SqlForm* form); + Q3SqlForm* form(); + + virtual void setConfirmEdits(bool confirm); + virtual void setConfirmInsert(bool confirm); + virtual void setConfirmUpdate(bool confirm); + virtual void setConfirmDelete(bool confirm); + virtual void setConfirmCancels(bool confirm); + bool confirmEdits() const; + bool confirmInsert() const; + bool confirmUpdate() const; + bool confirmDelete() const; + bool confirmCancels() const; + + virtual void setReadOnly(bool active); + bool isReadOnly() const; + virtual void setAutoEdit(bool autoEdit); + bool autoEdit() const; + + virtual bool seek(int i, bool relative = false); + +Q_SIGNALS: + void firstRecordAvailable(bool available); + void lastRecordAvailable(bool available); + void nextRecordAvailable(bool available); + void prevRecordAvailable(bool available); + + void currentChanged(const QSqlRecord* record); + void primeInsert(QSqlRecord* buf); + void primeUpdate(QSqlRecord* buf); + void primeDelete(QSqlRecord* buf); + void beforeInsert(QSqlRecord* buf); + void beforeUpdate(QSqlRecord* buf); + void beforeDelete(QSqlRecord* buf); + void cursorChanged(Q3SqlCursor::Mode mode); + +public Q_SLOTS: + virtual void refresh(); + + virtual void insert(); + virtual void update(); + virtual void del(); + + virtual void first(); + virtual void last(); + virtual void next(); + virtual void prev(); + + virtual void readFields(); + virtual void writeFields(); + virtual void clearValues(); + + void updateBoundary(); + +protected: + virtual bool insertCurrent(); + virtual bool updateCurrent(); + virtual bool deleteCurrent(); + virtual bool currentEdited(); + + virtual QSql::Confirm confirmEdit(QSql::Op m); + virtual QSql::Confirm confirmCancel(QSql::Op m); + + virtual void handleError(const QSqlError& error); + +private: + typedef bool (Q3SqlCursor::*Nav)(); + bool preNav(); + void postNav(bool primeUpd); + void nav(Nav nav); + Q3DataBrowserPrivate* d; + + Q_DISABLE_COPY(Q3DataBrowser) +}; + +#endif // QT_NO_SQL_VIEW_WIDGETS + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // Q3DATABROWSER_H diff --git a/src/qt3support/sql/q3datatable.cpp b/src/qt3support/sql/q3datatable.cpp new file mode 100644 index 0000000..559abaf --- /dev/null +++ b/src/qt3support/sql/q3datatable.cpp @@ -0,0 +1,2335 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt3Support module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "q3datatable.h" + +#ifndef QT_NO_SQL_VIEW_WIDGETS + +#include "qevent.h" +#include "qsqldriver.h" +#include "q3sqleditorfactory.h" +#include "q3sqlpropertymap.h" +#include "qapplication.h" +#include "qlayout.h" +#include "qpainter.h" +#include "q3popupmenu.h" +#include "q3valuelist.h" +#include "q3sqlmanager_p.h" +#include "qsqlfield.h" +#include "qdatetime.h" +#include "qcursor.h" +#include "qtimer.h" +#include "qpointer.h" + +QT_BEGIN_NAMESPACE + +//#define QT_DEBUG_DATATABLE + +class Q3DataTablePrivate +{ +public: + Q3DataTablePrivate() + : nullTxtChanged( false ), + haveAllRows( false ), + continuousEdit( false ), + editorFactory( 0 ), + propertyMap( 0 ), + datefmt( Qt::TextDate ), + editRow( -1 ), + editCol( -1 ), + insertRowLast( -1 ), + insertPreRows( -1 ), + editBuffer( 0 ), + cancelMode( false ), + cancelInsert( false ), + cancelUpdate( false ), + lastAt( -1 ) + {} + ~Q3DataTablePrivate() { if ( propertyMap ) delete propertyMap; } + + QString nullTxt; + bool nullTxtChanged; + typedef Q3ValueList< uint > ColIndex; + ColIndex colIndex; + bool haveAllRows; + bool continuousEdit; + Q3SqlEditorFactory* editorFactory; + Q3SqlPropertyMap* propertyMap; + QString trueTxt; + Qt::DateFormat datefmt; + QString falseTxt; + int editRow; + int editCol; + int insertRowLast; + QString insertHeaderLabelLast; + int insertPreRows; + QSqlRecord* editBuffer; + bool cancelMode; + bool cancelInsert; + bool cancelUpdate; + int lastAt; + QString ftr; + QStringList srt; + QStringList fld; + QStringList fldLabel; + Q3ValueList<int> fldWidth; + Q3ValueList<QIconSet> fldIcon; + Q3ValueList<bool> fldHidden; + Q3SqlCursorManager cur; + Q3DataManager dat; +}; + +#ifdef QT_DEBUG_DATATABLE +void qt_debug_buffer( const QString& msg, QSqlRecord* cursor ) +{ + qDebug("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++"); + qDebug(msg); + for ( uint j = 0; j < cursor->count(); ++j ) { + qDebug(cursor->field(j)->name() + " type:" + QString(cursor->field(j)->value().typeName()) + " value:" + cursor->field(j)->value().toString() ); + } +} +#endif + +/*! + \enum Q3DataTable::Refresh + + This enum describes the refresh options. + + \value RefreshData refresh the data, i.e. read it from the database + \value RefreshColumns refresh the list of fields, e.g. the column headings + \value RefreshAll refresh both the data and the list of fields +*/ + + +/*! + \class Q3DataTable + \brief The Q3DataTable class provides a flexible SQL table widget that supports browsing and editing. + + \compat + + Q3DataTable supports various functions for presenting and editing + SQL data from a \l Q3SqlCursor in a table. + + If you want a to present your data in a form use QDataBrowser, or + for read-only forms, use QDataView instead. + + When displaying data, Q3DataTable only retrieves data for visible + rows. If the driver supports the 'query size' property the + Q3DataTable will have the correct number of rows and the vertical + scroll bar will accurately reflect the number of rows displayed in + proportion to the number of rows in the dataset. If the driver + does not support the 'query size' property, rows are dynamically + fetched from the database on an as-needed basis with the scroll bar + becoming more accurate as the user scrolls down through the + records. This allows extremely large queries to be displayed as + quickly as possible, with minimum memory usage. + + Q3DataTable inherits Q3Table's API and extends it with functions to + sort and filter the data and sort columns. See setSqlCursor(), + setFilter(), setSort(), setSorting(), sortColumn() and refresh(). + + When displaying editable cursors, cell editing will be enabled. + (For more information on editable cursors, see \l Q3SqlCursor). + Q3DataTable can be used to modify existing data and to add new + records. When a user makes changes to a field in the table, the + cursor's edit buffer is used. The table will not send changes in + the edit buffer to the database until the user moves to a + different record in the grid or presses Enter. Cell editing is + initiated by pressing F2 (or right clicking and then clicking the + appropriate popup menu item) and canceled by pressing Esc. If + there is a problem updating or adding data, errors are handled + automatically (see handleError() to change this behavior). Note + that if autoEdit() is false navigating to another record will + cancel the insert or update. + + The user can be asked to confirm all edits with setConfirmEdits(). + For more precise control use setConfirmInsert(), + setConfirmUpdate(), setConfirmDelete() and setConfirmCancels(). + Use setAutoEdit() to control the behavior of the table when the + user edits a record and then navigates. (Note that setAutoDelete() + is unrelated; it is used to set whether the Q3SqlCursor is deleted + when the table is deleted.) + + Since the data table can perform edits, it must be able to + uniquely identify every record so that edits are correctly + applied. Because of this the underlying cursor must have a valid + primary index to ensure that a unique record is inserted, updated + or deleted within the database otherwise the database may be + changed to an inconsistent state. + + Q3DataTable creates editors using the default \l Q3SqlEditorFactory. + Different editor factories can be used by calling + installEditorFactory(). A property map is used to map between the + cell's value and the editor. You can use your own property map + with installPropertyMap(). + + The contents of a cell is available as a QString with text() or as + a QVariant with value(). The current record is returned by + currentRecord(). Use the find() function to search for a string in + the table. + + Editing actions can be applied programmatically. For example, the + insertCurrent() function reads the fields from the current record + into the cursor and performs the insert. The updateCurrent() and + deleteCurrent() functions perform similarly to update and delete + the current record respectively. + + Columns in the table can be created automatically based on the + cursor (see setSqlCursor()). Columns can be manipulated manually + using addColumn(), removeColumn() and setColumn(). + + The table automatically copies many of the properties of the + cursor to format the display of data within cells (alignment, + visibility, etc.). The cursor can be changed with setSqlCursor(). + The filter (see setFilter()) and sort defined within the table are + used instead of the filter and sort set on the cursor. For sorting + options see setSort(), sortColumn(), sortAscending() and + sortDescending(). Note that sorting operations will not behave as + expected if you are using a QSqlSelectCursor because it uses + user-defined SQL queries to obtain data. + + The text used to represent NULL, true and false values can be + changed with setNullText(), setTrueText() and setFalseText() + respectively. You can change the appearance of cells by + reimplementing paintField(). + + Whenever a new row is selected in the table the currentChanged() + signal is emitted. The primeInsert() signal is emitted when an + insert is initiated. The primeUpdate() and primeDelete() signals + are emitted when update and deletion are initiated respectively. + Just before the database is updated a signal is emitted; + beforeInsert(), beforeUpdate() or beforeDelete() as appropriate. + +*/ + +/*! + Constructs a data table which is a child of \a parent, called + name \a name. +*/ + +Q3DataTable::Q3DataTable ( QWidget * parent, const char * name ) + : Q3Table( parent, name ) +{ + init(); +} + +/*! + Constructs a data table which is a child of \a parent, called name + \a name using the cursor \a cursor. + + If \a autoPopulate is true (the default is false), columns are + automatically created based upon the fields in the \a cursor + record. Note that \a autoPopulate only governs the creation of + columns; to load the cursor's data into the table use refresh(). + + If the \a cursor is read-only, the table also becomes read-only. + In addition, the table adopts the cursor's driver's definition for + representing NULL values as strings. +*/ + +Q3DataTable::Q3DataTable ( Q3SqlCursor* cursor, bool autoPopulate, QWidget * parent, const char * name ) + : Q3Table( parent, name ) +{ + init(); + setSqlCursor( cursor, autoPopulate ); +} + +/*! \internal +*/ + + +void Q3DataTable::init() +{ + d = new Q3DataTablePrivate(); + setAutoEdit( true ); + setSelectionMode( SingleRow ); + setFocusStyle( FollowStyle ); + d->trueTxt = tr( "True" ); + d->falseTxt = tr( "False" ); + d->datefmt = Qt::LocalDate; + reset(); + connect( this, SIGNAL(selectionChanged()), + SLOT(updateCurrentSelection())); +} + +/*! + Destroys the object and frees any allocated resources. +*/ + +Q3DataTable::~Q3DataTable() +{ + delete d; +} + + +/*! + Adds the next column to be displayed using the field \a fieldName, + column label \a label, width \a width and iconset \a iconset. + + If \a label is specified, it is used as the column's header label, + otherwise the field's display label is used when setSqlCursor() is + called. The \a iconset is used to set the icon used by the column + header; by default there is no icon. + + \sa setSqlCursor() refresh() +*/ + +void Q3DataTable::addColumn( const QString& fieldName, + const QString& label, + int width, + const QIconSet& iconset ) +{ + d->fld += fieldName; + d->fldLabel += label; + d->fldIcon += iconset; + d->fldWidth += width; + d->fldHidden += false; +} + +/*! + Sets the \a col column to display using the field \a fieldName, + column label \a label, width \a width and iconset \a iconset. + + If \a label is specified, it is used as the column's header label, + otherwise the field's display label is used when setSqlCursor() is + called. The \a iconset is used to set the icon used by the column + header; by default there is no icon. + + \sa setSqlCursor() refresh() +*/ + +void Q3DataTable::setColumn( uint col, const QString& fieldName, + const QString& label, + int width, + const QIconSet& iconset ) +{ + d->fld[col]= fieldName; + d->fldLabel[col] = label; + d->fldIcon[col] = iconset; + d->fldWidth[col] = width; + d->fldHidden[col] = false; +} + +/*! + Removes column \a col from the list of columns to be displayed. If + \a col does not exist, nothing happens. + + \sa QSqlField +*/ + +void Q3DataTable::removeColumn( int col ) +{ + if ( d->fld.begin() + col != d->fld.end() ) { + d->fld.remove( d->fld.at( col ) ); + d->fldLabel.remove( d->fldLabel.at( col ) ); + d->fldIcon.remove( d->fldIcon.at( col ) ); + d->fldWidth.remove( d->fldWidth.at( col ) ); + d->fldHidden.remove( d->fldHidden.at( col ) ); + } +} + +/*! + Sets the column \a col to the width \a w. Note that unlike Q3Table + the Q3DataTable is not immediately redrawn, you must call + refresh(Q3DataTable::RefreshColumns) + yourself. + + \sa refresh() +*/ +void Q3DataTable::setColumnWidth( int col, int w ) +{ + if ( d->fldWidth.at( col ) != d->fldWidth.end() ) { + d->fldWidth[col] = w; + } +} + +/*! + Resizes column \a col so that the column width is wide enough to + display the widest item the column contains (including the column + label). If the table's Q3SqlCursor is not currently active, the + cursor will be refreshed before the column width is calculated. Be + aware that this function may be slow on tables that contain large + result sets. +*/ +void Q3DataTable::adjustColumn( int col ) +{ + Q3SqlCursor * cur = sqlCursor(); + if ( !cur || cur->count() <= col ) + return; + if ( !cur->isActive() ) { + d->cur.refresh(); + } + int oldRow = currentRow(); + int w = fontMetrics().width( horizontalHeader()->label( col ) + QLatin1Char('W') ); + cur->seek( QSql::BeforeFirst ); + while ( cur->next() ) { + w = qMax( w, fontMetrics().width( fieldToString( cur->fieldPtr( indexOf( col ) ) ) ) + 10 ); + } + setColumnWidth( col, w ); + cur->seek( oldRow ); + refresh( RefreshColumns ); +} + +/*! \reimp +*/ +void Q3DataTable::setColumnStretchable( int col, bool s ) +{ + if ( numCols() == 0 ) { + refresh( RefreshColumns ); + } + if ( numCols() > col ) { + Q3Table::setColumnStretchable( col, s ); + } +} + +QString Q3DataTable::filter() const +{ + return d->cur.filter(); +} + +/*! + \property Q3DataTable::filter + \brief the data filter for the data table + + The filter applies to the data shown in the table. To view data + with a new filter, use refresh(). A filter string is an SQL WHERE + clause without the WHERE keyword. + + There is no default filter. + + \sa sort() + +*/ + +void Q3DataTable::setFilter( const QString& filter ) +{ + d->cur.setFilter( filter ); +} + + +/*! + \property Q3DataTable::sort + \brief the data table's sort + + The table's sort affects the order in which data records are + displayed in the table. To apply a sort, use refresh(). + + When examining the sort property, a string list is returned with + each item having the form 'fieldname order' (e.g., 'id ASC', + 'surname DESC'). + + There is no default sort. + + Note that if you want to iterate over the sort list, you should + iterate over a copy, e.g. + \snippet doc/src/snippets/code/src_qt3support_sql_q3datatable.cpp 0 + + \sa filter() refresh() +*/ + +void Q3DataTable::setSort( const QStringList& sort ) +{ + d->cur.setSort( sort ); +} + +/*! + \overload + + Sets the sort to be applied to the displayed data to \a sort. If + there is no current cursor, nothing happens. A QSqlIndex contains + field names and their ordering (ASC or DESC); these are used to + compose the ORDER BY clause. + + \sa sort() +*/ + +void Q3DataTable::setSort( const QSqlIndex& sort ) +{ + d->cur.setSort( sort ); +} + +QStringList Q3DataTable::sort() const +{ + return d->cur.sort(); +} + +/*! + Returns the cursor used by the data table. +*/ + +Q3SqlCursor* Q3DataTable::sqlCursor() const +{ + return d->cur.cursor(); +} + +void Q3DataTable::setConfirmEdits( bool confirm ) +{ + d->dat.setConfirmEdits( confirm ); +} + +void Q3DataTable::setConfirmInsert( bool confirm ) +{ + d->dat.setConfirmInsert( confirm ); +} + +void Q3DataTable::setConfirmUpdate( bool confirm ) +{ + d->dat.setConfirmUpdate( confirm ); +} + +void Q3DataTable::setConfirmDelete( bool confirm ) +{ + d->dat.setConfirmDelete( confirm ); +} + +/*! + \property Q3DataTable::confirmEdits + \brief whether the data table confirms edit operations + + If the confirmEdits property is true, the data table confirms all + edit operations (inserts, updates and deletes). Finer control of + edit confirmation can be achieved using \l confirmCancels, \l + confirmInsert, \l confirmUpdate and \l confirmDelete. + + \sa confirmCancels() confirmInsert() confirmUpdate() confirmDelete() +*/ + +bool Q3DataTable::confirmEdits() const +{ + return ( d->dat.confirmEdits() ); +} + +/*! + \property Q3DataTable::confirmInsert + \brief whether the data table confirms insert operations + + If the confirmInsert property is true, all insertions must be + confirmed by the user through a message box (this behavior can be + changed by overriding the confirmEdit() function), otherwise all + insert operations occur immediately. + + \sa confirmCancels() confirmEdits() confirmUpdate() confirmDelete() +*/ + +bool Q3DataTable::confirmInsert() const +{ + return ( d->dat.confirmInsert() ); +} + +/*! + \property Q3DataTable::confirmUpdate + \brief whether the data table confirms update operations + + If the confirmUpdate property is true, all updates must be + confirmed by the user through a message box (this behavior can be + changed by overriding the confirmEdit() function), otherwise all + update operations occur immediately. + + \sa confirmCancels() confirmEdits() confirmInsert() confirmDelete() +*/ + +bool Q3DataTable::confirmUpdate() const +{ + return ( d->dat.confirmUpdate() ); +} + +/*! + \property Q3DataTable::confirmDelete + \brief whether the data table confirms delete operations + + If the confirmDelete property is true, all deletions must be + confirmed by the user through a message box (this behavior can be + changed by overriding the confirmEdit() function), otherwise all + delete operations occur immediately. + + \sa confirmCancels() confirmEdits() confirmUpdate() confirmInsert() +*/ + +bool Q3DataTable::confirmDelete() const +{ + return ( d->dat.confirmDelete() ); +} + +/*! + \property Q3DataTable::confirmCancels + \brief whether the data table confirms cancel operations + + If the confirmCancel property is true, all cancels must be + confirmed by the user through a message box (this behavior can be + changed by overriding the confirmCancel() function), otherwise all + cancels occur immediately. The default is false. + + \sa confirmEdits() confirmCancel() +*/ + +void Q3DataTable::setConfirmCancels( bool confirm ) +{ + d->dat.setConfirmCancels( confirm ); +} + +bool Q3DataTable::confirmCancels() const +{ + return d->dat.confirmCancels(); +} + +/*! + \reimp + + For an editable table, creates an editor suitable for the field in + column \a col. The editor is created using the default editor + factory, unless a different editor factory was installed with + installEditorFactory(). The editor is primed with the value of the + field in \a col using a property map. The property map used is the + default property map, unless a new property map was installed with + installPropertMap(). If \a initFromCell is true then the editor is + primed with the value in the Q3DataTable cell. +*/ + +QWidget * Q3DataTable::createEditor( int , int col, bool initFromCell ) const +{ + if ( d->dat.mode() == QSql::None ) + return 0; + + Q3SqlEditorFactory * f = (d->editorFactory == 0) ? + Q3SqlEditorFactory::defaultFactory() : d->editorFactory; + + Q3SqlPropertyMap * m = (d->propertyMap == 0) ? + Q3SqlPropertyMap::defaultMap() : d->propertyMap; + + QWidget * w = 0; + if( initFromCell && d->editBuffer ){ + w = f->createEditor( viewport(), d->editBuffer->fieldPtr( indexOf( col ) ) ); + if ( w ) + m->setProperty( w, d->editBuffer->value( indexOf( col ) ) ); + } + return w; +} + +/*! \reimp */ +bool Q3DataTable::eventFilter( QObject *o, QEvent *e ) +{ + if ( d->cancelMode ) + return true; + + int r = currentRow(); + int c = currentColumn(); + + if ( d->dat.mode() != QSql::None ) { + r = d->editRow; + c = d->editCol; + } + + d->cancelInsert = false; + d->cancelUpdate = false; + switch ( e->type() ) { + case QEvent::KeyPress: { + int conf = QSql::Yes; + QKeyEvent *ke = (QKeyEvent*)e; + if ( ( ke->key() == Qt::Key_Tab || ke->key() == Qt::Key_BackTab ) + && ke->state() & Qt::ControlButton ) + return false; + + if ( ke->key() == Qt::Key_Escape && d->dat.mode() == QSql::Insert ){ + if ( confirmCancels() && !d->cancelMode ) { + d->cancelMode = true; + conf = confirmCancel( QSql::Insert ); + d->cancelMode = false; + } + if ( conf == QSql::Yes ) { + d->cancelInsert = true; + } else { + QWidget *editorWidget = cellWidget( r, c ); + if ( editorWidget ) { + editorWidget->setActiveWindow(); + editorWidget->setFocus(); + } + return true; + } + } + if ( ke->key() == Qt::Key_Escape && d->dat.mode() == QSql::Update ) { + if ( confirmCancels() && !d->cancelMode ) { + d->cancelMode = true; + conf = confirmCancel( QSql::Update ); + d->cancelMode = false; + } + if ( conf == QSql::Yes ){ + d->cancelUpdate = true; + } else { + QWidget *editorWidget = cellWidget( r, c ); + if ( editorWidget ) { + editorWidget->setActiveWindow(); + editorWidget->setFocus(); + } + return true; + } + } + if ( ke->key() == Qt::Key_Insert && d->dat.mode() == QSql::None ) { + beginInsert(); + return true; + } + if ( ke->key() == Qt::Key_Delete && d->dat.mode() == QSql::None ) { + deleteCurrent(); + return true; + } + if ( d->dat.mode() != QSql::None ) { + if ( (ke->key() == Qt::Key_Tab) && (c < numCols() - 1) && (!isColumnReadOnly( c+1 ) || d->dat.mode() == QSql::Insert) ) + d->continuousEdit = true; + else if ( (ke->key() == Qt::Key_BackTab) && (c > 0) && (!isColumnReadOnly( c-1 ) || d->dat.mode() == QSql::Insert) ) + d->continuousEdit = true; + else + d->continuousEdit = false; + } + Q3SqlCursor * sql = sqlCursor(); + if ( sql && sql->driver() && + !sql->driver()->hasFeature( QSqlDriver::QuerySize ) && + ke->key() == Qt::Key_End && d->dat.mode() == QSql::None ) { +#ifndef QT_NO_CURSOR + QApplication::setOverrideCursor( Qt::WaitCursor ); +#endif + int i = sql->at(); + if ( i < 0 ) { + i = 0; + sql->seek(0); + } + while ( sql->next() ) + i++; + setNumRows( i+1 ); + setCurrentCell( i+1, currentColumn() ); +#ifndef QT_NO_CURSOR + QApplication::restoreOverrideCursor(); +#endif + return true; + } + break; + } + case QEvent::FocusOut: { + QWidget *editorWidget = cellWidget( r, c ); + repaintCell( currentRow(), currentColumn() ); + if ( !d->cancelMode && editorWidget && o == editorWidget && + ( d->dat.mode() == QSql::Insert) && !d->continuousEdit) { + setCurrentCell( r, c ); + d->cancelInsert = true; + } + d->continuousEdit = false; + break; + } + case QEvent::FocusIn: + repaintCell( currentRow(), currentColumn() ); + break; + default: + break; + } + return Q3Table::eventFilter( o, e ); +} + +/*! \reimp */ +void Q3DataTable::resizeEvent ( QResizeEvent * e ) +{ + if ( sqlCursor() && + sqlCursor()->driver() && + !sqlCursor()->driver()->hasFeature( QSqlDriver::QuerySize ) ) + loadNextPage(); + Q3Table::resizeEvent( e ); +} + +/*! \reimp */ +void Q3DataTable::contentsContextMenuEvent( QContextMenuEvent* e ) +{ + Q3Table::contentsContextMenuEvent( e ); + if ( isEditing() && d->dat.mode() != QSql::None ) + endEdit( d->editRow, d->editCol, autoEdit(), false ); + if ( !sqlCursor() ) + return; + if ( d->dat.mode() == QSql::None ) { + if ( isReadOnly() ) + return; + enum { + IdInsert, + IdUpdate, + IdDelete + }; + QPointer<Q3PopupMenu> popup = new Q3PopupMenu( this, "qt_datatable_menu" ); + int id[ 3 ]; + id[ IdInsert ] = popup->insertItem( tr( "Insert" ) ); + id[ IdUpdate ] = popup->insertItem( tr( "Update" ) ); + id[ IdDelete ] = popup->insertItem( tr( "Delete" ) ); + bool enableInsert = sqlCursor()->canInsert(); + popup->setItemEnabled( id[ IdInsert ], enableInsert ); + bool enableUpdate = currentRow() > -1 && sqlCursor()->canUpdate() && !isColumnReadOnly( currentColumn() ); + popup->setItemEnabled( id[ IdUpdate ], enableUpdate ); + bool enableDelete = currentRow() > -1 && sqlCursor()->canDelete(); + popup->setItemEnabled( id[ IdDelete ], enableDelete ); + int r = popup->exec( e->globalPos() ); + delete (Q3PopupMenu*) popup; + if ( r == id[ IdInsert ] ) + beginInsert(); + else if ( r == id[ IdUpdate ] ) { + if ( beginEdit( currentRow(), currentColumn(), false ) ) + setEditMode( Editing, currentRow(), currentColumn() ); + else + endUpdate(); + } + else if ( r == id[ IdDelete ] ) + deleteCurrent(); + e->accept(); + } +} + +/*! \reimp */ +void Q3DataTable::contentsMousePressEvent( QMouseEvent* e ) +{ + Q3Table::contentsMousePressEvent( e ); +} + +/*! \reimp */ +QWidget* Q3DataTable::beginEdit ( int row, int col, bool replace ) +{ + d->editRow = -1; + d->editCol = -1; + if ( !sqlCursor() ) + return 0; + if ( d->dat.mode() == QSql::Insert && !sqlCursor()->canInsert() ) + return 0; + if ( d->dat.mode() == QSql::Update && !sqlCursor()->canUpdate() ) + return 0; + d->editRow = row; + d->editCol = col; + if ( d->continuousEdit ) { + // see comment in beginInsert() + bool fakeReadOnly = isColumnReadOnly( col ); + setColumnReadOnly( col, false ); + QWidget* w = Q3Table::beginEdit( row, col, replace ); + setColumnReadOnly( col, fakeReadOnly ); + return w; + } + if ( d->dat.mode() == QSql::None && sqlCursor()->canUpdate() && sqlCursor()->primaryIndex().count() > 0 ) + return beginUpdate( row, col, replace ); + return 0; +} + +/*! \reimp */ +void Q3DataTable::endEdit( int row, int col, bool, bool ) +{ + bool accept = autoEdit() && !d->cancelInsert && !d->cancelUpdate; + + QWidget *editor = cellWidget( row, col ); + if ( !editor ) + return; + if ( d->cancelMode ) + return; + if ( d->dat.mode() != QSql::None && d->editBuffer ) { + Q3SqlPropertyMap * m = (d->propertyMap == 0) ? + Q3SqlPropertyMap::defaultMap() : d->propertyMap; + d->editBuffer->setValue( indexOf( col ), m->property( editor ) ); + clearCellWidget( row, col ); + if ( !d->continuousEdit ) { + switch ( d->dat.mode() ) { + case QSql::Insert: + if ( accept ) + QTimer::singleShot( 0, this, SLOT(doInsertCurrent()) ); + else + endInsert(); + break; + case QSql::Update: + if ( accept ) + QTimer::singleShot( 0, this, SLOT(doUpdateCurrent()) ); + else + endUpdate(); + break; + default: + break; + } + } + } else { + setEditMode( NotEditing, -1, -1 ); + } + if ( d->dat.mode() == QSql::None ) + viewport()->setFocus(); + updateCell( row, col ); + emit valueChanged( row, col ); +} + +/*! \internal */ +void Q3DataTable::doInsertCurrent() +{ + insertCurrent(); +} + +/*! \internal */ +void Q3DataTable::doUpdateCurrent() +{ + updateCurrent(); + if ( d->dat.mode() == QSql::None ) { + viewport()->setFocus(); + } +} + +/*! \reimp */ +void Q3DataTable::activateNextCell() +{ +// if ( d->dat.mode() == QSql::None ) +// Q3Table::activateNextCell(); +} + +/*! \internal +*/ + +void Q3DataTable::endInsert() +{ + if ( d->dat.mode() != QSql::Insert ) + return; + d->dat.setMode( QSql::None ); + d->editBuffer = 0; + verticalHeader()->setLabel( d->editRow, QString::number( d->editRow +1 ) ); + d->editRow = -1; + d->editCol = -1; + d->insertRowLast = -1; + d->insertHeaderLabelLast.clear(); + setEditMode( NotEditing, -1, -1 ); + setNumRows( d->insertPreRows ); + d->insertPreRows = -1; + viewport()->setFocus(); +} + +/*! \internal + */ +void Q3DataTable::endUpdate() +{ + d->dat.setMode( QSql::None ); + d->editBuffer = 0; + updateRow( d->editRow ); + d->editRow = -1; + d->editCol = -1; + setEditMode( NotEditing, -1, -1 ); +} + +/*! + Protected virtual function called when editing is about to begin + on a new record. If the table is read-only, or if there's no cursor + or the cursor does not allow inserts, nothing happens and false + is returned. Otherwise returns true. + + Editing takes place using the cursor's edit buffer(see + Q3SqlCursor::editBuffer()). + + When editing begins, a new row is created in the table marked with + an asterisk '*' in the row's vertical header column, i.e. at the + left of the row. +*/ +bool Q3DataTable::beginInsert() +{ + if ( !sqlCursor() || isReadOnly() || !numCols() ) + return false; + if ( !sqlCursor()->canInsert() ) + return false; + int i = 0; + int row = currentRow(); + + d->insertPreRows = numRows(); + if ( row < 0 || numRows() < 1 ) + row = 0; + setNumRows( d->insertPreRows + 1 ); + setCurrentCell( row, 0 ); + d->editBuffer = sqlCursor()->primeInsert(); + emit primeInsert( d->editBuffer ); + d->dat.setMode( QSql::Insert ); + int lastRow = row; + int lastY = contentsY() + visibleHeight(); + for ( i = row; i < numRows() ; ++i ) { + QRect cg = cellGeometry( i, 0 ); + if ( (cg.y()+cg.height()) > lastY ) { + lastRow = i; + break; + } + } + if ( lastRow == row && ( numRows()-1 > row ) ) + lastRow = numRows() - 1; + d->insertRowLast = lastRow; + d->insertHeaderLabelLast = verticalHeader()->label( d->insertRowLast ); + verticalHeader()->setLabel( row, QString(QLatin1Char('*')) ); + d->editRow = row; + // in the db world it's common to allow inserting new records + // into a table that has read-only columns - temporarily + // switch off read-only mode for such columns + bool fakeReadOnly = isColumnReadOnly( 0 ); + setColumnReadOnly( 0, false ); + if ( Q3Table::beginEdit( row, 0, false ) ) + setEditMode( Editing, row, 0 ); + setColumnReadOnly( 0, fakeReadOnly ); + return true; +} + +/*! + Protected virtual function called when editing is about to begin + on an existing row. If the table is read-only, or if there's no + cursor, nothing happens. + + Editing takes place using the cursor's edit buffer (see + Q3SqlCursor::editBuffer()). + + \a row and \a col refer to the row and column in the Q3DataTable. + + (\a replace is provided for reimplementors and reflects the API of + Q3Table::beginEdit().) +*/ + +QWidget* Q3DataTable::beginUpdate ( int row, int col, bool replace ) +{ + if ( !sqlCursor() || isReadOnly() || isColumnReadOnly( col ) ) + return 0; + setCurrentCell( row, col ); + d->dat.setMode( QSql::Update ); + if ( sqlCursor()->seek( row ) ) { + d->editBuffer = sqlCursor()->primeUpdate(); + sqlCursor()->seek( currentRow() ); + emit primeUpdate( d->editBuffer ); + return Q3Table::beginEdit( row, col, replace ); + } + return 0; +} + +/*! + For an editable table, issues an insert on the current cursor + using the values in the cursor's edit buffer. If there is no + current cursor or there is no current "insert" row, nothing + happens. If confirmEdits() or confirmInsert() is true, + confirmEdit() is called to confirm the insert. Returns true if the + insert succeeded; otherwise returns false. + + The underlying cursor must have a valid primary index to ensure + that a unique record is inserted within the database otherwise the + database may be changed to an inconsistent state. +*/ + +bool Q3DataTable::insertCurrent() +{ + if ( d->dat.mode() != QSql::Insert || ! numCols() ) + return false; + if ( !sqlCursor()->canInsert() ) { +#ifdef QT_CHECK_RANGE + qWarning("Q3DataTable::insertCurrent: insert not allowed for %s", + sqlCursor()->name().latin1() ); +#endif + endInsert(); + return false; + } + int b = 0; + int conf = QSql::Yes; + if ( confirmEdits() || confirmInsert() ) + conf = confirmEdit( QSql::Insert ); + switch ( conf ) { + case QSql::Yes: { +#ifndef QT_NO_CURSOR + QApplication::setOverrideCursor( Qt::waitCursor ); +#endif + emit beforeInsert( d->editBuffer ); + b = sqlCursor()->insert(); +#ifndef QT_NO_CURSOR + QApplication::restoreOverrideCursor(); +#endif + if ( ( !b && !sqlCursor()->isActive() ) || !sqlCursor()->isActive() ) { + handleError( sqlCursor()->lastError() ); + endInsert(); // cancel the insert if anything goes wrong + refresh(); + } else { + endInsert(); + refresh(); + QSqlIndex idx = sqlCursor()->primaryIndex(); + findBuffer( idx, d->lastAt ); + repaintContents( contentsX(), contentsY(), visibleWidth(), visibleHeight(), false ); + emit cursorChanged( QSql::Insert ); + } + break; + } + case QSql::No: + endInsert(); + break; + case QSql::Cancel: + if ( Q3Table::beginEdit( currentRow(), currentColumn(), false ) ) + setEditMode( Editing, currentRow(), currentColumn() ); + break; + } + return ( b > 0 ); +} + +/*! \internal + + Updates the row \a row. +*/ + +void Q3DataTable::updateRow( int row ) +{ + for ( int i = 0; i < numCols(); ++i ) + updateCell( row, i ); +} + +/*! + For an editable table, issues an update using the cursor's edit + buffer. If there is no current cursor or there is no current + selection, nothing happens. If confirmEdits() or confirmUpdate() + is true, confirmEdit() is called to confirm the update. Returns + true if the update succeeded; otherwise returns false. + + The underlying cursor must have a valid primary index to ensure + that a unique record is updated within the database otherwise the + database may be changed to an inconsistent state. +*/ + +bool Q3DataTable::updateCurrent() +{ + if ( d->dat.mode() != QSql::Update ) + return false; + if ( sqlCursor()->primaryIndex().count() == 0 ) { +#ifdef QT_CHECK_RANGE + qWarning("Q3DataTable::updateCurrent: no primary index for %s", + sqlCursor()->name().latin1() ); +#endif + endUpdate(); + return false; + } + if ( !sqlCursor()->canUpdate() ) { +#ifdef QT_CHECK_RANGE + qWarning("Q3DataTable::updateCurrent: updates not allowed for %s", + sqlCursor()->name().latin1() ); +#endif + endUpdate(); + return false; + } + int b = 0; + int conf = QSql::Yes; + if ( confirmEdits() || confirmUpdate() ) + conf = confirmEdit( QSql::Update ); + switch ( conf ) { + case QSql::Yes: { +#ifndef QT_NO_CURSOR + QApplication::setOverrideCursor( Qt::waitCursor ); +#endif + emit beforeUpdate( d->editBuffer ); + b = sqlCursor()->update(); +#ifndef QT_NO_CURSOR + QApplication::restoreOverrideCursor(); +#endif + if ( ( !b && !sqlCursor()->isActive() ) || !sqlCursor()->isActive() ) { + handleError( sqlCursor()->lastError() ); + endUpdate(); + refresh(); + setCurrentCell( d->editRow, d->editCol ); + if ( Q3Table::beginEdit( d->editRow, d->editCol, false ) ) + setEditMode( Editing, d->editRow, d->editCol ); + } else { + emit cursorChanged( QSql::Update ); + refresh(); + endUpdate(); + } + break; + } + case QSql::No: + endUpdate(); + setEditMode( NotEditing, -1, -1 ); + break; + case QSql::Cancel: + setCurrentCell( d->editRow, d->editCol ); + if ( Q3Table::beginEdit( d->editRow, d->editCol, false ) ) + setEditMode( Editing, d->editRow, d->editCol ); + break; + } + return ( b > 0 ); +} + +/*! + For an editable table, issues a delete on the current cursor's + primary index using the values of the currently selected row. If + there is no current cursor or there is no current selection, + nothing happens. If confirmEdits() or confirmDelete() is true, + confirmEdit() is called to confirm the delete. Returns true if the + delete succeeded; otherwise false. + + The underlying cursor must have a valid primary index to ensure + that a unique record is deleted within the database otherwise the + database may be changed to an inconsistent state. +*/ + +bool Q3DataTable::deleteCurrent() +{ + if ( !sqlCursor() || isReadOnly() ) + return false; + if ( sqlCursor()->primaryIndex().count() == 0 ) { +#ifdef QT_CHECK_RANGE + qWarning("Q3DataTable::deleteCurrent: no primary index %s", + sqlCursor()->name().latin1() ); +#endif + return false; + } + if ( !sqlCursor()->canDelete() ) + return false; + + int b = 0; + int conf = QSql::Yes; + if ( confirmEdits() || confirmDelete() ) + conf = confirmEdit( QSql::Delete ); + + // Have to have this here - the confirmEdit() might pop up a + // dialog that causes a repaint which the cursor to the + // record it has to repaint. + if ( !sqlCursor()->seek( currentRow() ) ) + return false; + switch ( conf ) { + case QSql::Yes:{ +#ifndef QT_NO_CURSOR + QApplication::setOverrideCursor( Qt::waitCursor ); +#endif + sqlCursor()->primeDelete(); + emit primeDelete( sqlCursor()->editBuffer() ); + emit beforeDelete( sqlCursor()->editBuffer() ); + b = sqlCursor()->del(); +#ifndef QT_NO_CURSOR + QApplication::restoreOverrideCursor(); +#endif + if ( !b ) + handleError( sqlCursor()->lastError() ); + refresh(); + emit cursorChanged( QSql::Delete ); + setCurrentCell( currentRow(), currentColumn() ); + repaintContents( contentsX(), contentsY(), visibleWidth(), visibleHeight(), false ); + verticalHeader()->repaint(); // get rid of trailing garbage + } + break; + case QSql::No: + setEditMode( NotEditing, -1, -1 ); + break; + } + return ( b > 0 ); +} + +/*! + Protected virtual function which returns a confirmation for an + edit of mode \a m. Derived classes can reimplement this function + to provide their own confirmation dialog. The default + implementation uses a message box which prompts the user to + confirm the edit action. +*/ + +QSql::Confirm Q3DataTable::confirmEdit( QSql::Op m ) +{ + return d->dat.confirmEdit( this, m ); +} + +/*! + Protected virtual function which returns a confirmation for + canceling an edit mode of \a m. Derived classes can reimplement + this function to provide their own cancel dialog. The default + implementation uses a message box which prompts the user to + confirm the cancel. +*/ + +QSql::Confirm Q3DataTable::confirmCancel( QSql::Op m ) +{ + return d->dat.confirmCancel( this, m ); +} + + +/*! + Searches the current cursor for a cell containing the string \a + str starting at the current cell and working forwards (or + backwards if \a backwards is true). If the string is found, the + cell containing the string is set as the current cell. If \a + caseSensitive is false the case of \a str will be ignored. + + The search will wrap, i.e. if the first (or if backwards is true, + last) cell is reached without finding \a str the search will + continue until it reaches the starting cell. If \a str is not + found the search will fail and the current cell will remain + unchanged. +*/ +void Q3DataTable::find( const QString & str, bool caseSensitive, bool backwards ) +{ + if ( !sqlCursor() ) + return; + + Q3SqlCursor * r = sqlCursor(); + QString tmp, text; + uint row = currentRow(), startRow = row, + col = backwards ? currentColumn() - 1 : currentColumn() + 1; + bool wrap = true, found = false; + + if( str.isEmpty() || str.isNull() ) + return; + + if( !caseSensitive ) + tmp = str.lower(); + else + tmp = str; + +#ifndef QT_NO_CURSOR + QApplication::setOverrideCursor( Qt::waitCursor ); +#endif + while( wrap ){ + while( !found && r->seek( row ) ){ + for( int i = col; backwards ? (i >= 0) : (i < (int) numCols()); + backwards ? i-- : i++ ) + { + text = r->value( indexOf( i ) ).toString(); + if( !caseSensitive ){ + text = text.lower(); + } + if( text.contains( tmp ) ){ + setCurrentCell( row, i ); + col = i; + found = true; + } + } + if( !backwards ){ + col = 0; + row++; + } else { + col = numCols() - 1; + row--; + } + } + if( !backwards ){ + if( startRow != 0 ){ + startRow = 0; + } else { + wrap = false; + } + r->first(); + row = 0; + } else { + if( startRow != (uint) (numRows() - 1) ){ + startRow = numRows() - 1; + } else { + wrap = false; + } + r->last(); + row = numRows() - 1; + } + } +#ifndef QT_NO_CURSOR + QApplication::restoreOverrideCursor(); +#endif +} + + +/*! + Resets the table so that it displays no data. + + \sa setSqlCursor() +*/ + +void Q3DataTable::reset() +{ + clearCellWidget( currentRow(), currentColumn() ); + switch ( d->dat.mode() ) { + case QSql::Insert: + endInsert(); + break; + case QSql::Update: + endUpdate(); + break; + default: + break; + } + ensureVisible( 0, 0 ); + verticalScrollBar()->setValue(0); + setNumRows(0); + + d->haveAllRows = false; + d->continuousEdit = false; + d->dat.setMode( QSql::None ); + d->editRow = -1; + d->editCol = -1; + d->insertRowLast = -1; + d->insertHeaderLabelLast.clear(); + d->cancelMode = false; + d->lastAt = -1; + d->fld.clear(); + d->fldLabel.clear(); + d->fldWidth.clear(); + d->fldIcon.clear(); + d->fldHidden.clear(); + if ( sorting() ) + horizontalHeader()->setSortIndicator( -1 ); +} + +/*! + Returns the index of the field within the current SQL query that + is displayed in column \a i. +*/ + +int Q3DataTable::indexOf( uint i ) const +{ + Q3DataTablePrivate::ColIndex::ConstIterator it = d->colIndex.at( i ); + if ( it != d->colIndex.end() ) + return *it; + return -1; +} + +/*! + Returns true if the table will automatically delete the cursor + specified by setSqlCursor(); otherwise returns false. +*/ + +bool Q3DataTable::autoDelete() const +{ + return d->cur.autoDelete(); +} + +/*! + Sets the cursor auto-delete flag to \a enable. If \a enable is + true, the table will automatically delete the cursor specified by + setSqlCursor(). If \a enable is false (the default), the cursor + will not be deleted. +*/ + +void Q3DataTable::setAutoDelete( bool enable ) +{ + d->cur.setAutoDelete( enable ); +} + +/*! + \property Q3DataTable::autoEdit + \brief whether the data table automatically applies edits + + The default value for this property is true. When the user begins + an insert or update in the table there are two possible outcomes + when they navigate to another record: + + \list 1 + \i the insert or update is is performed -- this occurs if autoEdit is true + \i the insert or update is abandoned -- this occurs if autoEdit is false + \endlist +*/ + +void Q3DataTable::setAutoEdit( bool autoEdit ) +{ + d->dat.setAutoEdit( autoEdit ); +} + +bool Q3DataTable::autoEdit() const +{ + return d->dat.autoEdit(); +} + +/*! + \property Q3DataTable::nullText + \brief the text used to represent NULL values + + The nullText property will be used to represent NULL values in the + table. The default value is provided by the cursor's driver. +*/ + +void Q3DataTable::setNullText( const QString& nullText ) +{ + d->nullTxt = nullText; + d->nullTxtChanged = true; +} + +QString Q3DataTable::nullText() const +{ + return d->nullTxt; +} + +/*! + \property Q3DataTable::trueText + \brief the text used to represent true values + + The trueText property will be used to represent NULL values in the + table. The default value is "True". +*/ + +void Q3DataTable::setTrueText( const QString& trueText ) +{ + d->trueTxt = trueText; +} + +QString Q3DataTable::trueText() const +{ + return d->trueTxt; +} + +/*! + \property Q3DataTable::falseText + \brief the text used to represent false values + + The falseText property will be used to represent NULL values in + the table. The default value is "False". +*/ + +void Q3DataTable::setFalseText( const QString& falseText ) +{ + d->falseTxt = falseText; +} + +QString Q3DataTable::falseText() const +{ + return d->falseTxt; +} + +/*! + \property Q3DataTable::dateFormat + \brief the format used for displaying date/time values + + The dateFormat property is used for displaying date/time values in + the table. The default value is Qt::LocalDate. +*/ + +void Q3DataTable::setDateFormat( const Qt::DateFormat f ) +{ + d->datefmt = f; +} + +Qt::DateFormat Q3DataTable::dateFormat() const +{ + return d->datefmt; +} + +/*! + \property Q3DataTable::numRows + + \brief the number of rows in the table +*/ + +int Q3DataTable::numRows() const +{ + return Q3Table::numRows(); +} + +/*! + \reimp + + The number of rows in the table will be determined by the cursor + (see setSqlCursor()), so normally this function should never be + called. It is included for completeness. +*/ + +void Q3DataTable::setNumRows ( int r ) +{ + Q3Table::setNumRows( r ); +} + +/*! + \reimp + + The number of columns in the table will be determined + automatically (see addColumn()), so normally this function should + never be called. It is included for completeness. +*/ + +void Q3DataTable::setNumCols ( int r ) +{ + Q3Table::setNumCols( r ); +} + +/*! + \property Q3DataTable::numCols + + \brief the number of columns in the table +*/ + +int Q3DataTable::numCols() const +{ + return Q3Table::numCols(); +} + +/*! + Returns the text in cell \a row, \a col, or an empty string if the + cell is empty. If the cell's value is NULL then nullText() will be + returned. If the cell does not exist then an empty string is + returned. +*/ + +QString Q3DataTable::text ( int row, int col ) const +{ + if ( !sqlCursor() ) + return QString(); + + QString s; + if ( sqlCursor()->seek( row ) ) + s = sqlCursor()->value( indexOf( col ) ).toString(); + sqlCursor()->seek( currentRow() ); + return s; +} + +/*! + Returns the value in cell \a row, \a col, or an invalid value if + the cell does not exist or has no value. +*/ + +QVariant Q3DataTable::value ( int row, int col ) const +{ + if ( !sqlCursor() ) + return QVariant(); + + QVariant v; + if ( sqlCursor()->seek( row ) ) + v = sqlCursor()->value( indexOf( col ) ); + sqlCursor()->seek( currentRow() ); + return v; +} + +/*! \internal + Used to update the table when the size of the result set cannot be + determined - divide the result set into pages and load the pages as + the user moves around in the table. +*/ +void Q3DataTable::loadNextPage() +{ + if ( d->haveAllRows ) + return; + if ( !sqlCursor() ) + return; + int pageSize = 0; + int lookAhead = 0; + if ( height() ) { + pageSize = (int)( height() * 2 / 20 ); + lookAhead = pageSize / 2; + } + int startIdx = verticalScrollBar()->value() / 20; + int endIdx = startIdx + pageSize + lookAhead; + if ( endIdx < numRows() || endIdx < 0 ) + return; + + // check for empty result set + if ( sqlCursor()->at() == QSql::BeforeFirst && !sqlCursor()->next() ) { + d->haveAllRows = true; + return; + } + + while ( endIdx > 0 && !sqlCursor()->seek( endIdx ) ) + endIdx--; + if ( endIdx != ( startIdx + pageSize + lookAhead ) ) + d->haveAllRows = true; + // small hack to prevent Q3Table from moving the view when a row + // is selected and the contents is resized + SelectionMode m = selectionMode(); + clearSelection(); + setSelectionMode( NoSelection ); + setNumRows( endIdx ? endIdx + 1 : 0 ); + sqlCursor()->seek( currentRow() ); + setSelectionMode( m ); +} + +/*! \internal */ +void Q3DataTable::sliderPressed() +{ + disconnect( verticalScrollBar(), SIGNAL(valueChanged(int)), + this, SLOT(loadNextPage()) ); +} + +/*! \internal */ +void Q3DataTable::sliderReleased() +{ + loadNextPage(); + connect( verticalScrollBar(), SIGNAL(valueChanged(int)), + this, SLOT(loadNextPage()) ); +} + +/*! + Sorts column \a col in ascending order if \a ascending is true + (the default); otherwise sorts in descending order. + + The \a wholeRows parameter is ignored; Q3DataTable always sorts + whole rows by the specified column. +*/ + +void Q3DataTable::sortColumn ( int col, bool ascending, + bool ) +{ + if ( sorting() ) { + if ( isEditing() && d->dat.mode() != QSql::None ) + endEdit( d->editRow, d->editCol, autoEdit(), false ); + if ( !sqlCursor() ) + return; + QSqlIndex lastSort = sqlCursor()->sort(); + QSqlIndex newSort( lastSort.cursorName(), QLatin1String("newSort") ); + const QSqlField *field = sqlCursor()->fieldPtr( indexOf( col ) ); + if ( field ) + newSort.append( *field ); + newSort.setDescending( 0, !ascending ); + horizontalHeader()->setSortIndicator( col, ascending ); + setSort( newSort ); + refresh(); + } +} + +/*! \reimp */ +void Q3DataTable::columnClicked ( int col ) +{ + if ( sorting() ) { + if ( !sqlCursor() ) + return; + QSqlIndex lastSort = sqlCursor()->sort(); + bool asc = true; + if ( lastSort.count() && lastSort.fieldPtr( 0 )->name() == sqlCursor()->fieldPtr( indexOf( col ) )->name() ) + asc = lastSort.isDescending( 0 ); + sortColumn( col, asc ); + emit currentChanged( sqlCursor() ); + } +} + +/*! + Repaints the cell at \a row, \a col. +*/ +void Q3DataTable::repaintCell( int row, int col ) +{ + QRect cg = cellGeometry( row, col ); + QRect re( QPoint( cg.x() - 2, cg.y() - 2 ), + QSize( cg.width() + 4, cg.height() + 4 ) ); + repaintContents( re, false ); +} + +/*! + \reimp + + This function renders the cell at \a row, \a col with the value of + the corresponding cursor field on the painter \a p. Depending on + the table's current edit mode, paintField() is called for the + appropriate cursor field. \a cr describes the cell coordinates in + the content coordinate system. If \a selected is true the cell has + been selected and would normally be rendered differently than an + unselected cell. + + \sa QSql::isNull() +*/ + +void Q3DataTable::paintCell( QPainter * p, int row, int col, const QRect & cr, + bool selected, const QColorGroup &cg ) +{ + Q3Table::paintCell( p, row, col, cr, selected, cg ); // empty cell + + if ( !sqlCursor() ) + return; + + p->setPen( selected ? cg.highlightedText() : cg.text() ); + if ( d->dat.mode() != QSql::None ) { + if ( row == d->editRow && d->editBuffer ) { + paintField( p, d->editBuffer->fieldPtr( indexOf( col ) ), cr, + selected ); + } else if ( row > d->editRow && d->dat.mode() == QSql::Insert ) { + if ( sqlCursor()->seek( row - 1 ) ) + paintField( p, sqlCursor()->fieldPtr( indexOf( col ) ), cr, + selected ); + } else { + if ( sqlCursor()->seek( row ) ) + paintField( p, sqlCursor()->fieldPtr( indexOf( col ) ), cr, + selected ); + } + } else { + if ( sqlCursor()->seek( row ) ) + paintField( p, sqlCursor()->fieldPtr( indexOf( col ) ), cr, selected ); + + } +} + + +/*! + Paints the \a field on the painter \a p. The painter has already + been translated to the appropriate cell's origin where the \a + field is to be rendered. \a cr describes the cell coordinates in + the content coordinate system. The \a selected parameter is + ignored. + + If you want to draw custom field content you must reimplement + paintField() to do the custom drawing. The default implementation + renders the \a field value as text. If the field is NULL, + nullText() is displayed in the cell. If the field is Boolean, + trueText() or falseText() is displayed as appropriate. +*/ + +void Q3DataTable::paintField( QPainter * p, const QSqlField* field, + const QRect & cr, bool ) +{ + if ( !field ) + return; + p->drawText( 2,2, cr.width()-4, cr.height()-4, fieldAlignment( field ), fieldToString( field ) ); +} + +/*! + Returns the alignment for \a field. +*/ + +int Q3DataTable::fieldAlignment( const QSqlField* /*field*/ ) +{ + return Qt::AlignLeft | Qt::AlignVCenter; //## Reggie: add alignment to Q3Table +} + + +/*! + If the cursor's \a sql driver supports query sizes, the number of + rows in the table is set to the size of the query. Otherwise, the + table dynamically resizes itself as it is scrolled. If \a sql is + not active, it is made active by issuing a select() on the cursor + using the \a sql cursor's current filter and current sort. +*/ + +void Q3DataTable::setSize( Q3SqlCursor* sql ) +{ + // ### what are the connect/disconnect calls doing here!? move to refresh() + if ( sql->driver() && sql->driver()->hasFeature( QSqlDriver::QuerySize ) ) { + setVScrollBarMode( Auto ); + disconnect( verticalScrollBar(), SIGNAL(sliderPressed()), + this, SLOT(sliderPressed()) ); + disconnect( verticalScrollBar(), SIGNAL(sliderReleased()), + this, SLOT(sliderReleased()) ); + disconnect( verticalScrollBar(), SIGNAL(valueChanged(int)), + this, SLOT(loadNextPage()) ); + if ( numRows() != sql->size() ) + setNumRows( sql->size() ); + } else { + setVScrollBarMode( AlwaysOn ); + connect( verticalScrollBar(), SIGNAL(sliderPressed()), + this, SLOT(sliderPressed()) ); + connect( verticalScrollBar(), SIGNAL(sliderReleased()), + this, SLOT(sliderReleased()) ); + connect( verticalScrollBar(), SIGNAL(valueChanged(int)), + this, SLOT(loadNextPage()) ); + setNumRows(0); + loadNextPage(); + } +} + +/*! + Sets \a cursor as the data source for the table. To force the + display of the data from \a cursor, use refresh(). If \a + autoPopulate is true, columns are automatically created based upon + the fields in the \a cursor record. If \a autoDelete is true (the + default is false), the table will take ownership of the \a cursor + and delete it when appropriate. If the \a cursor is read-only, the + table becomes read-only. The table adopts the cursor's driver's + definition for representing NULL values as strings. + + \sa refresh() setReadOnly() setAutoDelete() QSqlDriver::nullText() +*/ + +void Q3DataTable::setSqlCursor( Q3SqlCursor* cursor, bool autoPopulate, bool autoDelete ) +{ + setUpdatesEnabled( false ); + d->cur.setCursor( 0 ); + if ( cursor ) { + d->cur.setCursor( cursor, autoDelete ); + if ( autoPopulate ) { + d->fld.clear(); + d->fldLabel.clear(); + d->fldWidth.clear(); + d->fldIcon.clear(); + d->fldHidden.clear(); + for ( int i = 0; i < sqlCursor()->count(); ++i ) { + addColumn( sqlCursor()->fieldPtr( i )->name(), sqlCursor()->fieldPtr( i )->name() ); + setColumnReadOnly( i, sqlCursor()->fieldPtr( i )->isReadOnly() ); + } + } + setReadOnly( sqlCursor()->isReadOnly() ); + if ( sqlCursor()->driver() && !d->nullTxtChanged ) + setNullText(sqlCursor()->driver()->nullText() ); + setAutoDelete( autoDelete ); + } else { + setNumRows( 0 ); + setNumCols( 0 ); + } + setUpdatesEnabled( true ); +} + + +/*! + Protected virtual function which is called when an error \a e has + occurred on the current cursor(). The default implementation + displays a warning message to the user with information about the + error. +*/ +void Q3DataTable::handleError( const QSqlError& e ) +{ + d->dat.handleError( this, e ); +} + +/*! \reimp + */ + +void Q3DataTable::keyPressEvent( QKeyEvent* e ) +{ + switch( e->key() ) { + case Qt::Key_Left: + case Qt::Key_Right: + case Qt::Key_Up: + case Qt::Key_Down: + case Qt::Key_Prior: + case Qt::Key_Next: + case Qt::Key_Home: + case Qt::Key_End: + case Qt::Key_F2: + case Qt::Key_Enter: case Qt::Key_Return: + case Qt::Key_Tab: case Qt::Key_BackTab: + Q3Table::keyPressEvent( e ); + default: + return; + } +} + +/*! \reimp +*/ + +void Q3DataTable::resizeData ( int ) +{ + +} + +/*! \reimp +*/ + +Q3TableItem * Q3DataTable::item ( int, int ) const +{ + return 0; +} + +/*! \reimp +*/ + +void Q3DataTable::setItem ( int , int , Q3TableItem * ) +{ + +} + +/*! \reimp +*/ + +void Q3DataTable::clearCell ( int , int ) +{ + +} + +/*! \reimp +*/ + +void Q3DataTable::setPixmap ( int , int , const QPixmap & ) +{ + +} + +/*! \reimp */ +void Q3DataTable::takeItem ( Q3TableItem * ) +{ + +} + +/*! + Installs a new SQL editor factory \a f. This enables the user to + create and instantiate their own editors for use in cell editing. + Note that Q3DataTable takes ownership of this pointer, and will + delete it when it is no longer needed or when + installEditorFactory() is called again. + + \sa Q3SqlEditorFactory +*/ + +void Q3DataTable::installEditorFactory( Q3SqlEditorFactory * f ) +{ + if( f ) { + delete d->editorFactory; + d->editorFactory = f; + } +} + +/*! + Installs a new property map \a m. This enables the user to create + and instantiate their own property maps for use in cell editing. + Note that Q3DataTable takes ownership of this pointer, and will + delete it when it is no longer needed or when installPropertMap() + is called again. + + \sa Q3SqlPropertyMap +*/ + +void Q3DataTable::installPropertyMap( Q3SqlPropertyMap* m ) +{ + if ( m ) { + delete d->propertyMap; + d->propertyMap = m; + } +} + +/*! \internal + + Sets the current selection to \a row, \a col. +*/ + +void Q3DataTable::setCurrentSelection( int row, int ) +{ + if ( !sqlCursor() ) + return; + if ( row == d->lastAt ) + return; + if ( !sqlCursor()->seek( row ) ) + return; + d->lastAt = row; + emit currentChanged( sqlCursor() ); +} + +void Q3DataTable::updateCurrentSelection() +{ + setCurrentSelection( currentRow(), -1 ); +} + +/*! + Returns the currently selected record, or 0 if there is no current + selection. The table owns the pointer, so do \e not delete it or + otherwise modify it or the cursor it points to. +*/ + +QSqlRecord* Q3DataTable::currentRecord() const +{ + if ( !sqlCursor() || currentRow() < 0 ) + return 0; + if ( !sqlCursor()->seek( currentRow() ) ) + return 0; + return sqlCursor(); +} + +/*! + Sorts column \a col in ascending order. + + \sa setSorting() +*/ + +void Q3DataTable::sortAscending( int col ) +{ + sortColumn( col, true ); +} + +/*! + Sorts column \a col in descending order. + + \sa setSorting() +*/ + +void Q3DataTable::sortDescending( int col ) +{ + sortColumn( col, false ); +} + +/*! + \fn void Q3DataTable::refresh( Refresh mode ) + + Refreshes the table. If there is no currently defined cursor (see + setSqlCursor()), nothing happens. The \a mode parameter determines + which type of refresh will take place. + + \sa Refresh setSqlCursor() addColumn() +*/ + +void Q3DataTable::refresh( Q3DataTable::Refresh mode ) +{ + Q3SqlCursor* cur = sqlCursor(); + if ( !cur ) + return; + bool refreshData = ( (mode & RefreshData) == RefreshData ); + bool refreshCol = ( (mode & RefreshColumns) == RefreshColumns ); + if ( ( (mode & RefreshAll) == RefreshAll ) ) { + refreshData = true; + refreshCol = true; + } + if ( !refreshCol && d->fld.count() && numCols() == 0 ) + refreshCol = true; + viewport()->setUpdatesEnabled( false ); + d->haveAllRows = false; + if ( refreshData ) { + if ( !d->cur.refresh() && d->cur.cursor() ) { + handleError( d->cur.cursor()->lastError() ); + } + d->lastAt = -1; + } + if ( refreshCol ) { + setNumCols( 0 ); + d->colIndex.clear(); + if ( d->fld.count() ) { + const QSqlField* field = 0; + int i; + int fpos = -1; + for ( i = 0; i < (int)d->fld.count(); ++i ) { + if ( cur->fieldPtr( i ) && cur->fieldPtr( i )->name() == d->fld[ i ] ) + // if there is a field with the desired name on the desired position + // then we take that + fpos = i; + else + // otherwise we take the first field that matches the desired name + fpos = cur->position( d->fld[ i ] ); + field = cur->fieldPtr( fpos ); + if ( field && ( cur->isGenerated( fpos ) || + cur->isCalculated( field->name() ) ) ) + { + setNumCols( numCols() + 1 ); + d->colIndex.append( fpos ); + setColumnReadOnly( numCols()-1, field->isReadOnly() || isColumnReadOnly( numCols()-1 ) ); + horizontalHeader()->setLabel( numCols()-1, d->fldIcon[ i ], d->fldLabel[ i ] ); + if ( d->fldHidden[ i ] ) { + Q3Table::showColumn( i ); // ugly but necessary + Q3Table::hideColumn( i ); + } else { + Q3Table::showColumn( i ); + } + if ( d->fldWidth[ i ] > -1 ) + Q3Table::setColumnWidth( i, d->fldWidth[i] ); + } + } + } + } + viewport()->setUpdatesEnabled( true ); + viewport()->repaint( false ); + horizontalHeader()->repaint(); + verticalHeader()->repaint(); + setSize( cur ); + // keep others aware + if ( d->lastAt == -1 ) + setCurrentSelection( -1, -1 ); + else if ( d->lastAt != currentRow() ) + setCurrentSelection( currentRow(), currentColumn() ); + if ( cur->isValid() ) + emit currentChanged( sqlCursor() ); +} + +/*! + Refreshes the table. The cursor is refreshed using the current + filter, the current sort, and the currently defined columns. + Equivalent to calling refresh( Q3DataTable::RefreshData ). +*/ + +void Q3DataTable::refresh() +{ + refresh( RefreshData ); +} + +/*! + \internal + + Selects the record in the table using the current cursor edit + buffer and the fields specified by the index \a idx. If \a atHint + is specified, it will be used as a hint about where to begin + searching. +*/ + +bool Q3DataTable::findBuffer( const QSqlIndex& idx, int atHint ) +{ + Q3SqlCursor* cur = sqlCursor(); + if ( !cur ) + return false; + bool found = d->cur.findBuffer( idx, atHint ); + if ( found ) + setCurrentCell( cur->at(), currentColumn() ); + return found; +} + +/*! \internal + Returns the string representation of a database field. +*/ +QString Q3DataTable::fieldToString( const QSqlField * field ) +{ + QString text; + if ( field->isNull() ) { + text = nullText(); + } else { + QVariant val = field->value(); + switch ( val.type() ) { + case QVariant::Bool: + text = val.toBool() ? d->trueTxt : d->falseTxt; + break; + case QVariant::Date: + text = val.toDate().toString( d->datefmt ); + break; + case QVariant::Time: + text = val.toTime().toString( d->datefmt ); + break; + case QVariant::DateTime: + text = val.toDateTime().toString( d->datefmt ); + break; + default: + text = val.toString(); + break; + } + } + return text; +} + +/*! + \reimp +*/ + +void Q3DataTable::swapColumns( int col1, int col2, bool ) +{ + QString fld = d->fld[ col1 ]; + QString fldLabel = d->fldLabel[ col1 ]; + QIconSet fldIcon = d->fldIcon[ col1 ]; + int fldWidth = d->fldWidth[ col1 ]; + + d->fld[ col1 ] = d->fld[ col2 ]; + d->fldLabel[ col1 ] = d->fldLabel[ col2 ]; + d->fldIcon[ col1 ] = d->fldIcon[ col2 ]; + d->fldWidth[ col1 ] = d->fldWidth[ col2 ]; + + d->fld[ col2 ] = fld; + d->fldLabel[ col2 ] = fldLabel; + d->fldIcon[ col2 ] = fldIcon; + d->fldWidth[ col2 ] = fldWidth; + + int colIndex = d->colIndex[ col1 ]; + d->colIndex[ col1 ] = d->colIndex[ col2 ]; + d->colIndex[ col2 ] = colIndex; +} + +/*! + \reimp +*/ + +void Q3DataTable::drawContents( QPainter * p, int cx, int cy, int cw, int ch ) +{ + Q3Table::drawContents( p, cx, cy, cw, ch ); + if ( sqlCursor() && currentRow() >= 0 ) + sqlCursor()->seek( currentRow() ); +} + +/*! + \reimp + */ +void Q3DataTable::drawContents(QPainter *) +{ +} + +/*! + \reimp +*/ + +void Q3DataTable::hideColumn( int col ) +{ + d->fldHidden[col] = true; + refresh( RefreshColumns ); +} + +/*! + \reimp +*/ + +void Q3DataTable::showColumn( int col ) +{ + d->fldHidden[col] = false; + refresh( RefreshColumns ); +} + +/*! + \reimp +*/ +void Q3DataTable::selectRow(int row) +{ + setCurrentCell(row, currentColumn()); +} + +/*! + \fn void Q3DataTable::currentChanged( QSqlRecord* record ) + + This signal is emitted whenever a new row is selected in the + table. The \a record parameter points to the contents of the newly + selected record. +*/ + +/*! + \fn void Q3DataTable::primeInsert( QSqlRecord* buf ) + + This signal is emitted after the cursor is primed for insert by + the table, when an insert action is beginning on the table. The \a + buf parameter points to the edit buffer being inserted. Connect to + this signal in order to, for example, prime the record buffer with + default data values. +*/ + +/*! + \fn void Q3DataTable::primeUpdate( QSqlRecord* buf ) + + This signal is emitted after the cursor is primed for update by + the table, when an update action is beginning on the table. The \a + buf parameter points to the edit buffer being updated. Connect to + this signal in order to, for example, provide some visual feedback + that the user is in 'edit mode'. +*/ + +/*! + \fn void Q3DataTable::primeDelete( QSqlRecord* buf ) + + This signal is emitted after the cursor is primed for delete by + the table, when a delete action is beginning on the table. The \a + buf parameter points to the edit buffer being deleted. Connect to + this signal in order to, for example, record auditing information + on deletions. +*/ + +/*! + \fn void Q3DataTable::beforeInsert( QSqlRecord* buf ) + + This signal is emitted just before the cursor's edit buffer is + inserted into the database. The \a buf parameter points to the + edit buffer being inserted. Connect to this signal to, for + example, populate a key field with a unique sequence number. +*/ + +/*! + \fn void Q3DataTable::beforeUpdate( QSqlRecord* buf ) + + This signal is emitted just before the cursor's edit buffer is + updated in the database. The \a buf parameter points to the edit + buffer being updated. Connect to this signal when you want to + transform the user's data behind-the-scenes. +*/ + +/*! + \fn void Q3DataTable::beforeDelete( QSqlRecord* buf ) + + This signal is emitted just before the currently selected record + is deleted from the database. The \a buf parameter points to the + edit buffer being deleted. Connect to this signal to, for example, + copy some of the fields for later use. +*/ + +/*! + \fn void Q3DataTable::cursorChanged( QSql::Op mode ) + + This signal is emitted whenever the cursor record was changed due + to an edit. The \a mode parameter is the type of edit that just + took place. +*/ + +#endif + +QT_END_NAMESPACE diff --git a/src/qt3support/sql/q3datatable.h b/src/qt3support/sql/q3datatable.h new file mode 100644 index 0000000..bbfd01c --- /dev/null +++ b/src/qt3support/sql/q3datatable.h @@ -0,0 +1,251 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt3Support module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef Q3DATATABLE_H +#define Q3DATATABLE_H + +#include <QtCore/qstring.h> +#include <QtCore/qvariant.h> +#include <Qt3Support/q3table.h> +#include <QtSql/qsql.h> +#include <Qt3Support/q3sqlcursor.h> +#include <QtSql/qsqlindex.h> +#include <Qt3Support/q3sqleditorfactory.h> +#include <Qt3Support/qiconset.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Qt3Support) + +#ifndef QT_NO_SQL_VIEW_WIDGETS + +class QPainter; +class QSqlField; +class Q3SqlPropertyMap; +class Q3DataTablePrivate; + +class Q_COMPAT_EXPORT Q3DataTable : public Q3Table +{ + Q_OBJECT + + Q_PROPERTY( QString nullText READ nullText WRITE setNullText ) + Q_PROPERTY( QString trueText READ trueText WRITE setTrueText ) + Q_PROPERTY( QString falseText READ falseText WRITE setFalseText ) + Q_PROPERTY( Qt::DateFormat dateFormat READ dateFormat WRITE setDateFormat ) + Q_PROPERTY( bool confirmEdits READ confirmEdits WRITE setConfirmEdits ) + Q_PROPERTY( bool confirmInsert READ confirmInsert WRITE setConfirmInsert ) + Q_PROPERTY( bool confirmUpdate READ confirmUpdate WRITE setConfirmUpdate ) + Q_PROPERTY( bool confirmDelete READ confirmDelete WRITE setConfirmDelete ) + Q_PROPERTY( bool confirmCancels READ confirmCancels WRITE setConfirmCancels ) + Q_PROPERTY( bool autoEdit READ autoEdit WRITE setAutoEdit ) + Q_PROPERTY( QString filter READ filter WRITE setFilter ) + Q_PROPERTY( QStringList sort READ sort WRITE setSort ) + Q_PROPERTY( int numCols READ numCols ) + Q_PROPERTY( int numRows READ numRows ) + +public: + Q3DataTable ( QWidget* parent=0, const char* name=0 ); + Q3DataTable ( Q3SqlCursor* cursor, bool autoPopulate = false, QWidget* parent=0, const char* name=0 ); + ~Q3DataTable(); + + virtual void addColumn( const QString& fieldName, + const QString& label = QString(), + int width = -1, + const QIconSet& iconset = QIconSet() ); + virtual void removeColumn( int col ); + virtual void setColumn( uint col, const QString& fieldName, + const QString& label = QString(), + int width = -1, + const QIconSet& iconset = QIconSet() ); + + QString nullText() const; + QString trueText() const; + QString falseText() const; + Qt::DateFormat dateFormat() const; + bool confirmEdits() const; + bool confirmInsert() const; + bool confirmUpdate() const; + bool confirmDelete() const; + bool confirmCancels() const; + bool autoDelete() const; + bool autoEdit() const; + QString filter() const; + QStringList sort() const; + + virtual void setSqlCursor( Q3SqlCursor* cursor = 0, + bool autoPopulate = false, bool autoDelete = false ); + Q3SqlCursor* sqlCursor() const; + + virtual void setNullText( const QString& nullText ); + virtual void setTrueText( const QString& trueText ); + virtual void setFalseText( const QString& falseText ); + virtual void setDateFormat( const Qt::DateFormat f ); + virtual void setConfirmEdits( bool confirm ); + virtual void setConfirmInsert( bool confirm ); + virtual void setConfirmUpdate( bool confirm ); + virtual void setConfirmDelete( bool confirm ); + virtual void setConfirmCancels( bool confirm ); + virtual void setAutoDelete( bool enable ); + virtual void setAutoEdit( bool autoEdit ); + virtual void setFilter( const QString& filter ); + virtual void setSort( const QStringList& sort ); + virtual void setSort( const QSqlIndex& sort ); + + enum Refresh { + RefreshData = 1, + RefreshColumns = 2, + RefreshAll = 3 + }; + void refresh( Refresh mode ); + void sortColumn ( int col, bool ascending = true, + bool wholeRows = false ); + QString text ( int row, int col ) const; + QVariant value ( int row, int col ) const; + QSqlRecord* currentRecord() const; + + void installEditorFactory( Q3SqlEditorFactory * f ); + void installPropertyMap( Q3SqlPropertyMap* m ); + + int numCols() const; + int numRows() const; + void setNumCols( int c ); + void setNumRows ( int r ); + bool findBuffer( const QSqlIndex& idx, int atHint = 0 ); + + void hideColumn( int col ); + void showColumn( int col ); + int indexOf( uint i ) const; + void selectRow(int row); + +Q_SIGNALS: + void currentChanged( QSqlRecord* record ); + void primeInsert( QSqlRecord* buf ); + void primeUpdate( QSqlRecord* buf ); + void primeDelete( QSqlRecord* buf ); + void beforeInsert( QSqlRecord* buf ); + void beforeUpdate( QSqlRecord* buf ); + void beforeDelete( QSqlRecord* buf ); + void cursorChanged( QSql::Op mode ); + +public Q_SLOTS: + virtual void find( const QString & str, bool caseSensitive, + bool backwards ); + virtual void sortAscending( int col ); + virtual void sortDescending( int col ); + virtual void refresh(); + void setColumnWidth( int col, int w ); + void adjustColumn( int col ); + void setColumnStretchable( int col, bool stretch ); + void swapColumns( int col1, int col2, bool swapHeaders = false ); + +protected: + virtual bool insertCurrent(); + virtual bool updateCurrent(); + virtual bool deleteCurrent(); + + virtual QSql::Confirm confirmEdit( QSql::Op m ); + virtual QSql::Confirm confirmCancel( QSql::Op m ); + + virtual void handleError( const QSqlError& e ); + + virtual bool beginInsert(); + virtual QWidget* beginUpdate ( int row, int col, bool replace ); + + bool eventFilter( QObject *o, QEvent *e ); + void keyPressEvent( QKeyEvent* ); + void resizeEvent ( QResizeEvent * ); + void contentsMousePressEvent( QMouseEvent* e ); + void contentsContextMenuEvent( QContextMenuEvent* e ); + void endEdit( int row, int col, bool accept, bool replace ); + QWidget * createEditor( int row, int col, bool initFromCell ) const; + void activateNextCell(); + void reset(); + void setSize( Q3SqlCursor* sql ); + void repaintCell( int row, int col ); + void paintCell ( QPainter * p, int row, int col, const QRect & cr, + bool selected, const QColorGroup &cg ); + virtual void paintField( QPainter * p, const QSqlField* field, const QRect & cr, + bool selected ); + void drawContents( QPainter * p, int cx, int cy, int cw, int ch ); + virtual int fieldAlignment( const QSqlField* field ); + void columnClicked ( int col ); + void resizeData ( int len ); + + Q3TableItem * item ( int row, int col ) const; + void setItem ( int row, int col, Q3TableItem * item ); + void clearCell ( int row, int col ) ; + void setPixmap ( int row, int col, const QPixmap & pix ); + void takeItem ( Q3TableItem * i ); + +private Q_SLOTS: + void loadNextPage(); + void setCurrentSelection( int row, int col ); + void updateCurrentSelection(); + void sliderPressed(); + void sliderReleased(); + void doInsertCurrent(); + void doUpdateCurrent(); + +private: + void drawContents( QPainter *p); + QString fieldToString( const QSqlField * field ); + void init(); + QWidget* beginEdit ( int row, int col, bool replace ); + void updateRow( int row ); + void endInsert(); + void endUpdate(); + Q3DataTablePrivate* d; + +#if defined(Q_DISABLE_COPY) // Disabled copy constructor and operator= + Q3DataTable( const Q3DataTable & ); + Q3DataTable &operator=( const Q3DataTable & ); +#endif +}; + +#endif // QT_NO_SQL_VIEW_WIDGETS + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // Q3DATATABLE_H diff --git a/src/qt3support/sql/q3dataview.cpp b/src/qt3support/sql/q3dataview.cpp new file mode 100644 index 0000000..fb44996 --- /dev/null +++ b/src/qt3support/sql/q3dataview.cpp @@ -0,0 +1,208 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt3Support module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "q3dataview.h" + +#ifndef QT_NO_SQL_VIEW_WIDGETS + +#include "private/q3sqlmanager_p.h" + +QT_BEGIN_NAMESPACE + +class Q3DataViewPrivate +{ +public: + Q3DataViewPrivate() {} + Q3SqlFormManager frm; +}; + + +/*! + \class Q3DataView + \brief The Q3DataView class provides read-only SQL forms. + + \compat + + This class provides a form which displays SQL field data from a + record buffer. Because Q3DataView does not support editing it uses + less resources than a Q3DataBrowser. This class is well suited for + displaying read-only data from a SQL database. + + If you want a to present your data in an editable form use + Q3DataBrowser; if you want a table-based presentation of your data + use Q3DataTable. + + The form is associated with the data view with setForm() and the + record is associated with setRecord(). You can also pass a + QSqlRecord to the refresh() function which will set the record to + the given record and read the record's fields into the form. +*/ + +/*! + Constructs a data view which is a child of \a parent, called \a + name, and with widget flags \a fl. +*/ + +Q3DataView::Q3DataView(QWidget *parent, const char *name, Qt::WindowFlags fl) + : QWidget(parent, name, fl) +{ + d = new Q3DataViewPrivate(); +} + +/*! + Destroys the object and frees any allocated resources. +*/ + +Q3DataView::~Q3DataView() +{ + delete d; +} + +/*! + Clears the default form's values. If there is no default form, + nothing happens. All the values are set to their 'zero state', + e.g. 0 for numeric fields, "" for string fields. +*/ + +void Q3DataView::clearValues() +{ + d->frm.clearValues(); +} + +/*! + Sets the form used by the data view to \a form. If a record has + already been assigned to the data view, the form will display that + record's data. + + \sa form() +*/ + +void Q3DataView::setForm(Q3SqlForm* form) +{ + d->frm.setForm(form); +} + + +/*! + Returns the default form used by the data view, or 0 if there is + none. + + \sa setForm() +*/ + +Q3SqlForm* Q3DataView::form() +{ + return d->frm.form(); +} + + +/*! + Sets the record used by the data view to \a record. If a form has + already been assigned to the data view, the form will display the + data from \a record in that form. + + \sa record() +*/ + +void Q3DataView::setRecord(QSqlRecord* record) +{ + d->frm.setRecord(record); +} + + +/*! + Returns the default record used by the data view, or 0 if there is + none. + + \sa setRecord() +*/ + +QSqlRecord* Q3DataView::record() +{ + return d->frm.record(); +} + + +/*! + Causes the default form to read its fields from the record buffer. + If there is no default form, or no record, nothing happens. + + \sa setForm() +*/ + +void Q3DataView::readFields() +{ + d->frm.readFields(); +} + +/*! + Causes the default form to write its fields to the record buffer. + If there is no default form, or no record, nothing happens. + + \sa setForm() +*/ + +void Q3DataView::writeFields() +{ + d->frm.writeFields(); +} + +/*! + Causes the default form to display the contents of \a buf. If + there is no default form, nothing happens.The \a buf also becomes + the default record for all subsequent calls to readFields() and + writefields(). This slot is equivalant to calling: + + \snippet doc/src/snippets/code/src_qt3support_sql_q3dataview.cpp 0 + + \sa setRecord() readFields() +*/ + +void Q3DataView::refresh(QSqlRecord* buf) +{ + if (buf && buf != record()) + setRecord(buf); + readFields(); +} + +QT_END_NAMESPACE + +#endif // QT_NO_SQL_VIEW_WIDGETS diff --git a/src/qt3support/sql/q3dataview.h b/src/qt3support/sql/q3dataview.h new file mode 100644 index 0000000..f1b29d1 --- /dev/null +++ b/src/qt3support/sql/q3dataview.h @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt3Support module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef Q3DATAVIEW_H +#define Q3DATAVIEW_H + +#include <QtGui/qwidget.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Qt3Support) + +#ifndef QT_NO_SQL_VIEW_WIDGETS + +class Q3SqlForm; +class QSqlRecord; +class Q3DataViewPrivate; + +class Q_COMPAT_EXPORT Q3DataView : public QWidget +{ + Q_OBJECT + +public: + Q3DataView(QWidget* parent=0, const char* name=0, Qt::WindowFlags fl = 0); + ~Q3DataView(); + + virtual void setForm(Q3SqlForm* form); + Q3SqlForm* form(); + virtual void setRecord(QSqlRecord* record); + QSqlRecord* record(); + +public Q_SLOTS: + virtual void refresh(QSqlRecord* buf); + virtual void readFields(); + virtual void writeFields(); + virtual void clearValues(); + +private: + Q_DISABLE_COPY(Q3DataView) + + Q3DataViewPrivate* d; +}; + +#endif // QT_NO_SQL_VIEW_WIDGETS + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // Q3DATAVIEW_H diff --git a/src/qt3support/sql/q3editorfactory.cpp b/src/qt3support/sql/q3editorfactory.cpp new file mode 100644 index 0000000..e5d97c7 --- /dev/null +++ b/src/qt3support/sql/q3editorfactory.cpp @@ -0,0 +1,202 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt3Support module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "q3cleanuphandler.h" +#include "qlabel.h" +#include "qlineedit.h" +#include "qspinbox.h" +#include "qcombobox.h" + +#include "q3editorfactory.h" +#include "qdatetimeedit.h" + +#ifndef QT_NO_SQL_EDIT_WIDGETS + +QT_BEGIN_NAMESPACE + +/*! + \class Q3EditorFactory + \brief The Q3EditorFactory class is used to create editor widgets + for QVariant data types. + + \compat + + Each editor factory provides the createEditor() function which + given a QVariant will create and return a QWidget that can edit + that QVariant. For example if you have a QVariant::String type, a + QLineEdit would be the default editor returned, whereas a + QVariant::Int's default editor would be a QSpinBox. + + If you want to create different editors for fields with the same + data type, subclass Q3EditorFactory and reimplement the + createEditor() function. +*/ + +/*! + Constructs an editor factory with parent \a parent. +*/ + +Q3EditorFactory::Q3EditorFactory (QObject * parent) + : QObject(parent) +{ + +} + +/*! + Destroys the object and frees any allocated resources. +*/ + +Q3EditorFactory::~Q3EditorFactory() +{ + +} + +static Q3EditorFactory * defaultfactory = 0; +static Q3CleanupHandler< Q3EditorFactory > q_cleanup_editor_factory; + +/*! + Returns an instance of a default editor factory. +*/ + +Q3EditorFactory * Q3EditorFactory::defaultFactory() +{ + if(defaultfactory == 0){ + defaultfactory = new Q3EditorFactory(); + q_cleanup_editor_factory.add(&defaultfactory); + } + + return defaultfactory; +} + +/*! + Replaces the default editor factory with \a factory. + \e{Q3EditorFactory takes ownership of factory, and destroys it + when it is no longer needed.} +*/ + +void Q3EditorFactory::installDefaultFactory(Q3EditorFactory * factory) +{ + if(factory == 0 || factory == defaultfactory) return; + + if(defaultfactory != 0){ + q_cleanup_editor_factory.remove(&defaultfactory); + delete defaultfactory; + } + defaultfactory = factory; + q_cleanup_editor_factory.add(&defaultfactory); +} + +/*! + Creates and returns the appropriate editor for the QVariant \a v. + If the QVariant is invalid, 0 is returned. The \a parent is passed + to the appropriate editor's constructor. +*/ + +QWidget * Q3EditorFactory::createEditor(QWidget * parent, const QVariant & v) +{ + QWidget * w = 0; + switch(v.type()){ + case QVariant::Invalid: + w = 0; + break; + case QVariant::Bool: + w = new QComboBox(parent, "qt_editor_bool"); + ((QComboBox *) w)->insertItem(QLatin1String("False")); + ((QComboBox *) w)->insertItem(QLatin1String("True")); + break; + case QVariant::UInt: + w = new QSpinBox(0, 999999, 1, parent, "qt_editor_spinbox"); + break; + case QVariant::Int: + w = new QSpinBox(-999999, 999999, 1, parent, "qt_editor_int"); + break; + case QVariant::String: + case QVariant::Double: + w = new QLineEdit(parent, "qt_editor_double"); + ((QLineEdit*)w)->setFrame(false); + break; + case QVariant::Date: { + QDateTimeEdit *edit = new QDateTimeEdit(parent); + edit->setDisplayFormat(QLatin1String("yyyy/MM/dd")); + edit->setObjectName(QLatin1String("qt_editor_date")); + w = edit; } + break; + case QVariant::Time: { + QDateTimeEdit *edit = new QDateTimeEdit(parent); + edit->setDisplayFormat(QLatin1String("hh:mm")); + edit->setObjectName(QLatin1String("qt_editor_time")); + w = edit; } + break; + case QVariant::DateTime: + w = new QDateTimeEdit(parent); + w->setObjectName(QLatin1String("qt_editor_datetime")); + break; +#ifndef QT_NO_LABEL + case QVariant::Pixmap: + w = new QLabel(parent, QLatin1String("qt_editor_pixmap")); + break; +#endif + case QVariant::Palette: + case QVariant::Color: + case QVariant::Font: + case QVariant::Brush: + case QVariant::Bitmap: + case QVariant::Cursor: + case QVariant::Map: + case QVariant::StringList: + case QVariant::Rect: + case QVariant::Size: + case QVariant::IconSet: + case QVariant::Point: + case QVariant::PointArray: + case QVariant::Region: + case QVariant::SizePolicy: + case QVariant::ByteArray: + default: + w = new QWidget(parent, "qt_editor_default"); + break; + } + return w; +} + +QT_END_NAMESPACE + +#endif // QT_NO_SQL diff --git a/src/qt3support/sql/q3editorfactory.h b/src/qt3support/sql/q3editorfactory.h new file mode 100644 index 0000000..476b683 --- /dev/null +++ b/src/qt3support/sql/q3editorfactory.h @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt3Support module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef Q3EDITORFACTORY_H +#define Q3EDITORFACTORY_H + +#include <QtCore/qobject.h> +#include <QtCore/qvariant.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Qt3Support) + +#ifndef QT_NO_SQL_EDIT_WIDGETS + +class Q_COMPAT_EXPORT Q3EditorFactory : public QObject +{ +public: + Q3EditorFactory (QObject * parent = 0); + ~Q3EditorFactory(); + + virtual QWidget * createEditor(QWidget * parent, const QVariant & v); + + static Q3EditorFactory * defaultFactory(); + static void installDefaultFactory(Q3EditorFactory * factory); + +private: + Q_DISABLE_COPY(Q3EditorFactory) +}; + +#endif // QT_NO_SQL_EDIT_WIDGETS + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // Q3EDITORFACTORY_H diff --git a/src/qt3support/sql/q3sqlcursor.cpp b/src/qt3support/sql/q3sqlcursor.cpp new file mode 100644 index 0000000..5bc23c1 --- /dev/null +++ b/src/qt3support/sql/q3sqlcursor.cpp @@ -0,0 +1,1519 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt3Support module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <qplatformdefs.h> +#include "q3sqlcursor.h" + +#ifndef QT_NO_SQL + +#include "qsqldriver.h" +#include "qsqlresult.h" +#include "qdatetime.h" +#include "qsqldatabase.h" +#include "qsql.h" +#include "q3sqlrecordinfo.h" +#include "q3sqlfieldinfo.h" + +QT_BEGIN_NAMESPACE + +class Q3SqlCursorPrivate +{ +public: + + Q3SqlCursorPrivate(const QString& name, QSqlDatabase sdb) + : lastAt(QSql::BeforeFirst), nm(name), srt(name), md(0), db(sdb), q(0) + {} + ~Q3SqlCursorPrivate() + { + delete q; + } + + QSqlQuery* query() + { + if (!q) + q = new QSqlQuery(QString(), db); + return q; + } + + int lastAt; + QString nm; //name + QSqlIndex srt; //sort + QString ftr; //filter + int md; //mode + QSqlIndex priIndx; //primary index + QSqlRecord editBuffer; + // the primary index as it was before the user changed the values in editBuffer + QString editIndex; + Q3SqlRecordInfo infoBuffer; + QSqlDatabase db; + QSqlQuery *q; +}; + +QString qOrderByClause(const QSqlIndex & i, const QString& prefix = QString()) +{ + QString str; + int k = i.count(); + if(k == 0) + return QString(); + str = QLatin1String(" order by ") + i.toString(prefix); + return str; +} + +QString qWhereClause(const QString& prefix, QSqlField* field, const QSqlDriver* driver) +{ + QString f; + if (field && driver) { + if (!prefix.isEmpty()) + f += prefix + QLatin1Char('.'); + f += field->name(); + if (field->isNull()) { + f += QLatin1String(" IS NULL"); + } else { + f += QLatin1String(" = ") + driver->formatValue(field); + } + } + return f; +} + +QString qWhereClause(QSqlRecord* rec, const QString& prefix, const QString& sep, + const QSqlDriver* driver) +{ + static QString blank(QLatin1Char(' ')); + QString filter; + bool separator = false; + for (int j = 0; j < rec->count(); ++j) { + QSqlField f = rec->field(j); + if (rec->isGenerated(j)) { + if (separator) + filter += sep + blank; + filter += qWhereClause(prefix, &f, driver); + filter += blank; + separator = true; + } + } + return filter; +} + +/*! + \class Q3SqlCursor + \brief The Q3SqlCursor class provides browsing and editing of SQL + tables and views. + + \compat + + A Q3SqlCursor is a database record (see \l QSqlRecord) that + corresponds to a table or view within an SQL database (see \l + QSqlDatabase). There are two buffers in a cursor, one used for + browsing and one used for editing records. Each buffer contains a + list of fields which correspond to the fields in the table or + view. + + When positioned on a valid record, the browse buffer contains the + values of the current record's fields from the database. The edit + buffer is separate, and is used for editing existing records and + inserting new records. + + For browsing data, a cursor must first select() data from the + database. After a successful select() the cursor is active + (isActive() returns true), but is initially not positioned on a + valid record (isValid() returns false). To position the cursor on + a valid record, use one of the navigation functions, next(), + previous(), first(), last(), or seek(). Once positioned on a valid + record, data can be retrieved from the browse buffer using + value(). If a navigation function is not successful, it returns + false, the cursor will no longer be positioned on a valid record + and the values returned by value() are undefined. + + For example: + + \snippet doc/src/snippets/code/src_qt3support_sql_q3sqlcursor.cpp 0 + + In the above example, a cursor is created specifying a table or + view name in the database. Then, select() is called, which can be + optionally parameterised to filter and order the records + retrieved. Each record in the cursor is retrieved using next(). + When next() returns false, there are no more records to process, + and the loop terminates. + + For editing records (rows of data), a cursor contains a separate + edit buffer which is independent of the fields used when browsing. + The functions insert(), update() and del() operate on the edit + buffer. This allows the cursor to be repositioned to other + records while simultaneously maintaining a separate buffer for + edits. You can get a pointer to the edit buffer using + editBuffer(). The primeInsert(), primeUpdate() and primeDelete() + functions also return a pointer to the edit buffer and prepare it + for insert, update and delete respectively. Edit operations only + affect a single row at a time. Note that update() and del() + require that the table or view contain a primaryIndex() to ensure + that edit operations affect a unique record within the database. + + For example: + + \snippet doc/src/snippets/code/src_qt3support_sql_q3sqlcursor.cpp 1 + + To edit an existing database record, first move to the record you + wish to update. Call primeUpdate() to get the pointer to the + cursor's edit buffer. Then use this pointer to modify the values + in the edit buffer. Finally, call update() to save the changes to + the database. The values in the edit buffer will be used to + locate the appropriate record when updating the database (see + primaryIndex()). + + Similarly, when deleting an existing database record, first move + to the record you wish to delete. Then, call primeDelete() to get + the pointer to the edit buffer. Finally, call del() to delete the + record from the database. Again, the values in the edit buffer + will be used to locate and delete the appropriate record. + + To insert a new record, call primeInsert() to get the pointer to + the edit buffer. Use this pointer to populate the edit buffer + with new values and then insert() the record into the database. + + After calling insert(), update() or del(), the cursor is no longer + positioned on a valid record and can no longer be navigated + (isValid() return false). The reason for this is that any changes + made to the database will not be visible until select() is called + to refresh the cursor. You can change this behavior by passing + false to insert(), update() or del() which will prevent the cursor + from becoming invalid. The edits will still not be visible when + navigating the cursor until select() is called. + + Q3SqlCursor contains virtual methods which allow editing behavior + to be customized by subclasses. This allows custom cursors to be + created that encapsulate the editing behavior of a database table + for an entire application. For example, a cursor can be customized + to always auto-number primary index fields, or provide fields with + suitable default values, when inserting new records. Q3SqlCursor + generates SQL statements which are sent to the database engine; + you can control which fields are included in these statements + using setGenerated(). + + Note that Q3SqlCursor does not inherit from QObject. This means + that you are responsible for destroying instances of this class + yourself. However if you create a Q3SqlCursor and use it in a + \l Q3DataTable, \l Q3DataBrowser or a \l Q3DataView these classes will + usually take ownership of the cursor and destroy it when they + don't need it anymore. The documentation for Q3DataTable, + Q3DataBrowser and Q3DataView explicitly states which calls take + ownership of the cursor. +*/ + +/*! + \enum Q3SqlCursor::Mode + + This enum type describes how Q3SqlCursor operates on records in the + database. + + \value ReadOnly the cursor can only SELECT records from the + database. + + \value Insert the cursor can INSERT records into the database. + + \value Update the cursor can UPDATE records in the database. + + \value Delete the cursor can DELETE records from the database. + + \value Writable the cursor can INSERT, UPDATE and DELETE records + in the database. +*/ + +/*! + \fn QVariant Q3SqlCursor::value(const QString &name) const + + \overload + + Returns the value of the field named \a name. +*/ + +/*! + \fn void Q3SqlCursor::setValue(const QString &name, const QVariant &val) + + \overload + + Sets the value for the field named \a name to \a val. +*/ + +/*! + Constructs a cursor on database \a db using table or view \a name. + + If \a autopopulate is true (the default), the \a name of the + cursor must correspond to an existing table or view name in the + database so that field information can be automatically created. + If the table or view does not exist, the cursor will not be + functional. + + The cursor is created with an initial mode of Q3SqlCursor::Writable + (meaning that records can be inserted, updated or deleted using + the cursor). If the cursor does not have a unique primary index, + update and deletes cannot be performed. + + Note that \a autopopulate refers to populating the cursor with + meta-data, e.g. the names of the table's fields, not with + retrieving data. The select() function is used to populate the + cursor with data. + + \sa setName() setMode() +*/ + +Q3SqlCursor::Q3SqlCursor(const QString & name, bool autopopulate, QSqlDatabase db) + : QSqlRecord(), QSqlQuery(QString(), db) +{ + d = new Q3SqlCursorPrivate(name, db); + setMode(Writable); + if (!d->nm.isEmpty()) + setName(d->nm, autopopulate); +} + +/*! + Constructs a copy of \a other. +*/ + +Q3SqlCursor::Q3SqlCursor(const Q3SqlCursor & other) + : QSqlRecord(other), QSqlQuery(other) +{ + d = new Q3SqlCursorPrivate(other.d->nm, other.d->db); + d->lastAt = other.d->lastAt; + d->nm = other.d->nm; + d->srt = other.d->srt; + d->ftr = other.d->ftr; + d->priIndx = other.d->priIndx; + d->editBuffer = other.d->editBuffer; + d->infoBuffer = other.d->infoBuffer; + d->q = 0; // do not share queries + setMode(other.mode()); +} + +/*! + Destroys the object and frees any allocated resources. +*/ + +Q3SqlCursor::~Q3SqlCursor() +{ + delete d; +} + +/*! + Sets the cursor equal to \a other. +*/ + +Q3SqlCursor& Q3SqlCursor::operator=(const Q3SqlCursor& other) +{ + QSqlRecord::operator=(other); + QSqlQuery::operator=(other); + delete d; + d = new Q3SqlCursorPrivate(other.d->nm, other.d->db); + d->lastAt = other.d->lastAt; + d->nm = other.d->nm; + d->srt = other.d->srt; + d->ftr = other.d->ftr; + d->priIndx = other.d->priIndx; + d->editBuffer = other.d->editBuffer; + d->infoBuffer = other.d->infoBuffer; + d->q = 0; // do not share queries + setMode(other.mode()); + return *this; +} + +/*! + Sets the current sort to \a sort. Note that no new records are + selected. To select new records, use select(). The \a sort will + apply to any subsequent select() calls that do not explicitly + specify a sort. +*/ + +void Q3SqlCursor::setSort(const QSqlIndex& sort) +{ + d->srt = sort; +} + +/*! + Returns the current sort, or an empty index if there is no current + sort. +*/ +QSqlIndex Q3SqlCursor::sort() const +{ + return d->srt; +} + +/*! + Sets the current filter to \a filter. Note that no new records are + selected. To select new records, use select(). The \a filter will + apply to any subsequent select() calls that do not explicitly + specify a filter. + + The filter is a SQL \c WHERE clause without the keyword 'WHERE', + e.g. \c{name='Dave'} which will be processed by the DBMS. +*/ +void Q3SqlCursor::setFilter(const QString& filter) +{ + d->ftr = filter; +} + +/*! + Returns the current filter, or an empty string if there is no + current filter. +*/ +QString Q3SqlCursor::filter() const +{ + return d->ftr; +} + +/*! + Sets the name of the cursor to \a name. If \a autopopulate is true + (the default), the \a name must correspond to a valid table or + view name in the database. Also, note that all references to the + cursor edit buffer become invalidated when fields are + auto-populated. See the Q3SqlCursor constructor documentation for + more information. +*/ +void Q3SqlCursor::setName(const QString& name, bool autopopulate) +{ + d->nm = name; + if (autopopulate) { + if (driver()) { + d->infoBuffer = driver()->record(name); + *this = d->infoBuffer.toRecord(); + d->editBuffer = *this; + d->priIndx = driver()->primaryIndex(name); + } + if (isEmpty()) + qWarning("Q3SqlCursor::setName: unable to build record, does '%s' exist?", name.latin1()); + } +} + +/*! + Returns the name of the cursor. +*/ + +QString Q3SqlCursor::name() const +{ + return d->nm; +} + +/*! \internal +*/ + +QString Q3SqlCursor::toString(const QString& prefix, const QString& sep) const +{ + QString pflist; + QString pfix = prefix.isEmpty() ? prefix : prefix + QLatin1Char('.'); + bool comma = false; + + for (int i = 0; i < count(); ++i) { + const QString fname = fieldName(i); + if (isGenerated(i)) { + if(comma) + pflist += sep + QLatin1Char(' '); + pflist += pfix + driver()->escapeIdentifier(fname, QSqlDriver::FieldName); + comma = true; + } + } + return pflist; +} + +/*! + \internal + + Assigns the record \a list. + +*/ +QSqlRecord & Q3SqlCursor::operator=(const QSqlRecord & list) +{ + return QSqlRecord::operator=(list); +} + +/*! + Append a copy of field \a fieldInfo to the end of the cursor. Note + that all references to the cursor edit buffer become invalidated. +*/ + +void Q3SqlCursor::append(const Q3SqlFieldInfo& fieldInfo) +{ + d->editBuffer.append(fieldInfo.toField()); + d->infoBuffer.append(fieldInfo); + QSqlRecord::append(fieldInfo.toField()); +} + +/*! + Removes all fields from the cursor. Note that all references to + the cursor edit buffer become invalidated. +*/ +void Q3SqlCursor::clear() +{ + d->editBuffer.clear(); + d->infoBuffer.clear(); + QSqlRecord::clear(); +} + + +/*! + Insert a copy of \a fieldInfo at position \a pos. If a field + already exists at \a pos, it is removed. Note that all references + to the cursor edit buffer become invalidated. +*/ + +void Q3SqlCursor::insert(int pos, const Q3SqlFieldInfo& fieldInfo) +{ + d->editBuffer.replace(pos, fieldInfo.toField()); + d->infoBuffer[pos] = fieldInfo; + QSqlRecord::replace(pos, fieldInfo.toField()); +} + +/*! + Removes the field at \a pos. If \a pos does not exist, nothing + happens. Note that all references to the cursor edit buffer become + invalidated. +*/ + +void Q3SqlCursor::remove(int pos) +{ + d->editBuffer.remove(pos); + d->infoBuffer[pos] = Q3SqlFieldInfo(); + QSqlRecord::remove(pos); +} + +/*! + Sets the generated flag for the field \a name to \a generated. If + the field does not exist, nothing happens. Only fields that have + \a generated set to true are included in the SQL that is + generated by insert(), update() or del(). +*/ + +void Q3SqlCursor::setGenerated(const QString& name, bool generated) +{ + int pos = indexOf(name); + if (pos == -1) + return; + QSqlRecord::setGenerated(name, generated); + d->editBuffer.setGenerated(name, generated); + d->infoBuffer[pos].setGenerated(generated); +} + +/*! + \overload + + Sets the generated flag for the field \a i to \a generated. +*/ +void Q3SqlCursor::setGenerated(int i, bool generated) +{ + if (i < 0 || i >= (int)d->infoBuffer.count()) + return; + QSqlRecord::setGenerated(i, generated); + d->editBuffer.setGenerated(i, generated); + d->infoBuffer[i].setGenerated(generated); +} + +/*! + Returns the primary index associated with the cursor as defined in + the database, or an empty index if there is no primary index. If + \a setFromCursor is true (the default), the index fields are + populated with the corresponding values in the cursor's current + record. +*/ + +QSqlIndex Q3SqlCursor::primaryIndex(bool setFromCursor) const +{ + if (setFromCursor) { + for (int i = 0; i < d->priIndx.count(); ++i) { + const QString fn = d->priIndx.fieldName(i); + if (contains(fn)) + d->priIndx.setValue(i, QSqlRecord::value(fn)); + } + } + return d->priIndx; +} + +/*! + Sets the primary index associated with the cursor to the index \a + idx. Note that this index must contain a field or set of fields + which identify a unique record within the underlying database + table or view so that update() and del() will execute as expected. + + \sa update() del() +*/ + +void Q3SqlCursor::setPrimaryIndex(const QSqlIndex& idx) +{ + d->priIndx = idx; +} + + +/*! + Returns an index composed of \a fieldNames, all in ASCending + order. Note that all field names must exist in the cursor, + otherwise an empty index is returned. + + \sa QSqlIndex +*/ + +QSqlIndex Q3SqlCursor::index(const QStringList& fieldNames) const +{ + QSqlIndex idx; + for (QStringList::ConstIterator it = fieldNames.begin(); it != fieldNames.end(); ++it) { + QSqlField f = field((*it)); + if (!f.isValid()) { /* all fields must exist */ + idx.clear(); + break; + } + idx.append(f); + } + return idx; +} + +/*! + \overload + + Returns an index based on \a fieldName. +*/ + +QSqlIndex Q3SqlCursor::index(const QString& fieldName) const +{ + QStringList fl(fieldName); + return index(fl); +} + +/*! + Selects all fields in the cursor from the database matching the + filter criteria \a filter. The data is returned in the order + specified by the index \a sort. Returns true if the data was + successfully selected; otherwise returns false. + + The \a filter is a string containing a SQL \c WHERE clause but + without the 'WHERE' keyword. The cursor is initially positioned at + an invalid row after this function is called. To move to a valid + row, use seek(), first(), last(), previous() or next(). + + Example: + \snippet doc/src/snippets/code/src_qt3support_sql_q3sqlcursor.cpp 2 + + The filter will apply to any subsequent select() calls that do not + explicitly specify another filter. Similarly the sort will apply + to any subsequent select() calls that do not explicitly specify + another sort. + + \snippet doc/src/snippets/code/src_qt3support_sql_q3sqlcursor.cpp 3 + +*/ + +bool Q3SqlCursor::select(const QString & filter, const QSqlIndex & sort) +{ + QString fieldList(toString(d->nm)); + if (fieldList.isEmpty()) + return false; + QString str(QLatin1String("select ") + fieldList); + str += QLatin1String(" from ") + d->nm; + if (!filter.isEmpty()) { + d->ftr = filter; + str += QLatin1String(" where ") + filter; + } else + d->ftr.clear(); + if (sort.count() > 0) + str += QLatin1String(" order by ") + sort.toString(d->nm); + d->srt = sort; + return exec(str); +} + +/*! + \overload + + Selects all fields in the cursor from the database. The rows are + returned in the order specified by the last call to setSort() or + the last call to select() that specified a sort, whichever is the + most recent. If there is no current sort, the order in which the + rows are returned is undefined. The records are filtered according + to the filter specified by the last call to setFilter() or the + last call to select() that specified a filter, whichever is the + most recent. If there is no current filter, all records are + returned. The cursor is initially positioned at an invalid row. To + move to a valid row, use seek(), first(), last(), previous() or + next(). + + \sa setSort() setFilter() +*/ + +bool Q3SqlCursor::select() +{ + return select(filter(), sort()); +} + +/*! + \overload + + Selects all fields in the cursor from the database. The data is + returned in the order specified by the index \a sort. The records + are filtered according to the filter specified by the last call to + setFilter() or the last call to select() that specified a filter, + whichever is the most recent. The cursor is initially positioned + at an invalid row. To move to a valid row, use seek(), first(), + last(), previous() or next(). +*/ + +bool Q3SqlCursor::select(const QSqlIndex& sort) +{ + return select(filter(), sort); +} + +/*! + \overload + + Selects all fields in the cursor matching the filter index \a + filter. The data is returned in the order specified by the index + \a sort. The \a filter index works by constructing a WHERE clause + using the names of the fields from the \a filter and their values + from the current cursor record. The cursor is initially positioned + at an invalid row. To move to a valid row, use seek(), first(), + last(), previous() or next(). This function is useful, for example, + for retrieving data based upon a table's primary index: + + \snippet doc/src/snippets/code/src_qt3support_sql_q3sqlcursor.cpp 4 + + In this example the QSqlIndex, pk, is used for two different + purposes. When used as the filter (first) argument, the field + names it contains are used to construct the WHERE clause, each set + to the current cursor value, \c{WHERE id=10}, in this case. When + used as the sort (second) argument the field names it contains are + used for the ORDER BY clause, \c{ORDER BY id} in this example. +*/ + +bool Q3SqlCursor::select(const QSqlIndex & filter, const QSqlIndex & sort) +{ + return select(toString(filter, this, d->nm, QString(QLatin1Char('=')), QLatin1String("and")), sort); +} + +/*! + Sets the cursor mode to \a mode. This value can be an OR'ed + combination of \l Q3SqlCursor::Mode values. The default mode for a + cursor is Q3SqlCursor::Writable. + + \snippet doc/src/snippets/code/src_qt3support_sql_q3sqlcursor.cpp 5 +*/ + +void Q3SqlCursor::setMode(int mode) +{ + d->md = mode; +} + +/*! + Returns the current cursor mode. + + \sa setMode() +*/ + +int Q3SqlCursor::mode() const +{ + return d->md; +} + +/*! + Sets field \a name to \a calculated. If the field \a name does not + exist, nothing happens. The value of a calculated field is set by + the calculateField() virtual function which you must reimplement + (or the field value will be an invalid QVariant). Calculated + fields do not appear in generated SQL statements sent to the + database. + + \sa calculateField() +*/ + +void Q3SqlCursor::setCalculated(const QString& name, bool calculated) +{ + int pos = indexOf(name); + if (pos < 0) + return; + d->infoBuffer[pos].setCalculated(calculated); + if (calculated) + setGenerated(pos, false); +} + +/*! + Returns true if the field \a name exists and is calculated; + otherwise returns false. + + \sa setCalculated() +*/ + +bool Q3SqlCursor::isCalculated(const QString& name) const +{ + int pos = indexOf(name); + if (pos < 0) + return false; + return d->infoBuffer[pos].isCalculated(); +} + +/*! + Sets field \a{name}'s trimmed status to \a trim. If the field \a + name does not exist, nothing happens. + + When a trimmed field of type string is read from the + database any trailing (right-most) spaces are removed. + + \sa isTrimmed() QVariant +*/ + +void Q3SqlCursor::setTrimmed(const QString& name, bool trim) +{ + int pos = indexOf(name); + if (pos < 0) + return; + d->infoBuffer[pos].setTrim(trim); +} + +/*! + Returns true if the field \a name exists and is trimmed; otherwise + returns false. + + When a trimmed field of type string or cstring is read from the + database any trailing (right-most) spaces are removed. + + \sa setTrimmed() +*/ + +bool Q3SqlCursor::isTrimmed(const QString& name) const +{ + int pos = indexOf(name); + if (pos < 0) + return false; + return d->infoBuffer[pos].isTrim(); +} + +/*! + Returns true if the cursor is read-only; otherwise returns false. + The default is false. Read-only cursors cannot be edited using + insert(), update() or del(). + + \sa setMode() +*/ + +bool Q3SqlCursor::isReadOnly() const +{ + return d->md == 0; +} + +/*! + Returns true if the cursor will perform inserts; otherwise returns + false. + + \sa setMode() +*/ + +bool Q3SqlCursor::canInsert() const +{ + return ((d->md & Insert) == Insert) ; +} + + +/*! + Returns true if the cursor will perform updates; otherwise returns + false. + + \sa setMode() +*/ + +bool Q3SqlCursor::canUpdate() const +{ + return ((d->md & Update) == Update) ; +} + +/*! + Returns true if the cursor will perform deletes; otherwise returns + false. + + \sa setMode() +*/ + +bool Q3SqlCursor::canDelete() const +{ + return ((d->md & Delete) == Delete) ; +} + +/*! + \overload + + Returns a formatted string composed of the \a prefix (e.g. table + or view name), ".", the \a field name, the \a fieldSep and the + field value. If the \a prefix is empty then the string will begin + with the \a field name. This function is useful for generating SQL + statements. +*/ + +QString Q3SqlCursor::toString(const QString& prefix, QSqlField* field, const QString& fieldSep) const +{ + QString f; + if (field && driver()) { + f = (prefix.length() > 0 ? prefix + QLatin1Char('.') : QString()) + field->name(); + f += QLatin1Char(' ') + fieldSep + QLatin1Char(' '); + if (field->isNull()) { + f += QLatin1String("NULL"); + } else { + f += driver()->formatValue(field); + } + } + return f; +} + +/*! + Returns a formatted string composed of all the fields in \a rec. + Each field is composed of the \a prefix (e.g. table or view name), + ".", the field name, the \a fieldSep and the field value. If the + \a prefix is empty then each field will begin with the field name. + The fields are then joined together separated by \a sep. Fields + where isGenerated() returns false are not included. This function + is useful for generating SQL statements. +*/ + +QString Q3SqlCursor::toString(QSqlRecord* rec, const QString& prefix, const QString& fieldSep, + const QString& sep) const +{ + static QString blank(QLatin1Char(' ')); + QString filter; + bool separator = false; + for (int j = 0; j < count(); ++j) { + QSqlField f = rec->field(j); + if (rec->isGenerated(j)) { + if (separator) + filter += sep + blank; + filter += toString(prefix, &f, fieldSep); + filter += blank; + separator = true; + } + } + return filter; +} + +/*! + \overload + + Returns a formatted string composed of all the fields in the index + \a i. Each field is composed of the \a prefix (e.g. table or view + name), ".", the field name, the \a fieldSep and the field value. + If the \a prefix is empty then each field will begin with the field + name. The field values are taken from \a rec. The fields are then + joined together separated by \a sep. Fields where isGenerated() + returns false are ignored. This function is useful for generating + SQL statements. +*/ + +QString Q3SqlCursor::toString(const QSqlIndex& i, QSqlRecord* rec, const QString& prefix, + const QString& fieldSep, const QString& sep) const +{ + QString filter; + bool separator = false; + for(int j = 0; j < i.count(); ++j){ + if (rec->isGenerated(j)) { + if(separator) { + filter += QLatin1Char(' ') + sep + QLatin1Char(' ') ; + } + QString fn = i.fieldName(j); + QSqlField f = rec->field(fn); + filter += toString(prefix, &f, fieldSep); + separator = true; + } + } + return filter; +} + +/*! + Inserts the current contents of the cursor's edit record buffer + into the database, if the cursor allows inserts. Returns the + number of rows affected by the insert. For error information, use + lastError(). + + If \a invalidate is true (the default), the cursor will no longer + be positioned on a valid record and can no longer be navigated. A + new select() call must be made before navigating to a valid + record. + + \snippet doc/src/snippets/code/src_qt3support_sql_q3sqlcursor.cpp 6 + + In the above example, a cursor is created on the 'prices' table + and a pointer to the insert buffer is acquired using primeInsert(). + Each field's value is set to the desired value and then insert() + is called to insert the data into the database. Remember: all edit + operations (insert(), update() and delete()) operate on the + contents of the cursor edit buffer and not on the contents of the + cursor itself. + + \sa setMode() lastError() +*/ + +int Q3SqlCursor::insert(bool invalidate) +{ + if ((d->md & Insert) != Insert || !driver()) + return false; + int k = d->editBuffer.count(); + if (k == 0) + return 0; + + QString fList; + QString vList; + bool comma = false; + // use a prepared query if the driver supports it + if (driver()->hasFeature(QSqlDriver::PreparedQueries)) { + int cnt = 0; + bool oraStyle = driver()->hasFeature(QSqlDriver::NamedPlaceholders); + for(int j = 0; j < k; ++j) { + QSqlField f = d->editBuffer.field(j); + if (d->editBuffer.isGenerated(j)) { + if (comma) { + fList += QLatin1Char(','); + vList += QLatin1Char(','); + } + fList += driver()->escapeIdentifier(f.name(), QSqlDriver::FieldName); + vList += (oraStyle == true) ? QLatin1String(":f") + QString::number(cnt) : QString(QLatin1Char('?')); + cnt++; + comma = true; + } + } + if (!comma) { + return 0; + } + QString str; + str.append(QLatin1String("insert into ")).append(name()) + .append(QLatin1String(" (")).append(fList) + .append(QLatin1String(") values (")).append(vList). append(QLatin1Char(')')); + + return applyPrepared(str, invalidate); + } else { + for(int j = 0; j < k; ++j) { + QSqlField f = d->editBuffer.field(j); + if (d->editBuffer.isGenerated(j)) { + if (comma) { + fList += QLatin1Char(','); + vList += QLatin1Char(','); + } + fList += driver()->escapeIdentifier(f.name(), QSqlDriver::FieldName); + vList += driver()->formatValue(&f); + comma = true; + } + } + + if (!comma) { + // no valid fields found + return 0; + } + QString str; + str.append(QLatin1String("insert into ")).append(name()).append(QLatin1String(" (")) + .append(fList).append(QLatin1String(") values (")).append(vList). append (QLatin1String(")")); + return apply(str, invalidate); + } +} + +/*! + Returns the current internal edit buffer. If \a copy is true (the + default is false), the current cursor field values are first + copied into the edit buffer. The edit buffer is valid as long as + the cursor remains valid. The cursor retains ownership of the + returned pointer, so it must not be deleted or modified. + + \sa primeInsert(), primeUpdate() primeDelete() +*/ + +QSqlRecord* Q3SqlCursor::editBuffer(bool copy) +{ + sync(); + if (copy) { + for(int i = 0; i < d->editBuffer.count(); i++) { + if (QSqlRecord::isNull(i)) { + d->editBuffer.setNull(i); + } else { + d->editBuffer.setValue(i, value(i)); + } + } + } + return &d->editBuffer; +} + +/*! + This function primes the edit buffer's field values for update and + returns the edit buffer. The default implementation copies the + field values from the current cursor record into the edit buffer + (therefore, this function is equivalent to calling editBuffer( + true)). The cursor retains ownership of the returned pointer, so + it must not be deleted or modified. + + \sa editBuffer() update() +*/ + +QSqlRecord* Q3SqlCursor::primeUpdate() +{ + // memorize the primary keys as they were before the user changed the values in editBuffer + QSqlRecord* buf = editBuffer(true); + QSqlIndex idx = primaryIndex(false); + if (!idx.isEmpty()) + d->editIndex = toString(idx, buf, d->nm, QString(QLatin1Char('=')), QLatin1String("and")); + else + d->editIndex = qWhereClause(buf, d->nm, QLatin1String("and"), driver()); + return buf; +} + +/*! + This function primes the edit buffer's field values for delete and + returns the edit buffer. The default implementation copies the + field values from the current cursor record into the edit buffer + (therefore, this function is equivalent to calling editBuffer( + true)). The cursor retains ownership of the returned pointer, so + it must not be deleted or modified. + + \sa editBuffer() del() +*/ + +QSqlRecord* Q3SqlCursor::primeDelete() +{ + return editBuffer(true); +} + +/*! + This function primes the edit buffer's field values for insert and + returns the edit buffer. The default implementation clears all + field values in the edit buffer. The cursor retains ownership of + the returned pointer, so it must not be deleted or modified. + + \sa editBuffer() insert() +*/ + +QSqlRecord* Q3SqlCursor::primeInsert() +{ + d->editBuffer.clearValues(); + return &d->editBuffer; +} + + +/*! + Updates the database with the current contents of the edit buffer. + Returns the number of records which were updated. + For error information, use lastError(). + + Only records which meet the filter criteria specified by the + cursor's primary index are updated. If the cursor does not contain + a primary index, no update is performed and 0 is returned. + + If \a invalidate is true (the default), the current cursor can no + longer be navigated. A new select() call must be made before you + can move to a valid record. For example: + + \snippet doc/src/snippets/code/src_qt3support_sql_q3sqlcursor.cpp 7 + + In the above example, a cursor is created on the 'prices' table + and is positioned on the record to be updated. Then a pointer to + the cursor's edit buffer is acquired using primeUpdate(). A new + value is calculated and placed into the edit buffer with the + setValue() call. Finally, an update() call is made on the cursor + which uses the tables's primary index to update the record in the + database with the contents of the cursor's edit buffer. Remember: + all edit operations (insert(), update() and delete()) operate on + the contents of the cursor edit buffer and not on the contents of + the cursor itself. + + Note that if the primary index does not uniquely distinguish + records the database may be changed into an inconsistent state. + + \sa setMode() lastError() +*/ + +int Q3SqlCursor::update(bool invalidate) +{ + if (d->editIndex.isEmpty()) + return 0; + return update(d->editIndex, invalidate); +} + +/*! + \overload + + Updates the database with the current contents of the cursor edit + buffer using the specified \a filter. Returns the number of + records which were updated. + For error information, use lastError(). + + Only records which meet the filter criteria are updated, otherwise + all records in the table are updated. + + If \a invalidate is true (the default), the cursor can no longer + be navigated. A new select() call must be made before you can move + to a valid record. + + \sa primeUpdate() setMode() lastError() +*/ + +int Q3SqlCursor::update(const QString & filter, bool invalidate) +{ + if ((d->md & Update) != Update) { + return false; + } + int k = count(); + if (k == 0) { + return 0; + } + + // use a prepared query if the driver supports it + if (driver()->hasFeature(QSqlDriver::PreparedQueries)) { + QString fList; + bool comma = false; + int cnt = 0; + bool oraStyle = driver()->hasFeature(QSqlDriver::NamedPlaceholders); + for(int j = 0; j < k; ++j) { + QSqlField f = d->editBuffer.field(j); + if (d->editBuffer.isGenerated(j)) { + if (comma) { + fList += QLatin1Char(','); + } + fList += f.name() + QLatin1String(" = ") + (oraStyle == true ? QLatin1String(":f") + QString::number(cnt) : QString(QLatin1Char('?'))); + cnt++; + comma = true; + } + } + if (!comma) { + return 0; + } + QString str(QLatin1String("update ") + name() + QLatin1String(" set ") + fList); + if (filter.length()) { + str+= QLatin1String(" where ") + filter; + } + return applyPrepared(str, invalidate); + } else { + QString str = QLatin1String("update ") + name(); + str += QLatin1String(" set ") + toString(&d->editBuffer, QString(), QString(QLatin1Char('=')), QString(QLatin1Char(','))); + if (filter.length()) { + str+= QLatin1String(" where ") + filter; + } + return apply(str, invalidate); + } +} + +/*! + Deletes a record from the database using the cursor's primary + index and the contents of the cursor edit buffer. Returns the + number of records which were deleted. + For error information, use lastError(). + + Only records which meet the filter criteria specified by the + cursor's primary index are deleted. If the cursor does not contain + a primary index, no delete is performed and 0 is returned. If \a + invalidate is true (the default), the current cursor can no longer + be navigated. A new select() call must be made before you can move + to a valid record. For example: + + \snippet doc/src/snippets/code/src_qt3support_sql_q3sqlcursor.cpp 8 + + In the above example, a cursor is created on the 'prices' table + and positioned to the record to be deleted. First primeDelete() is + called to populate the edit buffer with the current cursor values, + e.g. with an id of 999, and then del() is called to actually + delete the record from the database. Remember: all edit operations + (insert(), update() and delete()) operate on the contents of the + cursor edit buffer and not on the contents of the cursor itself. + + \sa primeDelete() setMode() lastError() +*/ + +int Q3SqlCursor::del(bool invalidate) +{ + QSqlIndex idx = primaryIndex(false); + if (idx.isEmpty()) + return del(qWhereClause(&d->editBuffer, d->nm, QLatin1String("and"), driver()), invalidate); + return del(toString(primaryIndex(), &d->editBuffer, d->nm, QString(QLatin1Char('=')), QLatin1String("and")), invalidate); +} + +/*! + \overload + + Deletes the current cursor record from the database using the + filter \a filter. Only records which meet the filter criteria are + deleted. Returns the number of records which were deleted. If \a + invalidate is true (the default), the current cursor can no longer + be navigated. A new select() call must be made before you can move + to a valid record. For error information, use lastError(). + + The \a filter is an SQL \c WHERE clause, e.g. \c{id=500}. + + \sa setMode() lastError() +*/ + +int Q3SqlCursor::del(const QString & filter, bool invalidate) +{ + if ((d->md & Delete) != Delete) + return 0; + int k = count(); + if(k == 0) return 0; + QString str = QLatin1String("delete from ") + name(); + if (filter.length()) + str+= QLatin1String(" where ") + filter; + return apply(str, invalidate); +} + +/* + \internal +*/ + +int Q3SqlCursor::apply(const QString& q, bool invalidate) +{ + int ar = 0; + if (invalidate) { + if (exec(q)) + ar = numRowsAffected(); + } else if (driver()) { + QSqlQuery* sql = d->query(); + if (sql && sql->exec(q)) + ar = sql->numRowsAffected(); + } + return ar; +} + +/* + \internal +*/ + +int Q3SqlCursor::applyPrepared(const QString& q, bool invalidate) +{ + int ar = 0; + QSqlQuery* sql = 0; + + if (invalidate) { + sql = (QSqlQuery*)this; + d->lastAt = QSql::BeforeFirst; + } else { + sql = d->query(); + } + if (!sql) + return 0; + + if (invalidate || sql->lastQuery() != q) { + if (!sql->prepare(q)) + return 0; + } + + int cnt = 0; + int fieldCount = (int)count(); + for (int j = 0; j < fieldCount; ++j) { + const QSqlField f = d->editBuffer.field(j); + if (d->editBuffer.isGenerated(j)) { + if (f.type() == QVariant::ByteArray) + sql->bindValue(cnt, f.value(), QSql::In | QSql::Binary); + else + sql->bindValue(cnt, f.value()); + cnt++; + } + } + if (sql->exec()) { + ar = sql->numRowsAffected(); + } + return ar; +} + +/*! + Executes the SQL query \a sql. Returns true of the cursor is + active, otherwise returns false. +*/ +bool Q3SqlCursor::exec(const QString & sql) +{ + d->lastAt = QSql::BeforeFirst; + QSqlQuery::exec(sql); + return isActive(); +} + +/*! + Protected virtual function which is called whenever a field needs + to be calculated. If calculated fields are being used, derived + classes must reimplement this function and return the appropriate + value for field \a name. The default implementation returns an + invalid QVariant. + + \sa setCalculated() +*/ + +QVariant Q3SqlCursor::calculateField(const QString&) +{ + return QVariant(); +} + +/*! \internal + Ensure fieldlist is synced with query. + +*/ + +static QString qTrim(const QString& s) +{ + QString result = s; + int end = result.length() - 1; + while (end >= 0 && result[end].isSpace()) // skip white space from end + end--; + result.truncate(end + 1); + return result; +} + +/*! \internal + */ + +void Q3SqlCursor::sync() +{ + if (isActive() && isValid() && d->lastAt != at()) { + d->lastAt = at(); + int i = 0; + int j = 0; + bool haveCalculatedFields = false; + for (; i < count(); ++i) { + if (!haveCalculatedFields && d->infoBuffer[i].isCalculated()) { + haveCalculatedFields = true; + } + if (QSqlRecord::isGenerated(i)) { + QVariant v = QSqlQuery::value(j); + if ((v.type() == QVariant::String) && + d->infoBuffer[i].isTrim()) { + v = qTrim(v.toString()); + } + QSqlRecord::setValue(i, v); + if (QSqlQuery::isNull(j)) + QSqlRecord::field(i).clear(); + j++; + } + } + if (haveCalculatedFields) { + for (i = 0; i < count(); ++i) { + if (d->infoBuffer[i].isCalculated()) + QSqlRecord::setValue(i, calculateField(fieldName(i))); + } + } + } +} + +/*! + Returns the value of field number \a i. +*/ + +QVariant Q3SqlCursor::value(int i) const +{ + const_cast<Q3SqlCursor *>(this)->sync(); + return QSqlRecord::value(i); +} + +/*! \internal + cursors should be filled with Q3SqlFieldInfos... +*/ +void Q3SqlCursor::append(const QSqlField& field) +{ + append(Q3SqlFieldInfo(field)); +} + +/*! + Returns true if the field \a i is NULL or if there is no field at + position \a i; otherwise returns false. + + This is the same as calling QSqlRecord::isNull(\a i) +*/ +bool Q3SqlCursor::isNull(int i) const +{ + const_cast<Q3SqlCursor *>(this)->sync(); + return QSqlRecord::isNull(i); +} +/*! + \overload + + Returns true if the field called \a name is NULL or if there is no + field called \a name; otherwise returns false. + + This is the same as calling QSqlRecord::isNull(\a name) +*/ +bool Q3SqlCursor::isNull(const QString& name) const +{ + const_cast<Q3SqlCursor *>(this)->sync(); + return QSqlRecord::isNull(name); +} + +/*! \internal */ +void Q3SqlCursor::setValue(int i, const QVariant& val) +{ + sync(); +#ifdef QT_DEBUG + qDebug("Q3SqlCursor::setValue(): This will not affect actual database values. Use primeInsert(), primeUpdate() or primeDelete()."); +#endif + QSqlRecord::setValue(i, val); +} + +/*! \internal */ +bool Q3SqlCursor::seek(int i, bool relative) +{ + bool res = QSqlQuery::seek(i, relative); + sync(); + return res; +} + +/*! \internal */ +bool Q3SqlCursor::next() +{ + bool res = QSqlQuery::next(); + sync(); + return res; +} + +/*! + \fn Q3SqlCursor::previous() + + \internal +*/ + +/*! \internal */ +bool Q3SqlCursor::prev() +{ + bool res = QSqlQuery::previous(); + sync(); + return res; +} + +/*! \internal */ +bool Q3SqlCursor::first() +{ + bool res = QSqlQuery::first(); + sync(); + return res; +} + +/*! \internal */ +bool Q3SqlCursor::last() +{ + bool res = QSqlQuery::last(); + sync(); + return res; +} + +QT_END_NAMESPACE + +#endif diff --git a/src/qt3support/sql/q3sqlcursor.h b/src/qt3support/sql/q3sqlcursor.h new file mode 100644 index 0000000..9544fb5 --- /dev/null +++ b/src/qt3support/sql/q3sqlcursor.h @@ -0,0 +1,167 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt3Support module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef Q3SQLCURSOR_H +#define Q3SQLCURSOR_H + +#include <QtCore/qvariant.h> +#include <QtSql/qsqldatabase.h> +#include <QtSql/qsqlrecord.h> +#include <QtCore/qstringlist.h> +#include <QtSql/qsqlquery.h> +#include <QtSql/qsqlindex.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Qt3Support) + +#ifndef QT_NO_SQL + +class Q3SqlCursorPrivate; +class Q3SqlFieldInfo; + +class Q_COMPAT_EXPORT Q3SqlCursor : public QSqlRecord, public QSqlQuery +{ +public: + Q3SqlCursor(const QString & name = QString(), bool autopopulate = true, + QSqlDatabase db = QSqlDatabase()); + Q3SqlCursor(const Q3SqlCursor & other); + Q3SqlCursor& operator=(const Q3SqlCursor& other); + virtual ~Q3SqlCursor(); + + enum Mode { + ReadOnly = 0, + Insert = 1, + Update = 2, + Delete = 4, + Writable = 7 + }; + + QVariant value(int i) const; + inline QVariant value(const QString &name) const { return value(indexOf(name)); } + virtual void setValue(int i, const QVariant &val); + inline void setValue(const QString &name, const QVariant &val) { setValue(indexOf(name), val); } + virtual QSqlIndex primaryIndex(bool prime = true) const; + virtual QSqlIndex index(const QStringList& fieldNames) const; + QSqlIndex index(const QString& fieldName) const; + virtual void setPrimaryIndex(const QSqlIndex& idx); + + virtual void append(const Q3SqlFieldInfo& fieldInfo); + virtual void insert(int pos, const Q3SqlFieldInfo &fieldInfo); + virtual void remove(int pos); + virtual void clear(); + virtual void setGenerated(const QString& name, bool generated); + virtual void setGenerated(int i, bool generated); + + virtual QSqlRecord* editBuffer(bool copy = false); + virtual QSqlRecord* primeInsert(); + virtual QSqlRecord* primeUpdate(); + virtual QSqlRecord* primeDelete(); + virtual int insert(bool invalidate = true); + virtual int update(bool invalidate = true); + virtual int del(bool invalidate = true); + + virtual void setMode(int flags); + int mode() const; + virtual void setCalculated(const QString& name, bool calculated); + bool isCalculated(const QString& name) const; + virtual void setTrimmed(const QString& name, bool trim); + bool isTrimmed(const QString& name) const; + + bool isReadOnly() const; + bool canInsert() const; + bool canUpdate() const; + bool canDelete() const; + + bool select(); + bool select(const QSqlIndex& sort); + bool select(const QSqlIndex & filter, const QSqlIndex & sort); + virtual bool select(const QString & filter, const QSqlIndex & sort = QSqlIndex()); + + virtual void setSort(const QSqlIndex& sort); + QSqlIndex sort() const; + virtual void setFilter(const QString& filter); + QString filter() const; + virtual void setName(const QString& name, bool autopopulate = true); + QString name() const; + QString toString(const QString& prefix = QString(), + const QString& sep = QLatin1String(",")) const; + bool isNull(int i) const; + bool isNull(const QString& name) const; + virtual bool seek(int i, bool relative = false); + virtual bool next(); + inline bool previous() { return prev(); } + virtual bool prev(); + virtual bool first(); + virtual bool last(); + +protected: + virtual bool exec(const QString & sql); + + virtual QVariant calculateField(const QString& name); + virtual int update(const QString & filter, bool invalidate = true); + virtual int del(const QString & filter, bool invalidate = true); + + virtual QString toString(const QString& prefix, QSqlField* field, const QString& fieldSep) const; + virtual QString toString(QSqlRecord* rec, const QString& prefix, const QString& fieldSep, + const QString& sep) const; + virtual QString toString(const QSqlIndex& i, QSqlRecord* rec, const QString& prefix, + const QString& fieldSep, const QString& sep) const; + +private: + void sync(); + int apply(const QString& q, bool invalidate); + int applyPrepared(const QString& q, bool invalidate); + QSqlRecord& operator=(const QSqlRecord & list); + void append(const QSqlField& field); + + Q3SqlCursorPrivate* d; +}; + +#endif // QT_NO_SQL + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // Q3SQLCURSOR_H diff --git a/src/qt3support/sql/q3sqleditorfactory.cpp b/src/qt3support/sql/q3sqleditorfactory.cpp new file mode 100644 index 0000000..5d71ad7 --- /dev/null +++ b/src/qt3support/sql/q3sqleditorfactory.cpp @@ -0,0 +1,229 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt3Support module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "q3sqleditorfactory.h" + +#ifndef QT_NO_SQL_EDIT_WIDGETS + +#include "qsqlfield.h" +#include "q3cleanuphandler.h" +#include "qlabel.h" +#include "qlineedit.h" +#include "qspinbox.h" +#include "qcombobox.h" +#include "qdatetimeedit.h" + +QT_BEGIN_NAMESPACE + +/*! + \class Q3SqlEditorFactory + \brief The Q3SqlEditorFactory class is used to create the editors + used by Q3DataTable and Q3SqlForm. + + \compat + + Q3SqlEditorFactory is used by Q3DataTable and Q3SqlForm to + automatically create appropriate editors for a given QSqlField. + For example if the field is a QVariant::String a QLineEdit would + be the default editor, whereas a QVariant::Int's default editor + would be a QSpinBox. + + If you want to create different editors for fields with the same + data type, subclass Q3SqlEditorFactory and reimplement the + createEditor() function. + + \sa Q3DataTable, Q3SqlForm +*/ + + +/*! + Constructs a SQL editor factory with parent \a parent. +*/ + +Q3SqlEditorFactory::Q3SqlEditorFactory (QObject * parent) + : Q3EditorFactory(parent) +{ + +} + +/*! + Destroys the object and frees any allocated resources. +*/ + +Q3SqlEditorFactory::~Q3SqlEditorFactory() +{ + +} + +static Q3SqlEditorFactory * defaultfactory = 0; +static Q3CleanupHandler< Q3SqlEditorFactory > qsql_cleanup_editor_factory; + +/*! + Returns an instance of a default editor factory. +*/ + +Q3SqlEditorFactory * Q3SqlEditorFactory::defaultFactory() +{ + if(defaultfactory == 0){ + defaultfactory = new Q3SqlEditorFactory(); + qsql_cleanup_editor_factory.add(&defaultfactory); + } + + return defaultfactory; +} + +/*! + Replaces the default editor factory with \a factory. All + Q3DataTable and Q3SqlForm instantiations will use this new factory + for creating field editors. \e{Q3SqlEditorFactory takes ownership + of \a factory, and destroys it when it is no longer needed.} +*/ + +void Q3SqlEditorFactory::installDefaultFactory(Q3SqlEditorFactory * factory) +{ + if(factory == 0) return; + + if(defaultfactory != 0){ + qsql_cleanup_editor_factory.remove(&defaultfactory); + delete defaultfactory; + } + defaultfactory = factory; + qsql_cleanup_editor_factory.add(&defaultfactory); +} + +/*! + Creates and returns the appropriate editor widget for the QVariant + \a variant. + + The widget that is returned has the parent \a parent (which may be + zero). If \a variant is invalid, 0 is returned. +*/ + +QWidget * Q3SqlEditorFactory::createEditor(QWidget * parent, + const QVariant & variant) +{ + return Q3EditorFactory::createEditor(parent, variant); +} + +/*! + \overload + + Creates and returns the appropriate editor for the QSqlField \a + field. +*/ + +QWidget * Q3SqlEditorFactory::createEditor(QWidget * parent, + const QSqlField * field) +{ + if (!field) { + return 0; + } + + QWidget * w = 0; + switch(field->type()){ + case QVariant::Invalid: + w = 0; + break; + case QVariant::Bool: + w = new QComboBox(parent, "qt_editor_bool"); + ((QComboBox *) w)->insertItem(QLatin1String("False")); + ((QComboBox *) w)->insertItem(QLatin1String("True")); + break; + case QVariant::UInt: + w = new QSpinBox(0, 2147483647, 1, parent, "qt_editor_spinbox"); + break; + case QVariant::Int: + w = new QSpinBox(-2147483647, 2147483647, 1, parent, "qt_editor_int"); + break; + case QVariant::LongLong: + case QVariant::ULongLong: + case QVariant::String: + case QVariant::Double: + w = new QLineEdit(parent, "qt_editor_double"); + ((QLineEdit*)w)->setFrame(false); + break; + case QVariant::Date: { + QDateTimeEdit *edit = new QDateTimeEdit(parent); + edit->setDisplayFormat(QLatin1String("yyyy/MM/dd")); + edit->setObjectName(QLatin1String("qt_editor_date")); + w = edit; } + break; + case QVariant::Time: { + QDateTimeEdit *edit = new QDateTimeEdit(parent); + edit->setDisplayFormat(QLatin1String("hh:mm")); + edit->setObjectName(QLatin1String("qt_editor_time")); + w = edit; } + break; + case QVariant::DateTime: + w = new QDateTimeEdit(parent); + w->setObjectName(QLatin1String("qt_editor_datetime")); + break; +#ifndef QT_NO_LABEL + case QVariant::Pixmap: + w = new QLabel(parent, "qt_editor_pixmap"); + break; +#endif + case QVariant::Palette: + case QVariant::Color: + case QVariant::Font: + case QVariant::Brush: + case QVariant::Bitmap: + case QVariant::Cursor: + case QVariant::Map: + case QVariant::StringList: + case QVariant::Rect: + case QVariant::Size: + case QVariant::IconSet: + case QVariant::Point: + case QVariant::PointArray: + case QVariant::Region: + case QVariant::SizePolicy: + case QVariant::ByteArray: + default: + w = new QWidget(parent, "qt_editor_default"); + break; + } + return w; +} + +QT_END_NAMESPACE + +#endif // QT_NO_SQL diff --git a/src/qt3support/sql/q3sqleditorfactory.h b/src/qt3support/sql/q3sqleditorfactory.h new file mode 100644 index 0000000..803d0eb --- /dev/null +++ b/src/qt3support/sql/q3sqleditorfactory.h @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt3Support module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef Q3SQLEDITORFACTORY_H +#define Q3SQLEDITORFACTORY_H + +#include <Qt3Support/q3editorfactory.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Qt3Support) + +#ifndef QT_NO_SQL_EDIT_WIDGETS + +class QSqlField; + +class Q_COMPAT_EXPORT Q3SqlEditorFactory : public Q3EditorFactory +{ +public: + Q3SqlEditorFactory (QObject * parent = 0); + ~Q3SqlEditorFactory(); + virtual QWidget * createEditor(QWidget * parent, const QVariant & variant); + virtual QWidget * createEditor(QWidget * parent, const QSqlField * field); + + static Q3SqlEditorFactory * defaultFactory(); + static void installDefaultFactory(Q3SqlEditorFactory * factory); + +private: + Q_DISABLE_COPY(Q3SqlEditorFactory) +}; + +#endif // QT_NO_SQL_EDIT_WIDGETS + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // Q3SQLEDITORFACTORY_H diff --git a/src/qt3support/sql/q3sqlfieldinfo.h b/src/qt3support/sql/q3sqlfieldinfo.h new file mode 100644 index 0000000..e2b4db3 --- /dev/null +++ b/src/qt3support/sql/q3sqlfieldinfo.h @@ -0,0 +1,167 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt3Support module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef Q3SQLFIELDINFO_H +#define Q3SQLFIELDINFO_H + +#ifndef QT_NO_SQL + +#include <QtCore/qglobal.h> +#include <QtSql/qsqlfield.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Qt3Support) + +/* Q3SqlFieldInfo Class + obsoleted, use QSqlField instead +*/ + +class Q_COMPAT_EXPORT Q3SqlFieldInfo +{ + // class is obsoleted, won't change anyways, + // so no d pointer + int req, len, prec, tID; + uint gen: 1; + uint trim: 1; + uint calc: 1; + QString nm; + QVariant::Type typ; + QVariant defValue; + +public: + Q3SqlFieldInfo(const QString& name = QString(), + QVariant::Type typ = QVariant::Invalid, + int required = -1, + int len = -1, + int prec = -1, + const QVariant& defValue = QVariant(), + int sqlType = 0, + bool generated = true, + bool trim = false, + bool calculated = false) : + req(required), len(len), prec(prec), tID(sqlType), + gen(generated), trim(trim), calc(calculated), + nm(name), typ(typ), defValue(defValue) {} + + virtual ~Q3SqlFieldInfo() {} + + Q3SqlFieldInfo(const QSqlField & other) + { + nm = other.name(); + typ = other.type(); + switch (other.requiredStatus()) { + case QSqlField::Unknown: req = -1; break; + case QSqlField::Required: req = 1; break; + case QSqlField::Optional: req = 0; break; + } + len = other.length(); + prec = other.precision(); + defValue = other.defaultValue(); + tID = other.typeID(); + gen = other.isGenerated(); + calc = false; + trim = false; + } + + bool operator==(const Q3SqlFieldInfo& f) const + { + return (nm == f.nm && + typ == f.typ && + req == f.req && + len == f.len && + prec == f.prec && + defValue == f.defValue && + tID == f.tID && + gen == f.gen && + trim == f.trim && + calc == f.calc); + } + + QSqlField toField() const + { QSqlField f(nm, typ); + f.setRequiredStatus(QSqlField::RequiredStatus(req)); + f.setLength(len); + f.setPrecision(prec); + f.setDefaultValue(defValue); + f.setSqlType(tID); + f.setGenerated(gen); + return f; + } + int isRequired() const + { return req; } + QVariant::Type type() const + { return typ; } + int length() const + { return len; } + int precision() const + { return prec; } + QVariant defaultValue() const + { return defValue; } + QString name() const + { return nm; } + int typeID() const + { return tID; } + bool isGenerated() const + { return gen; } + bool isTrim() const + { return trim; } + bool isCalculated() const + { return calc; } + + virtual void setTrim(bool trim) + { this->trim = trim; } + virtual void setGenerated(bool generated) + { gen = generated; } + virtual void setCalculated(bool calculated) + { calc = calculated; } + +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QT_NO_SQL + +#endif // Q3SQLFIELDINFO_H diff --git a/src/qt3support/sql/q3sqlform.cpp b/src/qt3support/sql/q3sqlform.cpp new file mode 100644 index 0000000..f02e85e --- /dev/null +++ b/src/qt3support/sql/q3sqlform.cpp @@ -0,0 +1,378 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt3Support module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "q3sqlform.h" + +#ifndef QT_NO_SQL_FORM + +#include "qsqlfield.h" +#include "q3sqlpropertymap.h" +#include "qsqlrecord.h" +#include "qstringlist.h" +#include "qwidget.h" +#include "qhash.h" + +QT_BEGIN_NAMESPACE + +class Q3SqlFormPrivate +{ +public: + Q3SqlFormPrivate() : propertyMap(0), buf(0), dirty(false) {} + ~Q3SqlFormPrivate() { if (propertyMap) delete propertyMap; } + QStringList fld; + QHash <QString, QWidget*> wgt; + QMap <QWidget*, QSqlField *> map; + Q3SqlPropertyMap * propertyMap; + QSqlRecord* buf; + bool dirty; +}; + +/*! + \class Q3SqlForm + \brief The Q3SqlForm class creates and manages data entry forms + tied to SQL databases. + + \compat + + Typical use of a Q3SqlForm consists of the following steps: + \list + \i Create the widgets you want to appear in the form. + \i Create a cursor and navigate to the record to be edited. + \i Create the Q3SqlForm. + \i Set the form's record buffer to the cursor's update buffer. + \i Insert each widget and the field it is to edit into the form. + \i Use readFields() to update the editor widgets with values from + the database's fields. + \i Display the form and let the user edit values etc. + \i Use writeFields() to update the database's field values with + the values in the editor widgets. + \endlist + + Note that a Q3SqlForm does not access the database directly, but + most often via QSqlFields which are part of a Q3SqlCursor. A + Q3SqlCursor::insert(), Q3SqlCursor::update() or Q3SqlCursor::del() + call is needed to actually write values to the database. + + Some sample code to initialize a form successfully: + + \snippet doc/src/snippets/code/src_qt3support_sql_q3sqlform.cpp 0 + + If you want to use custom editors for displaying and editing data + fields, you must install a custom Q3SqlPropertyMap. The form + uses this object to get or set the value of a widget. + + \sa installPropertyMap(), Q3SqlPropertyMap +*/ + + +/*! + Constructs a Q3SqlForm with parent \a parent. +*/ +Q3SqlForm::Q3SqlForm(QObject * parent) + : QObject(parent) +{ + d = new Q3SqlFormPrivate(); +} + +/*! + Destroys the object and frees any allocated resources. +*/ +Q3SqlForm::~Q3SqlForm() +{ + delete d; +} + +/*! + Installs a custom Q3SqlPropertyMap. This is useful if you plan to + create your own custom editor widgets. + + Q3SqlForm takes ownership of \a pmap, so \a pmap is deleted when + Q3SqlForm goes out of scope. + + \sa Q3DataTable::installEditorFactory() +*/ +void Q3SqlForm::installPropertyMap(Q3SqlPropertyMap * pmap) +{ + if(d->propertyMap) + delete d->propertyMap; + d->propertyMap = pmap; +} + +/*! + Sets \a buf as the record buffer for the form. To force the + display of the data from \a buf, use readFields(). + + \sa readFields() writeFields() +*/ + +void Q3SqlForm::setRecord(QSqlRecord* buf) +{ + d->dirty = true; + d->buf = buf; +} + +/*! + Inserts a \a widget, and the name of the \a field it is to be + mapped to, into the form. To actually associate inserted widgets + with an edit buffer, use setRecord(). + + \sa setRecord() +*/ + +void Q3SqlForm::insert(QWidget * widget, const QString& field) +{ + d->dirty = true; + d->wgt.insert(field, widget); + d->fld += field; +} + +/*! + \overload + + Removes \a field from the form. +*/ + +void Q3SqlForm::remove(const QString& field) +{ + d->dirty = true; + int i = d->fld.indexOf(field); + if (i >= 0) + d->fld.removeAt(i); + d->wgt.remove(field); +} + +/*! + \overload + + Inserts a \a widget, and the \a field it is to be mapped to, into + the form. +*/ + +void Q3SqlForm::insert(QWidget * widget, QSqlField * field) +{ + d->map[widget] = field; +} + +/*! + Removes a \a widget, and hence the field it's mapped to, from the + form. +*/ + +void Q3SqlForm::remove(QWidget * widget) +{ + d->map.remove(widget); +} + +/*! + Clears the values in all the widgets, and the fields they are + mapped to, in the form, and sets them to NULL. +*/ +void Q3SqlForm::clearValues() +{ + QMap< QWidget *, QSqlField * >::Iterator it; + for(it = d->map.begin(); it != d->map.end(); ++it){ + QSqlField* f = (*it); + if (f) + f->clear(); + } + readFields(); +} + +/*! + Removes every widget, and the fields they're mapped to, from the form. +*/ +void Q3SqlForm::clear() +{ + d->dirty = true; + d->fld.clear(); + clearMap(); +} + +/*! + Returns the number of widgets in the form. +*/ +int Q3SqlForm::count() const +{ + return d->map.size(); +} + +/*! + Returns the \a{i}-th widget in the form. Useful for traversing + the widgets in the form. +*/ +QWidget * Q3SqlForm::widget(int i) const +{ + QMap< QWidget *, QSqlField * >::ConstIterator it; + int cnt = 0; + + if(i > d->map.size()) + return 0; + for(it = d->map.constBegin(); it != d->map.constEnd(); ++it){ + if(cnt++ == i) + return it.key(); + } + return 0; +} + +/*! + Returns the widget that field \a field is mapped to. +*/ +QWidget * Q3SqlForm::fieldToWidget(QSqlField * field) const +{ + QMap< QWidget *, QSqlField * >::ConstIterator it; + for(it = d->map.constBegin(); it != d->map.constEnd(); ++it){ + if(*it == field) + return it.key(); + } + return 0; +} + +/*! + Returns the SQL field that widget \a widget is mapped to. +*/ +QSqlField * Q3SqlForm::widgetToField(QWidget * widget) const +{ + return d->map.value(widget, 0); +} + +/*! + Updates the widgets in the form with current values from the SQL + fields they are mapped to. +*/ +void Q3SqlForm::readFields() +{ + sync(); + QSqlField * f; + QMap< QWidget *, QSqlField * >::Iterator it; + Q3SqlPropertyMap * pmap = (d->propertyMap == 0) ? + Q3SqlPropertyMap::defaultMap() : d->propertyMap; + for(it = d->map.begin() ; it != d->map.end(); ++it){ + f = widgetToField(it.key()); + if(!f) + continue; + pmap->setProperty(it.key(), f->value()); + } +} + +/*! + Updates the SQL fields with values from the widgets they are + mapped to. To actually update the database with the contents of + the record buffer, use Q3SqlCursor::insert(), Q3SqlCursor::update() + or Q3SqlCursor::del() as appropriate. +*/ +void Q3SqlForm::writeFields() +{ + sync(); + QSqlField * f; + QMap< QWidget *, QSqlField * >::Iterator it; + Q3SqlPropertyMap * pmap = (d->propertyMap == 0) ? + Q3SqlPropertyMap::defaultMap() : d->propertyMap; + + for(it = d->map.begin() ; it != d->map.end(); ++it){ + f = widgetToField(it.key()); + if(!f) + continue; + f->setValue(pmap->property(it.key())); + } +} + +/*! + Updates the widget \a widget with the value from the SQL field it + is mapped to. Nothing happens if no SQL field is mapped to the \a + widget. +*/ +void Q3SqlForm::readField(QWidget * widget) +{ + sync(); + QSqlField * field = 0; + Q3SqlPropertyMap * pmap = (d->propertyMap == 0) ? + Q3SqlPropertyMap::defaultMap() : d->propertyMap; + field = widgetToField(widget); + if(field) + pmap->setProperty(widget, field->value()); +} + +/*! + Updates the SQL field with the value from the \a widget it is + mapped to. Nothing happens if no SQL field is mapped to the \a + widget. +*/ +void Q3SqlForm::writeField(QWidget * widget) +{ + sync(); + QSqlField * field = 0; + Q3SqlPropertyMap * pmap = (d->propertyMap == 0) ? + Q3SqlPropertyMap::defaultMap() : d->propertyMap; + field = widgetToField(widget); + if(field) + field->setValue(pmap->property(widget)); +} + +/*! \internal +*/ + +void Q3SqlForm::sync() +{ + if (d->dirty) { + clearMap(); + if (d->buf) { + for (int i = 0; i < d->fld.count(); ++i) { + const QSqlField *field = d->buf->fieldPtr(d->fld.at(i)); + insert(d->wgt.value(d->fld.at(i)), const_cast<QSqlField *>(field)); + } + } + } + d->dirty = false; +} + +/*! \internal + + Clears the internal map of widget/field associations +*/ + +void Q3SqlForm::clearMap() +{ + d->map.clear(); +} + +QT_END_NAMESPACE + +#endif // QT_NO_SQL diff --git a/src/qt3support/sql/q3sqlform.h b/src/qt3support/sql/q3sqlform.h new file mode 100644 index 0000000..b6b76b7 --- /dev/null +++ b/src/qt3support/sql/q3sqlform.h @@ -0,0 +1,109 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt3Support module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef Q3SQLFORM_H +#define Q3SQLFORM_H + +#include <QtCore/qobject.h> +#include <QtCore/qmap.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Qt3Support) + +#ifndef QT_NO_SQL_FORM + +class QSqlField; +class QSqlRecord; +class Q3SqlEditorFactory; +class Q3SqlPropertyMap; +class QWidget; +class Q3SqlFormPrivate; + +class Q_COMPAT_EXPORT Q3SqlForm : public QObject +{ + Q_OBJECT +public: + Q3SqlForm(QObject * parent = 0); + ~Q3SqlForm(); + + virtual void insert(QWidget * widget, const QString& field); + virtual void remove(const QString& field); + int count() const; + + QWidget * widget(int i) const; + QSqlField * widgetToField(QWidget * widget) const; + QWidget * fieldToWidget(QSqlField * field) const; + + void installPropertyMap(Q3SqlPropertyMap * map); + + virtual void setRecord(QSqlRecord* buf); + +public Q_SLOTS: + virtual void readField(QWidget * widget); + virtual void writeField(QWidget * widget); + virtual void readFields(); + virtual void writeFields(); + + virtual void clear(); + virtual void clearValues(); + +protected: + virtual void insert(QWidget * widget, QSqlField * field); + virtual void remove(QWidget * widget); + void clearMap(); + +private: + Q_DISABLE_COPY(Q3SqlForm) + + virtual void sync(); + Q3SqlFormPrivate* d; +}; + +#endif // QT_NO_SQL_FORM + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // Q3SQLFORM_H diff --git a/src/qt3support/sql/q3sqlmanager_p.cpp b/src/qt3support/sql/q3sqlmanager_p.cpp new file mode 100644 index 0000000..146be96 --- /dev/null +++ b/src/qt3support/sql/q3sqlmanager_p.cpp @@ -0,0 +1,961 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt3Support module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "q3sqlmanager_p.h" + +#ifndef QT_NO_SQL + +#include "qapplication.h" +#include "qcursor.h" +#include "qwidget.h" +#include "q3sqlcursor.h" +#include "qsqlfield.h" +#include "q3sqlform.h" +#include "qsqldriver.h" +#include "qstring.h" +#include "qmessagebox.h" +#include "qbitarray.h" + +QT_BEGIN_NAMESPACE + +//#define QT_DEBUG_DATAMANAGER + +class Q3SqlCursorManagerPrivate +{ +public: + Q3SqlCursorManagerPrivate() + : cur(0), autoDelete(false) + {} + + QString ftr; + QStringList srt; + Q3SqlCursor* cur; + bool autoDelete; +}; + +static QSqlIndex indexFromStringList(const QStringList& l, const Q3SqlCursor* cursor) +{ + QSqlIndex newSort; + for (int i = 0; i < l.count(); ++i) { + QString f = l[i]; + bool desc = false; + if (f.mid(f.length()-3) == QLatin1String("ASC")) + f = f.mid(0, f.length()-3); + if (f.mid(f.length()-4) == QLatin1String("DESC")) { + desc = true; + f = f.mid(0, f.length()-4); + } + int dot = f.lastIndexOf(QLatin1Char('.')); + if (dot != -1) + f = f.mid(dot+1); + const QSqlField field = cursor->field(f.trimmed()); + if (field.isValid()) + newSort.append(field, desc); + else + qWarning("QSqlIndex::indexFromStringList: unknown field: '%s'", f.latin1()); + } + return newSort; +} + + +/*! + \class Q3SqlCursorManager + \brief The Q3SqlCursorManager class manages a database cursor. + + \compat + \internal + + This class provides common cursor management functionality. This + includes saving and applying sorts and filters, refreshing (i.e., + re-selecting) the cursor and searching for records within the + cursor. + +*/ + +/*! \internal + + Constructs a cursor manager. + +*/ + +Q3SqlCursorManager::Q3SqlCursorManager() +{ + d = new Q3SqlCursorManagerPrivate; +} + + +/*! \internal + + Destroys the object and frees any allocated resources. + +*/ + +Q3SqlCursorManager::~Q3SqlCursorManager() +{ + if (d->autoDelete) + delete d->cur; + delete d; +} + +/*! \internal + + Sets the manager's sort to the index \a sort. To apply the new + sort, use refresh(). + + */ + +void Q3SqlCursorManager::setSort(const QSqlIndex& sort) +{ + setSort(sort.toStringList()); +} + +/*! \internal + + Sets the manager's sort to the stringlist \a sort. To apply the + new sort, use refresh(). + + */ + +void Q3SqlCursorManager::setSort(const QStringList& sort) +{ + d->srt = sort; +} + +/*! \internal + + Returns the current sort, or an empty stringlist if there is none. + +*/ + +QStringList Q3SqlCursorManager::sort() const +{ + return d->srt; +} + +/*! \internal + + Sets the manager's filter to the string \a filter. To apply the + new filter, use refresh(). + +*/ + +void Q3SqlCursorManager::setFilter(const QString& filter) +{ + d->ftr = filter; +} + +/*! \internal + + Returns the current filter, or an empty string if there is none. + +*/ + +QString Q3SqlCursorManager::filter() const +{ + return d->ftr; +} + +/*! \internal + + Sets auto-delete to \a enable. If true, the default cursor will + be deleted when necessary. + + \sa autoDelete() +*/ + +void Q3SqlCursorManager::setAutoDelete(bool enable) +{ + d->autoDelete = enable; +} + + +/*! \internal + + Returns true if auto-deletion is enabled, otherwise false. + + \sa setAutoDelete() + +*/ + +bool Q3SqlCursorManager::autoDelete() const +{ + return d->autoDelete; +} + +/*! \internal + + Sets the default cursor used by the manager to \a cursor. If \a + autoDelete is true (the default is false), the manager takes + ownership of the \a cursor pointer, which will be deleted when the + manager is destroyed, or when setCursor() is called again. To + activate the \a cursor use refresh(). + + \sa cursor() + +*/ + +void Q3SqlCursorManager::setCursor(Q3SqlCursor* cursor, bool autoDelete) +{ + if (d->autoDelete) + delete d->cur; + d->cur = cursor; + d->autoDelete = autoDelete; +} + +/*! \internal + + Returns a pointer to the default cursor used for navigation, or 0 + if there is no default cursor. + + \sa setCursor() + +*/ + +Q3SqlCursor* Q3SqlCursorManager::cursor() const +{ + return d->cur; +} + + +/*! \internal + + Refreshes the manager using the default cursor. The manager's + filter and sort are applied. Returns true on success, false if an + error occurred or there is no current cursor. + + \sa setFilter() setSort() + +*/ + +bool Q3SqlCursorManager::refresh() +{ + Q3SqlCursor* cur = cursor(); + if (!cur) + return false; + QString currentFilter = d->ftr; + QStringList currentSort = d->srt; + QSqlIndex newSort = indexFromStringList(currentSort, cur); + return cur->select(currentFilter, newSort); +} + +/* \internal + + Returns true if the \a buf field values that correspond to \a idx + match the field values in \a cur that correspond to \a idx. +*/ + +static bool index_matches(const Q3SqlCursor* cur, const QSqlRecord* buf, + const QSqlIndex& idx) +{ + bool indexEquals = false; + for (int i = 0; i < idx.count(); ++i) { + const QString fn(idx.field(i).name()); + if (cur->value(fn) == buf->value(fn)) + indexEquals = true; + else { + indexEquals = false; + break; + } + } + return indexEquals; +} + +/* + Return less than, equal to or greater than 0 if buf1 is less than, + equal to or greater than buf2 according to fields described in idx. + (### Currently only uses first field.) +*/ + +static int compare_recs(const QSqlRecord* buf1, const QSqlRecord* buf2, + const QSqlIndex& idx) +{ + int cmp = 0; + + int i = 0; + const QString fn(idx.field(i).name()); + const QSqlField f1 = buf1->field(fn); + + if (f1.isValid()) { + switch (f1.type()) { // ### more types? + case QVariant::String: + cmp = f1.value().toString().trimmed().compare( + buf2->value(fn).toString().trimmed()); + break; + default: + if (f1.value().toDouble() < buf2->value(fn).toDouble()) + cmp = -1; + else if (f1.value().toDouble() > buf2->value(fn).toDouble()) + cmp = 1; + } + } + + if (idx.isDescending(i)) + cmp = -cmp; + return cmp; +} + +#ifdef QT_DEBUG_DATAMANAGER +static void debug_datamanager_buffer(const QString& msg, QSqlRecord* cursor) +{ + qDebug("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++"); + qDebug("%s", msg.latin1()); + for (int j = 0; j < cursor->count(); ++j) { + qDebug("%s", (cursor->field(j)->name() + " type:" + + QString(cursor->field(j)->value().typeName()) + + " value:" + cursor->field(j)->value().toString()) + .latin1()); + } +} +#endif + + +/*! \internal + + Relocates the default cursor to the record matching the cursor's +edit buffer. Only the field names specified by \a idx are used to +determine an exact match of the cursor to the edit buffer. However, +other fields in the edit buffer are also used during the search, +therefore all fields in the edit buffer should be primed with desired +values for the record being sought. This function is typically used +to relocate a cursor to the correct position after an insert or +update. For example: + +\snippet doc/src/snippets/code/src_qt3support_sql_q3sqlmanager_p.cpp 0 + +*/ + +//## possibly add sizeHint parameter +bool Q3SqlCursorManager::findBuffer(const QSqlIndex& idx, int atHint) +{ +#ifdef QT_DEBUG_DATAMANAGER + qDebug("Q3SqlCursorManager::findBuffer:"); +#endif + Q3SqlCursor* cur = cursor(); + if (!cur) + return false; + if (!cur->isActive()) + return false; + if (!idx.count()) { + if (cur->at() == QSql::BeforeFirst) + cur->next(); + return false; + } + QSqlRecord* buf = cur->editBuffer(); + bool indexEquals = false; +#ifdef QT_DEBUG_DATAMANAGER + qDebug(" Checking hint..."); +#endif + /* check the hint */ + if (cur->seek(atHint)) + indexEquals = index_matches(cur, buf, idx); + + if (!indexEquals) { +#ifdef QT_DEBUG_DATAMANAGER + qDebug(" Checking current page..."); +#endif + /* check current page */ + int pageSize = 20; + int startIdx = qMax(atHint - pageSize, 0); + int endIdx = atHint + pageSize; + for (int j = startIdx; j <= endIdx; ++j) { + if (cur->seek(j)) { + indexEquals = index_matches(cur, buf, idx); + if (indexEquals) + break; + } + } + } + + if (!indexEquals && cur->driver()->hasFeature(QSqlDriver::QuerySize) + && cur->sort().count()) { +#ifdef QT_DEBUG_DATAMANAGER + qDebug(" Using binary search..."); +#endif + // binary search based on record buffer and current sort fields + int lo = 0; + int hi = cur->size(); + int mid; + if (compare_recs(buf, cur, cur->sort()) >= 0) + lo = cur->at(); + while (lo != hi) { + mid = lo + (hi - lo) / 2; + if (!cur->seek(mid)) + break; + if (index_matches(cur, buf, idx)) { + indexEquals = true; + break; + } + int c = compare_recs(buf, cur, cur->sort()); + if (c < 0) { + hi = mid; + } else if (c == 0) { + // found it, but there may be duplicates + int at = mid; + do { + mid--; + if (!cur->seek(mid)) + break; + if (index_matches(cur, buf, idx)) { + indexEquals = true; + break; + } + } while (compare_recs(buf, cur, cur->sort()) == 0); + + if (!indexEquals) { + mid = at; + do { + mid++; + if (!cur->seek(mid)) + break; + if (index_matches(cur, buf, idx)) { + indexEquals = true; + break; + } + } while (compare_recs(buf, cur, cur->sort()) == 0); + } + break; + } else if (c > 0) { + lo = mid + 1; + } + } + } + + if (!indexEquals) { +#ifdef QT_DEBUG_DATAMANAGER + qDebug(" Using brute search..."); +#endif +#ifndef QT_NO_CURSOR + QApplication::setOverrideCursor(Qt::WaitCursor); +#endif + /* give up, use brute force */ + int startIdx = 0; + if (cur->at() != startIdx) { + cur->seek(startIdx); + } + for (;;) { + indexEquals = false; + indexEquals = index_matches(cur, buf, idx); + if (indexEquals) + break; + if (!cur->next()) + break; + } +#ifndef QT_NO_CURSOR + QApplication::restoreOverrideCursor(); +#endif + } +#ifdef QT_DEBUG_DATAMANAGER + qDebug(" Done, result:" + QString::number(indexEquals)); +#endif + return indexEquals; +} + +#ifndef QT_NO_SQL_FORM + +class Q3SqlFormManagerPrivate +{ +public: + Q3SqlFormManagerPrivate() : frm(0), rcd(0) {} + Q3SqlForm* frm; + QSqlRecord* rcd; +}; + + +/*! \internal + + Creates a form manager. + +*/ + +Q3SqlFormManager::Q3SqlFormManager() +{ + d = new Q3SqlFormManagerPrivate(); +} + +/*! \internal + + Destroys the object and frees any allocated resources. + +*/ + +Q3SqlFormManager::~Q3SqlFormManager() +{ + delete d; +} + +/*! \internal + + Clears the default form values. If there is no default form, + nothing happens, + +*/ + +void Q3SqlFormManager::clearValues() +{ + if (form()) + form()->clearValues(); +} + +/*! \internal + + Sets the form used by the form manager to \a form. If a record has + already been assigned to the form manager, that record is also used by + the \a form to display data. + + \sa form() + +*/ + +void Q3SqlFormManager::setForm(Q3SqlForm* form) +{ + d->frm = form; + if (d->rcd && d->frm) + d->frm->setRecord(d->rcd); +} + + +/*! \internal + + Returns the default form used by the form manager, or 0 if there is + none. + + \sa setForm() + +*/ + +Q3SqlForm* Q3SqlFormManager::form() +{ + return d->frm; +} + + +/*! \internal + + Sets the record used by the form manager to \a record. If a form has + already been assigned to the form manager, \a record is also used by + the default form to display data. + + \sa record() + +*/ + +void Q3SqlFormManager::setRecord(QSqlRecord* record) +{ + d->rcd = record; + if (d->frm) { + d->frm->setRecord(d->rcd); + } +} + + +/*! \internal + + Returns the default record used by the form manager, or 0 if there is + none. + + \sa setRecord() +*/ + +QSqlRecord* Q3SqlFormManager::record() +{ + return d->rcd; +} + + +/*! \internal + + Causes the default form to read its fields . If there is no + default form, nothing happens. + + \sa setForm() + +*/ + +void Q3SqlFormManager::readFields() +{ + if (d->frm) { + d->frm->readFields(); + } +} + +/*! \internal + + Causes the default form to write its fields . If there is no + default form, nothing happens. + + \sa setForm() + +*/ + +void Q3SqlFormManager::writeFields() +{ + if (d->frm) { + d->frm->writeFields(); + } +} + +#endif // QT_NO_SQL_FORM + +class Q3DataManagerPrivate +{ +public: + Q3DataManagerPrivate() + : mode(QSql::None), autoEd(true), confEdits(3), + confCancs(false) {} + QSql::Op mode; + bool autoEd; + QBitArray confEdits; + bool confCancs; + +}; + +/*! + \class Q3DataManager + + \brief The Q3DataManager class is an internal class for implementing + the data-aware widgets. + + \internal + \compat + + Q3DataManager is a strictly internal class that acts as a base class + for other data-aware widgets. + +*/ + + +/*! \internal + + Constructs an empty data handler. + +*/ + +Q3DataManager::Q3DataManager() +{ + d = new Q3DataManagerPrivate(); +} + + +/*! \internal + + Destroys the object and frees any allocated resources. + +*/ + +Q3DataManager::~Q3DataManager() +{ + delete d; +} + + +/*! \internal + + Virtual function which is called when an error has occurred The + default implementation displays a warning message to the user with + information about the error. + +*/ +void Q3DataManager::handleError(QWidget* parent, const QSqlError& e) +{ +#ifndef QT_NO_MESSAGEBOX + if (e.driverText().isEmpty() && e.databaseText().isEmpty()) { + QMessageBox::warning (parent, QLatin1String("Warning"), QLatin1String("An error occurred while accessing the database")); + } else { + QMessageBox::warning (parent, QLatin1String("Warning"), e.driverText() + QLatin1Char('\n') + e.databaseText(), + 0, 0); + } +#endif // QT_NO_MESSAGEBOX +} + + +/*! \internal + + Sets the internal mode to \a m. + +*/ + +void Q3DataManager::setMode(QSql::Op m) +{ + d->mode = m; +} + + +/*! \internal + + Returns the current mode. + +*/ + +QSql::Op Q3DataManager::mode() const +{ + return d->mode; +} + + +/*! \internal + + Sets the auto-edit mode to \a auto. + +*/ + +void Q3DataManager::setAutoEdit(bool autoEdit) +{ + d->autoEd = autoEdit; +} + + + +/*! \internal + + Returns true if auto-edit mode is enabled; otherwise returns false. + +*/ + +bool Q3DataManager::autoEdit() const +{ + return d->autoEd; +} + +/*! \internal + + If \a confirm is true, all edit operations (inserts, updates and + deletes) will be confirmed by the user. If \a confirm is false (the + default), all edits are posted to the database immediately. + +*/ +void Q3DataManager::setConfirmEdits(bool confirm) +{ + d->confEdits = QBitArray(d->confEdits.size(), confirm); +} + +/*! \internal + + If \a confirm is true, all inserts will be confirmed by the user. + If \a confirm is false (the default), all edits are posted to the + database immediately. + +*/ + +void Q3DataManager::setConfirmInsert(bool confirm) +{ + d->confEdits[QSql::Insert] = confirm; +} + +/*! \internal + + If \a confirm is true, all updates will be confirmed by the user. + If \a confirm is false (the default), all edits are posted to the + database immediately. + +*/ + +void Q3DataManager::setConfirmUpdate(bool confirm) +{ + d->confEdits[QSql::Update] = confirm; +} + +/*! \internal + + If \a confirm is true, all deletes will be confirmed by the user. + If \a confirm is false (the default), all edits are posted to the + database immediately. + +*/ + +void Q3DataManager::setConfirmDelete(bool confirm) +{ + d->confEdits[QSql::Delete] = confirm; +} + +/*! \internal + + Returns true if the table confirms all edit operations (inserts, + updates and deletes), otherwise returns false. +*/ + +bool Q3DataManager::confirmEdits() const +{ + return (confirmInsert() && confirmUpdate() && confirmDelete()); +} + +/*! \internal + + Returns true if the table confirms inserts, otherwise returns + false. +*/ + +bool Q3DataManager::confirmInsert() const +{ + return d->confEdits[QSql::Insert]; +} + +/*! \internal + + Returns true if the table confirms updates, otherwise returns + false. +*/ + +bool Q3DataManager::confirmUpdate() const +{ + return d->confEdits[QSql::Update]; +} + +/*! \internal + + Returns true if the table confirms deletes, otherwise returns + false. +*/ + +bool Q3DataManager::confirmDelete() const +{ + return d->confEdits[QSql::Delete]; +} + +/*! \internal + + If \a confirm is true, all cancels will be confirmed by the user + through a message box. If \a confirm is false (the default), all + cancels occur immediately. +*/ + +void Q3DataManager::setConfirmCancels(bool confirm) +{ + d->confCancs = confirm; +} + +/*! \internal + + Returns true if the table confirms cancels, otherwise returns false. +*/ + +bool Q3DataManager::confirmCancels() const +{ + return d->confCancs; +} + +/*! \internal + + Virtual function which returns a confirmation for an edit of mode \a + m. Derived classes can reimplement this function and provide their + own confirmation dialog. The default implementation uses a message + box which prompts the user to confirm the edit action. The dialog + is centered over \a parent. + +*/ + +QSql::Confirm Q3DataManager::confirmEdit(QWidget* parent, QSql::Op m) +{ + int ans = 2; + if (m == QSql::Delete) { +#ifndef QT_NO_MESSAGEBOX + ans = QMessageBox::information(parent, + qApp->translate("QSql", "Delete"), + qApp->translate("QSql", "Delete this record?"), + qApp->translate("QSql", "Yes"), + qApp->translate("QSql", "No"), + QString(), 0, 1); +#else + ans = QSql::No; +#endif // QT_NO_MESSAGEBOX + } else if (m != QSql::None) { + QString caption; + if (m == QSql::Insert) { + caption = qApp->translate("QSql", "Insert"); + } else { // QSql::Update + caption = qApp->translate("QSql", "Update"); + } +#ifndef QT_NO_MESSAGEBOX + ans = QMessageBox::information(parent, caption, + qApp->translate("QSql", "Save edits?"), + qApp->translate("QSql", "Yes"), + qApp->translate("QSql", "No"), + qApp->translate("QSql", "Cancel"), + 0, 2); +#else + ans = QSql::No; +#endif // QT_NO_MESSAGEBOX + } + + switch (ans) { + case 0: + return QSql::Yes; + case 1: + return QSql::No; + default: + return QSql::Cancel; + } +} + +/*! \internal + + Virtual function which returns a confirmation for canceling an edit + mode \a m. Derived classes can reimplement this function and + provide their own confirmation dialog. The default implementation + uses a message box which prompts the user to confirm the edit + action. The dialog is centered over \a parent. + + +*/ + +QSql::Confirm Q3DataManager::confirmCancel(QWidget* parent, QSql::Op) +{ +#ifndef QT_NO_MESSAGEBOX + switch (QMessageBox::information(parent, + qApp->translate("QSql", "Confirm"), + qApp->translate("QSql", "Cancel your edits?"), + qApp->translate("QSql", "Yes"), + qApp->translate("QSql", "No"), + QString(), 0, 1)) { + case 0: + return QSql::Yes; + case 1: + return QSql::No; + default: + return QSql::Cancel; + } +#else + return QSql::Yes; +#endif // QT_NO_MESSAGEBOX +} + +QT_END_NAMESPACE + +#endif diff --git a/src/qt3support/sql/q3sqlmanager_p.h b/src/qt3support/sql/q3sqlmanager_p.h new file mode 100644 index 0000000..b9ea5c7 --- /dev/null +++ b/src/qt3support/sql/q3sqlmanager_p.h @@ -0,0 +1,160 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt3Support module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef Q3SQLMANAGER_P_H +#define Q3SQLMANAGER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "QtCore/qvariant.h" +#include "QtCore/qglobal.h" +#include "QtCore/qstring.h" +#include "QtCore/qstringlist.h" +#include "QtSql/qsql.h" +#include "QtSql/qsqlerror.h" +#include "QtSql/qsqlindex.h" +#include "Qt3Support/q3sqlcursor.h" + +QT_BEGIN_NAMESPACE + +#ifndef QT_NO_SQL + +class Q3SqlCursor; +class Q3SqlForm; +class Q3SqlCursorManagerPrivate; + +class Q_COMPAT_EXPORT Q3SqlCursorManager +{ +public: + Q3SqlCursorManager(); + virtual ~Q3SqlCursorManager(); + + virtual void setSort(const QSqlIndex& sort); + virtual void setSort(const QStringList& sort); + QStringList sort() const; + virtual void setFilter(const QString& filter); + QString filter() const; + virtual void setCursor(Q3SqlCursor* cursor, bool autoDelete = false); + Q3SqlCursor* cursor() const; + + virtual void setAutoDelete(bool enable); + bool autoDelete() const; + + virtual bool refresh(); + virtual bool findBuffer(const QSqlIndex& idx, int atHint = 0); + +private: + Q3SqlCursorManagerPrivate* d; +}; + +#ifndef QT_NO_SQL_FORM + +class Q3SqlFormManagerPrivate; + +class Q_COMPAT_EXPORT Q3SqlFormManager +{ +public: + Q3SqlFormManager(); + virtual ~Q3SqlFormManager(); + + virtual void setForm(Q3SqlForm* form); + Q3SqlForm* form(); + virtual void setRecord(QSqlRecord* record); + QSqlRecord* record(); + + virtual void clearValues(); + virtual void readFields(); + virtual void writeFields(); + +private: + Q3SqlFormManagerPrivate* d; +}; + +#endif + +class QWidget; +class Q3DataManagerPrivate; + +class Q_COMPAT_EXPORT Q3DataManager +{ +public: + Q3DataManager(); + virtual ~Q3DataManager(); + + virtual void setMode(QSql::Op m); + QSql::Op mode() const; + virtual void setAutoEdit(bool autoEdit); + bool autoEdit() const; + + virtual void handleError(QWidget* parent, const QSqlError& error); + virtual QSql::Confirm confirmEdit(QWidget* parent, QSql::Op m); + virtual QSql::Confirm confirmCancel(QWidget* parent, QSql::Op m); + + virtual void setConfirmEdits(bool confirm); + virtual void setConfirmInsert(bool confirm); + virtual void setConfirmUpdate(bool confirm); + virtual void setConfirmDelete(bool confirm); + virtual void setConfirmCancels(bool confirm); + + bool confirmEdits() const; + bool confirmInsert() const; + bool confirmUpdate() const; + bool confirmDelete() const; + bool confirmCancels() const; + +private: + Q3DataManagerPrivate* d; +}; + +#endif // QT_NO_SQL + +QT_END_NAMESPACE + +#endif // Q3SQLMANAGER_P_H diff --git a/src/qt3support/sql/q3sqlpropertymap.cpp b/src/qt3support/sql/q3sqlpropertymap.cpp new file mode 100644 index 0000000..6e55af0 --- /dev/null +++ b/src/qt3support/sql/q3sqlpropertymap.cpp @@ -0,0 +1,276 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt3Support module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "q3sqlpropertymap.h" + +#ifndef QT_NO_SQL_FORM + +#include "qwidget.h" +#include "q3cleanuphandler.h" +#include "qmetaobject.h" +#include "qmap.h" + +QT_BEGIN_NAMESPACE + +class Q3SqlPropertyMapPrivate +{ +public: + Q3SqlPropertyMapPrivate() {} + QMap<QByteArray, QByteArray> propertyMap; +}; + +/*! + \class Q3SqlPropertyMap + \brief The Q3SqlPropertyMap class is used to map widgets to SQL fields. + + \compat + + The SQL module uses Qt \link properties.html object + properties\endlink to insert and extract values from editor + widgets. + + This class is used to map editors to SQL fields. This works by + associating SQL editor class names to the properties used to + insert and extract values to/from the editor. + + For example, a QLineEdit can be used to edit text strings and + other data types in Q3DataTables or Q3SqlForms. Several properties + are defined in QLineEdit, but only the \e text property is used to + insert and extract text from a QLineEdit. Both Q3DataTable and + Q3SqlForm use the global Q3SqlPropertyMap for inserting and + extracting values to and from an editor widget. The global + property map defines several common widgets and properties that + are suitable for many applications. You can add and remove widget + properties to suit your specific needs. + + If you want to use custom editors with your Q3DataTable or + Q3SqlForm, you must install your own Q3SqlPropertyMap for that table + or form. Example: + + \snippet doc/src/snippets/code/src_qt3support_sql_q3sqlpropertymap.cpp 0 + + You can also replace the global Q3SqlPropertyMap that is used by + default. (Bear in mind that Q3SqlPropertyMap takes ownership of the + new default map.) + + \snippet doc/src/snippets/code/src_qt3support_sql_q3sqlpropertymap.cpp 1 + + \sa Q3DataTable, Q3SqlForm, Q3SqlEditorFactory +*/ + +/*! + +Constructs a Q3SqlPropertyMap. + +The default property mappings used by Qt widgets are: +\table +\header \i Widgets \i Property +\row \i \l QCheckBox, + \l QRadioButton + \i checked +\row \i \l QComboBox, + \l Q3ListBox + \i currentItem +\row \i \l Q3DateEdit + \i date +\row \i \l Q3DateTimeEdit + \l QDateTimeEdit + \i dateTime +\row \i \l QTextBrowser + \i source +\row \i \l QAbstractButton, + \l QDial, + \l QLabel, + \l QLineEdit, + \l Q3MultiLineEdit, + \l QPushButton, + \l QTextEdit, + \i text +\row \i \l Q3TimeEdit + \i time +\row \i \l QLCDNumber, + \l QScrollBar + \l QSlider, + \l QSpinBox + \i value +\endtable +*/ + +Q3SqlPropertyMap::Q3SqlPropertyMap() +{ + d = new Q3SqlPropertyMapPrivate(); + const struct MapData { + const char *classname; + const char *property; + } mapData[] = { + { "Q3DateEdit", "date" }, + { "Q3DateTimeEdit", "dateTime" }, + { "Q3ListBox", "currentItem" }, + { "Q3TimeEdit", "time" }, + { "QAbstractButton", "text" }, + { "QCheckBox", "checked" }, + { "QRadioButton", "checked" }, + { "QComboBox", "currentIndex" }, + { "QDateTimeEdit", "dateTime" }, + { "QDial", "value" }, + { "QLabel", "text" }, + { "QLCDNumber", "value" }, + { "QLineEdit", "text" }, + { "QPushButton", "text" }, + { "QScrollBar", "value" }, + { "QSlider", "value" }, + { "QSpinBox", "value" }, + { "QTabBar", "currentTab" }, + { "QTabWidget", "currentPage" }, + { "QTextBrowser", "source" }, + { "QTextEdit", "text" }, + { "QGroupBox", "checked" } + }; + + const MapData *m = mapData; + for (uint i = 0; i < sizeof(mapData)/sizeof(MapData); i++, m++) + d->propertyMap.insert(m->classname, m->property); +} + +/*! + Destroys the Q3SqlPropertyMap. + + Note that if the Q3SqlPropertyMap is installed with + installPropertyMap() the object it was installed into, e.g. the + Q3SqlForm, takes ownership and will delete the Q3SqlPropertyMap when + necessary. +*/ +Q3SqlPropertyMap::~Q3SqlPropertyMap() +{ + delete d; +} + +/*! + Returns the mapped property of \a widget as a QVariant. +*/ +QVariant Q3SqlPropertyMap::property(QWidget * widget) +{ + if(!widget) return QVariant(); + const QMetaObject* mo = widget->metaObject(); + while (mo && !d->propertyMap.contains(mo->className())) + mo = mo->superClass(); + + if (!mo) { + qWarning("Q3SqlPropertyMap::property: %s does not exist", widget->metaObject()->className()); + return QVariant(); + } + return widget->property(d->propertyMap[mo->className()]); +} + +/*! + Sets the property of \a widget to \a value. +*/ +void Q3SqlPropertyMap::setProperty(QWidget * widget, const QVariant & value) +{ + if(!widget) return; + + const QMetaObject* mo = widget->metaObject(); + while (mo && !d->propertyMap.contains(mo->className())) + mo = mo->superClass(); + if (!mo) { + qWarning("Q3SqlPropertyMap::setProperty: %s not handled by Q3SqlPropertyMap", widget->metaObject()->className()); + return; + } + + widget->setProperty(d->propertyMap[mo->className()], value); +} + +/*! + Insert a new classname/property pair, which is used for custom SQL + field editors. There \e must be a Q_PROPERTY() clause in the \a + classname class declaration for the \a property. +*/ +void Q3SqlPropertyMap::insert(const QString & classname, + const QString & property) +{ + d->propertyMap[classname.latin1()] = property.latin1(); +} + +/*! + Removes \a classname from the map. +*/ +void Q3SqlPropertyMap::remove(const QString & classname) +{ + d->propertyMap.remove(classname.latin1()); +} + +static Q3SqlPropertyMap * defaultmap = 0; +static Q3CleanupHandler< Q3SqlPropertyMap > qsql_cleanup_property_map; + +/*! + Returns the application global Q3SqlPropertyMap. +*/ +Q3SqlPropertyMap * Q3SqlPropertyMap::defaultMap() +{ + if(defaultmap == 0){ + defaultmap = new Q3SqlPropertyMap(); + qsql_cleanup_property_map.add(&defaultmap); + } + return defaultmap; +} + +/*! + Replaces the global default property map with \a map. All + Q3DataTable and Q3SqlForm instantiations will use this new map for + inserting and extracting values to and from editors. + \e{Q3SqlPropertyMap takes ownership of \a map, and destroys it + when it is no longer needed.} +*/ +void Q3SqlPropertyMap::installDefaultMap(Q3SqlPropertyMap * map) +{ + if(map == 0) return; + + if(defaultmap != 0){ + qsql_cleanup_property_map.remove(&defaultmap); + delete defaultmap; + } + defaultmap = map; + qsql_cleanup_property_map.add(&defaultmap); +} + +QT_END_NAMESPACE + +#endif // QT_NO_SQL_FORM diff --git a/src/qt3support/sql/q3sqlpropertymap.h b/src/qt3support/sql/q3sqlpropertymap.h new file mode 100644 index 0000000..88660b4 --- /dev/null +++ b/src/qt3support/sql/q3sqlpropertymap.h @@ -0,0 +1,86 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt3Support module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef Q3SQLPROPERTYMAP_H +#define Q3SQLPROPERTYMAP_H + +#include <QtCore/qvariant.h> +#include <QtCore/qstring.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Qt3Support) + +#ifndef QT_NO_SQL_FORM + +class QWidget; +class Q3SqlPropertyMapPrivate; + +class Q_COMPAT_EXPORT Q3SqlPropertyMap +{ +public: + Q3SqlPropertyMap(); + virtual ~Q3SqlPropertyMap(); + + QVariant property(QWidget * widget); + virtual void setProperty(QWidget * widget, const QVariant & value); + + void insert(const QString & classname, const QString & property); + void remove(const QString & classname); + + static Q3SqlPropertyMap * defaultMap(); + static void installDefaultMap(Q3SqlPropertyMap * map); + +private: + Q_DISABLE_COPY(Q3SqlPropertyMap) + + Q3SqlPropertyMapPrivate* d; +}; + +#endif // QT_NO_SQL_FORM + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // Q3SQLPROPERTYMAP_H diff --git a/src/qt3support/sql/q3sqlrecordinfo.h b/src/qt3support/sql/q3sqlrecordinfo.h new file mode 100644 index 0000000..a81988d --- /dev/null +++ b/src/qt3support/sql/q3sqlrecordinfo.h @@ -0,0 +1,122 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt3Support module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef Q3SQLRECORDINFO_H +#define Q3SQLRECORDINFO_H + +#include <QtCore/qglobal.h> + +#ifndef QT_NO_SQL +# include <Qt3Support/q3valuelist.h> +# include <QtSql/qsqlrecord.h> +# include <Qt3Support/q3sqlfieldinfo.h> +#endif + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Qt3Support) + +#ifndef QT_NO_SQL + +/* Q3SqlRecordInfo Class + This class is obsolete, use QSqlRecord instead. +*/ + +typedef Q3ValueList<Q3SqlFieldInfo> Q3SqlFieldInfoList; + +class Q_COMPAT_EXPORT Q3SqlRecordInfo: public Q3SqlFieldInfoList +{ +public: + Q3SqlRecordInfo(): Q3SqlFieldInfoList() {} + Q3SqlRecordInfo(const Q3SqlFieldInfoList& other): Q3SqlFieldInfoList(other) {} + Q3SqlRecordInfo(const QSqlRecord& other) + { + for (int i = 0; i < other.count(); ++i) + push_back(Q3SqlFieldInfo(other.field(i))); + } + + size_type contains(const QString& fieldName) const; + Q3SqlFieldInfo find(const QString& fieldName) const; + QSqlRecord toRecord() const; +}; + +inline Q3SqlRecordInfo::size_type Q3SqlRecordInfo::contains(const QString& fieldName) const +{ + size_type i = 0; + QString fName = fieldName.toUpper(); + + for(const_iterator it = begin(); it != end(); ++it) { + if ((*it).name().toUpper() == fName) { + ++i; + } + } + return i; +} + +inline Q3SqlFieldInfo Q3SqlRecordInfo::find(const QString& fieldName) const +{ + QString fName = fieldName.toUpper(); + for(const_iterator it = begin(); it != end(); ++it) { + if ((*it).name().toUpper() == fName) { + return *it; + } + } + return Q3SqlFieldInfo(); +} + +inline QSqlRecord Q3SqlRecordInfo::toRecord() const +{ + QSqlRecord buf; + for(const_iterator it = begin(); it != end(); ++it) { + buf.append((*it).toField()); + } + return buf; +} + +#endif // QT_NO_SQL + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // Q3SQLRECORDINFO_H diff --git a/src/qt3support/sql/q3sqlselectcursor.cpp b/src/qt3support/sql/q3sqlselectcursor.cpp new file mode 100644 index 0000000..7726f02 --- /dev/null +++ b/src/qt3support/sql/q3sqlselectcursor.cpp @@ -0,0 +1,263 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt3Support module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "q3sqlselectcursor.h" +#include "qsqldriver.h" +#include "q3sqlrecordinfo.h" + +#ifndef QT_NO_SQL + +QT_BEGIN_NAMESPACE + +class Q3SqlSelectCursorPrivate +{ +public: + Q3SqlSelectCursorPrivate() : populated(false) {} + QString query; + bool populated : 1; +}; + +/*! + \class Q3SqlSelectCursor + \brief The Q3SqlSelectCursor class provides browsing of general SQL SELECT statements. + + \compat + + Q3SqlSelectCursor is a convenience class that makes it possible to + display result sets from general SQL \c SELECT statements in + data-aware Qt widgets. Q3SqlSelectCursor is read-only and does not + support \c INSERT, \c UPDATE or \c DELETE operations. + + Pass the query in at construction time, or use the + Q3SqlSelectCursor::exec() function. + + Example: + \snippet doc/src/snippets/code/src_qt3support_sql_q3sqlselectcursor.cpp 0 +*/ + +/*! + Constructs a read only cursor on database \a db using the query \a query. + */ +Q3SqlSelectCursor::Q3SqlSelectCursor(const QString& query, QSqlDatabase db) + : Q3SqlCursor(QString(), false, db) +{ + d = new Q3SqlSelectCursorPrivate; + d->query = query; + Q3SqlCursor::setMode(ReadOnly); + if (!query.isEmpty()) + exec(query); +} + +/*! Constructs a copy of \a other */ +Q3SqlSelectCursor::Q3SqlSelectCursor(const Q3SqlSelectCursor& other) + : Q3SqlCursor(other) +{ + d = new Q3SqlSelectCursorPrivate; + d->query = other.d->query; + d->populated = other.d->populated; +} + +/*! Destroys the object and frees any allocated resources */ +Q3SqlSelectCursor::~Q3SqlSelectCursor() +{ + delete d; +} + +/*! \internal */ +bool Q3SqlSelectCursor::exec(const QString& query) +{ + d->query = query; + bool ret = Q3SqlCursor::exec(query); + if (ret) { + Q3SqlCursor::clear(); + populateCursor(); + } + return ret; +} + +/*! \fn bool Q3SqlSelectCursor::select() + \internal +*/ + +/*! \internal */ +bool Q3SqlSelectCursor::select(const QString&, const QSqlIndex&) +{ + bool ret = Q3SqlCursor::exec(d->query); + if (ret && !d->populated) + populateCursor(); + return ret; +} + +/*! \internal */ +void Q3SqlSelectCursor::populateCursor() +{ + Q3SqlRecordInfo inf = Q3SqlRecordInfo(record()); + for (Q3SqlRecordInfo::const_iterator it = inf.begin(); it != inf.end(); ++it) + Q3SqlCursor::append(*it); + d->populated = true; +} + +/*! \fn QSqlIndex Q3SqlSelectCursor::primaryIndex(bool) const + \internal +*/ + +/*! \fn QSqlIndex Q3SqlSelectCursor::index(const QStringList&) const + \internal +*/ + +/*! \fn QSqlIndex Q3SqlSelectCursor::index(const QString&) const + \internal +*/ + +/*! \fn QSqlIndex Q3SqlSelectCursor::index(const char*) const + \internal +*/ + +/*! \fn void Q3SqlSelectCursor::setPrimaryIndex(const QSqlIndex&) + \internal +*/ + +/*! \fn void Q3SqlSelectCursor::append(const Q3SqlFieldInfo&) + \internal +*/ + +/*! \fn void Q3SqlSelectCursor::insert(int, const Q3SqlFieldInfo&) + \internal +*/ + +/*! \fn void Q3SqlSelectCursor::remove(int) + \internal +*/ + +/*! \fn void Q3SqlSelectCursor::clear() + \internal +*/ + +/*! \fn void Q3SqlSelectCursor::setGenerated(const QString&, bool) + \internal +*/ + +/*! \fn void Q3SqlSelectCursor::setGenerated(int, bool) + \internal +*/ + +/*! \fn QSqlRecord* Q3SqlSelectCursor::editBuffer(bool) + \internal +*/ + +/*! \fn QSqlRecord* Q3SqlSelectCursor::primeInsert() + \internal +*/ + +/*! \fn QSqlRecord* Q3SqlSelectCursor::primeUpdate() + \internal +*/ + +/*! \fn QSqlRecord* Q3SqlSelectCursor::primeDelete() + \internal +*/ + +/*! \fn int Q3SqlSelectCursor::insert(bool) + \internal +*/ + +/*! \fn int Q3SqlSelectCursor::update(bool) + \internal +*/ + +/*! \fn int Q3SqlSelectCursor::del(bool) + \internal +*/ + +/*! \fn void Q3SqlSelectCursor::setMode(int) + \internal +*/ + +/*! \fn void Q3SqlSelectCursor::setSort(const QSqlIndex&) + \internal +*/ + +/*! \fn QSqlIndex Q3SqlSelectCursor::sort() const + \internal +*/ + +/*! \fn void Q3SqlSelectCursor::setFilter(const QString&) + \internal +*/ + +/*! \fn QString Q3SqlSelectCursor::filter() const + \internal +*/ + +/*! \fn void Q3SqlSelectCursor::setName(const QString&, bool) + \internal +*/ + +/*! \fn QString Q3SqlSelectCursor::name() const + \internal +*/ + +/*! \fn QString Q3SqlSelectCursor::toString(const QString&, const QString&) const + \internal +*/ + +/*! + \fn int Q3SqlSelectCursor::update(const QString & filter, bool invalidate = true) + \overload + + Updates the database with the current contents of the cursor edit + buffer using the specified \a filter. Returns the number of + records which were updated. + For error information, use lastError(). + + Only records which meet the filter criteria are updated, otherwise + all records in the table are updated. + + If \a invalidate is true (the default), the cursor can no longer + be navigated. A new select() call must be made before you can move + to a valid record. + + \sa Q3SqlCursor::update() primeUpdate() setMode() lastError() +*/ + +QT_END_NAMESPACE + +#endif // QT_NO_SQL diff --git a/src/qt3support/sql/q3sqlselectcursor.h b/src/qt3support/sql/q3sqlselectcursor.h new file mode 100644 index 0000000..26103ba --- /dev/null +++ b/src/qt3support/sql/q3sqlselectcursor.h @@ -0,0 +1,115 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt3Support module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef Q3SQLSELECTCURSOR_H +#define Q3SQLSELECTCURSOR_H + +#include <Qt3Support/q3sqlcursor.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Qt3Support) + +#ifndef QT_NO_SQL + +class Q3SqlSelectCursorPrivate; + +class Q_COMPAT_EXPORT Q3SqlSelectCursor : public Q3SqlCursor +{ +public: + Q3SqlSelectCursor(const QString& query = QString(), QSqlDatabase db = QSqlDatabase()); + Q3SqlSelectCursor(const Q3SqlSelectCursor& other); + ~Q3SqlSelectCursor(); + bool exec(const QString& query); + bool select() { return Q3SqlCursor::select(); } + +protected: + QSqlIndex primaryIndex(bool = true) const { return QSqlIndex(); } + QSqlIndex index(const QStringList&) const { return QSqlIndex(); } + QSqlIndex index(const QString&) const { return QSqlIndex(); } + QSqlIndex index(const char*) const { return QSqlIndex(); } + void setPrimaryIndex(const QSqlIndex&) {} + void append(const Q3SqlFieldInfo&) {} + void insert(int, const Q3SqlFieldInfo&) {} + void remove(int) {} + void clear() {} + void setGenerated(const QString&, bool) {} + void setGenerated(int, bool) {} + QSqlRecord* editBuffer(bool = false) { return 0; } + QSqlRecord* primeInsert() { return 0; } + QSqlRecord* primeUpdate() { return 0; } + QSqlRecord* primeDelete() { return 0; } + int insert(bool = true) { return 0; } + int update(bool = true) { return 0; } + int del(bool = true) { return 0; } + void setMode(int) {} + + void setSort(const QSqlIndex&) {} + QSqlIndex sort() const { return QSqlIndex(); } + void setFilter(const QString&) {} + QString filter() const { return QString(); } + void setName(const QString&, bool = true) {} + QString name() const { return QString(); } + QString toString(const QString& = QString(), const QString& = QLatin1String(",")) const { return QString(); } + bool select(const QString &, const QSqlIndex& = QSqlIndex()); + +private: + void populateCursor(); + + Q3SqlSelectCursorPrivate * d; + +protected: +#if !defined(Q_NO_USING_KEYWORD) + using Q3SqlCursor::update; +#else + virtual int update(const QString & filter, bool invalidate = true) { return Q3SqlCursor::update(filter, invalidate); } +#endif +}; + +#endif // QT_NO_SQL + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // Q3SQLSELECTCURSOR_H diff --git a/src/qt3support/sql/sql.pri b/src/qt3support/sql/sql.pri new file mode 100644 index 0000000..41fd641 --- /dev/null +++ b/src/qt3support/sql/sql.pri @@ -0,0 +1,25 @@ +# Qt compat module + +HEADERS += sql/q3sqlfieldinfo.h \ + sql/q3sqlrecordinfo.h \ + sql/q3datatable.h \ + sql/q3dataview.h \ + sql/q3sqlcursor.h \ + sql/q3sqlselectcursor.h \ + sql/q3sqlform.h \ + sql/q3sqlmanager_p.h \ + sql/q3editorfactory.h \ + sql/q3sqleditorfactory.h \ + sql/q3sqlpropertymap.h \ + sql/q3databrowser.h \ + +SOURCES += sql/q3datatable.cpp \ + sql/q3dataview.cpp \ + sql/q3sqlcursor.cpp \ + sql/q3sqlselectcursor.cpp \ + sql/q3sqlform.cpp \ + sql/q3sqlmanager_p.cpp \ + sql/q3editorfactory.cpp \ + sql/q3sqleditorfactory.cpp \ + sql/q3sqlpropertymap.cpp \ + sql/q3databrowser.cpp |