Mercurial > octave
view libgui/src/variable-editor-model.cc @ 24627:a79f782653ce
when editing sub-variables, generate complete subscript, not format string
* variable-editor-model.h, variable-editor-model.cc
(variable_editor_model::subscript_expression): Rename from
variable_editor_model::parens. Generate complete subscript, not just
format string.
* variable-editor.cc (variable_editor::double_click):
Call variable_editor_model::subscript_expression, not
variable_editor_model::parens. Don't limit editing of sub-elements to
matrix objects.
author | John W. Eaton <jwe@octave.org> |
---|---|
date | Sat, 20 Jan 2018 16:00:01 -0500 |
parents | 2bb3f3de0b4e |
children | 428780eec08a |
line wrap: on
line source
/* Copyright (C) 2013-2017 John W. Eaton Copyright (C) 2015 Michael Barnes Copyright (C) 2013 RĂ¼diger Sonderfeld This file is part of Octave. Octave is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Octave is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Octave; see the file COPYING. If not, see <https://www.gnu.org/licenses/>. */ #ifdef HAVE_CONFIG_H # include <config.h> #endif #include <sstream> #include <QDebug> #include <QLabel> #include <QMessageBox> #include <QString> #include <QVector> #include "octave-qt-link.h" #include "variable-editor-model.h" #include "ov.h" #include "parse.h" #include "variables.h" // Pimpl/Dpointer for variable_editor_model. struct variable_editor_model::impl { struct cell { enum state_t { avail, notavail, pending, unset }; cell (void) : m_state (unset) { } cell (state_t s) : m_state (s) { } cell (const QString& d, const QString& s, const QString& t, bool rse, sub_editor_types edtype) : m_state (avail), m_data (d), m_status_tip (s), m_tool_tip (t), m_requires_sub_editor (rse), m_editor_type (edtype) { } state_t m_state; QVariant m_data; QVariant m_status_tip; QVariant m_tool_tip; QVariant m_background; bool m_requires_sub_editor; sub_editor_types m_editor_type; // FIXME: Other variables needed? }; void set (const QModelIndex& idx, const cell& dat) { if (idx.isValid ()) m_table[model_to_index (idx)] = dat; } void set (int r, int c, const cell& dat) { if (0 <= r && r < m_rows && 0 <= c && c <= columns ()) m_table[c * m_rows + r] = dat; } bool is_set (const QModelIndex& idx) const { return (idx.isValid () && m_table[model_to_index (idx)].m_state == cell::avail); } bool is_notavail (const QModelIndex& idx) const { return (idx.isValid () && m_table[model_to_index (idx)].m_state == cell::notavail); } bool is_pending (const QModelIndex& idx) const { return (idx.isValid () && m_table[model_to_index (idx)].m_state == cell::pending); } void pending (const QModelIndex& idx) { if (idx.isValid ()) m_table[model_to_index (idx)].m_state = cell::pending; } void notavail (int r, int c) { if (0 <= r && r < m_rows && 0 <= c && c <= columns ()) m_table[c * m_rows + r].m_state = cell::notavail; } bool requires_sub_editor (const QModelIndex& idx) { return (idx.isValid () && m_table[model_to_index (idx)].m_requires_sub_editor); } sub_editor_types sub_editor_type (const QModelIndex& idx) { return (idx.isValid () ? m_table[model_to_index (idx)].m_editor_type : sub_none); } void unset (int r, int c) { if (0 <= r && r < m_rows && 0 <= c && c <= columns ()) m_table[c * m_rows + r].m_state = cell::unset; } void clear (void) { for (int i = 0; i < m_table.size (); ++i) m_table[i].m_state = cell::unset; } QVariant data (const QModelIndex& idx, int role) const { if (idx.isValid ()) { const int i = model_to_index (idx); switch (role) { case Qt::DisplayRole: case Qt::EditRole: return m_table[i].m_data; case Qt::StatusTipRole: return m_table[i].m_status_tip; case Qt::ToolTipRole: return m_table[i].m_tool_tip; case Qt::BackgroundRole: return m_table[i].m_background; } } return QVariant (); } octave_idx_type rows (void) const { return m_rows; } octave_idx_type columns (void) const { return m_cols; } int model_to_index (const QModelIndex& idx) const { return idx.column () * m_rows + idx.row (); } impl (const QString& n, QLabel *l) : m_name (n.toStdString ()), m_type (), m_rows (0), m_cols (0), m_table (), m_label (l), m_validity (true), m_validtext () { } const std::string m_name; std::string m_type; octave_idx_type m_rows; octave_idx_type m_cols; QVector<cell> m_table; QLabel *m_label; bool m_validity; QString m_validtext; }; variable_editor_model::variable_editor_model (const QString& expr, QLabel *label, QObject *parent) : QAbstractTableModel (parent), m_parent (parent), m_d (new impl (expr, label)) { connect (this, SIGNAL (data_ready (int, int, const QString&, const QString&, int, int)), this, SLOT (received_data (int, int, const QString&, const QString&, int, int))); connect (this, SIGNAL (no_data (int, int)), this, SLOT (received_no_data (int, int))); connect (this, SIGNAL (unset_data (int, int)), this, SLOT (received_unset_data (int, int))); connect (this, SIGNAL (user_error (const QString&, const QString&)), this, SLOT (received_user_error (const QString&, const QString&))); connect (this, SIGNAL (initialize_data (const QString&, const QString&, int, int)), this, SLOT (received_initialize_data (const QString&, const QString&, int, int))); // Initializes everything. clear_data_cache (); } variable_editor_model::~variable_editor_model (void) { delete m_d; } int variable_editor_model::rowCount (const QModelIndex&) const { return m_d->m_validity ? m_d->rows () : 1; } int variable_editor_model::columnCount (const QModelIndex&) const { return m_d->m_validity ? m_d->columns () : 1; } QVariant variable_editor_model::data (const QModelIndex& idx, int role) const { if (! m_d->m_validity) { if (idx.isValid ()) { if (role == Qt::DisplayRole) return QVariant (QString ("Variable %d not found") .arg (QString::fromStdString (m_d->m_name))); } return QVariant (QString ("x")); } if (idx.isValid ()) { if (m_d->is_set (idx)) return m_d->data (idx, role); else { if (! m_d->is_pending (idx)) { octave_link::post_event<variable_editor_model, int, int, std::string> (const_cast<variable_editor_model *> (this), &variable_editor_model::get_data_oct, idx.row (), idx.column (), m_d->m_name); m_d->pending (idx); } if (role == Qt::DisplayRole) return QVariant (QString (m_d->is_notavail (idx) ? "⌛" : "✗")); else return QVariant (); } } // Invalid. return QVariant (); } bool variable_editor_model::setData (const QModelIndex& idx, const QVariant& v, int role) { if (idx.isValid () && role == Qt::EditRole) { if (v.type () != QVariant::String) { qDebug () << v.typeName () << " Expected String!"; return false; } octave_link::post_event<variable_editor_model, std::string, int, int, std::string> (this, &variable_editor_model::set_data_oct, m_d->m_name, idx.row (), idx.column (), v.toString ().toStdString ()); return true; } else return false; } Qt::ItemFlags variable_editor_model::flags (const QModelIndex& idx) const { if (m_d->m_validity) { if (requires_sub_editor (idx)) { if (editor_type (idx) != sub_string) return QAbstractTableModel::flags (idx); } return QAbstractTableModel::flags (idx) | Qt::ItemIsEditable; // FIXME: ??? // return requires_sub_editor(idx) ? QAbstractTableModel::flags (idx) : QAbstractTableModel::flags (idx) | Qt::ItemIsEditable; } return Qt::NoItemFlags; } bool variable_editor_model::insertRows (int row, int count, const QModelIndex&) { // FIXME: cells? octave_link::post_event <variable_editor_model, std::string, std::string> (this, &variable_editor_model::eval_oct, m_d->m_name, QString ("%1 = [ %1(1:%2,:) ; zeros(%3, columns(%1)) ; %1(%2+%3:end,:) ]") .arg (QString::fromStdString (m_d->m_name)) .arg (row) .arg (count) .toStdString ()); return true; } bool variable_editor_model::removeRows (int row, int count, const QModelIndex&) { if (row + count > m_d->m_rows) { qDebug () << "Tried to remove too many rows " << m_d->m_rows << " " << count << " (" << row << ")"; return false; } octave_link::post_event <variable_editor_model, std::string, std::string> (this, &variable_editor_model::eval_oct, m_d->m_name, QString ("%1(%2:%3, :) = []") .arg (QString::fromStdString (m_d->m_name)) .arg (row) .arg (row + count) .toStdString ()); return true; } bool variable_editor_model::insertColumns (int col, int count, const QModelIndex&) { octave_link::post_event <variable_editor_model, std::string, std::string> (this, &variable_editor_model::eval_oct, m_d->m_name, QString ("%1 = [ %1(:,1:%2) ; zeros(rows(%1), %3) %1(:,%2+%3:end) ]") .arg (QString::fromStdString (m_d->m_name)) .arg (col) .arg (count) .toStdString ()); return true; } bool variable_editor_model::removeColumns (int col, int count, const QModelIndex&) { if (col + count > m_d->m_cols) { qDebug () << "Tried to remove too many cols " << m_d->m_cols << " " << count << " (" << col << ")"; return false; } octave_link::post_event <variable_editor_model, std::string, std::string> (this, &variable_editor_model::eval_oct, m_d->m_name, QString ("%1(:, %2:%3) = []") .arg (QString::fromStdString (m_d->m_name)) .arg (col) .arg (col + count) .toStdString ()); return true; } void variable_editor_model::clear_data_cache (void) { octave_link::post_event (this, &variable_editor_model::init_from_oct, m_d->m_name); } bool variable_editor_model::requires_sub_editor (const QModelIndex& idx) const { return m_d->requires_sub_editor (idx); } bool variable_editor_model::editor_type_matrix (const QModelIndex& idx) const { return m_d->sub_editor_type (idx) == sub_matrix; } bool variable_editor_model::editor_type_string (const QModelIndex& idx) const { return m_d->sub_editor_type (idx) == sub_string; } QString variable_editor_model::subscript_expression (const QModelIndex& idx) const { return (QString (m_d->m_type == "{" ? "{%1, %2}" : "(%1, %2)") .arg (idx.row () + 1) .arg (idx.column () + 1)); } // Private slots. void variable_editor_model::received_data (int r, int c, const QString& dat, const QString& class_info, int rows, int cols) { // Trim data. const QString status_tip; const QString tool_tip = class_info + QString (": %1x%2").arg (rows).arg (cols); bool subedit = rows != 1 || cols != 1 || class_info == QString ("struct"); sub_editor_types edittype; if (! subedit) edittype = sub_none; else { if (class_info == QString ("char") && rows == 1) edittype = sub_string; else edittype = sub_matrix; } if (class_info == QString ("struct")) edittype = sub_struct; m_d->set (r, c, impl::cell (dat, status_tip, tool_tip, (rows > 1 || cols > 1 || class_info == QString ("struct")), edittype)); QModelIndex idx = QAbstractTableModel::index (r, c); emit dataChanged (idx, idx); } void variable_editor_model::received_no_data (int r, int c) { m_d->notavail (r, c); } void variable_editor_model::received_unset_data (int r, int c) { m_d->unset (r, c); } void variable_editor_model::received_user_error (const QString& title, const QString& msg) { QMessageBox::critical (nullptr, title, msg); } void variable_editor_model::received_initialize_data (const QString& class_name, const QString& paren, int rows, int cols) { if (! (m_d->m_validity)) return; m_d->m_type = paren.toStdString (); const int r = m_d->m_rows - rows; if (r > 0) emit beginRemoveRows (QModelIndex (), rows, m_d->m_rows - 1); else if (r < 0) emit beginInsertRows (QModelIndex (), m_d->m_rows, rows - 1); const int c = m_d->m_cols - cols; if (c > 0) emit beginRemoveColumns (QModelIndex (), cols, m_d->m_cols - 1); else if (c < 0) emit beginInsertColumns (QModelIndex (), m_d->m_cols, cols - 1); m_d->m_rows = rows; m_d->m_cols = cols; m_d->m_table.clear (); m_d->m_table.resize (rows * cols); if (c > 0) emit endRemoveColumns (); else if (c < 0) emit endInsertColumns (); if (r > 0) emit endRemoveRows (); else if (r < 0) emit endInsertRows (); emit dataChanged (QAbstractTableModel::index (0, 0), QAbstractTableModel::index (m_d->m_rows - 1, m_d->m_cols - 1)); m_d->m_label->setTextFormat (Qt::PlainText); QString description = (QString ("%1: %2 %3x%4") .arg (QString::fromStdString (m_d->m_name)) .arg (class_name) .arg (rows) .arg (cols)); m_d->m_label->setText (description); m_d->m_validtext = description; } // Private. void variable_editor_model::get_data_oct (const int& row, const int& col, const std::string& x) { int parse_status = 0; octave_value v = retrieve_variable (x, parse_status); // FIXME: What was the intent here? // eval_string (x, true, parse_status); // retrieve_variable (x, parse_status); // (symbol_exist (x, "var") > 0 // ? eval_string (x, true, parse_status) : octave_value ()); if (parse_status != 0 || ! v.is_defined ()) { emit no_data (row, col); m_d->m_validity = false; return; } octave_value_list ovlidx = ovl (row + 1, col + 1); /*const*/ octave_value elem = v.single_subsref (m_d->m_type, ovlidx); if (elem.is_defined ()) { std::stringstream ss; elem.print (ss, true); /*const*/ QString dat = QString::fromStdString (ss.str ()).trimmed (); const QString cname = QString::fromStdString (elem.class_name ()); // FIXME: This should not be necessary. if (dat == QString ("inf")) dat = "Inf"; if (dat == QString ("nan")) dat = "NaN"; emit data_ready (row, col, dat, cname, elem.rows (), elem.columns ()); } else emit no_data (row, col); } // val has to be copied! void variable_editor_model::set_data_oct (const std::string& x, const int& row, const int& col, const std::string& val) { m_d->m_validity = true; // Accessing directly since // 1) retrieve_variable does not support writeback, and // 2) we can be reasonably sure that this variable exists. int parse_status = 0; octave_value ret = octave::eval_string (val, true, parse_status); // FIXME: ??? // retrieve_variable(x, parse_status);//eval_string (val, true, parse_status); if (parse_status != 0 || ret.is_undefined ()) { emit user_error ("Invalid expression", QString ("Expression `%1' invalid") .arg (QString::fromStdString (val))); return; } parse_status = 0; octave_value v = retrieve_variable (x, parse_status); // FIXME: ??? // eval_string (x, true, parse_status); if (parse_status != 0 || ! v.is_defined ()) { m_d->m_validity = false; emit user_error ("Table invalid", QString ("Table expression `%1' invalid") .arg (QString::fromStdString (x))); return; } octave_value_list ovlidx = ovl (row + 1, col + 1); std::list<octave_value_list> idxl; idxl.push_back (ovlidx); v.subsasgn (m_d->m_type, idxl, ret); emit unset_data (row, col); QModelIndex idx = QAbstractTableModel::index (row, col); emit dataChanged (idx, idx); } void variable_editor_model::init_from_oct (const std::string& x) { int parse_status = 0; const octave_value ov = retrieve_variable (x, parse_status); // FIXME: What was the intent here? // eval_string (x, true, parse_status); m_d->m_validity = true; if (parse_status != 0 || ! ov.is_defined ()) { m_d->m_validity = false; display_invalid (); return; } // FIXME: Cell arrays? const QString class_name = QString::fromStdString (ov.class_name ()); const QString paren = ov.iscell () ? "{" : "("; const octave_idx_type rows = ov.rows (); const octave_idx_type cols = ov.columns (); display_valid (); emit initialize_data (class_name, paren, rows, cols); } void variable_editor_model::eval_oct (const std::string& name, const std::string& x) { int parse_status = 0; octave::eval_string (x, true, parse_status); if (parse_status != 0) emit user_error ("Evaluation failed", QString ("Evaluation of `%s' failed") .arg (QString::fromStdString (x))); init_from_oct (name); } // If the variable exists, load it into the data model. If it doesn't // exist, flag the data model as referring to a nonexistent variable. // This allows the variable to be opened before it is created. octave_value variable_editor_model::retrieve_variable (const std::string& x, int& parse_status) { std::string name = x; if (x.back () == ')' || x.back () == '}') name = x.substr (0, x.find (x.back () == ')' ? "(" : "{")); if (symbol_exist (name, "var") > 0) return octave::eval_string (x, true, parse_status); parse_status = -1; return octave_value (); } sub_editor_types variable_editor_model::editor_type (const QModelIndex& idx) const { return m_d->sub_editor_type (idx); } void variable_editor_model::display_invalid (void) { m_d->m_label->setTextFormat (Qt::PlainText); QString description = QString ("%1: [not found or out-of-scope]") .arg (QString::fromStdString (m_d->m_name)); m_d->m_label->setText (description); dynamic_cast<QWidget *> (m_parent)->setVisible (false); } void variable_editor_model::display_valid (void) { m_d->m_label->setTextFormat (Qt::PlainText); m_d->m_label->setText (m_d->m_validtext); dynamic_cast<QWidget *> (m_parent)->setVisible (true); }