13.3.2.  Editable Models

[ fromfile: tablemodels.xml id: editablemodels ]

For editable models, you must override flags() and setData(). If you want in-place editing (in the actual View), you would return Qt::ItemIsEditable from flags(). Because we still pop up an ActionEditorDialog when an Item is clicked, we do not need in-place editing here and so Example 13.16 simply returns Qt::ItemIsEnabled.

Example 13.16. src/libs/actioneditor/actiontablemodel.cpp

[ . . . . ]

Qt::ItemFlags ActionTableModel::
flags(const QModelIndex& index) const {
    if (index.isValid()) return Qt::ItemIsEnabled;
    else return 0;
}

<include segid="flags" mode="cpp" href="src/libs/actioneditor/actiontablemodel.cpp" id="actiontablemodel-flags-cpp" src="src/libs/actioneditor/actiontablemodel.cpp"/>


Example 13.17 shows how, in setData(), you can check for ambiguous shortcuts before actually setting new values. After the data is changed, it is important to emit a dataChanged() signal so that views that may be showing old data know it is time to fetch newer data from the model.

Example 13.17. src/libs/actioneditor/actiontablemodel.cpp

[ . . . . ]

bool ActionTableModel::
setData(const QModelIndex& index, const QVariant& value, int role) {
    if (role != Qt::EditRole) return false;
    int row = index.row();
    if ((row < 0) | (row >= m_actions.size())) return false;
    QString str = value.toString();
    QKeySequence ks(str);
    QAction* previousAction = 0;

    if (ks != QKeySequence() ) foreach (QAction* act, m_actions) {
        if (act->shortcut() == ks) {
            previousAction = act;
            break;
        }
    }
    if (previousAction != 0) {
        QString error = tr("%1 is already bound to %2.").
                arg(ks.toString()).arg(previousAction->text());
        bool answer = QMessageBox::question(0, error,
                    tr("%1\n Remove previous binding?").arg(error),
                    QMessageBox::Yes, QMessageBox::No);
        if (!answer) return false;
        previousAction->setShortcut(QKeySequence());
    }
    m_actions[row]->setShortcut(ks);
    QModelIndex changedIdx = createIndex(row, 1);   1
    emit dataChanged(changedIdx, changedIdx);       2
    return true;
}

1

Column 1 displays the shortcut.

2

Required for Views to know when/what to update.

<include segid="setdata" mode="cpp" href="src/libs/actioneditor/actiontablemodel.cpp" id="actiontablemodel-setdata-cpp" src="src/libs/actioneditor/actiontablemodel.cpp"/>


To support inserting/removing rows, there are analogous signals, rowsInserted() and rowsRemoved() that we must emit from our implementations of insertRows()/removeRows().

After 1 or more shortcuts have been changed, we save them to QSettings. Example 13.18 shows how to keep track of the modified QActions that need to be saved.

Example 13.18. src/libs/actioneditor/actiontableeditor.cpp

[ . . . . ]

void ActionTableEditor::
on_m_tableView_activated(const QModelIndex& idx) {
    int row = idx.row();
    QAction* action = m_model->action(row);
    ActionEditorDialog aed(action);

    int result = aed.exec();
    if (result ==  QDialog::Accepted) {
        QKeySequence ks = aed.keySequence();
        m_model->setData(idx, ks.toString());
        m_changedActions << action;
    }
}

<include segid="dialog" mode="cpp" href="src/libs/actioneditor/actiontableeditor.cpp" id="actioneditordialog-cpp" src="src/libs/actioneditor/actiontableeditor.cpp"/>


Example 13.19 shows how those shortcuts are saved to QSettings, but only if the user accepts the dialog.

Example 13.19. src/libs/actioneditor/actiontableeditor.cpp

[ . . . . ]

void ActionTableEditor::accept() {
    QSettings s;
    s.beginGroup("shortcut");
    foreach (QAction* act, m_changedActions) {
        s.setValue(act->text(), act->shortcut() );
    }
    s.endGroup();
    QDialog::accept();
}

<include segid="settings" mode="cpp" href="src/libs/actioneditor/actiontableeditor.cpp" id="actioneditor-savesettings-cpp" src="src/libs/actioneditor/actiontableeditor.cpp"/>