GNU Octave  6.2.0
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
variable-editor.cc
Go to the documentation of this file.
1 ////////////////////////////////////////////////////////////////////////
2 //
3 // Copyright (C) 2013-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 #ifdef HAVE_CONFIG_H
27 # include "config.h"
28 #endif
29 
30 #include <algorithm>
31 #include <limits>
32 
33 #include <QApplication>
34 #include <QClipboard>
35 #include <QFileDialog>
36 #include <QHeaderView>
37 #include <QLabel>
38 #include <QMdiArea>
39 #include <QMenu>
40 #include <QPalette>
41 #include <QScreen>
42 #include <QScrollBar>
43 #include <QStackedWidget>
44 #include <QTabWidget>
45 #include <QTableView>
46 #include <QTextEdit>
47 #include <QToolBar>
48 #include <QToolButton>
49 #include <QVBoxLayout>
50 
51 #include "dw-main-window.h"
52 #include "gui-preferences-cs.h"
53 #include "gui-preferences-global.h"
54 #include "gui-preferences-ve.h"
55 #include "octave-qobject.h"
56 #include "qt-utils.h"
57 #include "shortcut-manager.h"
58 #include "variable-editor-model.h"
59 #include "variable-editor.h"
60 
61 namespace octave
62 {
63  // Code reuse functions
64 
65  static QSignalMapper *
67  {
68  QList<QString> list;
69  list << "plot" << "bar" << "stem" << "stairs" << "area" << "pie" << "hist";
70 
71  QSignalMapper *plot_mapper = new QSignalMapper (menu);
72 
73  for (int i = 0; i < list.size(); ++i)
74  plot_mapper->setMapping
75  (menu->addAction (list.at (i), plot_mapper, SLOT (map ())), list.at (i));
76 
77  return plot_mapper;
78  }
79 
80  // Variable dock widget
81 
83  base_qobject& oct_qobj)
84  : label_dock_widget (p, oct_qobj)
85 // See Octave bug #53807 and https://bugreports.qt.io/browse/QTBUG-44813
86 #if (QT_VERSION >= 0x050302) && (QT_VERSION <= QTBUG_44813_FIX_VERSION)
87  , m_waiting_for_mouse_move (false)
88  , m_waiting_for_mouse_button_release (false)
89 #endif
90  {
91  setFocusPolicy (Qt::StrongFocus);
92  setAttribute (Qt::WA_DeleteOnClose);
93 
94  connect (m_dock_action, SIGNAL (triggered (bool)),
95  this, SLOT (change_floating (bool)));
96  connect (m_close_action, SIGNAL (triggered (bool)),
97  this, SLOT (change_existence (bool)));
98  connect (this, SIGNAL (topLevelChanged(bool)),
99  this, SLOT (toplevel_change (bool)));
100  connect (p, SIGNAL (visibilityChanged (bool)),
101  this, SLOT (setVisible (bool)));
102 
103 #if defined (HAVE_QGUIAPPLICATION)
104 #define DOCKED_FULLSCREEN_BUTTON_TOOLTIP "Fullscreen undock"
105 #define UNDOCKED_FULLSCREEN_BUTTON_TOOLTIP "Fullscreen"
106  // Add a fullscreen button
107 
108  m_fullscreen_action = nullptr;
109  m_full_screen = false;
110  m_prev_floating = false;
111  m_prev_geom = QRect (0, 0, 0, 0);
112 
113  QHBoxLayout *h_layout = m_title_widget->findChild<QHBoxLayout *> ();
115  m_fullscreen_action
116  = new QAction (rmgr.icon ("view-fullscreen", false), "", this);
117  m_fullscreen_action->setToolTip (tr (DOCKED_FULLSCREEN_BUTTON_TOOLTIP));
118  QToolButton *fullscreen_button = new QToolButton (m_title_widget);
119  fullscreen_button->setDefaultAction (m_fullscreen_action);
120  fullscreen_button->setFocusPolicy (Qt::NoFocus);
121  fullscreen_button->setIconSize (QSize (m_icon_size,m_icon_size));
122  QString css_button = QString ("QToolButton {background: transparent; border: 0px;}");
123  fullscreen_button->setStyleSheet (css_button);
124 
125  connect (m_fullscreen_action, SIGNAL (triggered ()),
126  this, SLOT (change_fullscreen ()));
127 
128  int index = -1;
129  QToolButton *first = m_title_widget->findChild<QToolButton *> ();
130  if (first != nullptr)
131  index = h_layout->indexOf (first);
132  h_layout->insertWidget (index, fullscreen_button);
133 #endif
134 
135  // Custom title bars cause loss of decorations, add a frame
136  m_frame = new QFrame (this);
137  m_frame->setFrameStyle (QFrame::Box | QFrame::Sunken);
138  m_frame->setAttribute (Qt::WA_TransparentForMouseEvents);
139  }
140 
141  // slot for (un)dock action
142  void
144  {
145 #if defined (HAVE_QGUIAPPLICATION)
146  if (isFloating ())
147  {
148  if (m_full_screen)
149  {
150  setGeometry (m_prev_geom);
152  m_fullscreen_action->setIcon (rmgr.icon ("view-fullscreen", false));
153  m_full_screen = false;
154  }
155  m_fullscreen_action->setToolTip (tr (DOCKED_FULLSCREEN_BUTTON_TOOLTIP));
156  }
157  else
158  m_fullscreen_action->setToolTip (tr (UNDOCKED_FULLSCREEN_BUTTON_TOOLTIP));
159 #endif
160 
161  setFloating (! isFloating ());
162  }
163 
164  // slot for hiding the widget
165  void
167  {
168  close ();
169  }
170 
171  void
173  {
174  if (toplevel)
175  {
176  m_dock_action->setIcon (QIcon (":/actions/icons/widget-dock.png"));
177  m_dock_action->setToolTip (tr ("Dock widget"));
178 
179  setWindowFlags (Qt::Window);
180  setWindowTitle (tr ("Variable Editor: ") + objectName ());
181 
182  show ();
183  activateWindow ();
184  setFocus ();
185 
186 // See Octave bug #53807 and https://bugreports.qt.io/browse/QTBUG-44813
187 #if (QT_VERSION >= 0x050302) && (QT_VERSION <= QTBUG_44813_FIX_VERSION)
188  m_waiting_for_mouse_move = true;
189 #endif
190  }
191  else
192  {
193  m_dock_action->setIcon (QIcon (":/actions/icons/widget-undock.png"));
194  m_dock_action->setToolTip (tr ("Undock widget"));
195 
196  setFocus ();
197 
198 // See Octave bug #53807 and https://bugreports.qt.io/browse/QTBUG-44813
199 #if (QT_VERSION >= 0x050302) && (QT_VERSION <= QTBUG_44813_FIX_VERSION)
200  m_waiting_for_mouse_move = false;
201  m_waiting_for_mouse_button_release = false;
202 #endif
203  }
204  }
205 
206  void
208  {
209 #if defined (HAVE_QGUIAPPLICATION)
211 
212  if (! m_full_screen)
213  {
214  m_prev_floating = isFloating ();
215  m_fullscreen_action->setIcon (rmgr.icon ("view-restore", false));
216  if (m_prev_floating)
217  m_fullscreen_action->setToolTip (tr ("Restore geometry"));
218  else
219  {
220  m_fullscreen_action->setToolTip (tr ("Redock"));
221  setFloating (true);
222  }
223  m_prev_geom = geometry ();
224 
225  // showFullscreen() and setWindowState() only work for QWindow objects.
226  QScreen *pscreen = QGuiApplication::primaryScreen ();
227  QRect rect (0, 0, 0, 0);
228  rect = pscreen->availableGeometry ();
229  setGeometry (rect);
230 
231  m_full_screen = true;
232  }
233  else
234  {
235  m_fullscreen_action->setIcon (rmgr.icon ("view-fullscreen", false));
236  setGeometry (m_prev_geom);
237  if (m_prev_floating)
238  m_fullscreen_action->setToolTip (tr (UNDOCKED_FULLSCREEN_BUTTON_TOOLTIP));
239  else
240  {
241  setFloating (false);
242  m_fullscreen_action->setToolTip (tr (DOCKED_FULLSCREEN_BUTTON_TOOLTIP));
243  }
244 
245  m_full_screen = false;
246  }
247 #undef DOCKED_FULLSCREEN_BUTTON_TOOLTIP
248 #undef UNDOCKED_FULLSCREEN_BUTTON_TOOLTIP
249 #endif
250  }
251 
252  void
254  {
255  QDockWidget::closeEvent (e);
256  }
257 
258  void
260  {
261  octave_unused_parameter (now);
262 
263  // This is a proxied test
264  if (hasFocus ())
265  {
266  if (old == this)
267  return;
268 
269  if (titleBarWidget () != nullptr)
270  {
271  QLabel *label = titleBarWidget ()->findChild<QLabel *> ();
272  if (label != nullptr)
273  {
274  label->setBackgroundRole (QPalette::Highlight);
275  label->setStyleSheet ("background-color: palette(highlight); color: palette(highlightedText);");
276  }
277  }
278 
279  emit variable_focused_signal (objectName ());
280  }
281  else if (old == focusWidget())
282  {
283  if (titleBarWidget () != nullptr)
284  {
285  QLabel *label = titleBarWidget ()->findChild<QLabel *> ();
286  if (label != nullptr)
287  {
288  label->setBackgroundRole (QPalette::NoRole);
289  label->setStyleSheet (";");
290  }
291  }
292  }
293  }
294 
295  void variable_dock_widget::resizeEvent (QResizeEvent *)
296  {
297  if (m_frame)
298  m_frame->resize (size ());
299  }
300 
301 // See Octave bug #53807 and https://bugreports.qt.io/browse/QTBUG-44813
302 #if (QT_VERSION >= 0x050302) && (QT_VERSION <= QTBUG_44813_FIX_VERSION)
303 
304  bool
305  variable_dock_widget::event (QEvent *event)
306  {
307  // low-level check of whether docked-widget became a window via
308  // via drag-and-drop
309  if (event->type () == QEvent::MouseButtonPress)
310  {
311  m_waiting_for_mouse_move = false;
312  m_waiting_for_mouse_button_release = false;
313  }
314  if (event->type () == QEvent::MouseMove && m_waiting_for_mouse_move)
315  {
316  m_waiting_for_mouse_move = false;
317  m_waiting_for_mouse_button_release = true;
318  }
319  if (event->type () == QEvent::MouseButtonRelease
320  && m_waiting_for_mouse_button_release)
321  {
322  m_waiting_for_mouse_button_release = false;
323  bool retval = QDockWidget::event (event);
324  if (isFloating ())
325  emit queue_unfloat_float ();
326  return retval;
327  }
328 
329  return QDockWidget::event (event);
330  }
331 
332  void
334  {
335  hide ();
336  setFloating (false);
337  // Avoid a Ubunty Unity issue by queuing this rather than direct.
338  emit queue_float ();
339  m_waiting_for_mouse_move = false;
340  m_waiting_for_mouse_button_release = false;
341  }
342 
343  void
345  {
346  setFloating (true);
347  m_waiting_for_mouse_move = false;
348  m_waiting_for_mouse_button_release = false;
349  show ();
350  activateWindow ();
351  setFocus ();
352  }
353 
354 #else
355 
356  void
358  { }
359 
360  void
362  { }
363 
364 #endif
365 
366  // Variable editor stack
367 
369  base_qobject& oct_qobj)
370  : QStackedWidget (p), m_octave_qobj (oct_qobj),
371  m_edit_view (new variable_editor_view (this, m_octave_qobj))
372  {
373  setFocusPolicy (Qt::StrongFocus);
374 
375  m_disp_view = make_disp_view (this);
376 
377  addWidget (m_edit_view);
378  addWidget (m_disp_view);
379  }
380 
381  QTextEdit *
383  {
384  QTextEdit *viewer = new QTextEdit (parent);
385 
386  viewer->setLineWrapMode (QTextEdit::NoWrap);
387  viewer->setReadOnly (true);
388 
389  return viewer;
390  }
391 
392  void
394  {
395  // The QTableView is for editable data models
396  // and the QTextEdit is for non-editable models.
397 
398  if (editable)
399  {
400  if (m_edit_view != nullptr)
401  {
402  setCurrentWidget (m_edit_view);
403  setFocusProxy (m_edit_view);
404  m_edit_view->setFocusPolicy (Qt::StrongFocus);
405  }
406 
407  if (m_disp_view != nullptr)
408  m_disp_view->setFocusPolicy (Qt::NoFocus);
409  }
410  else
411  {
412  if (m_disp_view != nullptr)
413  {
414  setCurrentWidget (m_disp_view);
415  setFocusProxy (m_disp_view);
416 
417  QAbstractTableModel *model = findChild<QAbstractTableModel *> ();
418  if (model != nullptr)
419  m_disp_view->setPlainText (model->data (QModelIndex ()).toString ());
420  else
421  m_disp_view->setPlainText ("");
422  }
423 
424  if (m_edit_view != nullptr)
425  m_edit_view->setFocusPolicy (Qt::NoFocus);
426  }
427  }
428 
429  void
431  {
432  if (! hasFocus ())
433  return;
434 
435  QString name = objectName ();
436 
437  // FIXME: Is there a better way?
438 
439  if (name.endsWith (')') || name.endsWith ('}'))
440  {
441  name.remove ( QRegExp ("[({][^({]*[)}]$)") );
443  }
444  }
445 
446  void
448  {
449  if (! hasFocus ())
450  return;
451 
452  // FIXME: Remove, if for all common KDE versions (bug #54607) is resolved.
453  int opts = 0; // No options by default.
456  if (! settings->value (global_use_native_dialogs).toBool ())
457  opts = QFileDialog::DontUseNativeDialog;
458 
459  QString name = objectName ();
460  QString file
461  = QFileDialog::getSaveFileName (this,
462  tr ("Save Variable %1 As").arg (name),
463  // FIXME: Should determine extension from save_default_options
464  QString ("./%1.txt").arg (name),
465  0, 0, QFileDialog::Option (opts));
466 
467  // FIXME: Type? binary, float-binary, ascii, text, hdf5, matlab format?
468  // FIXME: Call octave_value::save_* directly?
469 
470  if (! file.isEmpty ())
471  emit command_signal (QString ("save (\"%1\", \"%2\");")
472  .arg (file)
473  .arg (name));
474  }
475 
476 
477  // Custom editable variable table view
478 
480  base_qobject& oct_qobj)
481  : QTableView (p), m_octave_qobj (oct_qobj), m_var_model (nullptr)
482  {
483  setWordWrap (false);
484  setContextMenuPolicy (Qt::CustomContextMenu);
485  setSelectionMode (QAbstractItemView::ContiguousSelection);
486 
487  horizontalHeader ()->setContextMenuPolicy (Qt::CustomContextMenu);
488  verticalHeader ()->setContextMenuPolicy (Qt::CustomContextMenu);
489 
490  setHorizontalScrollMode (QAbstractItemView::ScrollPerPixel);
491  setVerticalScrollMode (QAbstractItemView::ScrollPerPixel);
492 
493 #if defined (HAVE_QHEADERVIEW_SETSECTIONRESIZEMODE)
494  verticalHeader ()->setSectionResizeMode (QHeaderView::Interactive);
495 #else
496  verticalHeader ()->setResizeMode (QHeaderView::Interactive);
497 #endif
498  }
499 
500  void
501  variable_editor_view::setModel (QAbstractItemModel *model)
502  {
503  QTableView::setModel (model);
504 
505 #if defined (HAVE_QHEADERVIEW_SETSECTIONRESIZEMODE)
506  horizontalHeader ()->setSectionResizeMode (QHeaderView::Interactive);
507 #else
508  horizontalHeader ()->setResizeMode (QHeaderView::Interactive);
509 #endif
510 
511  m_var_model = parent ()->findChild<variable_editor_model *> ();
512 
513  if (m_var_model != nullptr && m_var_model->column_width () > 0)
514  {
515  // col_width is in characters. The font should be a fixed-width
516  // font, so any character will do. If not, you lose!
517 
518  QFontMetrics fm (font ());
519  int w = (m_var_model->column_width ()
521  horizontalHeader ()->setDefaultSectionSize (w);
522  }
523  }
524 
525  QList<int>
527  {
528  QItemSelectionModel *sel = selectionModel ();
529 
530  // Return early if nothing selected.
531  if (! sel->hasSelection ())
532  return QList<int> ();
533 
534  QList<QModelIndex> indices = sel->selectedIndexes ();
535 
536  // FIXME: Shouldn't this be keyed to octave_idx_type?
537 
538  int32_t from_row = std::numeric_limits<int32_t>::max ();
539  int32_t to_row = 0;
540  int32_t from_col = std::numeric_limits<int32_t>::max ();
541  int32_t to_col = 0;
542 
543  for (const auto& idx : indices)
544  {
545  from_row = std::min (from_row, idx.row ());
546  to_row = std::max (to_row, idx.row ());
547  from_col = std::min (from_col, idx.column ());
548  to_col = std::max (to_col, idx.column ());
549  }
550 
551  QVector<int> vect;
552  vect << from_row + 1 << to_row + 1 << from_col + 1 << to_col + 1;
553  QList<int> range = QList<int>::fromVector(vect);
554 
555  return range;
556  }
557 
558  void
560  {
561  if (! hasFocus ())
562  return;
563 
564  QList<int> range = range_selected ();
565  if (range.isEmpty ())
566  {
567  // Nothing selected, apply print command to all data
568  range << 1 << m_var_model->data_rows ()
569  << 1 << m_var_model->data_columns ();
570  }
571 
572  int s1 = m_var_model->data_rows ();
573  int s2 = m_var_model->data_columns ();
574  if (s1 < range.at (0) || s2 < range.at (2))
575  return; // Selected range does not contain data
576 
577  s1 = std::min (s1, range.at (1));
578  s2 = std::min (s2, range.at (3));
579 
580  // Variable with desired range as string
581  QString variable = QString ("%1(%2:%3,%4:%5)")
582  .arg (objectName ())
583  .arg (range.at (0)).arg (s1)
584  .arg (range.at (2)).arg (s2);
585 
586  // Desired command as string
587  QString command;
588  if (cmd == "create")
589  command = QString ("unnamed = %1;").arg (variable);
590  else
591  command = QString ("figure (); %1 (%2); title ('%2');")
592  .arg (cmd).arg (variable);
593 
594  emit command_signal (command);
595  }
596 
597  void
599  const QString& qualifier_string)
600  {
602 
603  menu->addAction (rmgr.icon ("edit-cut"),
604  tr ("Cut") + qualifier_string,
605  this, SLOT (cutClipboard ()));
606 
607  menu->addAction (rmgr.icon ("edit-copy"),
608  tr ("Copy") + qualifier_string,
609  this, SLOT (copyClipboard ()));
610 
611  menu->addAction (rmgr.icon ("edit-paste"),
612  tr ("Paste"),
613  this, SLOT (pasteClipboard ()));
614 
615  menu->addSeparator ();
616 
617  menu->addAction (rmgr.icon ("edit-delete"),
618  tr ("Clear") + qualifier_string,
619  this, SLOT (clearContent ()));
620 
621  menu->addAction (rmgr.icon ("edit-delete"),
622  tr ("Delete") + qualifier_string,
623  this, SLOT (delete_selected ()));
624 
625  menu->addAction (rmgr.icon ("document-new"),
626  tr ("Variable from Selection"),
627  this, SLOT (createVariable ()));
628  }
629 
630  void
632  {
633  QModelIndex index = indexAt (qpos);
634 
635  if (index.isValid ())
636  {
637  QMenu *menu = new QMenu (this);
638 
639  add_edit_actions (menu, tr (""));
640 
641  // FIXME: addAction for sort?
642  // FIXME: Add icon for transpose.
643 
644  menu->addAction (tr ("Transpose"), this, SLOT (transposeContent ()));
645 
646  QItemSelectionModel *sel = selectionModel ();
647 
648  QList<QModelIndex> indices = sel->selectedIndexes ();
649 
650  if (! indices.isEmpty ())
651  {
652  menu->addSeparator ();
653 
654  QSignalMapper *plot_mapper = make_plot_mapper (menu);
655 
656  connect (plot_mapper, SIGNAL (mapped (const QString&)),
657  this, SLOT (selected_command_requested (const QString&)));
658  }
659 
660  menu->exec (mapToGlobal (qpos));
661  }
662  }
663 
664  void
666  {
667  int index = horizontalHeader ()->logicalIndexAt (pt);
668 
669  if (index < 0 || index > model ()->columnCount ())
670  return;
671 
672  QList<int> coords = range_selected ();
673 
674  bool nothingSelected = coords.isEmpty ();
675 
676  bool whole_columns_selected
677  = (nothingSelected
678  ? false
679  : (coords[0] == 1 && coords[1] == model ()->rowCount ()));
680 
681  bool current_column_selected
682  = nothingSelected ? false : (coords[2] <= index+1 && coords[3] > index);
683 
684  int column_selection_count
685  = nothingSelected ? 0 : (coords[3] - coords[2] + 1);
686 
687  if (! whole_columns_selected || ! current_column_selected)
688  {
689  selectColumn (index);
690  column_selection_count = 1;
691  }
692 
693  QString column_string
694  = column_selection_count > 1 ? tr (" columns") : tr (" column");
695 
696  QMenu *menu = new QMenu (this);
697 
698  add_edit_actions (menu, column_string);
699 
700  menu->addSeparator ();
701 
702  QSignalMapper *plot_mapper = make_plot_mapper (menu);
703 
704  connect (plot_mapper, SIGNAL (mapped (const QString&)),
705  this, SLOT (selected_command_requested (const QString&)));
706 
707  QPoint menupos = pt;
708  menupos.setY (horizontalHeader ()->height ());
709 
710  menu->exec (mapToGlobal (menupos));
711  }
712 
713  void
715  {
716  int index = verticalHeader ()->logicalIndexAt (pt);
717 
718  if (index < 0 || index > model ()->columnCount ())
719  return;
720 
721  QList<int> coords = range_selected ();
722 
723  bool nothingSelected = coords.isEmpty ();
724 
725  bool whole_rows_selected
726  = (nothingSelected
727  ? false
728  : (coords[2] == 1 && coords[3] == model ()->columnCount ()));
729 
730  bool current_row_selected
731  = (nothingSelected ? false : (coords[0] <= index+1 && coords[1] > index));
732 
733  int rowselection_count = nothingSelected ? 0 : (coords[3] - coords[2] + 1);
734 
735  if (! whole_rows_selected || ! current_row_selected)
736  {
737  selectRow (index);
738  rowselection_count = 1;
739  }
740 
741  QString row_string = rowselection_count > 1 ? tr (" rows") : tr (" row");
742 
743  QMenu *menu = new QMenu (this);
744 
745  add_edit_actions (menu, row_string);
746 
747  menu->addSeparator ();
748 
749  QSignalMapper *plot_mapper = make_plot_mapper (menu);
750 
751  connect (plot_mapper, SIGNAL (mapped (const QString&)),
752  this, SLOT (selected_command_requested (const QString&)));
753 
754  QPoint menupos = pt;
755  menupos.setX (verticalHeader ()->width ());
756 
757  // FIXME: What was the intent here?
758  // setY (verticalHeader ()->sectionPosition (index+1) +
759  // verticalHeader ()->sectionSize (index));
760 
761  menu->exec (mapToGlobal (menupos));
762  }
763 
764  void
766  {
767  // FIXME: Create unnamed1..n if exist ('unnamed', 'var') is true.
768 
769  selected_command_requested ("create");
770  }
771 
772  void
774  {
775  if (! hasFocus ())
776  return;
777 
778  emit command_signal (QString ("%1 = %1';").arg (objectName ()));
779  }
780 
781  void
783  {
784  if (! hasFocus ())
785  return;
786 
787  QAbstractItemModel *mod = model ();
788  QList<int> coords = range_selected ();
789 
790  if (coords.isEmpty ())
791  return;
792 
793  bool whole_columns_selected
794  = coords[0] == 1 && coords[1] == mod->rowCount ();
795 
796  bool whole_rows_selected
797  = coords[2] == 1 && coords[3] == mod->columnCount ();
798 
799  // Must be deleting whole columns or whole rows, and not the whole thing.
800 
801  if (whole_columns_selected == whole_rows_selected)
802  return;
803 
804  if (whole_rows_selected)
805  mod->removeRows (coords[0], coords[1] - coords[0]);
806 
807  if (whole_columns_selected)
808  mod->removeColumns (coords[2], coords[3] - coords[2]);
809  }
810 
811  void
813  {
814  if (! hasFocus ())
815  return;
816 
817  if (m_var_model == nullptr)
818  return;
819 
820  QItemSelectionModel *sel = selectionModel ();
821  QList<QModelIndex> indices = sel->selectedIndexes ();
822 
823  // FIXME: Use [] for empty cells?
824 
825  for (const auto& idx : indices)
826  m_var_model->clear_content (idx);
827  }
828 
829  void
831  {
832  copyClipboard ();
833 
834  clearContent ();
835  }
836 
837  void
839  {
840  if (! hasFocus ())
841  return;
842 
843  QItemSelectionModel *sel = selectionModel ();
844  QList<QModelIndex> indices = sel->selectedIndexes ();
845  std::sort (indices.begin (), indices.end ());
846 
847  if (indices.isEmpty ())
848  return;
849 
850  // Convert selected items into TSV format and copy that.
851  // Spreadsheet tools should understand that.
852 
853  QAbstractItemModel *mod = model ();
854  QModelIndex previous = indices.first ();
855  QString copy = mod->data (previous).toString ();
856  indices.removeFirst ();
857  for (auto idx : indices)
858  {
859  copy.push_back (previous.row () != idx.row () ? '\n' : '\t');
860  copy.append (mod->data (idx).toString ());
861  previous = idx;
862  }
863 
864  QClipboard *clipboard = QApplication::clipboard ();
865  clipboard->setText (copy);
866  }
867 
868  void
870  {
871  if (! hasFocus ())
872  return;
873 
874  QAbstractItemModel *mod = model ();
875  QItemSelectionModel *sel = selectionModel ();
876  QList<QModelIndex> indices = sel->selectedIndexes ();
877 
878  QClipboard *clipboard = QApplication::clipboard ();
879  QString text = clipboard->text ();
880 
881  QPoint start, end;
882 
883  QPoint tabsize = QPoint (mod->rowCount (), mod->columnCount ());
884 
885  if (indices.isEmpty ())
886  {
887  start = QPoint (0,0);
888  end = tabsize;
889  }
890  else if (indices.size () == 1)
891  {
892  start = QPoint (indices[0].row (), indices[0].column ());
893  end = tabsize;
894  }
895  else
896  {
897  end = QPoint (0,0);
898  start = tabsize;
899 
900  for (int i = 0; i < indices.size (); i++)
901  {
902  if (indices[i].column () < start.y ())
903  start.setY (indices[i].column ());
904 
905  if (indices[i].column () > end.y ())
906  end.setY (indices[i].column ());
907 
908  if (indices[i].row () < start.x ())
909  start.setX (indices[i].column ());
910 
911  if (indices[i].row () > end.x ())
912  end.setX (indices[i].column ());
913  }
914  }
915 
916  int rownum = 0;
917  int colnum = 0;
918 
919  QStringList rows = text.split ('\n');
920  for (const auto& row : rows)
921  {
922  if (rownum > end.x () - start.x ())
923  continue;
924 
925  QStringList cols = row.split ('\t');
926  if (cols.isEmpty ())
927  continue;
928 
929  for (const auto& col : cols)
930  {
931  if (col.isEmpty ())
932  continue;
933  if (colnum > end.y () - start.y () )
934  continue;
935 
936  mod->setData (mod->index (rownum + start.x (),
937  colnum + start.y ()),
938  QVariant (col));
939 
940  colnum++;
941  }
942 
943  colnum = 0;
944  rownum++;
945  }
946  }
947 
948  void
950  {
951  if (action == QAbstractSlider::SliderSingleStepAdd
952  || action == QAbstractSlider::SliderPageStepAdd
953  || action == QAbstractSlider::SliderToMaximum
954  || action == QAbstractSlider::SliderMove)
955  {
956  if (m_var_model != nullptr)
957  {
958  QScrollBar *sb = horizontalScrollBar ();
959 
960  if (sb && sb->value () == sb->maximum ())
961  {
962  int new_cols = m_var_model->display_columns () + 16;
963 
964  m_var_model->maybe_resize_columns (new_cols);
965  }
966  }
967  }
968  }
969 
970  void
972  {
973  if (action == QAbstractSlider::SliderSingleStepAdd
974  || action == QAbstractSlider::SliderPageStepAdd
975  || action == QAbstractSlider::SliderToMaximum
976  || action == QAbstractSlider::SliderMove)
977  {
978  if (m_var_model != nullptr)
979  {
980  QScrollBar *sb = verticalScrollBar ();
981 
982  if (sb && sb->value () == sb->maximum ())
983  {
984  int new_rows = m_var_model->display_rows () + 16;
985 
986  m_var_model->maybe_resize_rows (new_rows);
987  }
988  }
989  }
990  }
991 
992 
993  // Gadgets for focus restoration
994 
996  : QToolButton (parent)
997  {
998  installEventFilter (this);
999  }
1000 
1001  bool HoverToolButton::eventFilter (QObject *obj, QEvent *ev)
1002  {
1003  if (ev->type () == QEvent::HoverEnter)
1004  emit hovered_signal ();
1005  else if (ev->type () == QEvent::MouseButtonPress)
1006  emit popup_shown_signal ();
1007 
1008  return QToolButton::eventFilter (obj, ev);
1009  }
1010 
1012  : HoverToolButton (parent)
1013  {
1014  installEventFilter (this);
1015  }
1016 
1018  {
1019 
1020  if (ev->type () == QEvent::MouseButtonRelease && isDown ())
1021  {
1022  emit about_to_activate ();
1023 
1024  setDown (false);
1025  QAction *action = defaultAction ();
1026  if (action != nullptr)
1027  action->activate (QAction::Trigger);
1028 
1029  return true;
1030  }
1031 
1032  return HoverToolButton::eventFilter (obj, ev);
1033  }
1034 
1036  : QMenu (parent)
1037  {
1038  installEventFilter (this);
1039  }
1040 
1041  bool ReturnFocusMenu::eventFilter (QObject *obj, QEvent *ev)
1042  {
1043  if (ev->type () == QEvent::MouseButtonRelease && underMouse ())
1044  {
1045  emit about_to_activate ();
1046  }
1047 
1048  return QMenu::eventFilter (obj, ev);
1049  }
1050 
1051  // Variable editor.
1052 
1054  : octave_dock_widget ("VariableEditor", p, oct_qobj),
1055  m_main (new dw_main_window (oct_qobj)),
1056  m_tool_bar (new QToolBar (m_main)),
1057  m_default_width (30),
1058  m_default_height (100),
1059  m_add_font_height (0),
1060  m_use_terminal_font (true),
1061  m_alternate_rows (true),
1062  m_stylesheet (""),
1063  m_font (),
1064  m_sel_font (),
1065  m_table_colors (),
1066  m_current_focus_vname (""),
1067  m_hovered_focus_vname (""),
1068  m_plot_mapper (nullptr),
1069  m_focus_widget (nullptr),
1070  m_focus_widget_vdw (nullptr)
1071  {
1072  set_title (tr ("Variable Editor"));
1073  setStatusTip (tr ("Edit variables."));
1074  setWindowIcon (QIcon (":/actions/icons/logo.png"));
1075  setAttribute (Qt::WA_AlwaysShowToolTips);
1076 
1077  m_main->setParent (this);
1078 // See Octave bug #53409 and https://bugreports.qt.io/browse/QTBUG-55357
1079 #if (QT_VERSION < 0x050601) || (QT_VERSION >= 0x050701)
1080  m_main->setDockOptions (QMainWindow::AnimatedDocks |
1081  QMainWindow::AllowNestedDocks |
1082  QMainWindow::VerticalTabs);
1083 #else
1084  m_main->setDockNestingEnabled (true);
1085 #endif
1086 
1087  // Tool Bar.
1088 
1089  construct_tool_bar ();
1090  m_main->addToolBar (m_tool_bar);
1091 
1092  // Colors.
1093 
1094  for (int i = 0; i < ve_colors_count; i++)
1095  m_table_colors.append (QColor (Qt::white));
1096 
1097  // Use an MDI area that is shrunk to nothing as the central widget.
1098  // Future feature might be to switch to MDI mode in which the dock
1099  // area is shrunk to nothing and the widgets live in the MDI window.
1100 
1101  QMdiArea *central_mdiarea = new QMdiArea (m_main);
1102  central_mdiarea->setMinimumSize (QSize (0, 0));
1103  central_mdiarea->setMaximumSize (QSize (0, 0));
1104  central_mdiarea->resize (QSize (0, 0));
1105  m_main->setCentralWidget (central_mdiarea);
1106 
1107  setWidget (m_main);
1108 
1109  connect (this, SIGNAL (command_signal (const QString&)),
1110  p, SLOT (execute_command_in_terminal (const QString&)));
1111  }
1112 
1113  void variable_editor::focusInEvent (QFocusEvent *ev)
1114  {
1115  octave_dock_widget::focusInEvent (ev);
1116 
1117  // set focus to the current variable or most recent if still valid
1118  if (m_focus_widget != nullptr)
1119  {
1120  // Activating a floating window causes problems.
1121  if (! m_focus_widget_vdw->isFloating ())
1122  activateWindow ();
1123  m_focus_widget->setFocus ();
1124  }
1125  else
1126  {
1127  QWidget *fw = m_main->focusWidget ();
1128  if (fw != nullptr)
1129  {
1130  activateWindow ();
1131  fw->setFocus ();
1132  }
1133  else
1134  {
1135  QDockWidget *any_qdw = m_main->findChild<QDockWidget *> ();
1136  if (any_qdw != nullptr)
1137  {
1138  activateWindow ();
1139  any_qdw->setFocus ();
1140  }
1141  else
1142  setFocus();
1143  }
1144  }
1145  }
1146 
1147  // Add an action to a menu or the widget itself.
1148 
1149  QAction*
1150  variable_editor::add_action (QMenu *menu, const QIcon& icon,
1151  const QString& text,
1152  const char *member)
1153  {
1154  QAction *a;
1155 
1156  if (menu)
1157  a = menu->addAction (icon, text, this, member);
1158  else
1159  {
1160  a = new QAction (this);
1161  connect (a, SIGNAL (triggered ()), this, member);
1162  }
1163 
1164  addAction (a); // important for shortcut context
1165  a->setShortcutContext (Qt::WidgetWithChildrenShortcut);
1166 
1167  return a;
1168  }
1169 
1170  void
1171  variable_editor::edit_variable (const QString& name, const octave_value& val)
1172  {
1174 
1175  if (m_stylesheet.isEmpty ())
1176  {
1177  gui_settings *settings = rmgr.get_settings ();
1179  }
1180 
1181  QDockWidget *existing_qdw = m_main->findChild<QDockWidget *> (name);
1182  if (existing_qdw != NULL)
1183  {
1184  // Already open.
1185 
1186  // Put current focused variable out of focus
1187  if (m_main->focusWidget () != nullptr)
1188  {
1189  QFocusEvent event (QEvent::FocusOut, Qt::OtherFocusReason);
1190  QApplication::sendEvent (m_main->focusWidget (), &event);
1191  }
1192 
1193  // Put existing variable in focus and raise
1194  m_main->parentWidget ()->show ();
1195  existing_qdw->show ();
1196  existing_qdw->raise ();
1197  existing_qdw->activateWindow ();
1198  tab_to_front ();
1199  existing_qdw->setFocus ();
1200 
1201  return;
1202  }
1203 
1204  variable_dock_widget *page
1205  = new variable_dock_widget (this, m_octave_qobj);
1206 
1207  page->setObjectName (name);
1208  m_main->addDockWidget (Qt::LeftDockWidgetArea, page);
1209 
1210  connect (QApplication::instance(), SIGNAL (focusChanged (QWidget *, QWidget *)),
1211  page, SLOT (handle_focus_change (QWidget *, QWidget *)));
1212  connect (page, SIGNAL (destroyed (QObject *)),
1213  this, SLOT (variable_destroyed (QObject *)));
1214  connect (page, SIGNAL (variable_focused_signal (const QString&)),
1215  this, SLOT (variable_focused (const QString&)));
1216 // See Octave bug #53807 and https://bugreports.qt.io/browse/QTBUG-44813
1217 #if (QT_VERSION >= 0x050302) && (QT_VERSION <= QTBUG_44813_FIX_VERSION)
1218  connect (page, SIGNAL (queue_unfloat_float ()),
1219  page, SLOT (unfloat_float ()), Qt::QueuedConnection);
1220  connect (page, SIGNAL (queue_float ()),
1221  page, SLOT (refloat ()), Qt::QueuedConnection);
1222 #endif
1223 
1224  variable_editor_stack *stack
1225  = new variable_editor_stack (page, m_octave_qobj);
1226 
1227  stack->setObjectName (name);
1228  page->setWidget (stack);
1229  page->setFocusProxy (stack);
1230 
1231  connect (stack, SIGNAL (command_signal (const QString&)),
1232  this, SIGNAL (command_signal (const QString&)));
1233  connect (stack, SIGNAL (edit_variable_signal (const QString&, const octave_value&)),
1234  this, SLOT (edit_variable (const QString&, const octave_value&)));
1235  connect (this, SIGNAL (level_up_signal ()),
1236  stack, SLOT (levelUp ()));
1237  connect (this, SIGNAL (save_signal ()),
1238  stack, SLOT (save ()));
1239 
1240  variable_editor_view *edit_view = stack->edit_view ();
1241 
1242  edit_view->setObjectName (name);
1243  edit_view->setFont (m_font);
1244  edit_view->setStyleSheet (m_stylesheet);
1245  edit_view->setAlternatingRowColors (m_alternate_rows);
1246  edit_view->verticalHeader ()->setDefaultSectionSize (m_default_height
1247  + m_add_font_height);
1248 
1249  connect (m_plot_mapper, SIGNAL (mapped (const QString&)),
1250  edit_view, SLOT (selected_command_requested (const QString&)));
1251 
1252  connect (edit_view, SIGNAL (command_signal (const QString&)),
1253  this, SIGNAL (command_signal (const QString&)));
1254  connect (this, SIGNAL (delete_selected_signal ()),
1255  edit_view, SLOT (delete_selected ()));
1256  connect (this, SIGNAL (clear_content_signal ()),
1257  edit_view, SLOT (clearContent ()));
1258  connect (this, SIGNAL (copy_clipboard_signal ()),
1259  edit_view, SLOT (copyClipboard ()));
1260  connect (this, SIGNAL (paste_clipboard_signal ()),
1261  edit_view, SLOT (pasteClipboard ()));
1262  connect (edit_view->horizontalHeader (),
1263  SIGNAL (customContextMenuRequested (const QPoint&)),
1264  edit_view, SLOT (createColumnMenu (const QPoint&)));
1265  connect (edit_view->verticalHeader (),
1266  SIGNAL (customContextMenuRequested (const QPoint&)),
1267  edit_view, SLOT (createRowMenu (const QPoint&)));
1268  connect (edit_view, SIGNAL (customContextMenuRequested (const QPoint&)),
1269  edit_view, SLOT (createContextMenu (const QPoint&)));
1270  connect (edit_view->horizontalScrollBar (), SIGNAL (actionTriggered (int)),
1271  edit_view, SLOT (handle_horizontal_scroll_action (int)));
1272  connect (edit_view->verticalScrollBar (), SIGNAL (actionTriggered (int)),
1273  edit_view, SLOT (handle_vertical_scroll_action (int)));
1274 
1275  variable_editor_model *model =
1276  new variable_editor_model (name, val, stack);
1277 
1278  connect (model, SIGNAL (edit_variable_signal (const QString&, const octave_value&)),
1279  this, SLOT (edit_variable (const QString&, const octave_value&)));
1280  connect (model, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)),
1281  this, SLOT (callUpdate (const QModelIndex&, const QModelIndex&)));
1282  connect (this, SIGNAL (refresh_signal ()),
1283  model, SLOT (update_data_cache ()));
1284  connect (model, SIGNAL (set_editable_signal (bool)),
1285  stack, SLOT (set_editable (bool)));
1286 
1287  edit_view->setModel (model);
1288  connect (edit_view, SIGNAL (doubleClicked (const QModelIndex&)),
1289  model, SLOT (double_click (const QModelIndex&)));
1290 
1291  // Any interpreter_event signal from a variable_editor_model object is
1292  // handled the same as for the parent variable_editor object.
1293 
1294  connect (model, SIGNAL (interpreter_event (const fcn_callback&)),
1295  this, SIGNAL (interpreter_event (const fcn_callback&)));
1296 
1297  connect (model, SIGNAL (interpreter_event (const meth_callback&)),
1298  this, SIGNAL (interpreter_event (const meth_callback&)));
1299 
1300  // Must supply a title for a QLabel to be created. Calling set_title()
1301  // more than once will add more QLabels. Could change octave_dock_widget
1302  // to always supply a QLabel (initially empty) and then simply update its
1303  // contents.
1304  page->set_title (name);
1305  if (page->titleBarWidget () != nullptr)
1306  {
1307  QLabel *existing_ql = page->titleBarWidget ()->findChild<QLabel *> ();
1308  connect (model, SIGNAL (update_label_signal (const QString&)),
1309  existing_ql, SLOT (setText (const QString&)));
1310  existing_ql->setMargin (2);
1311  }
1312 
1313  model->update_data (val);
1314 
1315  QList<QTableView *> viewlist = findChildren<QTableView *> ();
1316  if (viewlist.size () == 1)
1317  m_tool_bar->setEnabled (true);
1318 
1319  m_main->parentWidget ()->show ();
1320  page->show ();
1321  page->raise ();
1322  page->activateWindow ();
1323  tab_to_front ();
1324  page->setFocus ();
1325  }
1326 
1327  void
1329  {
1330  if (parent () != nullptr)
1331  {
1332  QList<QTabBar *> barlist = main_win ()->findChildren<QTabBar *> ();
1333  QVariant this_value (reinterpret_cast<quintptr> (this));
1334 
1335  for (auto *tbar : barlist)
1336  for (int i = 0; i < tbar->count (); i++)
1337  if (tbar->tabData (i) == this_value)
1338  {
1339  tbar->setCurrentIndex (i);
1340  return;
1341  }
1342  }
1343  }
1344 
1345  void
1347  {
1348  emit refresh_signal ();
1349  }
1350 
1351  void
1352  variable_editor::callUpdate (const QModelIndex&, const QModelIndex&)
1353  {
1354  emit updated ();
1355  }
1356 
1357  void
1359  {
1360  m_main->notice_settings (settings); // update settings in parent main win
1361 
1362  m_default_width = settings->value (ve_column_width).toInt ();
1363 
1364  m_default_height = settings->value (ve_row_height).toInt ();
1365 
1366  m_alternate_rows = settings->value (ve_alternate_rows).toBool ();
1367 
1368  m_use_terminal_font = settings->value (ve_use_terminal_font).toBool ();
1369 
1370  QString font_name;
1371  int font_size;
1372  QString default_font = settings->value (global_mono_font).toString ();
1373 
1374  if (m_use_terminal_font)
1375  {
1376  font_name = settings->value (cs_font.key, default_font).toString ();
1377  font_size = settings->value (cs_font_size).toInt ();
1378  }
1379  else
1380  {
1381  font_name = settings->value (ve_font_name.key, default_font).toString ();
1382  font_size = settings->value (ve_font_size).toInt ();
1383  }
1384 
1385  m_font = QFont (font_name, font_size);
1386 
1387  QFontMetrics fm (m_font);
1388 
1389  m_add_font_height = fm.height ();
1390 
1391  for (int i = 0; i < ve_colors_count; i++)
1392  {
1393  // The default colors are given as color roles for
1394  // the application's palette
1395  QColor default_color = qApp->palette ().color
1396  (static_cast<QPalette::ColorRole> (ve_colors[i].def.toInt ()));
1397  // FIXME: use value<QPalette::ColorRole> instead of static cast after
1398  // dropping support of Qt 5.4
1399 
1400  QColor setting_color =
1401  settings->value (ve_colors[i].key, default_color).value<QColor> ();
1402 
1403  m_table_colors.replace (i, setting_color);
1404  }
1405 
1406  update_colors ();
1407 
1408  // Icon size in the toolbar.
1409 
1410  int size_idx = settings->value (global_icon_size).toInt ();
1411  size_idx = (size_idx > 0) - (size_idx < 0) + 1; // Make valid index from 0 to 2
1412 
1413  QStyle *st = style ();
1414  int icon_size = st->pixelMetric (global_icon_sizes[size_idx]);
1415  m_tool_bar->setIconSize (QSize (icon_size, icon_size));
1416  }
1417 
1418  void
1420  {
1421  emit finished ();
1422 
1424  }
1425 
1426  void
1428  {
1429  // Invalidate the focus-restoring widget pointer if currently active.
1430  if (m_focus_widget_vdw == obj)
1431  {
1432  m_focus_widget = nullptr;
1433  m_focus_widget_vdw = nullptr;
1434  }
1435 
1436  // If no variable pages remain, deactivate the tool bar.
1437  QList<variable_dock_widget *> vdwlist = findChildren<variable_dock_widget *> ();
1438  if (vdwlist.isEmpty ())
1439  m_tool_bar->setEnabled (false);
1440 
1441  QFocusEvent ev (QEvent::FocusIn);
1442  focusInEvent (&ev);
1443  }
1444 
1445  void
1447  {
1449 
1450  // focusWidget() appears lost in transition to/from main window
1451  // so keep a record of the widget.
1452 
1453  QWidget *current = QApplication::focusWidget ();
1454  m_focus_widget = nullptr;
1455  m_focus_widget_vdw = nullptr;
1456  if (current != nullptr)
1457  {
1458  QList<variable_dock_widget *> vdwlist = findChildren<variable_dock_widget *> ();
1459  for (int i = 0; i < vdwlist.size (); i++)
1460  {
1461  variable_dock_widget *vdw = vdwlist.at (i);
1462  if (vdw->isAncestorOf (current))
1463  {
1464  m_focus_widget = current;
1465  m_focus_widget_vdw = vdw;
1466  break;
1467  }
1468  }
1469  }
1470  }
1471 
1472  void
1474  {
1476  }
1477 
1478  void
1480  {
1481  variable_dock_widget *tofocus = findChild<variable_dock_widget *> (m_hovered_focus_vname);
1482  if (tofocus != nullptr)
1483  {
1484  // Note that this may be platform and window system dependent.
1485  // On a particular Linux system, activateWindow() alone didn't
1486  // immediately set the active window and there was a race
1487  // between the window focus and action signal. Setting the
1488  // active window via the QApplication route did work.
1489  QApplication::setActiveWindow(tofocus->window());
1490  tofocus->activateWindow ();
1491  tofocus->setFocus (Qt::OtherFocusReason);
1492  }
1493  }
1494 
1495  void
1497  {
1498  emit save_signal ();
1499  }
1500 
1501  void
1503  {
1504  copyClipboard ();
1505 
1506  emit clear_content_signal ();
1507  }
1508 
1509  void
1511  {
1512  emit copy_clipboard_signal ();
1513  }
1514 
1515  void
1517  {
1518  emit paste_clipboard_signal ();
1519 
1520  emit updated ();
1521  }
1522 
1523  void
1525  {
1526  emit level_up_signal ();
1527  }
1528 
1529  // Also updates the font.
1530 
1532  {
1533  m_stylesheet = "";
1534 
1535  if (m_table_colors.length () > 0)
1536  m_stylesheet += "QTableView::item{ foreground-color: "
1537  + m_table_colors[0].name () +" }";
1538 
1539  if (m_table_colors.length () > 1)
1540  m_stylesheet += "QTableView::item{ background-color: "
1541  + m_table_colors[1].name () +" }";
1542 
1543  if (m_table_colors.length () > 2)
1544  m_stylesheet += "QTableView::item{ selection-color: "
1545  + m_table_colors[2].name () +" }";
1546 
1547  if (m_table_colors.length () > 3)
1548  m_stylesheet += "QTableView::item:selected{ background-color: "
1549  + m_table_colors[3].name () +" }";
1550 
1551  if (m_table_colors.length () > 4 && m_alternate_rows)
1552  {
1553  m_stylesheet += "QTableView::item:alternate{ background-color: "
1554  + m_table_colors[4].name () +" }";
1555 
1556  m_stylesheet += "QTableView::item:alternate:selected{ background-color: "
1557  + m_table_colors[3].name () +" }";
1558  }
1559 
1560  QList<QTableView *> viewlist = findChildren<QTableView *> ();
1561  for (int i = 0; i < viewlist.size (); i++)
1562  {
1563  QTableView *view = viewlist.at (i);
1564 
1565  if (! view)
1566  continue;
1567 
1568  view->setAlternatingRowColors (m_alternate_rows);
1569  view->setStyleSheet (m_stylesheet);
1570  view->setFont (m_font);
1571  }
1572 
1573  }
1574 
1575  QAction *
1577  const QString &text,
1578  const QObject *receiver,
1579  const char *member)
1580  {
1581  QAction *action = new QAction (icon, text, this);
1582  connect(action, SIGNAL (triggered ()), receiver, member);
1584  button->setDefaultAction (action);
1585  button->setText (text);
1586  button->setToolTip (text);
1587  button->setIcon (icon);
1588  m_tool_bar->addWidget (button);
1589 
1590  return action;
1591  }
1592 
1593  void
1595  {
1596  m_tool_bar->setAllowedAreas (Qt::TopToolBarArea);
1597 
1598  m_tool_bar->setObjectName ("VariableEditorToolBar");
1599 
1600  m_tool_bar->setWindowTitle (tr ("Variable Editor Toolbar"));
1601 
1603 
1604  QAction *action;
1605  action = add_tool_bar_button (rmgr.icon ("document-save"), tr ("Save"),
1606  this, SLOT (save ()));
1607  addAction (action);
1608  action->setShortcutContext (Qt::WidgetWithChildrenShortcut);
1609  action->setShortcuts (QKeySequence::Save);
1610  action->setStatusTip(tr("Save variable to a file"));
1611 
1612  m_tool_bar->addSeparator ();
1613 
1614  action = add_tool_bar_button (rmgr.icon ("edit-cut"), tr ("Cut"),
1615  this, SLOT (cutClipboard ()));
1616  action->setStatusTip(tr("Cut data to clipboard"));
1617 
1618  action = add_tool_bar_button (rmgr.icon ("edit-copy"), tr ("Copy"),
1619  this, SLOT (copyClipboard ()));
1620  action->setStatusTip(tr("Copy data to clipboard"));
1621 
1622  action = add_tool_bar_button (rmgr.icon ("edit-paste"), tr ("Paste"),
1623  this, SLOT (pasteClipboard ()));
1624  action->setStatusTip(tr("Paste clipboard into variable data"));
1625 
1626  m_tool_bar->addSeparator ();
1627 
1628  // FIXME: Add a print item?
1629  // QAction *print_action; /icons/fileprint.png
1630  // m_tool_bar->addSeparator ();
1631 
1632  action = new QAction (rmgr.icon ("plot-xy-curve"), tr ("Plot"), m_tool_bar);
1633  action->setToolTip (tr ("Plot Selected Data"));
1634  QToolButton *plot_tool_button = new HoverToolButton (m_tool_bar);
1635  plot_tool_button->setDefaultAction (action);
1636 
1637  plot_tool_button->setText (tr ("Plot"));
1638  plot_tool_button->setToolTip (tr ("Plot selected data"));
1639  plot_tool_button->setIcon (rmgr.icon ("plot-xy-curve"));
1640 
1641  plot_tool_button->setPopupMode (QToolButton::InstantPopup);
1642 
1643  QMenu *plot_menu = new ReturnFocusMenu (plot_tool_button);
1644  plot_menu->setTitle (tr ("Plot"));
1645  plot_menu->setSeparatorsCollapsible (false);
1646 
1647  m_plot_mapper = make_plot_mapper (plot_menu);
1648 
1649  plot_tool_button->setMenu (plot_menu);
1650 
1651  m_tool_bar->addWidget (plot_tool_button);
1652 
1653  m_tool_bar->addSeparator ();
1654 
1655  action = add_tool_bar_button (rmgr.icon ("go-up"), tr ("Up"), this,
1656  SLOT (levelUp ()));
1657  action->setStatusTip(tr("Go one level up in variable hierarchy"));
1658 
1659  // The QToolButton mouse-clicks change active window, so connect all
1660  // HoverToolButton and ReturnFocusToolButton objects to the mechanism
1661  // that restores active window and focus before acting.
1662  QList<HoverToolButton *> hbuttonlist
1663  = m_tool_bar->findChildren<HoverToolButton *> (""
1664 #if defined (QOBJECT_FINDCHILDREN_ACCEPTS_FINDCHILDOPTIONS)
1665  , Qt::FindDirectChildrenOnly
1666 #endif
1667  );
1668  for (int i = 0; i < hbuttonlist.size (); i++)
1669  {
1670  connect (hbuttonlist.at (i), SIGNAL (hovered_signal ()),
1671  this, SLOT (record_hovered_focus_variable ()));
1672  connect (hbuttonlist.at (i), SIGNAL (popup_shown_signal ()),
1673  this, SLOT (restore_hovered_focus_variable ()));
1674  }
1675 
1676  QList<ReturnFocusToolButton *> rfbuttonlist
1677  = m_tool_bar->findChildren<ReturnFocusToolButton *> (""
1678 #if defined (QOBJECT_FINDCHILDREN_ACCEPTS_FINDCHILDOPTIONS)
1679  , Qt::FindDirectChildrenOnly
1680 #endif
1681  );
1682  for (int i = 0; i < rfbuttonlist.size (); i++)
1683  {
1684  connect (rfbuttonlist.at (i), SIGNAL (about_to_activate ()),
1685  this, SLOT (restore_hovered_focus_variable ()));
1686  }
1687 
1688  // Same for QMenu
1689  QList<ReturnFocusMenu *> menulist
1690  = m_tool_bar->findChildren<ReturnFocusMenu *> ();
1691  for (int i = 0; i < menulist.size (); i++)
1692  {
1693  connect (menulist.at (i), SIGNAL (about_to_activate ()),
1694  this, SLOT (restore_hovered_focus_variable ()));
1695  }
1696 
1697  m_tool_bar->setAttribute(Qt::WA_ShowWithoutActivating);
1698  m_tool_bar->setFocusPolicy (Qt::NoFocus);
1699 
1700  // Disabled when no tab is present.
1701 
1702  m_tool_bar->setEnabled (false);
1703  }
1704 }
charNDArray max(char d, const charNDArray &m)
Definition: chNDArray.cc:230
charNDArray min(char d, const charNDArray &m)
Definition: chNDArray.cc:207
HoverToolButton(QWidget *parent=nullptr)
bool eventFilter(QObject *obj, QEvent *ev)
void popup_shown_signal(void)
bool eventFilter(QObject *obj, QEvent *ev)
void about_to_activate(void)
ReturnFocusMenu(QWidget *parent=nullptr)
ReturnFocusToolButton(QWidget *parent=nullptr)
bool eventFilter(QObject *obj, QEvent *ev)
Base class for Octave interfaces that use Qt.
resource_manager & get_resource_manager(void)
void notice_settings(const gui_settings *)
void set_title(const QString &)
virtual void closeEvent(QCloseEvent *e)
QMainWindow * main_win(void)
void interpreter_event(const fcn_callback &fcn)
gui_settings * get_settings(void) const
QIcon icon(const QString &icon_name, bool fallback=true)
void resizeEvent(QResizeEvent *event)
variable_dock_widget(QWidget *p, base_qobject &oct_qobj)
void handle_focus_change(QWidget *old, QWidget *now)
virtual void closeEvent(QCloseEvent *e)
void variable_focused_signal(const QString &name)
bool clear_content(const QModelIndex &idx)
void update_data(const octave_value &val)
octave_idx_type data_rows(void) const
octave_idx_type data_columns(void) const
void edit_variable_signal(const QString &name, const octave_value &val)
variable_editor_view * m_edit_view
variable_editor_stack(QWidget *p, base_qobject &oct_qobj)
variable_editor_view * edit_view(void)
void command_signal(const QString &cmd)
void set_editable(bool editable)
QTextEdit * make_disp_view(QWidget *parent)
void add_edit_actions(QMenu *menu, const QString &qualifier_string)
QList< int > range_selected(void)
void createColumnMenu(const QPoint &pt)
void selected_command_requested(const QString &cmd)
void command_signal(const QString &cmd)
variable_editor_model * m_var_model
void createContextMenu(const QPoint &pt)
variable_editor_view(QWidget *p, base_qobject &oct_qobj)
void handle_vertical_scroll_action(int action)
void createRowMenu(const QPoint &pt)
void setModel(QAbstractItemModel *model)
void handle_horizontal_scroll_action(int action)
void variable_destroyed(QObject *obj)
void clear_content_signal(void)
void closeEvent(QCloseEvent *)
QAction * add_action(QMenu *menu, const QIcon &icon, const QString &text, const char *member)
void copy_clipboard_signal(void)
void record_hovered_focus_variable(void)
QList< QColor > m_table_colors
QAction * add_tool_bar_button(const QIcon &icon, const QString &text, const QObject *receiver, const char *member)
variable_editor(QWidget *parent, base_qobject &oct_qobj)
void notice_settings(const gui_settings *)
variable_dock_widget * m_focus_widget_vdw
dw_main_window * m_main
void paste_clipboard_signal(void)
void edit_variable(const QString &name, const octave_value &val)
void focusInEvent(QFocusEvent *ev)
void delete_selected_signal(void)
void command_signal(const QString &cmd)
void restore_hovered_focus_variable(void)
void callUpdate(const QModelIndex &, const QModelIndex &)
void variable_focused(const QString &name)
QSignalMapper * m_plot_mapper
text(const graphics_handle &mh, const graphics_handle &p)
Definition: graphics.in.h:4517
const gui_pref cs_font_size("terminal/fontSize", QVariant(10))
const gui_pref cs_font("terminal/fontName", QVariant())
QString name
const gui_pref global_mono_font("monospace_font", global_font_family)
const QStyle::PixelMetric global_icon_sizes[3]
const gui_pref global_use_native_dialogs("use_native_file_dialogs", QVariant(true))
const gui_pref global_icon_size("toolbar_icon_size", QVariant(0))
const int ve_colors_count
const gui_pref ve_column_width("variable_editor/column_width", QVariant(100))
const gui_pref ve_colors[ve_colors_count]
const gui_pref ve_use_terminal_font("variable_editor/use_terminal_font", QVariant(true))
const gui_pref ve_font_size("variable_editor/font_size", QVariant(10))
const gui_pref ve_alternate_rows("variable_editor/alternate_rows", QVariant(false))
const gui_pref ve_font_name("variable_editor/font_name", QVariant())
const gui_pref ve_row_height("variable_editor/row_height", QVariant(10))
std::complex< double > w(std::complex< double > z, double relerr=0)
std::function< void(octave::interpreter &)> meth_callback
Definition: event-manager.h:47
std::function< void(void)> fcn_callback
Definition: event-manager.h:46
static QSignalMapper * make_plot_mapper(QMenu *menu)
int qt_fontmetrics_horizontal_advance(const QFontMetrics &fm, QChar ch)
Definition: qt-utils.h:48
octave_int< T > mod(const octave_int< T > &x, const octave_int< T > &y)
Definition: oct-inttypes.h:932
octave_value::octave_value(const Array< char > &chm, char type) return retval
Definition: ov.cc:811
const QString key
#define QTBUG_44813_FIX_VERSION