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;
}