GNU Octave  6.2.0
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
workspace-view.cc
Go to the documentation of this file.
1 ////////////////////////////////////////////////////////////////////////
2 //
3 // Copyright (C) 2011-2021 The Octave Project Developers
4 //
5 // See the file COPYRIGHT.md in the top-level directory of this
6 // distribution or <https://octave.org/copyright/>.
7 //
8 // This file is part of Octave.
9 //
10 // Octave is free software: you can redistribute it and/or modify it
11 // under the terms of the GNU General Public License as published by
12 // the Free Software Foundation, either version 3 of the License, or
13 // (at your option) any later version.
14 //
15 // Octave is distributed in the hope that it will be useful, but
16 // WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 // GNU General Public License for more details.
19 //
20 // You should have received a copy of the GNU General Public License
21 // along with Octave; see the file COPYING. If not, see
22 // <https://www.gnu.org/licenses/>.
23 //
24 ////////////////////////////////////////////////////////////////////////
25 
26 #if defined (HAVE_CONFIG_H)
27 # include "config.h"
28 #endif
29 
30 #include <QApplication>
31 #include <QClipboard>
32 #include <QCompleter>
33 #include <QHBoxLayout>
34 #include <QHeaderView>
35 #include <QInputDialog>
36 #include <QLabel>
37 #include <QLineEdit>
38 #include <QMenu>
39 #include <QMessageBox>
40 #include <QPushButton>
41 #include <QSignalMapper>
42 #include <QVBoxLayout>
43 
44 #include "gui-preferences-ws.h"
45 #include "octave-qobject.h"
46 #include "workspace-view.h"
47 
48 #include "interpreter-private.h"
49 #include "interpreter.h"
50 #include "syminfo.h"
51 
52 namespace octave
53 {
55  : octave_dock_widget ("WorkspaceView", p, oct_qobj),
56  m_view (new QTableView (this)),
57  m_filter_checkbox (new QCheckBox ()),
58  m_filter (new QComboBox (this)),
59  m_filter_widget (new QWidget (this))
60  {
61  setWindowIcon (QIcon (":/actions/icons/logo.png"));
62  set_title (tr ("Workspace"));
63  setStatusTip (tr ("View the variables in the active workspace."));
64 
65  m_filter->setToolTip (tr ("Enter text to filter the workspace"));
66  m_filter->setEditable (true);
67  m_filter->setMaxCount (ws_max_filter_history.def.toInt ());
68  m_filter->setInsertPolicy (QComboBox::NoInsert);
69  m_filter->setSizeAdjustPolicy (QComboBox::AdjustToMinimumContentsLengthWithIcon);
70  QSizePolicy sizePol (QSizePolicy::Expanding, QSizePolicy::Preferred);
71  m_filter->setSizePolicy (sizePol);
72  m_filter->completer ()->setCaseSensitivity (Qt::CaseSensitive);
73 
74  QLabel *filter_label = new QLabel (tr ("Filter"));
75 
76  m_view->setWordWrap (false);
77  m_view->setContextMenuPolicy (Qt::CustomContextMenu);
78  m_view->setShowGrid (false);
79  (m_view->verticalHeader) ()->hide ();
80  m_view->setAlternatingRowColors (true);
82 
83  // Set an empty widget, so we can assign a layout to it.
84  setWidget (new QWidget (this));
85 
86  // Create the layouts
87  QHBoxLayout *filter_layout = new QHBoxLayout ();
88 
89  filter_layout->addWidget (filter_label);
90  filter_layout->addWidget (m_filter_checkbox);
91  filter_layout->addWidget (m_filter);
92  filter_layout->setMargin (0);
93 
94  m_filter_widget->setLayout (filter_layout);
95 
96  QVBoxLayout *ws_layout = new QVBoxLayout ();
97  ws_layout->addWidget (m_filter_widget);
98  ws_layout->addWidget (m_view);
99  ws_layout->setSpacing (0);
100 
103 
104  if (settings)
105  {
106  m_filter_shown = settings->value (ws_filter_shown).toBool ();
107  m_filter_widget->setVisible (m_filter_shown);
108 
109  ws_layout->setMargin (2);
110 
111  // Set the empty widget to have our layout.
112  widget ()->setLayout (ws_layout);
113 
114  // Initialize collapse/expand state of the workspace subcategories.
115 
116  //enable sorting (setting column and order after model was set)
117  m_view->setSortingEnabled (true);
118  // Initialize column order and width of the workspace
119  m_view->horizontalHeader ()->restoreState
120  (settings->value (ws_column_state.key).toByteArray ());
121 
122  // Set header properties for sorting
123 #if defined (HAVE_QHEADERVIEW_SETSECTIONSCLICKABLE)
124  m_view->horizontalHeader ()->setSectionsClickable (true);
125 #else
126  m_view->horizontalHeader ()->setClickable (true);
127 #endif
128 #if defined (HAVE_QHEADERVIEW_SETSECTIONSMOVABLE)
129  m_view->horizontalHeader ()->setSectionsMovable (true);
130 #else
131  m_view->horizontalHeader ()->setMovable (true);
132 #endif
133  m_view->horizontalHeader ()->setSortIndicator (
134  settings->value (ws_sort_column).toInt (),
135  static_cast<Qt::SortOrder> (settings->value (ws_sort_order).toUInt ()));
136  // FIXME: use value<Qt::SortOrder> instead of static cast after
137  // dropping support of Qt 5.4
138 
139  m_view->horizontalHeader ()->setSortIndicatorShown (true);
140 
141  m_view->horizontalHeader ()->setContextMenuPolicy (Qt::CustomContextMenu);
142  connect (m_view->horizontalHeader (),
143  SIGNAL (customContextMenuRequested (const QPoint &)),
144  this, SLOT (header_contextmenu_requested (const QPoint &)));
145 
146  // Init state of the filter
147  m_filter->addItems (settings->value (ws_mru_list.key).toStringList ());
148 
149  bool filter_state =
150  settings->value (ws_filter_active).toBool ();
151  m_filter_checkbox->setChecked (filter_state);
152  filter_activate (filter_state);
153  }
154 
155  // Connect signals and slots.
156 
157  connect (m_filter, SIGNAL (editTextChanged (const QString&)),
158  this, SLOT (filter_update (const QString&)));
159  connect (m_filter_checkbox, SIGNAL (toggled (bool)),
160  this, SLOT (filter_activate (bool)));
161  connect (m_filter->lineEdit (), SIGNAL (editingFinished ()),
162  this, SLOT (update_filter_history ()));
163 
164  connect (m_view, SIGNAL (customContextMenuRequested (const QPoint&)),
165  this, SLOT (contextmenu_requested (const QPoint&)));
166 
167  connect (m_view, SIGNAL (activated (QModelIndex)),
168  this, SLOT (handle_contextmenu_edit (void)));
169 
170  connect (this, SIGNAL (command_requested (const QString&)),
171  p, SLOT (execute_command_in_terminal (const QString&)));
172 
173  connect (this,
174  SIGNAL (edit_variable_signal (const QString&, const octave_value&)),
175  p, SLOT (edit_variable (const QString&, const octave_value&)));
176  }
177 
179  {
180  m_filter_model.setSourceModel (model);
181  m_filter_model.setFilterKeyColumn(0);
182 
183  m_view->setModel (&m_filter_model);
184 
185  // set the sorting after the model is set, it would be ignored otherwise
188  m_view->sortByColumn (
189  settings->value (ws_sort_column).toInt (),
190  static_cast<Qt::SortOrder> (settings->value (ws_sort_order).toUInt ()));
191  // FIXME: use value<Qt::SortOrder> instead of static cast after
192  // dropping support of Qt 5.4
193 
194  m_model = model;
195  }
196 
197  void
199  {
200  m_model->notice_settings (settings); // update colors of model first
201 
202  for (int i = 0; i < ws_columns_shown.length (); i++)
203  m_view->setColumnHidden (i + 1, ! settings->value (ws_columns_shown_keys.at (i),true).toBool ());
204 
205  QString tool_tip;
206 
207  if (settings->value (ws_enable_colors).toBool ()
208  && ! settings->value (ws_hide_tool_tips).toBool ())
209  {
210  tool_tip = QString (tr ("View the variables in the active workspace.<br>"));
211  tool_tip += QString (tr ("Colors for variable attributes:"));
212 
213  for (int i = 0; i < ws_colors_count; i++)
214  {
215  tool_tip +=
216  QString (R"(<div style="background-color:%1;color:%2">%3</div>)")
217  .arg (m_model->storage_class_color (i).name ())
218  .arg (m_model->storage_class_color (i + ws_colors_count).name ())
219  .arg (QCoreApplication::translate ("octave::settings_dialog",
220  ws_color_names.at (i).toStdString ().data ()));
221  }
222  }
223 
224  setToolTip (tool_tip);
225  }
226 
227  void
229  {
232 
233  if (! settings)
234  return;
235 
236  settings->setValue (ws_column_state.key,
237  m_view->horizontalHeader ()->saveState ());
238 
239  int sort_column = m_view->horizontalHeader ()->sortIndicatorSection ();
240  Qt::SortOrder sort_order = m_view->horizontalHeader ()->sortIndicatorOrder ();
241  settings->setValue (ws_sort_column.key, sort_column);
242  settings->setValue (ws_sort_order.key, sort_order);
243 
244  settings->setValue (ws_filter_active.key, m_filter_checkbox->isChecked ());
246 
247  QStringList mru;
248  for (int i = 0; i < m_filter->count (); i++)
249  mru.append (m_filter->itemText (i));
250  settings->setValue (ws_mru_list.key, mru);
251 
252  settings->sync ();
253 
255  }
256 
257  void
258  workspace_view::filter_update (const QString& expression)
259  {
260  m_filter_model.setFilterWildcard (expression);
262  }
263 
264  void
266  {
267  m_filter->setEnabled (state);
268  m_filter_model.setDynamicSortFilter (state);
269 
270  if (state)
271  filter_update (m_filter->currentText ());
272  else
273  filter_update (QString ());
274  }
275 
276  void
278  {
279  QString text = m_filter->currentText (); // get current text
280  int index = m_filter->findText (text); // and its actual index
281 
282  if (index > -1)
283  m_filter->removeItem (index); // remove if already existing
284 
285  m_filter->insertItem (0, text); // (re)insert at beginning
286  m_filter->setCurrentIndex (0);
287  }
288 
289  void
291  {
292  QMenu menu (this);
293  QSignalMapper sig_mapper (this);
294 
297 
298  for (int i = 0; i < ws_columns_shown.length (); i++)
299  {
300  QAction *action
301  = menu.addAction (tr (ws_columns_shown.at (i).toStdString ().data ()),
302  &sig_mapper, SLOT (map ()));
303  sig_mapper.setMapping (action, i);
304  action->setCheckable (true);
305  action->setChecked (settings->value (ws_columns_shown_keys.at (i),true).toBool ());
306  }
307 
308  connect (&sig_mapper, SIGNAL (mapped (int)),
309  this, SLOT (toggle_header (int)));
310 
311  menu.exec (m_view->mapToGlobal (mpos));
312  }
313 
314  void
316  {
319 
320  QString key = ws_columns_shown_keys.at (col);
321  bool shown = settings->value (key,true).toBool ();
322 
323  m_view->setColumnHidden (col + 1, shown);
324 
325  settings->setValue (key, ! shown);
326  settings->sync ();
327 
329  }
330 
331  void
333  {
334  QMenu menu (this);
335 
336  QModelIndex index = m_view->indexAt (qpos);
337 
338  // if it isn't Local, Global etc, allow the ctx menu
339  if (index.isValid () && index.column () == 0)
340  {
341  QString var_name = get_var_name (index);
342 
343  menu.addAction (tr ("Open in Variable Editor"), this,
344  SLOT (handle_contextmenu_edit ()));
345 
346  menu.addAction (tr ("Copy name"), this,
347  SLOT (handle_contextmenu_copy ()));
348 
349  menu.addAction (tr ("Copy value"), this,
351 
352  QAction *rename = menu.addAction (tr ("Rename"), this,
353  SLOT (handle_contextmenu_rename ()));
354 
355  // Use m_model here instead of using "m_view->model ()" because
356  // that points to the proxy model.
357  if (! m_model->is_top_level ())
358  {
359  rename->setDisabled (true);
360  rename->setToolTip (tr ("Only top-level symbols may be renamed"));
361  }
362 
363  menu.addAction ("Clear " + var_name, this,
364  SLOT (handle_contextmenu_clear ()));
365 
366  menu.addSeparator ();
367 
368  menu.addAction ("disp (" + var_name + ')', this,
369  SLOT (handle_contextmenu_disp ()));
370 
371  menu.addAction ("plot (" + var_name + ')', this,
372  SLOT (handle_contextmenu_plot ()));
373 
374  menu.addAction ("stem (" + var_name + ')', this,
375  SLOT (handle_contextmenu_stem ()));
376 
377  menu.addSeparator ();
378 
379  }
380 
381  if (m_filter_shown)
382  menu.addAction (tr ("Hide filter"), this,
383  SLOT (handle_contextmenu_filter ()));
384  else
385  menu.addAction (tr ("Show filter"), this,
386  SLOT (handle_contextmenu_filter ()));
387 
388  menu.exec (m_view->mapToGlobal (qpos));
389  }
390 
391  void
393  {
394  QModelIndex index = m_view->currentIndex ();
395 
396  if (index.isValid ())
397  {
398  QString var_name = get_var_name (index);
399 
400  QClipboard *clipboard = QApplication::clipboard ();
401 
402  clipboard->setText (var_name);
403  }
404  }
405 
406  void
408  {
409  QModelIndex index = m_view->currentIndex ();
410 
411  if (index.isValid ())
412  {
413  QString var_name = get_var_name (index);
414 
415  emit interpreter_event
416  ([var_name] (interpreter& interp)
417  {
418  // INTERPRETER THREAD
419 
420  octave_value val = interp.varval (var_name.toStdString ());
421 
422  if (val.is_undefined ())
423  val = 0;
424 
425  std::ostringstream buf;
426  val.print_raw (buf, true);
427 
428  // FIXME: is the following operation thread safe or should
429  // it be done with a signal/slot connection?
430 
431  QClipboard *clipboard = QApplication::clipboard ();
432  clipboard->setText (QString::fromStdString (buf.str ()));
433  });
434  }
435  }
436 
437  void
439  {
440  QModelIndex index = m_view->currentIndex ();
441 
442  if (index.isValid ())
443  {
444  QString var_name = get_var_name (index);
445 
446  QInputDialog *inputDialog = new QInputDialog ();
447 
448  inputDialog->setOptions (QInputDialog::NoButtons);
449 
450  bool ok = false;
451 
452  QString new_name
453  = inputDialog->getText (nullptr, "Rename Variable", "New name:",
454  QLineEdit::Normal, var_name, &ok);
455 
456  if (ok && ! new_name.isEmpty ())
457  emit rename_variable_signal (var_name, new_name);
458  }
459  }
460 
461  void
463  {
464  QModelIndex index = m_view->currentIndex ();
465 
466  if (index.isValid ())
467  {
468  QString var_name = get_var_name (index);
469 
471  octave_value val = syminfo.varval (var_name.toStdString ());
472 
473  emit edit_variable_signal (var_name, val);
474  }
475  }
476 
477  void
479  {
480  relay_contextmenu_command ("clear", true);
481  }
482 
483  void
485  {
486  relay_contextmenu_command ("disp");
487  }
488 
489  void
491  {
492  relay_contextmenu_command ("figure (); plot");
493  }
494 
495  void
497  {
498  relay_contextmenu_command ("figure (); stem");
499  }
500 
501  void
503  {
505  m_filter_widget->setVisible (m_filter_shown);
506  }
507 
508  void
510  {
511  // m_view->resizeRowsToContents ();
512  // Just modify those rows that have been added rather than go through
513  // the whole list. For-loop test will handle when number of rows reduced.
514  QFontMetrics fm = m_view->fontMetrics ();
515  int row_height = fm.height ();
516  int new_row_count = m_filter_model.rowCount ();
517  for (int i = m_view_previous_row_count; i < new_row_count; i++)
518  m_view->setRowHeight (i, row_height);
519  m_view_previous_row_count = new_row_count;
520  }
521 
522  void
524  {
525  if (m_view->hasFocus ())
527  }
528 
529  void
531  {
532  if (m_view->hasFocus ())
533  m_view->selectAll ();
534  }
535 
536  void
537  workspace_view::relay_contextmenu_command (const QString& cmdname, bool str)
538  {
539  QModelIndex index = m_view->currentIndex ();
540 
541  if (index.isValid ())
542  {
543  QString var_name;
544 
545  if (str)
546  var_name = "\'" + get_var_name (index) + "\'";
547  else
548  var_name = get_var_name (index);
549 
550  emit command_requested (cmdname + " (" + var_name + ");");
551  }
552  }
553 
554  QString
555  workspace_view::get_var_name (const QModelIndex& index)
556  {
557  // We are using a sort model proxy so m_model won't provide the
558  // correct ordering.
559 
560  QAbstractItemModel *m = m_view->model ();
561 
562  QMap<int, QVariant> item_data
563  = m->itemData (index.sibling (index.row (), 0));
564 
565  return item_data[0].toString ();
566  }
567 }
Base class for Octave interfaces that use Qt.
resource_manager & get_resource_manager(void)
octave_value varval(const std::string &name) const
void set_title(const QString &)
void interpreter_event(const fcn_callback &fcn)
gui_settings * get_settings(void) const
octave_value varval(const std::string &name) const
Definition: syminfo.cc:168
bool is_top_level(void) const
QColor storage_class_color(int s_class)
void notice_settings(const gui_settings *)
symbol_info_list get_symbol_info(void) const
void handle_contextmenu_stem(void)
void toggle_header(int column)
void rename_variable_signal(const QString &, const QString &)
Signal that user wants to rename a variable.
void header_contextmenu_requested(const QPoint &mpos)
void relay_contextmenu_command(const QString &cmdname, bool str=false)
void handle_contextmenu_filter(void)
void handle_contextmenu_edit(void)
void contextmenu_requested(const QPoint &pos)
void filter_activate(bool enable)
void edit_variable_signal(const QString &, const octave_value &)
Signal that user wants to edit a variable.
void handle_contextmenu_plot(void)
workspace_model * m_model
void notice_settings(const gui_settings *)
void update_filter_history(void)
void command_requested(const QString &cmd)
Signal that user had requested a command on a variable.
QCheckBox * m_filter_checkbox
void handle_contextmenu_copy_value(void)
void setModel(workspace_model *model)
void handle_contextmenu_clear(void)
void handle_contextmenu_rename(void)
void handle_contextmenu_disp(void)
void filter_update(const QString &expression)
workspace_view(QWidget *parent, base_qobject &oct_qobj)
QString get_var_name(const QModelIndex &index)
QSortFilterProxyModel m_filter_model
void handle_contextmenu_copy(void)
void print_raw(std::ostream &os, bool pr_as_read_syntax=false) const
Definition: ov.h:1233
bool is_undefined(void) const
Definition: ov.h:554
void translate(Matrix &m, double x, double y, double z)
Definition: graphics.cc:5852
const QStringList ws_columns_shown(QStringList()<< QT_TRANSLATE_NOOP("octave::workspace_view", "Class")<< QT_TRANSLATE_NOOP("octave::workspace_view", "Dimension")<< QT_TRANSLATE_NOOP("octave::workspace_view", "Value")<< QT_TRANSLATE_NOOP("octave::workspace_view", "Attribute"))
const gui_pref ws_column_state("workspaceview/column_state", QVariant())
const gui_pref ws_sort_order("workspaceview/sort_order", QVariant(Qt::AscendingOrder))
const gui_pref ws_filter_shown("workspaceview/filter_shown", QVariant(true))
const QStringList ws_color_names(QStringList()<< QT_TRANSLATE_NOOP("octave::settings_dialog", "argument")<< QT_TRANSLATE_NOOP("octave::settings_dialog", "global")<< QT_TRANSLATE_NOOP("octave::settings_dialog", "persistent"))
const gui_pref ws_mru_list("workspaceview/mru_list", QVariant())
const QStringList ws_columns_shown_keys(QStringList()<< "workspaceview/show_class"<< "workspaceview/show_dimension"<< "workspaceview/show_value"<< "workspaceview/show_attribute")
const gui_pref ws_sort_column("workspaceview/sort_by_column", QVariant(0))
const gui_pref ws_enable_colors("workspaceview/enable_colors", QVariant(false))
const gui_pref ws_max_filter_history("workspaceview/max_filter_history", QVariant(10))
const gui_pref ws_filter_active("workspaceview/filter_active", QVariant(false))
const gui_pref ws_hide_tool_tips("workspaceview/hide_tools_tips", QVariant(false))
const int ws_colors_count
T octave_idx_type m
Definition: mx-inlines.cc:773
QString fromStdString(const std::string &s)
int rename(const std::string &from, const std::string &to)
Definition: file-ops.cc:508
static uint32_t state[624]
Definition: randmtzig.cc:190
const QString key
const QVariant def