GNU Octave 7.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-2022 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
49namespace octave
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 setWindowIcon (QIcon (":/actions/icons/logo.png"));
59 set_title (tr ("Workspace"));
60 setStatusTip (tr ("View the variables in the active workspace."));
61
62 m_filter->setToolTip (tr ("Enter text to filter the workspace"));
63 m_filter->setEditable (true);
64 m_filter->setMaxCount (ws_max_filter_history.def.toInt ());
65 m_filter->setInsertPolicy (QComboBox::NoInsert);
66 m_filter->setSizeAdjustPolicy (QComboBox::AdjustToMinimumContentsLengthWithIcon);
67 QSizePolicy sizePol (QSizePolicy::Expanding, QSizePolicy::Preferred);
68 m_filter->setSizePolicy (sizePol);
69 m_filter->completer ()->setCaseSensitivity (Qt::CaseSensitive);
70
71 QLabel *filter_label = new QLabel (tr ("Filter"));
72
73 m_view->setWordWrap (false);
74 m_view->setContextMenuPolicy (Qt::CustomContextMenu);
75 m_view->setShowGrid (false);
76 (m_view->verticalHeader) ()->hide ();
77 m_view->setAlternatingRowColors (true);
79
80 // Set an empty widget, so we can assign a layout to it.
81 setWidget (new QWidget (this));
82
83 // Create the layouts
84 QHBoxLayout *filter_layout = new QHBoxLayout ();
85
86 filter_layout->addWidget (filter_label);
87 filter_layout->addWidget (m_filter_checkbox);
88 filter_layout->addWidget (m_filter);
89 filter_layout->setMargin (0);
90
91 m_filter_widget->setLayout (filter_layout);
92
93 QVBoxLayout *ws_layout = new QVBoxLayout ();
94 ws_layout->addWidget (m_filter_widget);
95 ws_layout->addWidget (m_view);
96 ws_layout->setSpacing (0);
97
100
101 if (settings)
102 {
103 m_filter_shown = settings->value (ws_filter_shown).toBool ();
104 m_filter_widget->setVisible (m_filter_shown);
105
106 ws_layout->setMargin (2);
107
108 // Set the empty widget to have our layout.
109 widget ()->setLayout (ws_layout);
110
111 // Initialize collapse/expand state of the workspace subcategories.
112
113 //enable sorting (setting column and order after model was set)
114 m_view->setSortingEnabled (true);
115 // Initialize column order and width of the workspace
116 m_view->horizontalHeader ()->restoreState
117 (settings->value (ws_column_state.key).toByteArray ());
118
119 // Set header properties for sorting
120 m_view->horizontalHeader ()->setSectionsClickable (true);
121 m_view->horizontalHeader ()->setSectionsMovable (true);
122 m_view->horizontalHeader ()->setSortIndicator (
123 settings->value (ws_sort_column).toInt (),
124 static_cast<Qt::SortOrder> (settings->value (ws_sort_order).toUInt ()));
125 // FIXME: use value<Qt::SortOrder> instead of static cast after
126 // dropping support of Qt 5.4
127
128 m_view->horizontalHeader ()->setSortIndicatorShown (true);
129
130 m_view->horizontalHeader ()->setContextMenuPolicy (Qt::CustomContextMenu);
131 connect (m_view->horizontalHeader (),
132 &QTableView::customContextMenuRequested,
134
135 // Init state of the filter
136 m_filter->addItems (settings->value (ws_mru_list.key).toStringList ());
137
138 bool filter_state =
139 settings->value (ws_filter_active).toBool ();
140 m_filter_checkbox->setChecked (filter_state);
141 filter_activate (filter_state);
142 }
143
144 // Connect signals and slots.
145
146 connect (m_filter, &QComboBox::editTextChanged,
148 connect (m_filter_checkbox, &QCheckBox::toggled,
150 connect (m_filter->lineEdit (), &QLineEdit::editingFinished,
152
153 connect (m_view, &QTableView::customContextMenuRequested,
155
156 connect (m_view, &QTableView::activated,
158
159 if (! p)
160 make_window ();
161 }
162
164 {
165 m_filter_model.setSourceModel (model);
166 m_filter_model.setFilterKeyColumn(0);
167
168 m_view->setModel (&m_filter_model);
169
170 // set the sorting after the model is set, it would be ignored otherwise
173 m_view->sortByColumn (
174 settings->value (ws_sort_column).toInt (),
175 static_cast<Qt::SortOrder> (settings->value (ws_sort_order).toUInt ()));
176 // FIXME: use value<Qt::SortOrder> instead of static cast after
177 // dropping support of Qt 5.4
178
179 m_model = model;
180 }
181
182 void
184 {
185 m_model->notice_settings (settings); // update colors of model first
186
187 for (int i = 0; i < ws_columns_shown.length (); i++)
188 m_view->setColumnHidden (i + 1, ! settings->value (ws_columns_shown_keys.at (i), true).toBool ());
189
190 QString tool_tip;
191
192 if (settings->value (ws_enable_colors).toBool ()
193 && ! settings->value (ws_hide_tool_tips).toBool ())
194 {
195 tool_tip = QString (tr ("View the variables in the active workspace.<br>"));
196 tool_tip += QString (tr ("Colors for variable attributes:"));
197
198 for (int i = 0; i < ws_colors_count; i++)
199 {
200 tool_tip +=
201 QString (R"(<div style="background-color:%1;color:%2">%3</div>)")
202 .arg (m_model->storage_class_color (i).name ())
203 .arg (m_model->storage_class_color (i + ws_colors_count).name ())
204 .arg (QCoreApplication::translate ("octave::settings_dialog",
205 ws_color_names.at (i).toStdString ().data ()));
206 }
207 }
208
209 setToolTip (tool_tip);
210 }
211
212 void
214 {
217
218 if (! settings)
219 return;
220
221 settings->setValue (ws_column_state.key,
222 m_view->horizontalHeader ()->saveState ());
223
224 int sort_column = m_view->horizontalHeader ()->sortIndicatorSection ();
225 Qt::SortOrder sort_order = m_view->horizontalHeader ()->sortIndicatorOrder ();
226 settings->setValue (ws_sort_column.key, sort_column);
227 settings->setValue (ws_sort_order.key, sort_order);
228
229 settings->setValue (ws_filter_active.key, m_filter_checkbox->isChecked ());
231
232 QStringList mru;
233 for (int i = 0; i < m_filter->count (); i++)
234 mru.append (m_filter->itemText (i));
235 settings->setValue (ws_mru_list.key, mru);
236
237 settings->sync ();
238
240 }
241
243 {
244 if (focus)
245 {
246 m_filter->setFocus ();
247 setFocusProxy (m_filter);
248 }
249 else
250 {
251 m_view->setFocus ();
252 setFocusProxy (m_view);
253 }
254 }
255
256 void
257 workspace_view::filter_update (const QString& expression)
258 {
259 m_filter_model.setFilterWildcard (expression);
261 }
262
263 void
265 {
266 m_filter->setEnabled (state);
267 m_filter_model.setDynamicSortFilter (state);
268
269 if (state)
270 filter_update (m_filter->currentText ());
271 else
272 filter_update (QString ());
273
275 }
276
277 void
279 {
280 QString text = m_filter->currentText (); // get current text
281 int index = m_filter->findText (text); // and its actual index
282
283 if (index > -1)
284 m_filter->removeItem (index); // remove if already existing
285
286 m_filter->insertItem (0, text); // (re)insert at beginning
287 m_filter->setCurrentIndex (0);
288 }
289
290 void
292 {
293 QMenu menu (this);
294 QSignalMapper sig_mapper (this);
295
298
299 for (int i = 0; i < ws_columns_shown.length (); i++)
300 {
301 QAction *action
302 = menu.addAction (tr (ws_columns_shown.at (i).toStdString ().data ()),
303 &sig_mapper, SLOT (map ()));
304 sig_mapper.setMapping (action, i);
305 action->setCheckable (true);
306 action->setChecked (settings->value (ws_columns_shown_keys.at (i), true).toBool ());
307 }
308
309 // FIXME: We could use
310 //
311 // connect (&m_sig_mapper, QOverload<int>::of (&QSignalMapper::mapped),
312 // this, &workspace_view::toggle_header);
313 //
314 // but referring to QSignalMapper::mapped will generate deprecated
315 // function warnings from GCC. We could also use
316 //
317 // connect (&m_sig_mapper, &QSignalMapper::mappedInt,
318 // this, &workspace_view::toggle_header);
319 //
320 // but the function mappedInt was not introduced until Qt 5.15 so
321 // we'll need a feature test.
322
323 connect (&sig_mapper, SIGNAL (mapped (int)),
324 this, SLOT (toggle_header (int)));
325
326 menu.exec (m_view->mapToGlobal (mpos));
327 }
328
329 void
331 {
334
335 QString key = ws_columns_shown_keys.at (col);
336 bool shown = settings->value (key, true).toBool ();
337
338 m_view->setColumnHidden (col + 1, shown);
339
340 settings->setValue (key, ! shown);
341 settings->sync ();
342
344 }
345
346 void
348 {
349 QMenu menu (this);
350
351 QModelIndex index = m_view->indexAt (qpos);
352
353 // if it isn't Local, Global etc, allow the ctx menu
354 if (index.isValid () && index.column () == 0)
355 {
356 QString var_name = get_var_name (index);
357
358 menu.addAction (tr ("Open in Variable Editor"), this,
360
361 menu.addAction (tr ("Copy name"), this,
363
364 menu.addAction (tr ("Copy value"), this,
366
367 QAction *rename
368 = menu.addAction (tr ("Rename"), this,
370
371 // Use m_model here instead of using "m_view->model ()" because
372 // that points to the proxy model.
373 if (! m_model->is_top_level ())
374 {
375 rename->setDisabled (true);
376 rename->setToolTip (tr ("Only top-level symbols may be renamed"));
377 }
378
379 menu.addAction ("Clear " + var_name, this,
381
382 menu.addSeparator ();
383
384 menu.addAction ("disp (" + var_name + ')', this,
386
387 menu.addAction ("plot (" + var_name + ')', this,
389
390 menu.addAction ("stem (" + var_name + ')', this,
392
393 menu.addSeparator ();
394
395 }
396
397 if (m_filter_shown)
398 menu.addAction (tr ("Hide filter"), this,
400 else
401 menu.addAction (tr ("Show filter"), this,
403
404 menu.exec (m_view->mapToGlobal (qpos));
405 }
406
407 void
409 {
410 QModelIndex index = m_view->currentIndex ();
411
412 if (index.isValid ())
413 {
414 QString var_name = get_var_name (index);
415
416 QClipboard *clipboard = QApplication::clipboard ();
417
418 clipboard->setText (var_name);
419 }
420 }
421
422 void
424 {
425 QModelIndex index = m_view->currentIndex ();
426
427 if (index.isValid ())
429 }
430
431 void
433 {
434 QModelIndex index = m_view->currentIndex ();
435
436 if (index.isValid ())
437 {
438 QString var_name = get_var_name (index);
439
440 QInputDialog *inputDialog = new QInputDialog ();
441
442 inputDialog->setOptions (QInputDialog::NoButtons);
443
444 bool ok = false;
445
446 QString new_name
447 = inputDialog->getText (nullptr, "Rename Variable", "New name:",
448 QLineEdit::Normal, var_name, &ok);
449
450 if (ok && ! new_name.isEmpty ())
451 emit rename_variable_signal (var_name, new_name);
452 }
453 }
454
455 void
457 {
458 QModelIndex index = m_view->currentIndex ();
459
460 if (index.isValid ())
461 emit edit_variable_signal (get_var_name (index));
462 }
463
464 void
466 {
467 relay_contextmenu_command ("clear", true);
468 }
469
470 void
472 {
474 }
475
476 void
478 {
479 relay_contextmenu_command ("figure (); plot");
480 }
481
482 void
484 {
485 relay_contextmenu_command ("figure (); stem");
486 }
487
488 void
490 {
492 m_filter_widget->setVisible (m_filter_shown);
493
495 }
496
497 void
499 {
500 // m_view->resizeRowsToContents ();
501 // Just modify those rows that have been added rather than go through
502 // the whole list. For-loop test will handle when number of rows reduced.
503 QFontMetrics fm = m_view->fontMetrics ();
504 int row_height = fm.height ();
505 int new_row_count = m_filter_model.rowCount ();
506 for (int i = m_view_previous_row_count; i < new_row_count; i++)
507 m_view->setRowHeight (i, row_height);
508 m_view_previous_row_count = new_row_count;
509 }
510
511 void
513 {
514 if (m_view->hasFocus ())
516 }
517
518 void
520 {
521 if (m_view->hasFocus ())
522 m_view->selectAll ();
523 }
524
525 void
526 workspace_view::relay_contextmenu_command (const QString& cmdname, bool str)
527 {
528 QModelIndex index = m_view->currentIndex ();
529
530 if (index.isValid ())
531 {
532 QString var_name;
533
534 if (str)
535 var_name = "\'" + get_var_name (index) + "\'";
536 else
537 var_name = get_var_name (index);
538
539 emit command_requested (cmdname + " (" + var_name + ");");
540 }
541 }
542
543 QString
544 workspace_view::get_var_name (const QModelIndex& index)
545 {
546 // We are using a sort model proxy so m_model won't provide the
547 // correct ordering.
548
549 QAbstractItemModel *m = m_view->model ();
550
551 QMap<int, QVariant> item_data
552 = m->itemData (index.sibling (index.row (), 0));
553
554 return item_data[0].toString ();
555 }
556}
Base class for Octave interfaces that use Qt.
resource_manager & get_resource_manager(void)
void set_title(const QString &)
void make_window(bool widget_was_dragged=false)
virtual void save_settings(void)
gui_settings * get_settings(void) const
bool is_top_level(void) const
QColor storage_class_color(int s_class)
void notice_settings(const gui_settings *)
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 set_filter_focus(bool focus)
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 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 edit_variable_signal(const QString &)
Signal that user wants to edit a variable.
void handle_contextmenu_rename(void)
void handle_contextmenu_disp(void)
void copy_variable_value_to_clipboard(const QString &)
Signal that user wnats to copy a variable value to the clipboard.
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 translate(Matrix &m, double x, double y, double z)
Definition: graphics.cc:5899
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
int rename(const std::string &from, const std::string &to)
Definition: file-ops.cc:503
static uint32_t state[624]
Definition: randmtzig.cc:192
const QString key
const QVariant def