Specific implementation of BaseEditor for use with widgets. More...
#include <baseeditor.h>


Public Member Functions | |
| MappedBaseEditor (QWidget *parent=0) | |
| const QSqlRecord & | currentPrimaryKey () |
| const int | currentMapperRow () |
| void | setTable (const QString &table, const QString &fieldName=QString(), Qt::SortOrder order=Qt::AscendingOrder, const QString &filter=QString()) |
| void | select () |
| virtual bool | isDirty () const |
| SqlTableModel * | model () |
| DataWidgetMapper * | mapper () |
| void | setCurrent (const QString &fieldIdName, const QVariant &fieldId) |
Protected Slots | |
| virtual void | onCurrentRecordChange (int row) |
| virtual void | onQueryChange () |
| virtual void | onNewButtonPress () |
| virtual void | onDeleteButtonPress () |
| void | onUndoButtonPress () |
| virtual void | onSubmitButtonPress () |
| virtual void | updateButtons () |
Protected Member Functions | |
| void | addWidgetMapping (QWidget *widget, const QString &mapFieldName, const QByteArray &propertyName="") |
| void | addWidget (QWidget *widget, int row, int column, int rowSpan, int columnSpan) |
| void | addWidget (QWidget *widget, int row, int column, int rowSpan, int columnSpan, const QString &mapFieldName, const QByteArray &propertyName="") |
| void | addWidget (QComboBox *widget, int row, int column, int rowSpan, int columnSpan, const QSqlRelation &relation, const QString &fkColumnName=QString(), const QByteArray &propertyName="") |
| WidgetLayout * | widgetLayout () |
| bool | isCurrentRecordNew () const |
Specific implementation of BaseEditor for use with widgets.
| MappedBaseEditor::MappedBaseEditor | ( | QWidget * | parent = 0 |
) |
: BaseEditor(parent), m_currentRecordIsNew(false) { // member initialization m_widgetLayout = new WidgetLayout(); m_mapper = new DataWidgetMapper(this); m_model = new SqlTableModel(this, Database::staticCon()); // model setup m_model->setEditStrategy(QSqlTableModel::OnRowChange); // button layout setup QVBoxLayout *buttonLayout = new QVBoxLayout(); buttonLayout->addWidget(button(BaseEditor::ButtonNext)); buttonLayout->addWidget(button(BaseEditor::ButtonPrevious)); buttonLayout->addStretch(); buttonLayout->addWidget(button(BaseEditor::ButtonNew)); buttonLayout->addWidget(button(BaseEditor::ButtonDelete)); buttonLayout->addStretch(); buttonLayout->addWidget(button(BaseEditor::ButtonUndo)); buttonLayout->addWidget(button(BaseEditor::ButtonSubmit)); buttonLayout->addStretch(1); buttonLayout->addWidget(button(BaseEditor::ButtonClose)); // setup widget groupBox QGroupBox *wBox = new QGroupBox("Record", this); wBox->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); wBox->setLayout(m_widgetLayout); // setup seeker Seeker *seeker = new Seeker(this); seeker->setModel(m_model); seeker->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Maximum); // main layout setup mainLayout()->addWidget(seeker, 0, 0, 1, 2); mainLayout()->addWidget(wBox, 1, 0, 1, 1); mainLayout()->addLayout(buttonLayout, 1, 1, 2, 1); // connection setup connect(button(BaseEditor::ButtonPrevious), SIGNAL(clicked()), m_mapper, SLOT(toPrevious())); connect(button(BaseEditor::ButtonNext), SIGNAL(clicked()), m_mapper, SLOT(toNext())); connect(button(BaseEditor::ButtonDelete), SIGNAL(clicked()), this, SLOT(onDeleteButtonPress())); connect(m_mapper, SIGNAL(currentIndexChanged(int)), this, SLOT(onCurrentRecordChange(int))); connect(m_model, SIGNAL(queryChanged()), this, SLOT(onQueryChange())); connect(seeker, SIGNAL(found(QModelIndex)), m_mapper, SLOT(setCurrentModelIndex(QModelIndex))); // mapper setup m_mapper->setModel(m_model); m_mapper->setSubmitPolicy(DataWidgetMapper::ManualSubmit); m_mapper->setOrientation(Qt::Horizontal); m_mapper->setItemDelegate(new MapperDelegate(this)); }
| void MappedBaseEditor::addWidget | ( | QWidget * | widget, | |
| int | row, | |||
| int | column, | |||
| int | rowSpan, | |||
| int | columnSpan | |||
| ) | [protected] |
Referenced by addWidget(), InstallationEditor::InstallationEditor(), LocationEditor::LocationEditor(), ProjectEditor::ProjectEditor(), SensorCategoryEditor::SensorCategoryEditor(), SensorChannelCalibrationEditor::SensorChannelCalibrationEditor(), SensorChannelEditor::SensorChannelEditor(), SensorEditor::SensorEditor(), UnitEditor::UnitEditor(), and UserEditor::UserEditor().
{
Q_ASSERT(m_widgetLayout);
m_widgetLayout->addWidget(widget, row, column, rowSpan, columnSpan);
}
| void MappedBaseEditor::addWidget | ( | QWidget * | widget, | |
| int | row, | |||
| int | column, | |||
| int | rowSpan, | |||
| int | columnSpan, | |||
| const QString & | mapFieldName, | |||
| const QByteArray & | propertyName = "" | |||
| ) | [protected] |
{
addWidget(widget, row, column, rowSpan, columnSpan);
addWidgetMapping(widget, mapFieldName, propertyName);
}
| void MappedBaseEditor::addWidget | ( | QComboBox * | widget, | |
| int | row, | |||
| int | column, | |||
| int | rowSpan, | |||
| int | columnSpan, | |||
| const QSqlRelation & | relation, | |||
| const QString & | fkColumnName = QString(), |
|||
| const QByteArray & | propertyName = "" | |||
| ) | [protected] |
{
Q_ASSERT(m_mapper);
QString fkColumnN = fkColumnName.isNull() ? relation.indexColumn() : fkColumnName;
int fkColumn = m_model->fieldIndex(fkColumnN);
Q_ASSERT(fkColumn != -1 || !Database::staticCon().isOpen()); //Relations indexColumn should exist in model als fk!
m_model->setRelation(fkColumn, relation); //Hereafter, the query()-string changes to reflect the relation at fkIndex, so select() must be executed to update the query().
QSqlTableModel *widgetModel = m_model->relationModel(fkColumn);
Q_ASSERT(widgetModel);
widget->setModel(widgetModel);
widget->setModelColumn(widgetModel->fieldIndex(relation.displayColumn()));
addWidget(widget, row, column, rowSpan, columnSpan, fkColumnN, propertyName);
}
| void MappedBaseEditor::addWidgetMapping | ( | QWidget * | widget, | |
| const QString & | mapFieldName, | |||
| const QByteArray & | propertyName = "" | |||
| ) | [protected] |
Referenced by addWidget(), and UserEditor::UserEditor().
{
Q_ASSERT(m_mapper);
// setup vars
int mapFieldIndex = m_model->fieldIndex(mapFieldName);
// setup mapper
m_mapper->addMapping(widget, mapFieldIndex, propertyName);
m_mapper->revert(); //mapper doesn't populate the widget with the data: revert() or toFirst() must be called. revert() only calls d->populate() so it's the prefered way;
// setup completer
if (QLineEdit *edit = qobject_cast<QLineEdit*>(widget)){
QCompleter *nameCompleter = new QCompleter(m_model, edit);
nameCompleter->setCompletionColumn(mapFieldIndex);
nameCompleter->setCaseSensitivity(Qt::CaseInsensitive); //cs or ci? depends on feedback.
nameCompleter->setCompletionMode(QCompleter::InlineCompletion); //should be the default, but the problem is that it also shows duplicate items
edit->setCompleter(nameCompleter);
}
}
| const int MappedBaseEditor::currentMapperRow | ( | ) |
{
m_mapper->currentIndex();
}
| const QSqlRecord & MappedBaseEditor::currentPrimaryKey | ( | ) |
Referenced by onSubmitButtonPress().
{
return m_currentPrimaryKey;
}
| bool MappedBaseEditor::isCurrentRecordNew | ( | ) | const [protected] |
Referenced by SensorEditor::updateButtons(), SensorChannelEditor::updateButtons(), and updateButtons().
{
return m_currentRecordIsNew;
}
| bool MappedBaseEditor::isDirty | ( | ) | const [virtual] |
| DataWidgetMapper * MappedBaseEditor::mapper | ( | ) |
{
return m_mapper;
}
| SqlTableModel * MappedBaseEditor::model | ( | ) |
Referenced by SensorChannelEditor::onNewButtonPress(), SensorChannelCalibrationEditor::onNewButtonPress(), setCurrent(), SensorEditor::updateButtons(), SensorChannelEditor::updateButtons(), and UserEditor::UserEditor().
{
return m_model;
}
| void MappedBaseEditor::onCurrentRecordChange | ( | int | row | ) | [protected, virtual, slot] |
Reimplemented in UserEditor, ProjectEditor, SensorChannelEditor, and SensorEditor.
Referenced by MappedBaseEditor().
{
updateCurrentPrimaryKey();
updateButtons();
}
| void MappedBaseEditor::onDeleteButtonPress | ( | ) | [protected, virtual, slot] |
Referenced by MappedBaseEditor().
{
if (m_currentRecordIsNew){
m_model->revert();
m_mapper->setCurrentIndex(m_model->rowCount()-1);
m_currentRecordIsNew = false;
} else {
int button = QMessageBox::warning(this, "Delete", "Are you sure you wish to delete the current record?", QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
if (button == QMessageBox::No)
return;
int idAboutToBeDeleted = m_mapper->currentIndex();
if (!m_model->removeRow(idAboutToBeDeleted)) { //calls onQueryChange() implicitely!
qWarning() << "Record could not be deleted! \n " << m_model->lastError().text();
return;
} else {
m_mapper->setCurrentIndex(m_model->rowCount() == idAboutToBeDeleted ? idAboutToBeDeleted - 1 : idAboutToBeDeleted);
Database::notifyTableChange(m_model->tableName());
}
}
updateButtons();
}
| void MappedBaseEditor::onNewButtonPress | ( | ) | [protected, virtual, slot] |
Implements BaseEditor.
Reimplemented in SensorChannelCalibrationEditor, and SensorChannelEditor.
{
// Notes on model->insertRecord(-1, QSqlRecord());:
// - Geeft QSqlError(-1, "No Fields to update", "") bij strategy: QSqlTableModel::OnRowChange
// - De nieuwe QModelIndexes worden ook niet gemarkeerd als dirty
// - Doorgegeven QSqlRecord() wordt aangepast (i.e. krijgt extra fields)
// en dus geldt niet: model->record(model->rowCount() - 1) == QSqlRecord(),
// Verder geeft QSqlRecord::isNull "true" voor elke field. (Dit kan als toets gebruikt worden.)
// - Er wordt maar 1 record achteraan gecreëerd met PK == 0,
// ongeacht hoeveel keer men model->insertRecord(-1, QSqlRecord()); uitvoert
// - Because this makes an empty record in the model itself, it can not be
// reverted by mapper.
// - Return value is always false (so its not written to the DB), but is still cached
// so it can be undone with model->revert() or deleted.
m_model->insertRecord(-1, QSqlRecord());
Q_ASSERT(m_model->rowCount() - 1 > -1);
m_currentRecordIsNew = true;
m_mapper->setCurrentIndex(m_model->rowCount()-1);
updateButtons();
}
| void MappedBaseEditor::onQueryChange | ( | ) | [protected, virtual, slot] |
This function is primarily due to a bug in Qt where the mappers indexes become invalid after Model::select() is called, which also emits queryChanged(). cf. http://bugreports.qt.nokia.com/browse/QTBUG-1086 One consequence of this is that mapper->currentIndex() == -1 so it can not be used as currentIndex.
Reimplemented in SensorEditor.
Referenced by MappedBaseEditor().
{
// This is no longer required, as submit calls it's own updateButtons()
// // An issue arises when one calls submit() and it is successfull, and this function is
// // executed sooner than m_currentRecordIsNew is set to false. This gives a wrong effect
// // because it's used in updateButtons(). I think it is safe to set
// // it to false when it's called from the model. An other way to do this is to "slow down" the
// // emitting process of the model, but Qt::QueuedConnection didn't seem to work.
// if (sender() == model) m_currentRecordIsNew = false;
//qDebug() << "Executing onQueryChange()";
bool modelIsEmpty = m_model->rowCount() == 0;
int newIndex;
if (modelIsEmpty) m_mapper->revert(); //because the mapper will otherwise not empty the widgets
if (m_currentPrimaryKey.isEmpty()){ //model was empty || it's a view without pk set!
Q_ASSERT(m_mapper->currentIndex() == -1);
if (m_model->rowCount() != 0) newIndex = 0;
} else {
newIndex = seekRow(m_model->query(), m_currentPrimaryKey);
if (newIndex == -1) //an item has been deleted
newIndex = 0;
}
Q_ASSERT(newIndex > -1);
m_mapper->setCurrentIndex(newIndex);
updateButtons();
}
| void MappedBaseEditor::onSubmitButtonPress | ( | ) | [protected, virtual, slot] |
Implements BaseEditor.
{
bool isUpdate = !m_currentRecordIsNew;
// Confirm update
if (isUpdate){
// Hier zou eventueel ook kunnen geschreven worden welke veranderingen er
// aangebracht gaan worden, maar is niet op één twee drie te implementeren.
// DataWidgetMapper zou extended moeten worden voor het geven van QVariant van
// elke widget waarvoor em moet bemiddelen, of iets dergelijk.
int button = QMessageBox::warning(this, "Update", "Are you sure you wish to update the current record?", QMessageBox::Yes | QMessageBox::No | QMessageBox::Discard, QMessageBox::No);
if (button == QMessageBox::No)
return;
if (button == QMessageBox::Discard){
onUndoButtonPress();
return;
}
}
// Submit data
// - Note that submit() submits all the data from the mapped widgets. This
// corresponds to submitting only one row!
// - Note also that submit() internaly calls model()::submit() which only works
// with OnRowChange or OnFieldChange, but does nothing for the OnManualSubmit
// strategy. So make sure the model's strategy is set on OnRowChange.
// - Note also that submitting executes model()'s queryChanged() and thus onQueryChanged()
if (!m_mapper->submit()) { //is false only when the QModelIndex is invalid;
qWarning() << "Record could not be submitted: " << m_model->lastError();
// Model caches the changes, and model::revert() could be called here.
// But then the new record is ereased and the currentIndex points to an
// invalid index: QModelIndex of the current row is invalid. It only can be
// used on update.
if (isUpdate) m_model->revert();
updateButtons();
return;
}
Database::notifyTableChange(m_model->tableName());
// Revert to the just submitted record.
// There are two options here: either it was a submit, or it was an update.
int row = -1;
if (isUpdate)
row = seekRow(m_model->query(), currentPrimaryKey());
else
row = seekRow(m_model->query(), Database::lastInserted(m_model->tableName()));
if (row == -1){
qDebug() << "Last inserted record not found for table " << m_model->tableName();
row = 0; // vroeger stond hier een Q_ASSERT: probleem is dat indien lastInserted niets oplevert, zoals bij tbl_users, proggie blokt
}
m_mapper->setCurrentIndex(row);
m_currentRecordIsNew = false;
updateButtons();
}
| void MappedBaseEditor::onUndoButtonPress | ( | ) | [protected, virtual, slot] |
Implements BaseEditor.
Referenced by onSubmitButtonPress().
{
m_mapper->revert();
updateButtons();
}
| void MappedBaseEditor::select | ( | ) |
Referenced by InstallationEditor::InstallationEditor(), LocationEditor::LocationEditor(), ProjectEditor::ProjectEditor(), SensorCategoryEditor::SensorCategoryEditor(), SensorChannelCalibrationEditor::SensorChannelCalibrationEditor(), SensorChannelEditor::SensorChannelEditor(), SensorEditor::SensorEditor(), UnitEditor::UnitEditor(), and UserEditor::UserEditor().
{
if (!m_model->select())
qWarning() << m_model->lastError() << " from query: " << m_model->query().lastQuery();
}
| void MappedBaseEditor::setTable | ( | const QString & | table, | |
| const QString & | fieldName = QString(), |
|||
| Qt::SortOrder | order = Qt::AscendingOrder, |
|||
| const QString & | filter = QString() | |||
| ) |
Referenced by InstallationEditor::InstallationEditor(), LocationEditor::LocationEditor(), ProjectEditor::ProjectEditor(), SensorCategoryEditor::SensorCategoryEditor(), SensorChannelCalibrationEditor::SensorChannelCalibrationEditor(), SensorChannelEditor::SensorChannelEditor(), SensorEditor::SensorEditor(), UnitEditor::UnitEditor(), and UserEditor::UserEditor().
{
m_model->setTable(table); //resets order, filter, etc...
m_model->setSort(m_model->fieldIndex(fieldName), order);
m_model->setFilter(filter);
if (!Database::execStaticR(QString("SELECT fnc_has_edit_privileges('").append(table).append("'::regclass)")).value(0).toBool())
qWarning() << "You do not have full privileges for editing this table. Please contact the administrator if you want this restriction changed.";
}
| void MappedBaseEditor::updateButtons | ( | ) | [protected, virtual, slot] |
Implements BaseEditor.
Reimplemented in SensorChannelEditor, and SensorEditor.
Referenced by InstallationEditor::InstallationEditor(), LocationEditor::LocationEditor(), onCurrentRecordChange(), onDeleteButtonPress(), onNewButtonPress(), onQueryChange(), onSubmitButtonPress(), onUndoButtonPress(), ProjectEditor::ProjectEditor(), SensorCategoryEditor::SensorCategoryEditor(), SensorChannelCalibrationEditor::SensorChannelCalibrationEditor(), UnitEditor::UnitEditor(), and UserEditor::UserEditor().
{
//qDebug() << "Button update requested by: " << sender();
Q_ASSERT(m_model);
Q_ASSERT(m_mapper);
bool newRecord = isCurrentRecordNew();
bool modelNotEmpty = m_model->rowCount() > 0;
bool editorDirty = isDirty();
button(BaseEditor::ButtonSubmit)->setEnabled(editorDirty || newRecord);
button(BaseEditor::ButtonUndo)->setEnabled(editorDirty);
button(BaseEditor::ButtonPrevious)->setEnabled(m_mapper->currentIndex() > 0);
button(BaseEditor::ButtonNext)->setEnabled(m_mapper->currentIndex() < m_model->rowCount() - 1);
button(BaseEditor::ButtonNew)->setEnabled(!newRecord || !modelNotEmpty);
button(BaseEditor::ButtonDelete)->setEnabled(modelNotEmpty);
// special cases, to do: make conditionals per button. (= possible?)
if (newRecord){
button(BaseEditor::ButtonNext)->setEnabled(!newRecord);
button(BaseEditor::ButtonPrevious)->setEnabled(!newRecord);
}
// Update submit button name
newRecord ? button(BaseEditor::ButtonSubmit)->setText("Submit") : button(BaseEditor::ButtonSubmit)->setText("Update");
m_widgetLayout->setWidgetsEnabled(modelNotEmpty);
}
| WidgetLayout * MappedBaseEditor::widgetLayout | ( | ) | [protected] |
{
return m_widgetLayout;
}
1.7.1