# HG changeset patch # User Michael Barnes # Date 1495210548 -7200 # Fri May 19 18:15:48 2017 +0200 # Node ID 98d1174516392f9087ba60d2877665bf5caac7e8 # Parent 528b124e337f41848a6ced61e706dffa4fbfba3a variable-editor patch update to Octave-4.3.0+ July 12, 2017 This patch is the work of Rüdiger Sonderfeld Philip Nienhuis Michael Barnes jwe diff --git a/libgui/src/main-window.cc b/libgui/src/main-window.cc --- a/libgui/src/main-window.cc +++ b/libgui/src/main-window.cc @@ -151,9 +151,9 @@ main_window::main_window (QWidget *p, oc m_interpreter (new octave_interpreter (app_context)), m_main_thread (new QThread ()), _workspace_model (0), status_bar (0), command_window (0), history_window (0), file_browser_window (0), - doc_browser_window (0), editor_window (0), workspace_window (0), - _settings_dlg (0), find_files_dlg (0), release_notes_window (0), - community_news_window (0), _octave_qt_link (0), + doc_browser_window (0), editor_window (0),variable_editor_window (0), + workspace_window (0), _settings_dlg (0), find_files_dlg (0), + release_notes_window (0), community_news_window (0), _octave_qt_link (0), _clipboard (QApplication::clipboard ()), _prevent_readline_conflicts (true), _suppress_dbg_location (true), @@ -169,6 +169,7 @@ main_window::main_window (QWidget *p, oc file_browser_window = new files_dock_widget (this); doc_browser_window = new documentation_dock_widget (this); editor_window = create_default_editor (this); + variable_editor_window = new variable_editor (this); workspace_window = new workspace_view (this); } @@ -230,6 +231,11 @@ main_window::~main_window (void) delete history_window; delete status_bar; delete _workspace_model; + if(variable_editor_window) + { + delete variable_editor_window; + variable_editor_window = 0; + } delete m_interpreter; delete m_main_thread; if (find_files_dlg) @@ -1301,7 +1307,7 @@ main_window::selectAll (void) // is not a global variable and not accessible for connecting. void -main_window::connect_uiwidget_links (void) +main_window::connect_uiwidget_links () { connect (&uiwidget_creator, SIGNAL (create_dialog (const QString&, const QString&, @@ -1677,11 +1683,26 @@ main_window::construct (void) connect (_workspace_model, SIGNAL (model_changed (void)), workspace_window, SLOT (handle_model_changed (void))); + connect (_octave_qt_link, + SIGNAL (open_variable (const QString&)), + this, + SLOT (edit_variable (const QString&))); + + connect (_octave_qt_link, + SIGNAL (refresh_variable_editor()), + this, + SLOT (clear_variable_editor_cache())); + connect (_workspace_model, SIGNAL (rename_variable (const QString&, const QString&)), this, SLOT (handle_rename_variable_request (const QString&, - const QString&))); + const QString&))); + + connect (variable_editor_window, + SIGNAL (updated()), + this, + SLOT (variable_editor_callback ())); connect (command_window, SIGNAL (interrupt_signal (void)), this, SLOT (interrupt_interpreter (void))); @@ -1752,6 +1773,8 @@ main_window::construct (void) addDockWidget (Qt::RightDockWidgetArea, editor_window); tabifyDockWidget (command_window, editor_window); #endif + addDockWidget (Qt::TopDockWidgetArea, variable_editor_window); + tabifyDockWidget (command_window, variable_editor_window); addDockWidget (Qt::LeftDockWidgetArea, file_browser_window); addDockWidget (Qt::LeftDockWidgetArea, workspace_window); @@ -2283,6 +2306,9 @@ main_window::construct_window_menu (QMen _show_documentation_action = construct_window_menu_item (window_menu, tr ("Show Documentation"), true, doc_browser_window); + _show_variable_editor_action = construct_window_menu_item + (window_menu, tr ("Show Variable Editor"), true, variable_editor_window); + window_menu->addSeparator (); _command_window_action = construct_window_menu_item @@ -2303,6 +2329,9 @@ main_window::construct_window_menu (QMen _documentation_action = construct_window_menu_item (window_menu, tr ("Documentation"), false, doc_browser_window); + _variable_editor_action = construct_window_menu_item + (window_menu, tr ("Documentation"), false, variable_editor_window); + window_menu->addSeparator (); _reset_windows_action = add_action (window_menu, QIcon (), @@ -2373,6 +2402,7 @@ main_window::construct_tool_bar (void) _main_tool_bar->addAction (_copy_action); _main_tool_bar->addAction (_paste_action); + _main_tool_bar->addAction (_undo_action); _main_tool_bar->addSeparator (); @@ -2416,6 +2446,23 @@ main_window::construct_tool_bar (void) } void +main_window::variable_editor_callback() +{ + // Called when the variable editor makes changes. + octave_link::post_event(this, &main_window::force_refresh_workspace); +} + +void +main_window::force_refresh_workspace() +{ + octave::symbol_table::scope *scope + = octave::__get_current_scope__ ("main_window::load_workspace_callback"); + + if (scope) + octave_link::set_workspace (true, scope->workspace_info (), false); +} + +void main_window::save_workspace_callback (const std::string& file) { Fsave (ovl (file)); @@ -2602,6 +2649,7 @@ main_window::configure_shortcuts () "main_window:file_browser"); shortcut_manager::set_shortcut (_editor_action, "main_window:editor"); shortcut_manager::set_shortcut (_documentation_action, "main_window:doc"); + shortcut_manager::set_shortcut (_variable_editor_action, "main_window:variable_editor"); shortcut_manager::set_shortcut (_reset_windows_action, "main_window:reset"); // help menu @@ -2687,6 +2735,8 @@ main_window::dock_widget_list () list.append (static_cast (editor_window)); #endif list.append (static_cast (workspace_window)); + //if (variable_editor_window) + list.append (static_cast (variable_editor_window)); return list; } @@ -2729,6 +2779,25 @@ main_window::clear_clipboard () } void +main_window::edit_variable (const QString &expr) +{ + variable_editor_window->edit_variable (expr); + + if (! variable_editor_window->isVisible ()) + { + variable_editor_window->show (); + variable_editor_window->raise (); + } + +} + +void +main_window::clear_variable_editor_cache () +{ + variable_editor_window->clear_data_cache (); +} + +void main_window::interrupt_interpreter (void) { m_interpreter->interrupt (); diff --git a/libgui/src/main-window.h b/libgui/src/main-window.h --- a/libgui/src/main-window.h +++ b/libgui/src/main-window.h @@ -57,6 +57,7 @@ along with Octave; see the file COPYING. #include "octave-qt-link.h" #include "resource-manager.h" #include "terminal-dock-widget.h" +#include "variable-editor.h" #include "thread-manager.h" #include "workspace-model.h" #include "workspace-view.h" @@ -272,6 +273,15 @@ private slots: void set_file_encoding (const QString& new_encoding); void request_open_files (const QStringList& open_file_names); + // open variable_editor + void + edit_variable (const QString &name); + + void + clear_variable_editor_cache (); + + void variable_editor_callback(); + protected: void closeEvent (QCloseEvent *closeEvent); @@ -340,6 +350,10 @@ private: QThread *m_main_thread; + bool confirm_exit_octave (); + + void force_refresh_workspace(); + workspace_model *_workspace_model; QHash _hash_menu_text; @@ -354,6 +368,7 @@ private: documentation_dock_widget *doc_browser_window; file_editor_interface *editor_window; workspace_view *workspace_window; + variable_editor *variable_editor_window; external_editor_interface *_external_editor; QWidget *_active_editor; @@ -399,12 +414,14 @@ private: QAction *_show_file_browser_action; QAction *_show_editor_action; QAction *_show_documentation_action; + QAction *_show_variable_editor_action; QAction *_command_window_action; QAction *_history_action; QAction *_workspace_action; QAction *_file_browser_action; QAction *_editor_action; QAction *_documentation_action; + QAction *_variable_editor_action; QAction *_reset_windows_action; QAction *_ondisk_doc_action; diff --git a/libgui/src/module.mk b/libgui/src/module.mk --- a/libgui/src/module.mk +++ b/libgui/src/module.mk @@ -114,6 +114,8 @@ OCTAVE_GUI_SRC_MOC = \ %reldir%/moc-welcome-wizard.cc \ %reldir%/moc-workspace-model.cc \ %reldir%/moc-workspace-view.cc \ + %reldir%/moc-variable-editor.cc \ + %reldir%/moc-variable-editor-model.cc \ %reldir%/moc-find-files-dialog.cc \ %reldir%/moc-find-files-model.cc \ %reldir%/qtinfo/moc-parser.cc \ @@ -170,7 +172,9 @@ noinst_HEADERS += \ %reldir%/find-files-dialog.h \ %reldir%/find-files-model.h \ %reldir%/workspace-model.h \ - %reldir%/workspace-view.h + %reldir%/workspace-view.h \ + %reldir%/variable-editor.h \ + %reldir%/variable-editor-model.h %canon_reldir%_%canon_reldir%_la_SOURCES = \ %reldir%/dialog.cc \ @@ -201,7 +205,9 @@ noinst_HEADERS += \ %reldir%/find-files-dialog.cc \ %reldir%/find-files-model.cc \ %reldir%/workspace-model.cc \ - %reldir%/workspace-view.cc + %reldir%/workspace-view.cc \ + %reldir%/variable-editor.cc \ + %reldir%/variable-editor-model.cc nodist_%canon_reldir%_%canon_reldir%_la_SOURCES = \ $(octave_gui_MOC) \ diff --git a/libgui/src/octave-qt-link.cc b/libgui/src/octave-qt-link.cc --- a/libgui/src/octave-qt-link.cc +++ b/libgui/src/octave-qt-link.cc @@ -429,7 +429,8 @@ octave_qt_link::do_execute_command_in_te void octave_qt_link::do_set_workspace (bool top_level, bool debug, - const std::list& ws) + const std::list& ws, + const bool& update_variable_editor) { if (! top_level && ! debug) return; @@ -457,6 +458,9 @@ octave_qt_link::do_set_workspace (bool t emit set_workspace_signal (top_level, debug, scopes, symbols, class_names, dimensions, values, complex_flags); + + if (update_variable_editor) + emit refresh_variable_editor(); } void @@ -632,6 +636,12 @@ octave_qt_link::do_show_preferences () } void +octave_qt_link::do_openvar (const std::string &expr) +{ + emit open_variable (QString::fromStdString (expr)); +} + +void octave_qt_link::do_show_doc (const std::string& file) { emit show_doc_signal (QString::fromStdString (file)); diff --git a/libgui/src/octave-qt-link.h b/libgui/src/octave-qt-link.h --- a/libgui/src/octave-qt-link.h +++ b/libgui/src/octave-qt-link.h @@ -105,7 +105,8 @@ public: void do_execute_command_in_terminal (const std::string& command); void do_set_workspace (bool top_level, bool debug, - const std::list& ws); + const std::list& ws, + const bool& variable_editor_too = true); void do_clear_workspace (void); @@ -138,6 +139,7 @@ public: void update_directory (void); + void do_openvar (const std::string &name); private: bool _shutdown_confirm_result; @@ -192,6 +194,10 @@ signals: void show_doc_signal (const QString& file); + void open_variable (const QString &name); + + void refresh_variable_editor(); + void confirm_shutdown_signal (void); }; diff --git a/libgui/src/resource-manager.cc b/libgui/src/resource-manager.cc --- a/libgui/src/resource-manager.cc +++ b/libgui/src/resource-manager.cc @@ -45,6 +45,7 @@ along with Octave; see the file COPYING. #include "QTerminal.h" #include "workspace-model.h" +#include "variable-editor.h" #include "resource-manager.h" resource_manager *resource_manager::instance = nullptr; @@ -319,6 +320,18 @@ resource_manager::terminal_default_color return QTerminal::default_colors (); } +QList +resource_manager::varedit_default_colors(void) +{ + return variable_editor::default_colors (); +} + +QStringList +resource_manager::varedit_color_names(void) +{ + return variable_editor::color_names (); +} + QIcon resource_manager::do_icon (const QString& icon_name, bool fallback) { diff --git a/libgui/src/resource-manager.h b/libgui/src/resource-manager.h --- a/libgui/src/resource-manager.h +++ b/libgui/src/resource-manager.h @@ -118,6 +118,10 @@ public slots: static void cleanup_instance (void) { delete instance; instance = 0; } + static QString varedit_color_chars (void) {return "fbsha"; } + static QStringList varedit_color_names (void); + static QList varedit_default_colors (void); + private: static bool instance_ok (void); diff --git a/libgui/src/settings-dialog.cc b/libgui/src/settings-dialog.cc --- a/libgui/src/settings-dialog.cc +++ b/libgui/src/settings-dialog.cc @@ -26,6 +26,7 @@ along with Octave; see the file COPYING. #include "resource-manager.h" #include "shortcut-manager.h" +#include "variable-editor.h" #include "workspace-model.h" #include "settings-dialog.h" #include "ui-settings-dialog.h" @@ -576,6 +577,21 @@ settings_dialog::settings_dialog (QWidge // terminal colors read_terminal_colors (settings); + // variable editor + ui->varedit_columnWidth->setText(settings->value("variable_editor/column_width","100").toString()); + ui->varedit_autoFitColumnWidth->setChecked(settings->value("variable_editor/autofit_column_width",false).toBool()); + ui->varedit_autofitType->setCurrentIndex(settings->value("autofit_type",0).toInt()); + ui->varedit_rowHeight->setText(settings->value("variable_editor/row_height","2").toString()); + ui->varedit_rowAutofit->setChecked(settings->value("variable_editor/autofit_row_height",true).toBool()); + ui->varedit_font->setFont(QFont(settings->value("variable_editor/font",settings->value("terminal/FontName","Courier New")).toString())); + ui->varedit_fontSize->setValue(settings->value("variable_editor/font_size",QVariant(10)).toInt()); + ui->varedit_useTerminalFont->setChecked(settings->value("variable_editor/use_terminal_font",false).toBool()); + ui->varedit_alternate->setChecked(settings->value("variable_editor/alternate_rows",QVariant(false)).toBool()); + ui->varedit_toolbarSize->setValue(settings->value("variable_editor/toolbar_size",24).toInt()); + + // variable editor colors + read_varedit_colors(settings); + // shortcuts ui->cb_prevent_readline_conflicts->setChecked ( @@ -739,6 +755,64 @@ settings_dialog::read_terminal_colors (Q } void +settings_dialog::write_varedit_colors (QSettings *settings) +{ + QString class_chars = resource_manager::varedit_color_chars (); + color_picker *color; + + for (int i = 0; i < class_chars.length (); i++) + { + color = ui->varedit_colors_box->findChild ( + "varedit_color_"+class_chars.mid (i,1)); + if (color) + settings->setValue ("variable_editor/color_"+class_chars.mid (i,1), + color->color ()); + } + settings->sync (); +} + + +void +settings_dialog::read_varedit_colors (QSettings *settings) +{ + QList default_colors = variable_editor::default_colors (); + QStringList class_names = variable_editor::color_names (); + QString class_chars = resource_manager::varedit_color_chars (); + int nr_of_classes = class_chars.length (); + + QGridLayout *style_grid = new QGridLayout (); + QVector description (nr_of_classes); + QVector color (nr_of_classes); + + int column = 0; + int row = 0; + for (int i = 0; i < nr_of_classes; i++) + { + description[i] = new QLabel (" " + class_names.at (i)); + description[i]->setAlignment (Qt::AlignRight); + QVariant default_var = default_colors.at (i); + QColor setting_color = settings->value ("variable_editor/color_" + + class_chars.mid (i,1), + default_var).value (); + color[i] = new color_picker (setting_color); + color[i]->setObjectName ("varedit_color_"+class_chars.mid (i, 1)); + color[i]->setMinimumSize (30, 10); + style_grid->addWidget (description[i], row, 2*column); + style_grid->addWidget (color[i], row, 2*column+1); + if (++column == 2) + { + style_grid->setColumnStretch (3*column, 10); + row++; + column = 0; + } + } + + // place grid with elements into the tab + ui->varedit_colors_box->setLayout (style_grid); + +} + +void settings_dialog::write_changed_settings (bool closing) { QSettings *settings = resource_manager::get_settings (); @@ -968,6 +1042,19 @@ settings_dialog::write_changed_settings // Terminal write_terminal_colors (settings); + // Variable editor + settings->setValue("variable_editor/autofit_column_width",ui->varedit_autoFitColumnWidth->isChecked()); + settings->setValue("variable_editor/autofit_type",ui->varedit_autofitType->currentIndex()); + settings->setValue("variable_editor/column_width",ui->varedit_columnWidth->text()); + settings->setValue("variable_editor/row_height", ui->varedit_rowHeight->text()); + settings->setValue("variable_editor/autofit_row_height",ui->varedit_rowAutofit->isChecked()); + settings->setValue("variable_editor/use_terminal_font",ui->varedit_useTerminalFont->isChecked()); + settings->setValue("variable_editor/alternate_rows",ui->varedit_alternate->isChecked()); + settings->setValue("variable_editor/font_name",ui->varedit_font->currentFont().family()); + settings->setValue("variable_editor/font_size",ui->varedit_fontSize->value()); + settings->setValue("variable_editor/toolbar_size",ui->varedit_toolbarSize->value()); + write_varedit_colors(settings); + // shortcuts settings->setValue ("shortcuts/prevent_readline_conflicts", ui->cb_prevent_readline_conflicts->isChecked ()); diff --git a/libgui/src/settings-dialog.h b/libgui/src/settings-dialog.h --- a/libgui/src/settings-dialog.h +++ b/libgui/src/settings-dialog.h @@ -70,6 +70,9 @@ private: void read_terminal_colors (QSettings *settings); void write_terminal_colors (QSettings *settings); + void read_varedit_colors (QSettings *settings); + void write_varedit_colors (QSettings *settings); + color_picker *_widget_title_bg_color; color_picker *_widget_title_bg_color_active; color_picker *_widget_title_fg_color; diff --git a/libgui/src/settings-dialog.ui b/libgui/src/settings-dialog.ui --- a/libgui/src/settings-dialog.ui +++ b/libgui/src/settings-dialog.ui @@ -2655,6 +2655,228 @@ + + + Variable Editor + + + + + + true + + + + + 0 + 0 + 678 + 384 + + + + + + 0 + 0 + 678 + 384 + + + + + + + + + + Default row height + + + + + + + Font + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + 20 + + + + + + + Default column width + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Use Terminal Font + + + true + + + + + + + 10 + + + + + + + Font size + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + 2 + + + + + + + + Liberation Mono + + + + + + + + Autofit + + + + + + + Plus font height + + + true + + + + + + + 0 + + + + By Column + + + + + Uniform + + + + + + + + Toolbar Size + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + 8 + + + 64 + + + 8 + + + 32 + + + + + + + + + Variable Editor Colours + + + + + + + + + true + + + Use alternating row colours + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + diff --git a/libgui/src/variable-editor-model.cc b/libgui/src/variable-editor-model.cc new file mode 100644 --- /dev/null +++ b/libgui/src/variable-editor-model.cc @@ -0,0 +1,674 @@ +/* + +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 +. + +*/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "variable-editor-model.h" +#include "octave-qt-link.h" + +#include + +#include +#include +#include +#include //DBG + +#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 () + : state (unset) + { } + + cell (state_t s) + : state (s) + { } + + cell (const QString &d, const QString &s, const QString &t, + bool rse, sub_editor_types edtype) + : state (avail), data (d), status_tip (s), tool_tip (t), + requires_sub_editor(rse), editor_type(edtype) + { } + + state_t state; + QVariant data; + QVariant status_tip; + QVariant tool_tip; + QVariant background; + bool requires_sub_editor; + + sub_editor_types editor_type; + + // ...? + }; + + void set (const QModelIndex &idx, const cell &dat) + { + if (idx.isValid ()) + table[model_to_index (idx)] = dat; + } + void set (int r, int c, const cell &dat) + { + if (0 <= r && r < _rows && 0 <= c && c <= columns ()) + table[c * _rows + r] = dat; + } + bool is_set (const QModelIndex &idx) const + { + return idx.isValid () + && table [model_to_index (idx)].state == cell::avail; + } + bool is_notavail (const QModelIndex &idx) const + { + return idx.isValid () + && table [model_to_index (idx)].state == cell::notavail; + } + bool is_pending (const QModelIndex &idx) const + { + return idx.isValid () + && table [model_to_index (idx)].state == cell::pending; + } + void pending (const QModelIndex &idx) + { + if (idx.isValid ()) + table [model_to_index (idx)].state = cell::pending; + } + void notavail (int r, int c) + { + if (0 <= r && r < _rows && 0 <= c && c <= columns ()) + table[c * _rows + r].state = cell::notavail; + } + bool requires_sub_editor (const QModelIndex &idx) + { + return idx.isValid () + && table [model_to_index (idx)].requires_sub_editor; + } + + sub_editor_types sub_editor_type(const QModelIndex &idx) + { + return idx.isValid() ? table [model_to_index (idx)].editor_type : sub_none; + } + + void unset (int r, int c) + { + if (0 <= r && r < _rows && 0 <= c && c <= columns ()) + table[c * _rows + r].state = cell::unset; + } + void clear () + { + for (int i = 0; i < table.size(); ++i) + table[i].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 table[i].data; + case Qt::StatusTipRole: + return table[i].status_tip; + case Qt::ToolTipRole: + return table[i].tool_tip; + case Qt::BackgroundRole: + return table[i].background; + } + } + return QVariant (); + } + octave_idx_type rows () const + { return _rows; } + octave_idx_type columns () const + { return _cols; } + + int model_to_index (const QModelIndex &idx) const + { + return idx.column () * _rows + idx.row (); + } + + impl (const QString &n, QLabel *l) + : name (n.toStdString ()), + _rows (0), _cols (0), + label (l), _validity(true) + { } + + const std::string name; + std::string type; + octave_idx_type _rows; + octave_idx_type _cols; + QVector table; + QLabel *label; + bool _validity; + QString validtext; +}; + +variable_editor_model::variable_editor_model (const QString &expr, + QLabel *label, + QObject *parent) + : QAbstractTableModel (parent), d (new impl (expr, label)), p (parent) +{ + 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))); + + clear_data_cache (); // initializes everything +} + +variable_editor_model::~variable_editor_model () +{ + delete d; +} + +void +variable_editor_model::clear_data_cache () +{ + octave_link::post_event + (this, &variable_editor_model::init_from_oct, d->name); +} + +bool +variable_editor_model::requires_sub_editor (const QModelIndex &idx) const +{ + return d->requires_sub_editor (idx); +} + +bool variable_editor_model::editor_type_matrix (const QModelIndex &idx) const +{ + return d->sub_editor_type (idx) == sub_matrix; +} + +bool variable_editor_model::editor_type_string (const QModelIndex &idx) const +{ + return d->sub_editor_type (idx) == sub_string; +} + +sub_editor_types variable_editor_model::editor_type(const QModelIndex &idx) const +{ + return d->sub_editor_type(idx); +} + +QString +variable_editor_model::parens () const +{ + if (d->type == "{") + return "{%1, %2}"; + return "(%1, %2)"; +} + +int +variable_editor_model::rowCount (const QModelIndex&) const +{ + if (d->_validity) + return d->rows (); + + return 1; +} + +int +variable_editor_model::columnCount (const QModelIndex&) const +{ + if (d->_validity) + return d->columns (); + + return 1; +} + +QVariant +variable_editor_model::data (const QModelIndex &idx, int role) const +{ + if (!(d->_validity)) + { + if (idx.isValid ()) + { + if (role == Qt::DisplayRole) + return QVariant(QString("Variable %d not found").arg(QString::fromStdString(d->name))); + } + return QVariant(QString("x")); + } + + if (idx.isValid ()) + { + if (d->is_set (idx)) + return d->data (idx, role); + else + { + if (! d->is_pending (idx)) + { + octave_link::post_event + (const_cast (this), + &variable_editor_model::get_data_oct, + idx.row (), idx.column (), d->name); + d->pending (idx); + } + if (role == Qt::DisplayRole) + return QVariant (QString (d->is_notavail (idx) ? "⌛" : "✗")); + else + return QVariant (); + } + } + return QVariant (); // invalid +} + +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!"; // DBG + return false; + } + octave_link::post_event + (this, &variable_editor_model::set_data_oct, + d->name, idx.row (), idx.column (), + v.toString ().toStdString ()); + return true; + } + else + return false; +} + +bool +variable_editor_model::insertRows (int row, int count, const QModelIndex&) +{ + // TODO cells? + octave_link::post_event + (this, &variable_editor_model::eval_oct, d->name, + QString ("%1 = [ %1(1:%2,:) ; zeros(%3, columns(%1)) ; %1(%2+%3:end,:) ]") + .arg (QString::fromStdString (d->name)) + .arg (row) + .arg (count) + .toStdString ()); + return true; +} + +bool +variable_editor_model::removeRows (int row, int count, const QModelIndex&) +{ + if (row + count > d->_rows) + { + qDebug () << "Try to remove too many rows " << d->_rows << " " + << count << " (" << row << ")"; //DBG + return false; + } + ; + octave_link::post_event + (this, &variable_editor_model::eval_oct, d->name, + QString ("%1 (%2:%3, :) = []") + .arg (QString::fromStdString (d->name)) + .arg (row) + .arg (row + count) + .toStdString ()); + return true; +} + +bool +variable_editor_model::insertColumns (int col, int count, const QModelIndex&) +{ + octave_link::post_event + (this, &variable_editor_model::eval_oct, d->name, + QString ("%1 = [ %1(:,1:%2) ; zeros(rows(%1), %3) %1(:,%2+%3:end) ]") + .arg (QString::fromStdString (d->name)) + .arg (col) + .arg (count) + .toStdString ()); + return true; +} + +bool +variable_editor_model::removeColumns (int col, int count, const QModelIndex&) +{ + if (col + count > d->_cols) + { + qDebug () << "Try to remove too many cols " << d->_cols << " " + << count << " (" << col << ")"; //DBG + return false; + } + ; + octave_link::post_event + (this, &variable_editor_model::eval_oct, d->name, + QString ("%1 (:, %2:%3) = []") + .arg (QString::fromStdString (d->name)) + .arg (col) + .arg (col + count) + .toStdString ()); + return true; +} + +Qt::ItemFlags +variable_editor_model::flags (const QModelIndex &idx) const +{ + if (d->_validity) + { + if (requires_sub_editor(idx)) + { + if (editor_type(idx) != sub_string) + return QAbstractTableModel::flags (idx); + } + return QAbstractTableModel::flags (idx) | Qt::ItemIsEditable; + //return requires_sub_editor(idx) ? QAbstractTableModel::flags (idx) : QAbstractTableModel::flags (idx) | Qt::ItemIsEditable; + } + return 0; +} + +// 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; + + + + 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) +{ + d->notavail (r, c); +} + +void +variable_editor_model::received_unset_data (int r, int c) +{ + d->unset (r, c); +} + +void +variable_editor_model::received_user_error (const QString &title, + const QString &msg) +{ + QMessageBox::critical (0x0, title, msg); +} + +void +variable_editor_model::received_initialize_data (const QString &class_name, + const QString &paren, + int rows, int cols) +{ + if (!(d->_validity)) + { + return; + } + d->type = paren.toStdString (); + + const int r = d->_rows - rows; + if (r > 0) + emit beginRemoveRows (QModelIndex (), rows, d->_rows - 1); + else if(r < 0) + emit beginInsertRows (QModelIndex (), d->_rows, rows - 1); + + const int c = d->_cols - cols; + if (c > 0) + emit beginRemoveColumns (QModelIndex (), cols, d->_cols - 1); + else if(c < 0) + emit beginInsertColumns (QModelIndex (), d->_cols, cols - 1); + + d->_rows = rows; + d->_cols = cols; + d->table.clear (); + d->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 (d->_rows - 1, d->_cols - 1)); + + d->label->setTextFormat (Qt::PlainText); + QString description = QString ("%1: %2 %3x%4") + .arg (QString::fromStdString (d->name)) + .arg (class_name) + .arg (rows) + .arg (cols); + d->label->setText (description); + d->validtext=description; +} + +// private + +void +variable_editor_model::get_data_oct (int row, int col, const std::string &x) +{ + int parse_status = 0; + + octave_value v = retrieve_variable(x, parse_status);//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); + d->_validity = false; + return; + } + octave_value_list ovlidx = ovl (row + 1, col + 1); + /*const*/ octave_value elem = v.single_subsref (d->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 ()); + + // ToDo: 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, int row, int col, + std::string val) +{ + d->_validity = true; + int parse_status = 0; + // Accessing directly since 1) retrieve_variable does not support writeback, and + // 2) we can be reasonably sure that this variable exists. + octave_value ret = octave::eval_string (val, true, parse_status);//retrieve_variable(x, parse_status);//eval_string (val, true, parse_status); + if (parse_status != 0 || ! ret.is_defined ()) + { + 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);//eval_string (x, true, parse_status); + if (parse_status != 0 || ! v.is_defined ()) + { + d->_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 idxl; + idxl.push_back (ovlidx); + v.subsasgn (d->type, idxl, ret); + emit unset_data (row, col); + QModelIndex idx = QAbstractTableModel::index (row, col); + emit dataChanged (idx, idx); +} + +/** + * If the variable exists, load it into the data model. If it doesn't exist, flag the data model as referring to a nonexistant 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[x.length()-1] == ')' || x[x.length()-1] == '}') + { + name = x.substr(0,x.find(x[x.length()-1] == ')' ? "(" : "{")); + } + + if (symbol_exist(name,"var") > 0) + return octave::eval_string (x, true, parse_status); + + parse_status = -1; + return octave_value(); +} + + +void +variable_editor_model::init_from_oct (const std::string &x) +{ + int parse_status = 0; + const octave_value ov = retrieve_variable(x, parse_status);//eval_string (x, true, parse_status); + d->_validity = true; + if (parse_status != 0 || ! ov.is_defined ()) + { + d->_validity = false; + display_invalid(); + return; + } + const QString class_name = QString::fromStdString (ov.class_name ()); + const QString paren = ov.is_cell () ? "{" : "("; // TODO + 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, 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); +} + +void +variable_editor_model::display_invalid() +{ + d->label->setTextFormat (Qt::PlainText); + QString description = QString ("%1: [Not found or Out-of-scope]") + .arg (QString::fromStdString (d->name)); + d->label->setText (description); + dynamic_cast(p)->setVisible(false); +} + +void +variable_editor_model::display_valid() +{ + d->label->setTextFormat (Qt::PlainText); + d->label->setText (d->validtext); + dynamic_cast(p)->setVisible(true); +} + diff --git a/libgui/src/variable-editor-model.h b/libgui/src/variable-editor-model.h new file mode 100644 --- /dev/null +++ b/libgui/src/variable-editor-model.h @@ -0,0 +1,165 @@ +/* + +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 +. + +*/ + +#if !defined (variable_editor_model_h) +#define variable_editor_model_h 1 + +#include +#include "ov.h" + +class QLabel; + +enum sub_editor_types +{ + sub_none, + sub_matrix, + sub_string, + sub_struct +}; + +class +variable_editor_model : public QAbstractTableModel +{ + Q_OBJECT + +public: + variable_editor_model (const QString &expr, + QLabel *label, + QObject *p = 0); + ~variable_editor_model (); + + int rowCount (const QModelIndex & = QModelIndex ()) const; + int columnCount (const QModelIndex & = QModelIndex ()) const; + QVariant data (const QModelIndex &idx, int role = Qt::DisplayRole) const; + bool setData (const QModelIndex &idx, const QVariant &v, int role = Qt::EditRole); + Qt::ItemFlags flags (const QModelIndex &idx) const; + bool insertRows (int row, int count, + const QModelIndex &parent = QModelIndex()); + bool removeRows (int row, int count, + const QModelIndex &parent = QModelIndex()); + bool insertColumns (int column, int count, + const QModelIndex &parent = QModelIndex()); + bool removeColumns (int column, int count, + const QModelIndex &parent = QModelIndex()); + + void clear_data_cache (); + /// Is cell at idx complex enough to require a sub editor? + bool requires_sub_editor (const QModelIndex &idx) const; + + /// If a sub editor is required, is it a standard type? + bool editor_type_matrix(const QModelIndex &idx) const; + bool editor_type_string(const QModelIndex &idx) const; + + /** Return the proper parens to access the data structure. + * + * {%1,%2} for cell and (%1,%2) for matrices. Use QString::arg to + * set the index. + */ + QString parens () const; + + // TODO insertRows(), removeRows(), insertColumns(), and removeColumns(). + +signals: // private + + void + data_ready (int r, int c, + const QString &data, + const QString &class_info, + int rows, int cols); + + void + no_data (int r, int c); + + void + unset_data (int r, int c); + + void + user_error (const QString &title, const QString &msg); + + void + initialize_data (const QString &class_name, const QString &paren, + int rows, int cols); + + void + updated(); + +private slots: + + void + received_data (int r, int c, + const QString &dat, + const QString &class_info, + int rows, int cols); + void + received_no_data (int r, int c); + void + received_unset_data (int r, int c); + void + received_user_error (const QString &title, const QString &msg); + void + received_initialize_data (const QString &class_name, const QString &paren, + int rows, int cols); + +private: + /** Get data for ov(row, col) + * This must be executed in the octave thread! + */ + void + get_data_oct (int row, int col, const std::string &v) /*const*/; + + void + set_data_oct (const std::string &v, int row, int col, + std::string val); + + void + init_from_oct (const std::string &x); + + void + eval_oct (const std::string &name, std::string expr); + + octave_value + retrieve_variable (const std::string &x, int &parse_status); + + sub_editor_types editor_type(const QModelIndex &idx) const; + + Q_DISABLE_COPY (variable_editor_model) + + /** Change the display if the variable + * does not exist (Yet) + */ + void + display_invalid(); + /** Change the display now that the + * variable exists + */ + void + display_valid(); + + QObject *p; + struct impl; + impl *d; + +}; + +#endif diff --git a/libgui/src/variable-editor.cc b/libgui/src/variable-editor.cc new file mode 100644 --- /dev/null +++ b/libgui/src/variable-editor.cc @@ -0,0 +1,1169 @@ +/* + +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 +. + +*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include //DBG +#include +#include +#include + +#include "resource-manager.h" +#include "variable-editor.h" +#include "variable-editor-model.h" +#include "octave-qt-link.h" + +#include "ov.h" +#include "operators/ops.h" + +variable_editor::variable_editor (QWidget *p) + : octave_dock_widget (p), + main (new QMainWindow ()), + tool_bar (new QToolBar (main)), + tab_widget (new QTabWidget (main)), + default_width(20), + stylesheet(""), + font(QFont()), + default_height(100), add_font_height(0), + autofit(false), autofit_max(false) +{ + + /* + We use a MainWindow + */ + + setObjectName ("variable_editor"); + setWindowTitle (tr ("Variable Editor")); + set_title (tr ("Variable Editor")); + setStatusTip (tr ("Edit variables.")); + setWindowIcon (QIcon (":/actions/icons/logo.png")); + + // Tool Bar + construct_tool_bar (); + main->addToolBar (tool_bar); + + for (int i = 0; isetTabsClosable (true); + tab_widget->setMovable (true); + connect (tab_widget, SIGNAL (tabCloseRequested (int)), + this, SLOT (closeTab (int))); + main->setCentralWidget (tab_widget); + + // Main + main->setParent (this); + setWidget (main); + + connect (this, SIGNAL (command_requested (const QString&)), + p, SLOT (execute_command_in_terminal (const QString&))); +} + +void +variable_editor::construct_tool_bar () +{ + tool_bar->setObjectName ("VariableEditorToolBar"); + tool_bar->setWindowTitle (tr ("Variable Editor Toolbar")); + + tool_bar->addAction + (resource_manager::icon("document-save"), + tr ("Save"), + this, SLOT (save ())); + tool_bar->addSeparator (); + + tool_bar->addAction + (resource_manager::icon("edit-cut"), + tr ("Cut"), + this, SLOT (cutClipboard ())); + tool_bar->addAction + (resource_manager::icon("edit-copy"), + tr ("Copy"), + this, SLOT (copyClipboard ())); + tool_bar->addAction + (resource_manager::icon("edit-paste"), + tr ("Paste"), + this, SLOT (pasteClipboard ())); + tool_bar->addAction + (resource_manager::icon("edit-paste"),// TODO - different icon? + tr ("Paste Table"), + this, SLOT (pasteTableClipboard ())); + tool_bar->addSeparator (); + + //QAction *print_action; /icons/fileprint.png + //tool_bar->addSeparator (); + + QToolButton *plot_tool_button = new QToolButton (tool_bar); + plot_tool_button->setText (tr ("Plot")); + plot_tool_button->setIcon (resource_manager::icon("logo"));//QIcon (":/actions/icons/gear.png")); // TODO + + 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); + tool_bar->addWidget (plot_tool_button); + + + + tool_bar->addSeparator (); + tool_bar->addAction + (QIcon (":/actions/icons/arrow_up.png"), + tr ("Up"), + this, SLOT (up ())); +} + +/*variable_editor::~variable_editor () +{ +}*/ + +namespace +{ + /// Helper struct to store widget pointers in "data" Tab property. + struct table_data + { + table_data (QTableView *t = 0x0) + : table (t) + { } + QTableView *table; + }; + + table_data get_table_data (QTabWidget *w, int tidx) + { + return w->widget (tidx)->property("data").value (); + } + + table_data get_table_data (QTabWidget *w) + { + return get_table_data (w, w->currentIndex ()); + } +} +Q_DECLARE_METATYPE (table_data) + +void +variable_editor::edit_variable (const QString &name) +{ + + if (stylesheet == "") + { + QSettings *settings = resource_manager::get_settings (); + notice_settings(settings); + } + const int tab_count = tab_widget->count (); + for (int i = 0; i < tab_count; ++i) + if (tab_widget->tabText (i) == name) + { + 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 = tab_widget->addTab (page, name); + tab_widget->setCurrentIndex (tab_idx); + if (autofit) + { + table->resizeColumnsToContents(); + if (autofit_max) + { + int mx = 0; + for (int i = 0;imodel()->columnCount();i++) + { + if (table->columnWidth(i) > mx) + mx = table->columnWidth(i); + } + table->horizontalHeader()->setDefaultSectionSize(mx); + } + } + else + { + table->horizontalHeader()->setDefaultSectionSize(default_width); + } + table->setFont(font); + table->setStyleSheet(stylesheet); + table->setAlternatingRowColors(alternate_rows); +#if defined (HAVE_QT4) + table->verticalHeader()->setResizeMode(QHeaderView::Interactive); +#else + table->verticalHeader()->setSectionResizeMode(QHeaderView::Interactive); +#endif + table->verticalHeader()->setDefaultSectionSize(default_height+add_font_height); +} + +void +variable_editor::callUpdate(const QModelIndex& aa,const QModelIndex& bb) +{ + if (autofit) + { + QTableView *view = get_table_data(tab_widget).table; + view->resizeColumnsToContents(); + if (autofit_max) + { + int mx = 0; + for (int i = 0;imodel()->columnCount();i++) + { + if (view->columnWidth(i) > mx) + mx = view->columnWidth(i); + } + view->horizontalHeader()->setDefaultSectionSize(mx); + } + + } + + emit updated(); +} + +void +variable_editor::closeTab (int idx) +{ + if (idx < 0 || idx > tab_widget->count ()) + return; + + QWidget *const wdgt = tab_widget->widget (idx); + tab_widget->removeTab (idx); + delete wdgt; +} + +void +variable_editor::closeEvent (QCloseEvent *e) +{ + emit finished (); + octave_dock_widget::closeEvent (e); +} + +void +variable_editor::contextmenu_requested (const QPoint &qpos) +{ + QTableView *view = get_table_data (tab_widget).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 ())); + menu->addAction + (resource_manager::icon("edit-paste"), // TODO + 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 ())); + + // TODO sort + + menu->addAction + ( //QIcon (), TODO + tr ("Transpose"), + this, SLOT (transposeContent ())); + + QItemSelectionModel *sel = view->selectionModel (); + QList 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)); + } +} + +QList +variable_editor::octave_to_coords(QString& selection) +{ + //TODO: 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(); + } + + QList 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 = 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; +} + +void +variable_editor::delete_selected() +{ + QTableView *view = get_table_data (tab_widget).table; + QString selection = selected_to_octave(); + QList 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::columnmenu_requested(const QPoint &pt) +{ + QTableView *view = get_table_data (tab_widget).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 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 ())); + menu->addAction + (resource_manager::icon("edit-paste"), // TODO + 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 (tab_widget).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 coords = octave_to_coords(selection); + + bool nothingSelected = false; + if (coords.isEmpty()) + nothingSelected = true; + + 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 ())); + menu->addAction + (resource_manager::icon("edit-paste"), // TODO + 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)); +} + +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); +} + +QString +variable_editor::selected_to_octave () +{ + QString name = tab_widget->tabText (tab_widget->currentIndex ()); + QTableView *view = get_table_data (tab_widget).table; + QItemSelectionModel *sel = view->selectionModel (); + + if (!(sel->hasSelection())) + return name; // Nothing selected + + QList indices = sel->selectedIndexes (); // it's indices! + + int32_t from_row = std::numeric_limits::max (); + int32_t to_row = 0; + int32_t from_col = std::numeric_limits::max (); + int32_t to_col = 0; + + foreach(QModelIndex 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); + + // TODO Cells + return QString ("%1 (%2, %3)").arg (name).arg (rows).arg (cols); +} + +void +variable_editor::double_click (const QModelIndex &idx) +{ + QString name = tab_widget->tabText (tab_widget->currentIndex ()); + QTableView *const table = get_table_data (tab_widget).table; + variable_editor_model *const model = + qobject_cast(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::relay_command (const QString &cmd) +{ + emit command_requested (cmd.arg (selected_to_octave ())); +} + +void +variable_editor::save () +{ + QString name = tab_widget->tabText (tab_widget->currentIndex ()); + QString file = + QFileDialog::getSaveFileName (this, + tr ("Save Variable %1 As").arg (name), + ".", 0, 0, + QFileDialog::DontUseNativeDialog); + // TODO type? binary, float-binary, ascii, text, hdf5 matlab format? + if (! file.isEmpty ()) + // TODO use octave_value::save_*? + emit command_requested (QString ("save ('%1', '%2');") + .arg (file) + .arg (name)); +} + +void +variable_editor::clearContent () +{ + // TODO shift? + QTableView *view = get_table_data (tab_widget).table; + QAbstractItemModel *model = view->model (); + QItemSelectionModel *sel = view->selectionModel (); + QList indices = sel->selectedIndexes (); + foreach (QModelIndex idx, indices) + model->setData(idx, QVariant ("0")); // TODO [] for cell +} + +void +variable_editor::cutClipboard () +{ + if (!has_focus()) + return; + + copyClipboard (); + clearContent (); +} + +void +variable_editor::copyClipboard () +{ + if (!has_focus()) + return; + + QTableView *view = get_table_data (tab_widget).table; + QAbstractItemModel *model = view->model (); + QItemSelectionModel *sel = view->selectionModel (); + QList indices = sel->selectedIndexes (); + qSort(indices); + if (indices.size () <= 0) + 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.append (previous.row () != idx.row () ? '\n' : '\t'); + copy.append (model->data (idx).toString ()); + previous = idx; + } + copy.append ('\n'); + + QClipboard *clipboard = QApplication::clipboard (); + clipboard->setText (copy); +} + +bool +variable_editor::has_focus() +{ + // ToDo: This only generates exceptions in certain circumstances. Get + // a definitive list and eliminate the need to handle exceptions + + if (tab_widget->currentIndex() == -1) + return false; // No tabs + + try + { + QTableView *view = get_table_data(tab_widget).table; + if (view) + return view->hasFocus(); + + return false; + } + catch (...) + { + return false; + } + return false; +} + +void +variable_editor::pasteClipboard () +{ + // TODO + + if (!has_focus()) + return; + + QClipboard *clipboard = QApplication::clipboard (); + QString text = clipboard->text (); + + QTableView *view = get_table_data (tab_widget).table; + QItemSelectionModel *sel = view->selectionModel (); + QList indices = sel->selectedIndexes (); + + variable_editor_model *model = static_cast(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;imodel()->setData(indices[i],text.toDouble()); + } + } + + emit updated(); +} + +void variable_editor::pasteTableClipboard () +{ + if (!has_focus()) + return; + + QClipboard *clipboard = QApplication::clipboard (); + QString text = clipboard->text (); + + + QTableView *view = get_table_data (tab_widget).table; + QItemSelectionModel *sel = view->selectionModel (); + QList indices = sel->selectedIndexes (); + + variable_editor_model *model = static_cast(view->model()); + + QPoint start; + QPoint 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 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,colnum = 0; + + QStringList rows = text.split ('\n'); + foreach (QString row, rows) + { + if (rownum > end.x() - start.x()) + continue; + + QStringList cols = row.split ('\t'); + if (cols.isEmpty()) + continue; + + foreach (QString 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 () +{ + // TODO unnamed1..n if exist ('unnamed', 'var') + relay_command ("unnamed = %1"); +} + +void +variable_editor::transposeContent () +{ + QString name = tab_widget->tabText (tab_widget->currentIndex ()); + emit command_requested (QString ("%1 = %1';").arg (name)); + emit updated(); +} + +void +variable_editor::up () +{ + QString name = tab_widget->tabText (tab_widget->currentIndex ()); + // TODO 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::clear_data_cache () +{ + for (int i = 0; i < tab_widget->count (); ++i) + { + QTableView *const table = get_table_data (tab_widget, i).table; + QAbstractItemModel *const model = table->model (); + qobject_cast(model)->clear_data_cache (); + } +} + +void variable_editor::notice_settings(const QSettings *settings) +{ + default_width = settings->value("variable_editor/column_width", QVariant("100")).toString().toInt(); + autofit = settings->value("variable_editor/autofit_column_width", QVariant(false)).toBool(); + if (autofit) + { + if (settings->value("variable_editor/autofit_type",0).toInt() == 1) + { + autofit_max = true; + } + } + + default_height = settings->value("variable_editor/row_height",QVariant("10")).toString().toInt(); + + + alternate_rows = settings->value("variable_editor/alternate_rows", QVariant(false)).toBool(); + QList _default_colors = + resource_manager::varedit_default_colors (); + QString class_chars = resource_manager::varedit_color_chars (); + + + use_terminal_font = settings->value("variable_editor/use_terminal_font",true).toBool(); + + QString font_name; + int font_size; + + if (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(); + } + font = QFont(font_name,font_size); + + if (settings->value("variable_editor/autofit_row_height",false).toBool()) + { + QFontMetrics fm(font); + add_font_height = fm.height(); + } + else + 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 (); + table_colors.replace (i,setting_color); + } + update_colors(); + int toolsize = settings->value("variable_editor/toolbar_size",QVariant(0)).toInt(); + if (toolsize > 0) + tool_bar->setIconSize(QSize(toolsize,toolsize)); +} + +/// Also updates the font +void variable_editor::update_colors() +{ + stylesheet=""; + stylesheet += "QTableView::item{ foreground-color: " + table_colors[0].name() +" }"; + stylesheet += "QTableView::item{ background-color: " + table_colors[1].name() +" }"; + stylesheet += "QTableView::item{ selection-color: " + table_colors[2].name() +" }"; + stylesheet += "QTableView::item:selected{ background-color: " + table_colors[3].name() +" }"; + if ((table_colors.length() > 4) && alternate_rows) + { + stylesheet += "QTableView::item:alternate{ background-color: " + table_colors[4].name() +" }"; + stylesheet += "QTableView::item:alternate:selected{ background-color: " + table_colors[3].name() +" }"; + } + + if (tab_widget->count() < 1) + return; + + for (int i=0;icount();i++) + { + QTableView *view = get_table_data(tab_widget).table; + view->setAlternatingRowColors(alternate_rows); + view->setStyleSheet(stylesheet); + view->setFont(font); + } + +} + +QStringList variable_editor::color_names() +{ + QStringList output; + + output << "Foreground"; + output << "Background"; + output << "Selected Foreground"; + output << "Selected Background"; + output << "Alternate Background"; + + return output; + +} + +QList variable_editor::default_colors() +{ + // fbsa + QList 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; +} + diff --git a/libgui/src/variable-editor.h b/libgui/src/variable-editor.h new file mode 100644 --- /dev/null +++ b/libgui/src/variable-editor.h @@ -0,0 +1,127 @@ +/* + +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 +. + +*/ + +#ifndef variable_editor_h +#define variable_editor_h + +#include "octave-dock-widget.h" +#include +#include + +class QTabWidget; +class QToolBar; +class QMainWindow; +class QTableView; +class QModelIndex; + +class variable_editor : public octave_dock_widget +{ + Q_OBJECT + +public: + + variable_editor (QWidget *parent = 0x0); + + //~variable_editor (); + + void edit_variable (const QString &name); + + /// Clear all the models' data cache + void clear_data_cache (); + + bool has_focus(); + + static QList default_colors(); + static QStringList color_names(); + +public slots: + + void callUpdate(const QModelIndex&,const QModelIndex&); + void notice_settings (const QSettings *); + +protected slots: + + void closeEvent (QCloseEvent *); + + void closeTab (int idx); + + void contextmenu_requested (const QPoint &pt); + void columnmenu_requested (const QPoint &pt); + void rowmenu_requested (const QPoint &pt); + + void double_click (const QModelIndex &idx); + + void save (); + void clearContent (); + void cutClipboard (); + void copyClipboard (); + void pasteClipboard (); + void pasteTableClipboard (); + void createVariable (); + void transposeContent (); + void up (); + + void delete_selected(); + + + /** Send command to Octave interpreter. + * %1 in CMD is replaced with the value of selected_to_octave. + */ + void relay_command (const QString &cmd); + +signals: + + void updated (); + void finished (); + void command_requested (const QString &cmd); + +private: + bool autofit; + bool autofit_max; + QFont font; + QFont sel_font; // If use_terminal_font then this will be different since "font" will contain the terminal font. + QList table_colors; + bool use_terminal_font; + bool alternate_rows; + int default_width; + int default_height; + int add_font_height; + + QString stylesheet; + + void update_colors(); + void construct_tool_bar (); + + + /// Convert selection to an Octave expression. + QString selected_to_octave (); + QList octave_to_coords (QString &); + + QMainWindow *main; + QToolBar *tool_bar; + int tool_bar_size; + QTabWidget *tab_widget; +}; + +#endif //variable_editor_h diff --git a/libgui/src/workspace-model.h b/libgui/src/workspace-model.h --- a/libgui/src/workspace-model.h +++ b/libgui/src/workspace-model.h @@ -88,6 +88,7 @@ public slots: signals: void model_changed (void); + void prompt_variable_editor(void); void rename_variable (const QString& old_name, const QString& new_name); diff --git a/libgui/src/workspace-view.cc b/libgui/src/workspace-view.cc --- a/libgui/src/workspace-view.cc +++ b/libgui/src/workspace-view.cc @@ -152,6 +152,9 @@ workspace_view::workspace_view (QWidget connect (this, SIGNAL (command_requested (const QString&)), p, SLOT (execute_command_in_terminal (const QString&))); + + connect (this, SIGNAL (edit_variable_signal (const QString&)), + p, SLOT (edit_variable (const QString&))); } void @@ -324,6 +327,9 @@ workspace_view::contextmenu_requested (c rename->setToolTip (tr ("Only top-level symbols may be renamed")); } + menu.addAction (tr ("Open in Variable Editor"), this, + SLOT (handle_contextmenu_edit ())); + menu.addSeparator (); menu.addAction ("disp (" + var_name + ")", this, @@ -413,6 +419,25 @@ workspace_view::handle_contextmenu_renam } void +workspace_view::handle_contextmenu_edit (void) +{ + QModelIndex index = view->currentIndex (); + + if (index.isValid ()) + { + index = index.sibling (index.row (), 0); + + QAbstractItemModel *m = view->model (); + + QMap item_data = m->itemData (index); + + QString var_name = item_data[0].toString (); + + emit edit_variable_signal (var_name); + } +} + +void workspace_view::handle_contextmenu_disp (void) { relay_contextmenu_command ("disp"); diff --git a/libgui/src/workspace-view.h b/libgui/src/workspace-view.h --- a/libgui/src/workspace-view.h +++ b/libgui/src/workspace-view.h @@ -58,6 +58,9 @@ signals: /** signal that user had requested a command on a variable */ void command_requested (const QString& cmd); + /// signal that user wants to edit a variable + void edit_variable_signal (const QString&); + protected: void closeEvent (QCloseEvent *event); @@ -70,6 +73,7 @@ protected slots: void handle_contextmenu_copy (void); void handle_contextmenu_copy_value (void); void handle_contextmenu_rename (void); + void handle_contextmenu_edit (void); void handle_contextmenu_disp (void); void handle_contextmenu_plot (void); void handle_contextmenu_stem (void); diff --git a/libinterp/corefcn/octave-link.cc b/libinterp/corefcn/octave-link.cc --- a/libinterp/corefcn/octave-link.cc +++ b/libinterp/corefcn/octave-link.cc @@ -381,7 +381,28 @@ Undocumented internal function. { return ovl (octave_link::show_preferences ()); } + +DEFUN (openvar, args, , + "-*- texinfo -*-\n\ +@deftypefn {Built-in Function} openvar (@var{name})\n\ +Open the variable @var{name} in the GUI Variable Editor.\n\ +@end deftypefn") +{ + octave_value retval; + if (args.length () == 1) + { + std::string name = args (0).string_value (); + if (! error_state) + octave_link::openvar (name); + else + error ("invalid arguments"); + } + else + print_usage (); + + return retval; +} DEFUN (__octave_link_show_doc__, args, , doc: /* -*- texinfo -*- @deftypefn {} {} __octave_link_show_doc__ (@var{filename}) diff --git a/libinterp/corefcn/octave-link.h b/libinterp/corefcn/octave-link.h --- a/libinterp/corefcn/octave-link.h +++ b/libinterp/corefcn/octave-link.h @@ -120,6 +120,33 @@ public: instance->do_post_event (obj, method, arg); } + template + static void post_event (T *obj, void (T::*method) (A, B), + A arg_a, B arg_b) + { + if (enabled ()) + instance->do_post_event (obj, method, arg_a, arg_b); + } + + template + static void post_event (T *obj, + void (T::*method) (A, B, C), + A arg_a, B arg_b, C arg_c) + { + if (enabled ()) + instance->do_post_event (obj, method, arg_a, arg_b, arg_c); + } + + template + static void post_event (T *obj, + void (T::*method) (A, B, C, D), + A arg_a, B arg_b, C arg_c, D arg_d) + { + if (enabled ()) + instance->do_post_event + (obj, method, arg_a, arg_b, arg_c, arg_d); + } + static void entered_readline_hook (void) { if (enabled ()) @@ -233,10 +260,10 @@ public: static void set_workspace (void); static void set_workspace (bool top_level, - const std::list& ws) + const std::list& ws, const bool& update_variable_editor = true) { if (enabled ()) - instance->do_set_workspace (top_level, instance->debugging, ws); + instance->do_set_workspace (top_level, instance->debugging, ws, update_variable_editor); } static void clear_workspace (void) @@ -388,6 +415,18 @@ public: } + static bool + openvar (const std::string &name) + { + if (enabled ()) + { + instance->do_openvar (name); + return true; + } + else + return false; + } + private: static octave_link *instance; @@ -427,6 +466,30 @@ protected: gui_event_queue.add_method (obj, method, arg); } + template + void do_post_event (T *obj, void (T::*method) (A, B), + A arg_a, B arg_b) + { + gui_event_queue.add_method + (obj, method, arg_a, arg_b); + } + + template + void do_post_event (T *obj, void (T::*method) (A, B, C), + A arg_a, B arg_b, C arg_c) + { + gui_event_queue.add_method + (obj, method, arg_a, arg_b, arg_c); + } + + template + void do_post_event (T *obj, void (T::*method) (A, B, C, D), + A arg_a, B arg_b, C arg_c, D arg_d) + { + gui_event_queue.add_method + (obj, method, arg_a, arg_b, arg_c, arg_d); + } + void do_entered_readline_hook (void) { } void do_finished_readline_hook (void) { } @@ -479,7 +542,8 @@ protected: virtual void do_set_workspace (bool top_level, bool debug, - const std::list& ws) = 0; + const std::list& ws, + const bool& variable_editor_too = true) = 0; virtual void do_clear_workspace (void) = 0; @@ -508,6 +572,8 @@ protected: virtual void do_show_preferences (void) = 0; virtual void do_show_doc (const std::string& file) = 0; + + virtual void do_openvar (const std::string& name) = 0; }; #endif diff --git a/libinterp/corefcn/variables.cc b/libinterp/corefcn/variables.cc --- a/libinterp/corefcn/variables.cc +++ b/libinterp/corefcn/variables.cc @@ -482,6 +482,15 @@ symbol_exist (octave::interpreter& inter return 0; } +int +symbol_exist (const std::string& name, const std::string& type) +{ + octave::interpreter& interp = octave::__get_interpreter__ ("symbol_exist"); + + return symbol_exist (interp, name, type); +} + + #define GET_IDX(LEN) \ static_cast ((LEN-1) * static_cast (rand ()) / RAND_MAX) diff --git a/libinterp/corefcn/variables.h b/libinterp/corefcn/variables.h --- a/libinterp/corefcn/variables.h +++ b/libinterp/corefcn/variables.h @@ -70,6 +70,9 @@ generate_struct_completions (const std:: extern OCTINTERP_API bool looks_like_struct (const std::string& text, char prev_char); +extern OCTINTERP_API int +symbol_exist (const std::string& name, const std::string& type = "any"); + extern OCTINTERP_API std::string unique_symbol_name (const std::string& basename); diff --git a/liboctave/util/action-container.h b/liboctave/util/action-container.h --- a/liboctave/util/action-container.h +++ b/liboctave/util/action-container.h @@ -213,6 +213,91 @@ namespace octave A e_arg; }; + /// An element for calling a member function with two arguments + template + class method_arg2_elem : public elem + { + public: + method_arg2_elem (T *obj, void (T::*method) (A, B), + A arg_a, B arg_b) + : e_obj (obj), e_method (method), + e_arg_a (arg_a), e_arg_b (arg_b) { } + + void run (void) { (e_obj->*e_method) (e_arg_a, e_arg_b); } + + private: + + T *e_obj; + void (T::*e_method) (A, B); + A e_arg_a; + B e_arg_b; + + // No copying! + + method_arg2_elem (const method_arg2_elem&); + + method_arg2_elem operator = (const method_arg2_elem&); + }; + + /// An element for calling a member function with three arguments + template + class method_arg3_elem : public elem + { + public: + method_arg3_elem (T *obj, void (T::*method) (A, B, C), + A arg_a, B arg_b, C arg_c) + : e_obj (obj), e_method (method), + e_arg_a (arg_a), e_arg_b (arg_b), e_arg_c (arg_c) + { } + + void run (void) { (e_obj->*e_method) (e_arg_a, e_arg_b, e_arg_c); } + + private: + + T *e_obj; + void (T::*e_method) (A, B, C); + A e_arg_a; + B e_arg_b; + C e_arg_c; + + // No copying! + + method_arg3_elem (const method_arg3_elem&); + + method_arg3_elem operator = (const method_arg3_elem&); + }; + + /// An element for calling a member function with three arguments + template + class method_arg4_elem : public elem + { + public: + method_arg4_elem (T *obj, void (T::*method) (A, B, C, D), + A arg_a, B arg_b, C arg_c, D arg_d) + : e_obj (obj), e_method (method), + e_arg_a (arg_a), e_arg_b (arg_b), e_arg_c (arg_c), e_arg_d (arg_d) + { } + + void run (void) { + (e_obj->*e_method) (e_arg_a, e_arg_b, e_arg_c, e_arg_d); + } + + private: + + T *e_obj; + void (T::*e_method) (A, B, C, D); + A e_arg_a; + B e_arg_b; + C e_arg_c; + D e_arg_d; + + // No copying! + + method_arg4_elem (const method_arg4_elem&); + + method_arg4_elem operator = (const method_arg4_elem&); + }; + // An element that stores arbitrary variable, and restores it. template @@ -330,6 +415,33 @@ namespace octave add (new method_crefarg_elem (obj, method, arg)); } + // Call to T::method (A, B). + template + void add_method (T *obj, void (T::*method) (A, B), + A arg_a, B arg_b) + { + add (new method_arg2_elem (obj, method, arg_a, arg_b)); + } + + // Call to T::method (A, B, C). + template + void add_method (T *obj, void (T::*method) (A, B, C), + A arg_a, B arg_b, C arg_c) + { + add (new method_arg3_elem (obj, method, arg_a, + arg_b, arg_c)); + } + + // Call to T::method (A, B, C, D). + template + void add_method (T *obj, void (T::*method) (A, B, C, D), + A arg_a, B arg_b, + C arg_c, D arg_d) + { + add (new method_arg4_elem (obj, method, arg_a, + arg_b, arg_c, arg_d)); + } + // Call to delete (T*). template