GNU Octave  8.1.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-2023 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 "octave-qtutils.h"
47 #include "workspace-view.h"
48 
50 
52 : octave_dock_widget ("WorkspaceView", p, oct_qobj),
53  m_view (new QTableView (this)),
54  m_filter_checkbox (new QCheckBox ()),
55  m_filter (new QComboBox (this)),
56  m_filter_widget (new QWidget (this))
57 {
58  set_title (tr ("Workspace"));
59  setStatusTip (tr ("View the variables in the active workspace."));
60 
61  m_filter->setToolTip (tr ("Enter text to filter the workspace"));
62  m_filter->setEditable (true);
63  m_filter->setMaxCount (ws_max_filter_history.def.toInt ());
64  m_filter->setInsertPolicy (QComboBox::NoInsert);
65  m_filter->setSizeAdjustPolicy (QComboBox::AdjustToMinimumContentsLengthWithIcon);
66  QSizePolicy sizePol (QSizePolicy::Expanding, QSizePolicy::Preferred);
67  m_filter->setSizePolicy (sizePol);
68  m_filter->completer ()->setCaseSensitivity (Qt::CaseSensitive);
69 
70  QLabel *filter_label = new QLabel (tr ("Filter"));
71 
72  m_view->setWordWrap (false);
73  m_view->setContextMenuPolicy (Qt::CustomContextMenu);
74  m_view->setShowGrid (false);
75  (m_view->verticalHeader) ()->hide ();
76  m_view->setAlternatingRowColors (true);
77  m_view_previous_row_count = 0;
78 
79  // Set an empty widget, so we can assign a layout to it.
80  setWidget (new QWidget (this));
81 
82  // Create the layouts
83  QHBoxLayout *filter_layout = new QHBoxLayout ();
84 
85  filter_layout->addWidget (filter_label);
86  filter_layout->addWidget (m_filter_checkbox);
87  filter_layout->addWidget (m_filter);
88  filter_layout->setMargin (0);
89 
90  m_filter_widget->setLayout (filter_layout);
91 
92  QVBoxLayout *ws_layout = new QVBoxLayout ();
93  ws_layout->addWidget (m_filter_widget);
94  ws_layout->addWidget (m_view);
95  ws_layout->setSpacing (0);
96 
97  resource_manager& rmgr = m_octave_qobj.get_resource_manager ();
99 
100  if (settings)
101  {
102  m_filter_shown = settings->value (ws_filter_shown).toBool ();
103  m_filter_widget->setVisible (m_filter_shown);
104 
105  ws_layout->setMargin (2);
106 
107  // Set the empty widget to have our layout.
108  widget ()->setLayout (ws_layout);
109 
110  // Initialize collapse/expand state of the workspace subcategories.
111 
112  //enable sorting (setting column and order after model was set)
113  m_view->setSortingEnabled (true);
114  // Initialize column order and width of the workspace
115  m_view->horizontalHeader ()->restoreState
116  (settings->value (ws_column_state.key).toByteArray ());
117 
118  // Set header properties for sorting
119  m_view->horizontalHeader ()->setSectionsClickable (true);
120  m_view->horizontalHeader ()->setSectionsMovable (true);
121  m_view->horizontalHeader ()->setSortIndicator (
122  settings->value (ws_sort_column).toInt (),
123  static_cast<Qt::SortOrder> (settings->value (ws_sort_order).toUInt ()));
124  // FIXME: use value<Qt::SortOrder> instead of static cast after
125  // dropping support of Qt 5.4
126 
127  m_view->horizontalHeader ()->setSortIndicatorShown (true);
128 
129  m_view->horizontalHeader ()->setContextMenuPolicy (Qt::CustomContextMenu);
130  connect (m_view->horizontalHeader (),
131  &QTableView::customContextMenuRequested,
133 
134  // Init state of the filter
135  m_filter->addItems (settings->value (ws_mru_list.key).toStringList ());
136 
137  bool filter_state =
138  settings->value (ws_filter_active).toBool ();
139  m_filter_checkbox->setChecked (filter_state);
140  filter_activate (filter_state);
141  }
142 
143  // Connect signals and slots.
144 
145  connect (m_filter, &QComboBox::editTextChanged,
147  connect (m_filter_checkbox, &QCheckBox::toggled,
149  connect (m_filter->lineEdit (), &QLineEdit::editingFinished,
151 
152  connect (m_view, &QTableView::customContextMenuRequested,
154 
155  connect (m_view, &QTableView::activated,
157 
158  if (! p)
159  make_window ();
160 }
161 
163 {
164  m_filter_model.setSourceModel (model);
165  m_filter_model.setFilterKeyColumn(0);
166 
167  m_view->setModel (&m_filter_model);
168 
169  // set the sorting after the model is set, it would be ignored otherwise
172  m_view->sortByColumn (
173  settings->value (ws_sort_column).toInt (),
174  static_cast<Qt::SortOrder> (settings->value (ws_sort_order).toUInt ()));
175  // FIXME: use value<Qt::SortOrder> instead of static cast after
176  // dropping support of Qt 5.4
177 
178  m_model = model;
179 }
180 
181 void
183 {
184  m_model->notice_settings (settings); // update colors of model first
185 
186  for (int i = 0; i < ws_columns_shown.length (); i++)
187  m_view->setColumnHidden (i + 1, ! settings->value (ws_columns_shown_keys.at (i), true).toBool ());
188 
189  QString tool_tip;
190 
191  if (settings->value (ws_enable_colors).toBool ()
192  && ! settings->value (ws_hide_tool_tips).toBool ())
193  {
194  tool_tip = QString (tr ("View the variables in the active workspace.<br>"));
195  tool_tip += QString (tr ("Colors for variable attributes:"));
196 
197  for (int i = 0; i < ws_colors_count; i++)
198  {
199  tool_tip +=
200  QString (R"(<div style="background-color:%1;color:%2">%3</div>)")
201  .arg (m_model->storage_class_color (i).name ())
202  .arg (m_model->storage_class_color (i + ws_colors_count).name ())
203  .arg (QCoreApplication::translate ("octave::settings_dialog",
204  ws_color_names.at (i).toStdString ().data ()));
205  }
206  }
207 
208  setToolTip (tool_tip);
209 }
210 
211 void
213 {
216 
217  if (! settings)
218  return;
219 
220  settings->setValue (ws_column_state.key,
221  m_view->horizontalHeader ()->saveState ());
222 
223  int sort_column = m_view->horizontalHeader ()->sortIndicatorSection ();
224  Qt::SortOrder sort_order = m_view->horizontalHeader ()->sortIndicatorOrder ();
225  settings->setValue (ws_sort_column.key, sort_column);
226  settings->setValue (ws_sort_order.key, sort_order);
227 
228  settings->setValue (ws_filter_active.key, m_filter_checkbox->isChecked ());
230 
231  QStringList mru;
232  for (int i = 0; i < m_filter->count (); i++)
233  mru.append (m_filter->itemText (i));
234  settings->setValue (ws_mru_list.key, mru);
235 
236  settings->sync ();
237 
239 }
240 
242 {
243  if (focus)
244  {
245  m_filter->setFocus ();
246  setFocusProxy (m_filter);
247  }
248  else
249  {
250  m_view->setFocus ();
251  setFocusProxy (m_view);
252  }
253 }
254 
255 void
256 workspace_view::filter_update (const QString& expression)
257 {
258  m_filter_model.setFilterWildcard (expression);
260 }
261 
262 void
264 {
265  m_filter->setEnabled (state);
266  m_filter_model.setDynamicSortFilter (state);
267 
268  if (state)
269  filter_update (m_filter->currentText ());
270  else
271  filter_update (QString ());
272 
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  // FIXME: We could use
309  //
310  // connect (&m_sig_mapper, QOverload<int>::of (&QSignalMapper::mapped),
311  // this, &workspace_view::toggle_header);
312  //
313  // but referring to QSignalMapper::mapped will generate deprecated
314  // function warnings from GCC. We could also use
315  //
316  // connect (&m_sig_mapper, &QSignalMapper::mappedInt,
317  // this, &workspace_view::toggle_header);
318  //
319  // but the function mappedInt was not introduced until Qt 5.15 so
320  // we'll need a feature test.
321 
322  connect (&sig_mapper, SIGNAL (mapped (int)),
323  this, SLOT (toggle_header (int)));
324 
325  menu.exec (m_view->mapToGlobal (mpos));
326 }
327 
328 void
330 {
333 
334  QString key = ws_columns_shown_keys.at (col);
335  bool shown = settings->value (key, true).toBool ();
336 
337  m_view->setColumnHidden (col + 1, shown);
338 
339  settings->setValue (key, ! shown);
340  settings->sync ();
341 
343 }
344 
345 void
347 {
348  QMenu menu (this);
349 
350  QModelIndex index = m_view->indexAt (qpos);
351 
352  // if it isn't Local, Global etc, allow the ctx menu
353  if (index.isValid () && index.column () == 0)
354  {
355  QString var_name = get_var_name (index);
356 
357  menu.addAction (tr ("Open in Variable Editor"), this,
359 
360  menu.addAction (tr ("Copy name"), this,
362 
363  menu.addAction (tr ("Copy value"), this,
365 
366  QAction *rename
367  = menu.addAction (tr ("Rename"), this,
369 
370  // Use m_model here instead of using "m_view->model ()" because
371  // that points to the proxy model.
372  if (! m_model->is_top_level ())
373  {
374  rename->setDisabled (true);
375  rename->setToolTip (tr ("Only top-level symbols may be renamed"));
376  }
377 
378  menu.addAction ("Clear " + var_name, this,
380 
381  menu.addSeparator ();
382 
383  menu.addAction ("disp (" + var_name + ')', this,
385 
386  menu.addAction ("plot (" + var_name + ')', this,
388 
389  menu.addAction ("stem (" + var_name + ')', this,
391 
392  menu.addSeparator ();
393 
394  }
395 
396  if (m_filter_shown)
397  menu.addAction (tr ("Hide filter"), this,
399  else
400  menu.addAction (tr ("Show filter"), this,
402 
403  menu.exec (m_view->mapToGlobal (qpos));
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  QClipboard *clipboard = QApplication::clipboard ();
416 
417  clipboard->setText (var_name);
418  }
419 }
420 
421 void
423 {
424  QModelIndex index = m_view->currentIndex ();
425 
426  if (index.isValid ())
428 }
429 
430 void
432 {
433  QModelIndex index = m_view->currentIndex ();
434 
435  if (index.isValid ())
436  {
437  QString var_name = get_var_name (index);
438 
439  QInputDialog *inputDialog = new QInputDialog ();
440 
441  inputDialog->setOptions (QInputDialog::NoButtons);
442 
443  bool ok = false;
444 
445  QString new_name
446  = inputDialog->getText (nullptr, "Rename Variable", "New name:",
447  QLineEdit::Normal, var_name, &ok);
448 
449  if (ok && ! new_name.isEmpty ())
450  emit rename_variable_signal (var_name, new_name);
451  }
452 }
453 
454 void
456 {
457  QModelIndex index = m_view->currentIndex ();
458 
459  if (index.isValid ())
460  emit edit_variable_signal (get_var_name (index));
461 }
462 
463 void
465 {
466  relay_contextmenu_command ("clear", true);
467 }
468 
469 void
471 {
472  relay_contextmenu_command ("disp");
473 }
474 
475 void
477 {
478  relay_contextmenu_command ("figure (); plot");
479 }
480 
481 void
483 {
484  relay_contextmenu_command ("figure (); stem");
485 }
486 
487 void
489 {
491  m_filter_widget->setVisible (m_filter_shown);
492 
494 }
495 
496 void
498 {
499  // m_view->resizeRowsToContents ();
500  // Just modify those rows that have been added rather than go through
501  // the whole list. For-loop test will handle when number of rows reduced.
502  QFontMetrics fm = m_view->fontMetrics ();
503  int row_height = fm.height ();
504  int new_row_count = m_filter_model.rowCount ();
505  for (int i = m_view_previous_row_count; i < new_row_count; i++)
506  m_view->setRowHeight (i, row_height);
507  m_view_previous_row_count = new_row_count;
508 }
509 
510 void
512 {
513  if (m_view->hasFocus ())
515 }
516 
517 void
519 {
520  if (m_view->hasFocus ())
521  m_view->selectAll ();
522 }
523 
524 void
525 workspace_view::relay_contextmenu_command (const QString& cmdname, bool str)
526 {
527  QModelIndex index = m_view->currentIndex ();
528 
529  if (index.isValid ())
530  {
531  QString var_name;
532 
533  if (str)
534  var_name = "\'" + get_var_name (index) + "\'";
535  else
536  var_name = get_var_name (index);
537 
538  emit command_requested (cmdname + " (" + var_name + ");");
539  }
540 }
541 
542 QString
543 workspace_view::get_var_name (const QModelIndex& index)
544 {
545  // We are using a sort model proxy so m_model won't provide the
546  // correct ordering.
547 
548  QAbstractItemModel *m = m_view->model ();
549 
550  QMap<int, QVariant> item_data
551  = m->itemData (index.sibling (index.row (), 0));
552 
553  return item_data[0].toString ();
554 }
555 
OCTAVE_END_NAMESPACE(octave)
Base class for Octave interfaces that use Qt.
resource_manager & get_resource_manager(void)
base_qobject & m_octave_qobj
virtual void save_settings(void)
gui_settings * get_settings(void) const
QColor storage_class_color(int s_class)
bool is_top_level(void) const
void notice_settings(const gui_settings *)
void rename_variable_signal(const QString &, const QString &)
Signal that user wants to rename a variable.
void handle_contextmenu_edit(void)
void filter_activate(bool enable)
void copyClipboard(void)
void update_filter_history(void)
QCheckBox * m_filter_checkbox
void command_requested(const QString &cmd)
Signal that user had requested a command on a variable.
void handle_contextmenu_disp(void)
void handle_contextmenu_filter(void)
void save_settings(void)
void edit_variable_signal(const QString &)
Signal that user wants to edit a variable.
void relay_contextmenu_command(const QString &cmdname, bool str=false)
int m_view_previous_row_count
void handle_contextmenu_clear(void)
void notice_settings(const gui_settings *)
QString get_var_name(const QModelIndex &index)
QSortFilterProxyModel m_filter_model
void selectAll(void)
void handle_contextmenu_copy_value(void)
void handle_contextmenu_stem(void)
void setModel(workspace_model *model)
QWidget * m_filter_widget
void handle_contextmenu_plot(void)
void toggle_header(int column)
void handle_contextmenu_copy(void)
workspace_model * m_model
void copy_variable_value_to_clipboard(const QString &)
Signal that user wnats to copy a variable value to the clipboard.
void set_filter_focus(bool focus)
QComboBox * m_filter
QTableView * m_view
void handle_contextmenu_rename(void)
void contextmenu_requested(const QPoint &pos)
void filter_update(const QString &expression)
void handle_model_changed(void)
void header_contextmenu_requested(const QPoint &mpos)
OCTAVE_BEGIN_NAMESPACE(octave) static octave_value daspk_fcn
int rename(const std::string &from, const std::string &to)
Definition: file-ops.cc:559
void translate(Matrix &m, double x, double y, double z)
Definition: graphics.cc:5857
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 gui_pref ws_mru_list("workspaceview/mru_list", QVariant())
const gui_pref ws_sort_column("workspaceview/sort_by_column", QVariant(0))
const gui_pref ws_enable_colors("workspaceview/enable_colors", QVariant(false))
const QStringList ws_color_names
const QStringList ws_columns_shown_keys
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
const QStringList ws_columns_shown
T octave_idx_type m
Definition: mx-inlines.cc:773
static uint32_t state[624]
Definition: randmtzig.cc:193
const QString key
const QVariant def