Mercurial > octave
view libgui/src/variable-editor.cc @ 24021:46dc1ba54f7f
Free variable editor QMainWindow memory upon delete (bug #51954).
* variable-editor.cc (~variable_editor): Delete the m_main window from memory
and, consequently, m_tool_bar and m_tab_widget.
author | Daniel J Sebald <daniel.sebald@ieee.org> |
---|---|
date | Wed, 06 Sep 2017 21:54:31 -0500 |
parents | b0e696ea6487 |
children | ca4ab27152a9 |
line wrap: on
line source
/* Copyright (C) 2015 Michael Barnes Copyright (C) 2013 RĂ¼diger Sonderfeld Copyright (C) 2013 John W. Eaton 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 <http://www.gnu.org/licenses/>. */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include <algorithm> #include <limits> #include <QApplication> #include <QClipboard> #include <QDebug> #include <QFileDialog> #include <QHeaderView> #include <QLabel> #include <QMainWindow> #include <QMenu> #include <QPalette> #include <QSignalMapper> #include <QTableView> #include <QTabWidget> #include <QToolBar> #include <QToolButton> #include <QVBoxLayout> #include "octave-qt-link.h" #include "resource-manager.h" #include "variable-editor.h" #include "variable-editor-model.h" #include "operators/ops.h" #include "ov.h" namespace { // Helper struct to store widget pointers in "data" Tab property. struct table_data { table_data (QTableView *t = nullptr) : m_table (t) { } QTableView *m_table; }; table_data get_table_data (QTabWidget *w, int tidx) { return w->widget (tidx)->property ("data").value<table_data> (); } table_data get_table_data (QTabWidget *w) { return get_table_data (w, w->currentIndex ()); } } Q_DECLARE_METATYPE (table_data) static QString idx_to_expr (int32_t from, int32_t to) { if (from == to) return QString ("%1").arg (from + 1); else return QString ("%1:%2").arg (from + 1).arg (to + 1); } variable_editor::variable_editor (QWidget *p) : octave_dock_widget (p), m_main (new QMainWindow ()), m_tool_bar (new QToolBar (m_main)), m_tab_widget (new QTabWidget (m_main)), m_default_width (20), m_default_height (100), m_add_font_height (0), m_autofit (false), m_autofit_max (false), m_use_terminal_font (true), m_alternate_rows (true), m_stylesheet (""), m_font (), m_sel_font (), m_table_colors () { // Use a MainWindow setObjectName ("variable_editor"); set_title (tr ("Variable Editor")); setStatusTip (tr ("Edit variables.")); setWindowIcon (QIcon (":/actions/icons/logo.png")); // Tool Bar construct_tool_bar (); m_main->addToolBar (m_tool_bar); for (int i = 0; i < resource_manager::varedit_color_chars ().length (); i++) m_table_colors.append (QColor (Qt::white)); // Tab Widget m_tab_widget->setTabsClosable (true); m_tab_widget->setMovable (true); connect (m_tab_widget, SIGNAL (tabCloseRequested (int)), this, SLOT (closeTab (int))); m_main->setCentralWidget (m_tab_widget); // Main m_main->setParent (this); setWidget (m_main); connect (this, SIGNAL (command_requested (const QString&)), p, SLOT (execute_command_in_terminal (const QString&))); } variable_editor::~variable_editor (void) { // m_tool_bar and m_tab_widget are contained within m_main delete m_main; } void variable_editor::edit_variable (const QString& name) { if (m_stylesheet.isEmpty ()) { QSettings *settings = resource_manager::get_settings (); notice_settings (settings); } const int tab_count = m_tab_widget->count (); for (int i = 0; i < tab_count; ++i) if (m_tab_widget->tabText (i) == name) { m_tab_widget->setCurrentIndex (i); return; // already open } QWidget *page = new QWidget; // Do not set parent. QVBoxLayout *vbox = new QVBoxLayout (page); page->setLayout (vbox); QLabel *label = new QLabel (page); label->setTextFormat (Qt::PlainText); label->setText (name); vbox->addWidget (label); QTableView *table = new QTableView (page); variable_editor_model *model = new variable_editor_model (name, label, table); table->setModel (model); table->setWordWrap (false); table->setContextMenuPolicy (Qt::CustomContextMenu); table->setSelectionMode (QAbstractItemView::ContiguousSelection); table->horizontalHeader ()->setContextMenuPolicy (Qt::CustomContextMenu); table->verticalHeader ()->setContextMenuPolicy (Qt::CustomContextMenu); connect (table->horizontalHeader (), SIGNAL (customContextMenuRequested (const QPoint&)), this, SLOT (columnmenu_requested (const QPoint&))); connect (table->verticalHeader (), SIGNAL (customContextMenuRequested (const QPoint&)), this, SLOT (rowmenu_requested (const QPoint&))); connect (table, SIGNAL (customContextMenuRequested (const QPoint&)), this, SLOT (contextmenu_requested (const QPoint&))); connect (table, SIGNAL (doubleClicked (const QModelIndex&)), this, SLOT (double_click (const QModelIndex&))); connect (model, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)), this, SLOT (callUpdate (const QModelIndex&, const QModelIndex&))); vbox->addWidget (table); page->setProperty ("data", QVariant::fromValue (table_data (table))); int tab_idx = m_tab_widget->addTab (page, name); m_tab_widget->setCurrentIndex (tab_idx); if (m_tab_widget->count () == 1) m_tool_bar->setEnabled (true); // This is the first tab -> enable tool bar if (m_autofit) { table->resizeColumnsToContents (); if (m_autofit_max) { int mx = 0; for (int i = 0; i < table->model ()->columnCount (); i++) { if (table->columnWidth (i) > mx) mx = table->columnWidth (i); } table->horizontalHeader ()->setDefaultSectionSize (mx); } } else { table->horizontalHeader ()->setDefaultSectionSize (m_default_width); } table->setFont (m_font); table->setStyleSheet (m_stylesheet); table->setAlternatingRowColors (m_alternate_rows); #if defined (HAVE_QT4) table->verticalHeader ()->setResizeMode (QHeaderView::Interactive); #else table->verticalHeader ()->setSectionResizeMode (QHeaderView::Interactive); #endif table->verticalHeader ()->setDefaultSectionSize (m_default_height + m_add_font_height); } void variable_editor::clear_data_cache (void) { for (int i = 0; i < m_tab_widget->count (); ++i) { QTableView *const table = get_table_data (m_tab_widget, i).m_table; QAbstractItemModel *const model = table->model (); qobject_cast<variable_editor_model *> (model)->clear_data_cache (); } } bool variable_editor::has_focus (void) { // FIXME: This only generates exceptions in certain circumstances. // Get a definitive list and eliminate the need to handle exceptions. if (m_tab_widget->currentIndex () == -1) return false; // No tabs try { QTableView *view = get_table_data (m_tab_widget).m_table; if (view) return view->hasFocus (); return false; } catch (...) { return false; } return false; } QList<QColor> variable_editor::default_colors (void) { QList<QColor> colorlist; colorlist << qApp->palette ().color (QPalette::WindowText); colorlist << qApp->palette ().color (QPalette::Base); colorlist << qApp->palette ().color (QPalette::HighlightedText); colorlist << qApp->palette ().color (QPalette::Highlight); colorlist << qApp->palette ().color (QPalette::AlternateBase); return colorlist; } QStringList variable_editor::color_names (void) { QStringList output; output << "Foreground"; output << "Background"; output << "Selected Foreground"; output << "Selected Background"; output << "Alternate Background"; return output; } void variable_editor::callUpdate (const QModelIndex&, const QModelIndex&) { if (m_autofit) { QTableView *view = get_table_data (m_tab_widget).m_table; view->resizeColumnsToContents (); if (m_autofit_max) { int mx = 0; for (int i = 0; i < view->model ()->columnCount (); i++) { if (view->columnWidth (i) > mx) mx = view->columnWidth (i); } view->horizontalHeader ()->setDefaultSectionSize (mx); } } emit updated (); } void variable_editor::notice_settings (const QSettings *settings) { // FIXME: Why use object->tostring->toint? Why not just 100? m_default_width = settings->value ("variable_editor/column_width", QVariant ("100")).toString ().toInt (); m_autofit = settings->value ("variable_editor/autofit_column_width", QVariant (false)).toBool (); // FIXME: Magic Number 1 here, why not use enum? if (m_autofit) { if (settings->value ("variable_editor/autofit_type", 0).toInt () == 1) m_autofit_max = true; } m_default_height = settings->value ("variable_editor/row_height", QVariant ("10")).toString ().toInt (); m_alternate_rows = settings->value ("variable_editor/alternate_rows", QVariant (false)).toBool (); QList<QColor> _default_colors = resource_manager::varedit_default_colors (); QString class_chars = resource_manager::varedit_color_chars (); m_use_terminal_font = settings->value ("variable_editor/use_terminal_font", true).toBool (); QString font_name; int font_size; if (m_use_terminal_font) { font_name = settings->value ("terminal/fontName", "").toString (); font_size = settings->value ("terminal/fontSize", 10).toInt (); } else { font_name = settings->value ("variable_editor/font_name", "").toString (); font_size = settings->value ("variable_editor/font_size", 10).toInt (); } m_font = QFont (font_name, font_size); if (settings->value ("variable_editor/autofit_row_height", false).toBool ()) { QFontMetrics fm (m_font); m_add_font_height = fm.height (); } else m_add_font_height = 0; for (int i = 0; i < class_chars.length (); i++) { QVariant default_var = _default_colors.at (i); QColor setting_color = settings->value ("variable_editor/color_" + class_chars.mid (i, 1), default_var).value<QColor> (); m_table_colors.replace (i, setting_color); } update_colors (); // Icon size in the toolbar int icon_size_settings = settings->value ("toolbar_icon_size", 0).toInt (); QStyle *st = style (); int icon_size = st->pixelMetric (QStyle::PM_ToolBarIconSize); // FIXME: Magic numbers. Use enum? if (icon_size_settings == 1) icon_size = st->pixelMetric (QStyle::PM_LargeIconSize); else if (icon_size_settings == -1) icon_size = st->pixelMetric (QStyle::PM_SmallIconSize); m_tool_bar->setIconSize (QSize (icon_size, icon_size)); } void variable_editor::closeEvent (QCloseEvent *e) { emit finished (); octave_dock_widget::closeEvent (e); } void variable_editor::closeTab (int idx) { if (idx < 0 || idx > m_tab_widget->count ()) return; QWidget *const wdgt = m_tab_widget->widget (idx); m_tab_widget->removeTab (idx); delete wdgt; if (m_tab_widget->count () == 0) m_tool_bar->setEnabled (false); // This was the last tab, disable tool bar. } void variable_editor::contextmenu_requested (const QPoint& qpos) { QTableView *view = get_table_data (m_tab_widget).m_table; QModelIndex index = view->indexAt (qpos); if (index.isValid ()) { QMenu *menu = new QMenu (this); menu->addAction (resource_manager::icon ("edit-cut"), tr ("Cut"), this, SLOT (cutClipboard ())); menu->addAction (resource_manager::icon ("edit-copy"), tr ("Copy"), this, SLOT (copyClipboard ())); menu->addAction (resource_manager::icon ("edit-paste"), tr ("Paste"), this, SLOT (pasteClipboard ())); // FIXME: better icon for paste table? menu->addAction (resource_manager::icon ("edit-paste"), tr ("Paste Table"), this, SLOT (pasteTableClipboard ())); menu->addSeparator (); menu->addAction (resource_manager::icon ("edit-delete"), tr ("Clear"), this, SLOT (clearContent ())); menu->addAction (resource_manager::icon ("document-new"), tr ("Variable from Selection"), this, SLOT (createVariable ())); // FIXME: addAction for sort? menu->addAction ( //QIcon (), FIXME: Add icon for transpose tr ("Transpose"), this, SLOT (transposeContent ())); QItemSelectionModel *sel = view->selectionModel (); QList<QModelIndex> indices = sel->selectedIndexes (); if (! indices.isEmpty ()) { menu->addSeparator (); QSignalMapper *plot_mapper = new QSignalMapper (menu); plot_mapper->setMapping (menu->addAction ("plot", plot_mapper, SLOT (map ())), "figure (); plot (%1);"); plot_mapper->setMapping (menu->addAction ("bar", plot_mapper, SLOT (map ())), "figure (); bar (%1);"); plot_mapper->setMapping (menu->addAction ("stem", plot_mapper, SLOT (map ())), "figure (); stem (%1);"); plot_mapper->setMapping (menu->addAction ("stairs", plot_mapper, SLOT (map ())), "figure (); stairs (%1);"); plot_mapper->setMapping (menu->addAction ("area", plot_mapper, SLOT (map ())), "figure (); area (%1);"); plot_mapper->setMapping (menu->addAction ("pie", plot_mapper, SLOT (map ())), "figure (); pie (%1);"); plot_mapper->setMapping (menu->addAction ("hist", plot_mapper, SLOT (map ())), "figure (); hist (%1);"); connect (plot_mapper, SIGNAL (mapped (const QString&)), this, SLOT (relay_command (const QString&))); } menu->exec (view->mapToGlobal (qpos)); } } void variable_editor::columnmenu_requested (const QPoint& pt) { QTableView *view = get_table_data (m_tab_widget).m_table; int index = view->horizontalHeader ()->logicalIndexAt (pt); //emit command_requested (QString ("disp ('") + QString::number (index) + "');"); if (index < 0 || index > view->model ()->columnCount ()) return; QString selection = selected_to_octave (); QList<int> coords = octave_to_coords (selection); bool nothingSelected = false; if (coords.isEmpty ()) nothingSelected = true; bool whole_columns_selected = nothingSelected ? false : (coords[0] == 1 && coords[1] == view->model ()->rowCount ()); bool current_column_selected = nothingSelected ? false : (coords[2] <= index+1 && coords[3] > index); int column_selection_count = nothingSelected ? 0 : (coords[3] - coords[2] + 1); if (! whole_columns_selected || ! current_column_selected) { view->selectColumn (index); column_selection_count = 1; current_column_selected = true; whole_columns_selected = true; } QString column_string = tr (column_selection_count > 1 ? " columns" : " column"); QMenu *menu = new QMenu (this); menu->addAction (resource_manager::icon ("edit-cut"), tr ("Cut") + column_string, this, SLOT (cutClipboard ())); menu->addAction (resource_manager::icon ("edit-copy"), tr ("Copy") + column_string, this, SLOT (copyClipboard ())); menu->addAction (resource_manager::icon ("edit-paste"), tr ("Paste"), this, SLOT (pasteClipboard ())); // FIXME: better icon for Paste Table? menu->addAction (resource_manager::icon ("edit-paste"), tr ("Paste Table"), this, SLOT (pasteTableClipboard ())); menu->addSeparator (); menu->addAction (resource_manager::icon ("edit-delete"), tr ("Clear") + column_string, this, SLOT (clearContent ())); menu->addAction (resource_manager::icon ("edit-delete"), tr ("Delete") + column_string, this, SLOT (delete_selected ())); menu->addAction (resource_manager::icon ("document-new"), tr ("Variable from Selection"), this, SLOT (createVariable ())); menu->addSeparator (); QSignalMapper *plot_mapper = new QSignalMapper (menu); plot_mapper->setMapping (menu->addAction ("plot", plot_mapper, SLOT (map ())), "figure (); plot (%1);"); plot_mapper->setMapping (menu->addAction ("bar", plot_mapper, SLOT (map ())), "figure (); bar (%1);"); plot_mapper->setMapping (menu->addAction ("stem", plot_mapper, SLOT (map ())), "figure (); stem (%1);"); plot_mapper->setMapping (menu->addAction ("stairs", plot_mapper, SLOT (map ())), "figure (); stairs (%1);"); plot_mapper->setMapping (menu->addAction ("area", plot_mapper, SLOT (map ())), "figure (); area (%1);"); plot_mapper->setMapping (menu->addAction ("pie", plot_mapper, SLOT (map ())), "figure (); pie (%1);"); plot_mapper->setMapping (menu->addAction ("hist", plot_mapper, SLOT (map ())), "figure (); hist (%1);"); connect (plot_mapper, SIGNAL (mapped (const QString&)), this, SLOT (relay_command (const QString&))); QPoint menupos = pt; menupos.setY (view->horizontalHeader ()->height ()); menu->exec (view->mapToGlobal (menupos)); } void variable_editor::rowmenu_requested (const QPoint& pt) { QTableView *view = get_table_data (m_tab_widget).m_table; int index = view->verticalHeader ()->logicalIndexAt (pt); //emit command_requested (QString ("disp ('") + QString::number (index) + "');"); if (index < 0 || index > view->model ()->columnCount ()) return; QString selection = selected_to_octave (); QList<int> coords = octave_to_coords (selection); bool nothingSelected; if (coords.isEmpty ()) nothingSelected = true; else nothingSelected = false; bool whole_rows_selected = nothingSelected ? false : (coords[2] == 1 && coords[3] == view->model ()->columnCount ()); bool current_row_selected = nothingSelected ? false : (coords[0] <= index+1 && coords[1] > index); int rowselection_count = nothingSelected ? 0 : (coords[3] - coords[2] + 1); if (! whole_rows_selected || ! current_row_selected) { view->selectRow (index); rowselection_count = 1; current_row_selected = true; whole_rows_selected = true; } QString row_string = tr (rowselection_count > 1 ? " rows" : " row"); QMenu *menu = new QMenu (this); menu->addAction (resource_manager::icon ("edit-cut"), tr ("Cut") + row_string, this, SLOT (cutClipboard ())); menu->addAction (resource_manager::icon ("edit-copy"), tr ("Copy") + row_string, this, SLOT (copyClipboard ())); menu->addAction (resource_manager::icon ("edit-paste"), tr ("Paste"), this, SLOT (pasteClipboard ())); // FIXME: better icon for Paste Table? menu->addAction (resource_manager::icon ("edit-paste"), tr ("Paste Table"), this, SLOT (pasteTableClipboard ())); menu->addSeparator (); menu->addAction (resource_manager::icon ("edit-delete"), tr ("Clear") + row_string, this, SLOT (clearContent ())); menu->addAction (resource_manager::icon ("edit-delete"), tr ("Delete") + row_string, this, SLOT (delete_selected ())); menu->addAction (resource_manager::icon ("document-new"), tr ("Variable from Selection"), this, SLOT (createVariable ())); menu->addSeparator (); QSignalMapper *plot_mapper = new QSignalMapper (menu); plot_mapper->setMapping (menu->addAction ("plot", plot_mapper, SLOT (map ())), "figure (); plot (%1);"); plot_mapper->setMapping (menu->addAction ("bar", plot_mapper, SLOT (map ())), "figure (); bar (%1);"); plot_mapper->setMapping (menu->addAction ("stem", plot_mapper, SLOT (map ())), "figure (); stem (%1);"); plot_mapper->setMapping (menu->addAction ("stairs", plot_mapper, SLOT (map ())), "figure (); stairs (%1);"); plot_mapper->setMapping (menu->addAction ("area", plot_mapper, SLOT (map ())), "figure (); area (%1);"); plot_mapper->setMapping (menu->addAction ("pie", plot_mapper, SLOT (map ())), "figure (); pie (%1);"); plot_mapper->setMapping (menu->addAction ("hist", plot_mapper, SLOT (map ())), "figure (); hist (%1);"); connect (plot_mapper, SIGNAL (mapped (const QString&)), this, SLOT (relay_command (const QString&))); QPoint menupos = pt; menupos.setX (view->verticalHeader ()->width ()); //setY (view->verticalHeader ()->sectionPosition (index+1) + // view->verticalHeader ()->sectionSize (index)); menu->exec (view->mapToGlobal (menupos)); } void variable_editor::double_click (const QModelIndex& idx) { QString name = m_tab_widget->tabText (m_tab_widget->currentIndex ()); QTableView *const table = get_table_data (m_tab_widget).m_table; variable_editor_model *const model = qobject_cast<variable_editor_model *> (table->model ()); if (model->requires_sub_editor (idx)) { if (model ->editor_type_matrix (idx)) edit_variable (name + model->parens () .arg (idx.row () + 1) .arg (idx.column () + 1)); /* emit command_requested ("openvar ('" + name + model->parens () .arg (idx.row () + 1) .arg (idx.column () + 1) + "');"); */ } } void variable_editor::save (void) { QString name = m_tab_widget->tabText (m_tab_widget->currentIndex ()); QString file = QFileDialog::getSaveFileName (this, tr ("Save Variable %1 As").arg (name), ".", 0, 0, QFileDialog::DontUseNativeDialog); // FIXME: Type? binary, float-binary, ascii, text, hdf5, matlab format? if (! file.isEmpty ()) // FIXME: Use octave_value::save_*? emit command_requested (QString ("save ('%1', '%2');") .arg (file) .arg (name)); } void variable_editor::clearContent (void) { // FIXME: shift? QTableView *view = get_table_data (m_tab_widget).m_table; QAbstractItemModel *model = view->model (); QItemSelectionModel *sel = view->selectionModel (); QList<QModelIndex> indices = sel->selectedIndexes (); for (const auto& idx : indices) model->setData (idx, QVariant ("0")); // FIXME: Use [] for empty cells } void variable_editor::cutClipboard (void) { if (! has_focus ()) return; copyClipboard (); clearContent (); } void variable_editor::copyClipboard (void) { if (! has_focus ()) return; QTableView *view = get_table_data (m_tab_widget).m_table; QAbstractItemModel *model = view->model (); QItemSelectionModel *sel = view->selectionModel (); QList<QModelIndex> indices = sel->selectedIndexes (); qSort (indices); if (indices.isEmpty ()) return; // Convert selected items into TSV format and copy that. // Spreadsheet tools should understand that. QModelIndex previous = indices.first (); QString copy = model->data (previous).toString (); indices.removeFirst (); foreach (QModelIndex idx, indices) { copy.push_back (previous.row () != idx.row () ? '\n' : '\t'); copy.append (model->data (idx).toString ()); previous = idx; } copy.push_back ('\n'); QClipboard *clipboard = QApplication::clipboard (); clipboard->setText (copy); } void variable_editor::pasteClipboard (void) { // FIXME: ??? if (! has_focus ()) return; QClipboard *clipboard = QApplication::clipboard (); QString text = clipboard->text (); QTableView *view = get_table_data (m_tab_widget).m_table; QItemSelectionModel *sel = view->selectionModel (); QList<QModelIndex> indices = sel->selectedIndexes (); variable_editor_model *model = static_cast<variable_editor_model *> (view->model ()); if (indices.isEmpty ()) { if (view->size () == QSize (1,1)) { model->setData (view->model ()->index (0,0), text.toDouble ()); } else if (view->size () == QSize (0,0)) { model->insertColumn (0); model->insertRow (0); model->setData (view->model ()->index (0,0), text.toDouble ()); } } else { for (int i = 0; i < indices.size (); i++) view->model ()->setData (indices[i], text.toDouble ()); } emit updated (); } void variable_editor::pasteTableClipboard (void) { if (! has_focus ()) return; QClipboard *clipboard = QApplication::clipboard (); QString text = clipboard->text (); QTableView *view = get_table_data (m_tab_widget).m_table; QItemSelectionModel *sel = view->selectionModel (); QList<QModelIndex> indices = sel->selectedIndexes (); variable_editor_model *model = static_cast<variable_editor_model *> (view->model ()); QPoint start, end; QPoint tabsize = QPoint (model->rowCount (), model->columnCount ()); if (indices.isEmpty ()) { start = QPoint (0,0); end = tabsize; } else if (indices.size () == 1) { start = QPoint (indices[0].row (), indices[0].column ()); end = tabsize; } else { end = QPoint (0,0); start = tabsize; for (int i = 0; i < indices.size (); i++) { if (indices[i].column () < start.y ()) start.setY (indices[i].column ()); if (indices[i].column () > end.y ()) end.setY (indices[i].column ()); if (indices[i].row () < start.x ()) start.setX (indices[i].column ()); if (indices[i].row () > end.x ()) end.setX (indices[i].column ()); } } int rownum = 0; int colnum = 0; QStringList rows = text.split ('\n'); for (const auto& row : rows) { if (rownum > end.x () - start.x ()) continue; QStringList cols = row.split ('\t'); if (cols.isEmpty ()) continue; for (const auto& col : cols) { if (col.isEmpty ()) continue; if (colnum > end.y () - start.y () ) continue; model->setData (model->index (rownum + start.x (), colnum + start.y ()), QVariant (col)); // relay_command ("disp ('" + QString::number (colnum+start.y ()) + "," + QString::number (rownum+start.x ()) +"');"); colnum++; } colnum = 0; rownum++; } emit updated (); } void variable_editor::createVariable (void) { // FIXME: unnamed1..n if exist ('unnamed', 'var') relay_command ("unnamed = %1"); } void variable_editor::transposeContent (void) { QString name = m_tab_widget->tabText (m_tab_widget->currentIndex ()); emit command_requested (QString ("%1 = %1';").arg (name)); emit updated (); } void variable_editor::up (void) { QString name = m_tab_widget->tabText (m_tab_widget->currentIndex ()); // FIXME: is there a better way? if (name.endsWith (')') || name.endsWith ('}')) { qDebug () << "up"; name.remove (QRegExp ("(\\(|\\{)[^({]*(\\)|\\})$")); edit_variable (name); //emit command_requested (QString ("openvar ('%1');").arg (name)); } } void variable_editor::delete_selected (void) { QTableView *view = get_table_data (m_tab_widget).m_table; QString selection = selected_to_octave (); QList<int> coords = octave_to_coords (selection); if (coords.isEmpty ()) return; bool whole_columns_selected = coords[0] == 1 && coords[1] == view->model ()->rowCount (); bool whole_rows_selected = coords[2] == 1 && coords[3] == view->model ()->columnCount (); emit command_requested (QString ("disp ('") + QString::number (coords[0]) + "," + QString::number (coords[1]) + "," + QString::number (coords[2]) + "," + QString::number (coords[3]) + "');"); // Must be deleting whole columns or whole rows, and not the whole thing. if (whole_columns_selected == whole_rows_selected) // all or nothing return; if (whole_rows_selected) view->model ()->removeRows (coords[0], coords[1] - coords[0]); if (whole_columns_selected) view->model ()->removeColumns (coords[2], coords[3] - coords[2]); emit updated (); } void variable_editor::relay_command (const QString& cmd) { emit command_requested (cmd.arg (selected_to_octave ())); } QList<int> variable_editor::octave_to_coords (QString& selection) { // FIXME: Is this necessary or would it be quicker to clone the function that // gives us the QString? // sanity check if (selection.count (",") != 1) return QList<int> (); QList<int> output; output.clear (); // remove braces int firstbracket = std::max (selection.indexOf ("("), selection.indexOf ("{")); selection = selection.mid (firstbracket + 1, selection.length () - (firstbracket + 2)); QString rows = selection.left (selection.indexOf (",")); if (! rows.contains (":")) { // Only one row output.push_back (rows.toInt ()); output.push_back (output.last ()); } else { output.push_back (rows.left (rows.indexOf (":")).toInt ()); output.push_back (rows.right (rows.length () - (rows.indexOf (":") + 1)) .toInt ()); } QString cols; cols = selection.right (selection.length () - (selection.indexOf (",") + 1)); if (cols.left (1) == " ") cols = cols.right (cols.length () - 1); if (! cols.contains (":")) { // Only one row output.push_back (cols.toInt ()); output.push_back (output.last ()); } else { output.push_back (cols.left (cols.indexOf (":")).toInt ()); output.push_back (cols.right (cols.length () - (cols.indexOf (":") + 1)) .toInt ()); } return output; } QString variable_editor::selected_to_octave (void) { QString name = m_tab_widget->tabText (m_tab_widget->currentIndex ()); QTableView *view = get_table_data (m_tab_widget).m_table; QItemSelectionModel *sel = view->selectionModel (); if (! sel->hasSelection ()) return name; // Nothing selected QList<QModelIndex> indices = sel->selectedIndexes (); // it's indices! int32_t from_row = std::numeric_limits<int32_t>::max (); int32_t to_row = 0; int32_t from_col = std::numeric_limits<int32_t>::max (); int32_t to_col = 0; for (const auto& idx : indices) { from_row = std::min (from_row, idx.row ()); to_row = std::max (to_row, idx.row ()); from_col = std::min (from_col, idx.column ()); to_col = std::max (to_col, idx.column ()); } QString rows = idx_to_expr (from_row, to_row); QString cols = idx_to_expr (from_col, to_col); // FIXME: Cells? return QString ("%1 (%2, %3)").arg (name).arg (rows).arg (cols); } /// Also updates the font void variable_editor::update_colors (void) { m_stylesheet = ""; m_stylesheet += "QTableView::item{ foreground-color: " + m_table_colors[0].name () +" }"; m_stylesheet += "QTableView::item{ background-color: " + m_table_colors[1].name () +" }"; m_stylesheet += "QTableView::item{ selection-color: " + m_table_colors[2].name () +" }"; m_stylesheet += "QTableView::item:selected{ background-color: " + m_table_colors[3].name () +" }"; if (m_table_colors.length () > 4 && m_alternate_rows) { m_stylesheet += "QTableView::item:alternate{ background-color: " + m_table_colors[4].name () +" }"; m_stylesheet += "QTableView::item:alternate:selected{ background-color: " + m_table_colors[3].name () +" }"; } if (m_tab_widget->count () < 1) return; for (int i=0; i < m_tab_widget->count (); i++) { QTableView *view = get_table_data (m_tab_widget).m_table; view->setAlternatingRowColors (m_alternate_rows); view->setStyleSheet (m_stylesheet); view->setFont (m_font); } } void variable_editor::construct_tool_bar (void) { m_tool_bar->setObjectName ("VariableEditorToolBar"); m_tool_bar->setWindowTitle (tr ("Variable Editor Toolbar")); m_tool_bar->addAction (resource_manager::icon ("document-save"), tr ("Save"), this, SLOT (save ())); m_tool_bar->addSeparator (); m_tool_bar->addAction (resource_manager::icon ("edit-cut"), tr ("Cut"), this, SLOT (cutClipboard ())); m_tool_bar->addAction (resource_manager::icon ("edit-copy"), tr ("Copy"), this, SLOT (copyClipboard ())); m_tool_bar->addAction (resource_manager::icon ("edit-paste"), tr ("Paste"), this, SLOT (pasteClipboard ())); // FIXME: Different icon for Paste Table? m_tool_bar->addAction (resource_manager::icon ("edit-paste"), tr ("Paste Table"), this, SLOT (pasteTableClipboard ())); m_tool_bar->addSeparator (); // FIXME: Add a print item? //QAction *print_action; /icons/fileprint.png //m_tool_bar->addSeparator (); QToolButton *plot_tool_button = new QToolButton (m_tool_bar); plot_tool_button->setText (tr ("Plot")); plot_tool_button->setIcon (resource_manager::icon ("applications-system")); plot_tool_button->setPopupMode (QToolButton::InstantPopup); QMenu *plot_menu = new QMenu (tr ("Plot"), plot_tool_button); plot_menu->setSeparatorsCollapsible (false); QSignalMapper *plot_mapper = new QSignalMapper (plot_menu); plot_mapper->setMapping (plot_menu->addAction ("plot", plot_mapper, SLOT (map ())), "figure (); plot (%1);"); plot_mapper->setMapping (plot_menu->addAction ("bar", plot_mapper, SLOT (map ())), "figure (); bar (%1);"); plot_mapper->setMapping (plot_menu->addAction ("stem", plot_mapper, SLOT (map ())), "figure (); stem (%1);"); plot_mapper->setMapping (plot_menu->addAction ("stairs", plot_mapper, SLOT (map ())), "figure (); stairs (%1);"); plot_mapper->setMapping (plot_menu->addAction ("area", plot_mapper, SLOT (map ())), "figure (); area (%1);"); plot_mapper->setMapping (plot_menu->addAction ("pie", plot_mapper, SLOT (map ())), "figure (); pie (%1);"); plot_mapper->setMapping (plot_menu->addAction ("hist", plot_mapper, SLOT (map ())), "figure (); hist (%1);"); connect (plot_mapper, SIGNAL (mapped (const QString&)), this, SLOT (relay_command (const QString&))); plot_tool_button->setMenu (plot_menu); m_tool_bar->addWidget (plot_tool_button); m_tool_bar->addSeparator (); m_tool_bar->addAction (QIcon (resource_manager::icon ("go-up")), tr ("Up"), this, SLOT (up ())); m_tool_bar->setEnabled (false); // Disabled when no tab is present }