src/qt3support/sql/q3databrowser.cpp
changeset 0 1918ee327afb
child 4 3b1da2848fc7
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/qt3support/sql/q3databrowser.cpp	Mon Jan 11 14:00:40 2010 +0000
@@ -0,0 +1,1281 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@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