GNU Octave  8.1.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-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 #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 "builtin-defun-decls.h"
52 #include "dw-main-window.h"
53 #include "gui-preferences-cs.h"
54 #include "gui-preferences-dw.h"
55 #include "gui-preferences-global.h"
56 #include "gui-preferences-sc.h"
57 #include "gui-preferences-ve.h"
58 #include "octave-qobject.h"
59 #include "octave-qtutils.h"
60 #include "ovl.h"
61 #include "qt-utils.h"
62 #include "shortcut-manager.h"
63 #include "variable-editor-model.h"
64 #include "variable-editor.h"
65 
67 
68 // Code reuse functions
69 
70 static QSignalMapper *
72 {
73  QList<QString> list;
74  list << "plot" << "bar" << "stem" << "stairs" << "area" << "pie" << "hist";
75 
76  QSignalMapper *plot_mapper = new QSignalMapper (menu);
77 
78  for (int i = 0; i < list.size(); ++i)
79  plot_mapper->setMapping
80  (menu->addAction (list.at (i), plot_mapper, SLOT (map ())), list.at (i));
81 
82  return plot_mapper;
83 }
84 
85 // Variable dock widget
86 
88  base_qobject& oct_qobj)
89  : label_dock_widget (p, oct_qobj)
90  // See Octave bug #53807 and https://bugreports.qt.io/browse/QTBUG-44813
91 #if (QT_VERSION >= 0x050302) && (QT_VERSION <= QTBUG_44813_FIX_VERSION)
92  , m_waiting_for_mouse_move (false)
93  , m_waiting_for_mouse_button_release (false)
94 #endif
95 {
96  setFocusPolicy (Qt::StrongFocus);
97  setAttribute (Qt::WA_DeleteOnClose);
98 
99  connect (m_dock_action, &QAction::triggered,
101  connect (m_close_action, &QAction::triggered,
103  connect (this, &variable_dock_widget::topLevelChanged,
105 
106 #define DOCKED_FULLSCREEN_BUTTON_TOOLTIP "Fullscreen undock"
107 #define UNDOCKED_FULLSCREEN_BUTTON_TOOLTIP "Fullscreen"
108  // Add a fullscreen button
109 
110  m_fullscreen_action = nullptr;
111  m_full_screen = false;
112  m_prev_floating = false;
113  m_prev_geom = QRect (0, 0, 0, 0);
114 
115  QHBoxLayout *h_layout = m_title_widget->findChild<QHBoxLayout *> ();
118  = new QAction (rmgr.icon ("view-fullscreen", false), "", this);
120  QToolButton *fullscreen_button = new QToolButton (m_title_widget);
121  fullscreen_button->setDefaultAction (m_fullscreen_action);
122  fullscreen_button->setFocusPolicy (Qt::NoFocus);
123  fullscreen_button->setIconSize (QSize (m_icon_size, m_icon_size));
124  QString css_button = QString ("QToolButton {background: transparent; border: 0px;}");
125  fullscreen_button->setStyleSheet (css_button);
126 
127  connect (m_fullscreen_action, &QAction::triggered,
129 
130  int index = -1;
131  QToolButton *first = m_title_widget->findChild<QToolButton *> ();
132  if (first != nullptr)
133  index = h_layout->indexOf (first);
134  h_layout->insertWidget (index, fullscreen_button);
135 
136  // Custom title bars cause loss of decorations, add a frame
137  m_frame = new QFrame (this);
138  m_frame->setFrameStyle (QFrame::Box | QFrame::Sunken);
139  m_frame->setAttribute (Qt::WA_TransparentForMouseEvents);
140 }
141 
142 // slot for (un)dock action
143 void
145 {
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  }
156  }
157  else
159 
160  setFloating (! isFloating ());
161 }
162 
163 // slot for hiding the widget
164 void
166 {
167  close ();
168 }
169 
170 void
172 {
173  if (toplevel)
174  {
176  + "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  {
194  + "widget-undock.png"));
195  m_dock_action->setToolTip (tr ("Undock widget"));
196 
197  setFocus ();
198 
199  // See Octave bug #53807 and https://bugreports.qt.io/browse/QTBUG-44813
200 #if (QT_VERSION >= 0x050302) && (QT_VERSION <= QTBUG_44813_FIX_VERSION)
201  m_waiting_for_mouse_move = false;
202  m_waiting_for_mouse_button_release = false;
203 #endif
204  }
205 }
206 
207 void
209 {
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)
239  else
240  {
241  setFloating (false);
243  }
244 
245  m_full_screen = false;
246  }
247 #undef DOCKED_FULLSCREEN_BUTTON_TOOLTIP
248 #undef UNDOCKED_FULLSCREEN_BUTTON_TOOLTIP
249 }
250 
251 void
253 {
254  QDockWidget::closeEvent (e);
255 }
256 
257 void
259 {
260  octave_unused_parameter (now);
261 
262  // This is a proxied test
263  if (hasFocus ())
264  {
265  if (old == this)
266  return;
267 
268  if (titleBarWidget () != nullptr)
269  {
270  QLabel *label = titleBarWidget ()->findChild<QLabel *> ();
271  if (label != nullptr)
272  {
273  label->setBackgroundRole (QPalette::Highlight);
274  label->setStyleSheet ("background-color: palette(highlight); color: palette(highlightedText);");
275  }
276  }
277 
278  emit variable_focused_signal (objectName ());
279  }
280  else if (old == focusWidget())
281  {
282  if (titleBarWidget () != nullptr)
283  {
284  QLabel *label = titleBarWidget ()->findChild<QLabel *> ();
285  if (label != nullptr)
286  {
287  label->setBackgroundRole (QPalette::NoRole);
288  label->setStyleSheet (";");
289  }
290  }
291  }
292 }
293 
295 {
296  if (m_frame)
297  m_frame->resize (size ());
298 }
299 
300 // See Octave bug #53807 and https://bugreports.qt.io/browse/QTBUG-44813
301 #if (QT_VERSION >= 0x050302) && (QT_VERSION <= QTBUG_44813_FIX_VERSION)
302 
303 bool
304 variable_dock_widget::event (QEvent *event)
305 {
306  // low-level check of whether docked-widget became a window via
307  // via drag-and-drop
308  if (event->type () == QEvent::MouseButtonPress)
309  {
310  m_waiting_for_mouse_move = false;
311  m_waiting_for_mouse_button_release = false;
312  }
313  if (event->type () == QEvent::MouseMove && m_waiting_for_mouse_move)
314  {
315  m_waiting_for_mouse_move = false;
316  m_waiting_for_mouse_button_release = true;
317  }
318  if (event->type () == QEvent::MouseButtonRelease
319  && m_waiting_for_mouse_button_release)
320  {
321  m_waiting_for_mouse_button_release = false;
322  bool retval = QDockWidget::event (event);
323  if (isFloating ())
324  emit queue_unfloat_float ();
325  return retval;
326  }
327 
328  return QDockWidget::event (event);
329 }
330 
331 void
333 {
334  hide ();
335  setFloating (false);
336  // Avoid a Ubunty Unity issue by queuing this rather than direct.
337  emit queue_float ();
338  m_waiting_for_mouse_move = false;
339  m_waiting_for_mouse_button_release = false;
340 }
341 
342 void
344 {
345  setFloating (true);
346  m_waiting_for_mouse_move = false;
347  m_waiting_for_mouse_button_release = false;
348  show ();
349  activateWindow ();
350  setFocus ();
351 }
352 
353 #else
354 
355 void
357 { }
358 
359 void
361 { }
362 
363 #endif
364 
365 // Variable editor stack
366 
368  base_qobject& oct_qobj)
369  : QStackedWidget (p), m_octave_qobj (oct_qobj),
370  m_edit_view (new variable_editor_view (this, m_octave_qobj))
371 {
372  setFocusPolicy (Qt::StrongFocus);
373 
374  m_disp_view = make_disp_view (this);
375 
376  addWidget (m_edit_view);
377  addWidget (m_disp_view);
378 }
379 
380 QTextEdit *
382 {
383  QTextEdit *viewer = new QTextEdit (parent);
384 
385  viewer->setLineWrapMode (QTextEdit::NoWrap);
386  viewer->setReadOnly (true);
387 
388  return viewer;
389 }
390 
391 void
393 {
394  // The QTableView is for editable data models
395  // and the QTextEdit is for non-editable models.
396 
397  if (editable)
398  {
399  if (m_edit_view != nullptr)
400  {
401  setCurrentWidget (m_edit_view);
402  setFocusProxy (m_edit_view);
403  m_edit_view->setFocusPolicy (Qt::StrongFocus);
404  }
405 
406  if (m_disp_view != nullptr)
407  m_disp_view->setFocusPolicy (Qt::NoFocus);
408  }
409  else
410  {
411  if (m_disp_view != nullptr)
412  {
413  setCurrentWidget (m_disp_view);
414  setFocusProxy (m_disp_view);
415 
416  QAbstractTableModel *model = findChild<QAbstractTableModel *> ();
417  if (model != nullptr)
418  m_disp_view->setPlainText (model->data (QModelIndex ()).toString ());
419  else
420  m_disp_view->setPlainText ("");
421  }
422 
423  if (m_edit_view != nullptr)
424  m_edit_view->setFocusPolicy (Qt::NoFocus);
425  }
426 }
427 
428 void
430 {
431  if (! hasFocus ())
432  return;
433 
434  QString name = objectName ();
435 
436  // FIXME: Is there a better way?
437 
438  if (name.endsWith (')') || name.endsWith ('}'))
439  {
440  name.remove ( QRegExp ("[({][^({]*[)}]$)") );
441  emit edit_variable_signal (name, octave_value ());
442  }
443 }
444 
445 // Slot for saving a variable into a file
446 void
448 {
449  if (! hasFocus ())
450  return;
451 
452  // Check whether a format for saving the variable is given
453  QString format_string;
454  if (! format.isEmpty ())
455  {
456  format_string = "-" + format;
458  return;
459  }
460 
461  // The interpreter_event callback function below emits a signal.
462  // Because we don't control when that happens, use a guarded pointer
463  // so that the callback can abort if this object is no longer valid.
464 
465  QPointer<variable_editor_stack> this_ves (this);
466 
467  // No format given, test save default options
468  emit interpreter_event
469  ([=] (interpreter& interp)
470  {
471  // INTERPRETER THREAD
472 
473  // We can skip the entire callback function because it does not
474  // make any changes to the interpreter state.
475 
476  if (this_ves.isNull ())
477  return;
478 
479  octave_value_list argout
480  = Fsave_default_options (interp, octave_value_list (), 1);
481  QString save_opts = QString::fromStdString (argout(0).string_value ());
482 
485 
486  emit (do_save_signal (format_string, save_opts));
487  });
488 }
489 
490 // Perform saving the variable after desired format is determined
491 void
492 variable_editor_stack::do_save (const QString& format, const QString& save_opts)
493 {
494  QString file_ext = "txt";
495  for (int i = 0; i < ve_save_formats_ext.length ()/2; i++)
496  {
497  if (save_opts.contains (ve_save_formats_ext.at (2*i), Qt::CaseInsensitive))
498  {
499  file_ext = ve_save_formats_ext.at (2*i + 1);
500  break;
501  }
502  }
503 
504  // FIXME: Remove, if for all common KDE versions (bug #54607) is resolved.
505  int opts = 0; // No options by default.
508  if (! settings->value (global_use_native_dialogs).toBool ())
509  opts = QFileDialog::DontUseNativeDialog;
510 
511  QString name = objectName ();
512  QString file
513  = QFileDialog::getSaveFileName (this,
514  tr ("Save Variable %1 As").arg (name),
515  QString ("./%1.%2").arg (name).arg (file_ext),
516  0, 0, QFileDialog::Option (opts));
517 
518  if (file.isEmpty ())
519  return; // No file selected: Just return
520 
521  // Let the interpreter thread do the saving
522  emit interpreter_event
523  ([=] (interpreter& interp)
524  {
525  // INTERPRETER THREAD
526 
528  std::list<octave_value> str_list
529  = {octave_value (file.toStdString ()),
530  octave_value (name.toStdString ())};
531  if (! format.isEmpty ())
532  str_list.push_front (octave_value (format.toStdString ()));
533 
534  Fsave (interp, octave_value_list (str_list));
535  });
536 }
537 
538 // Custom editable variable table view
539 
541  base_qobject& oct_qobj)
542  : QTableView (p), m_octave_qobj (oct_qobj), m_var_model (nullptr)
543 {
544  setWordWrap (false);
545  setContextMenuPolicy (Qt::CustomContextMenu);
546  setSelectionMode (QAbstractItemView::ContiguousSelection);
547 
548  horizontalHeader ()->setContextMenuPolicy (Qt::CustomContextMenu);
549  verticalHeader ()->setContextMenuPolicy (Qt::CustomContextMenu);
550 
551  setHorizontalScrollMode (QAbstractItemView::ScrollPerPixel);
552  setVerticalScrollMode (QAbstractItemView::ScrollPerPixel);
553 
554  verticalHeader ()->setSectionResizeMode (QHeaderView::Interactive);
555 }
556 
557 void
558 variable_editor_view::setModel (QAbstractItemModel *model)
559 {
560  QTableView::setModel (model);
561 
562  horizontalHeader ()->setSectionResizeMode (QHeaderView::Interactive);
563 
564  m_var_model = parent ()->findChild<variable_editor_model *> ();
565 
566  if (m_var_model != nullptr && m_var_model->column_width () > 0)
567  {
568  // col_width is in characters. The font should be a fixed-width
569  // font, so any character will do. If not, you lose!
570 
571  QFontMetrics fm (font ());
572  int w = (m_var_model->column_width ()
574  horizontalHeader ()->setDefaultSectionSize (w);
575  }
576 }
577 
580 {
581  QItemSelectionModel *sel = selectionModel ();
582 
583  // Return early if nothing selected.
584  if (! sel->hasSelection ())
585  return QList<int> ();
586 
587  QList<QModelIndex> indices = sel->selectedIndexes ();
588 
589  // FIXME: Shouldn't this be keyed to octave_idx_type?
590 
591  int32_t from_row = std::numeric_limits<int32_t>::max ();
592  int32_t to_row = 0;
593  int32_t from_col = std::numeric_limits<int32_t>::max ();
594  int32_t to_col = 0;
595 
596  for (const auto& idx : indices)
597  {
598  from_row = std::min (from_row, idx.row ());
599  to_row = std::max (to_row, idx.row ());
600  from_col = std::min (from_col, idx.column ());
601  to_col = std::max (to_col, idx.column ());
602  }
603 
604  QVector<int> vect;
605  vect << from_row + 1 << to_row + 1 << from_col + 1 << to_col + 1;
607 
608  return range;
609 }
610 
611 void
613 {
614  if (! hasFocus ())
615  return;
616 
618  if (range.isEmpty ())
619  {
620  // Nothing selected, apply print command to all data
621  range << 1 << m_var_model->data_rows ()
622  << 1 << m_var_model->data_columns ();
623  }
624 
625  int s1 = m_var_model->data_rows ();
626  int s2 = m_var_model->data_columns ();
627  if (s1 < range.at (0) || s2 < range.at (2))
628  return; // Selected range does not contain data
629 
630  s1 = std::min (s1, range.at (1));
631  s2 = std::min (s2, range.at (3));
632 
633  // Variable with desired range as string
634  QString variable = QString ("%1(%2:%3,%4:%5)")
635  .arg (objectName ())
636  .arg (range.at (0)).arg (s1)
637  .arg (range.at (2)).arg (s2);
638 
639  // Desired command as string
640  QString command;
641  if (cmd == "create")
642  command = QString ("unnamed = %1;").arg (variable);
643  else
644  command = QString ("figure (); %1 (%2); title ('%2');")
645  .arg (cmd).arg (variable);
646 
647  emit command_signal (command);
648 }
649 
650 void
652  const QString& qualifier_string)
653 {
655 
656  menu->addAction (rmgr.icon ("edit-cut"),
657  tr ("Cut") + qualifier_string,
659 
660  menu->addAction (rmgr.icon ("edit-copy"),
661  tr ("Copy") + qualifier_string,
663 
664  menu->addAction (rmgr.icon ("edit-paste"),
665  tr ("Paste"),
667 
668  menu->addSeparator ();
669 
670  menu->addAction (rmgr.icon ("edit-delete"),
671  tr ("Clear") + qualifier_string,
673 
674  menu->addAction (rmgr.icon ("edit-delete"),
675  tr ("Delete") + qualifier_string,
677 
678  menu->addAction (rmgr.icon ("document-new"),
679  tr ("Variable from Selection"),
681 }
682 
683 void
685 {
686  QModelIndex index = indexAt (qpos);
687 
688  if (index.isValid ())
689  {
690  QMenu *menu = new QMenu (this);
691 
692  add_edit_actions (menu, tr (""));
693 
694  // FIXME: addAction for sort?
695  // FIXME: Add icon for transpose.
696 
697  menu->addAction (tr ("Transpose"),
699 
700  QItemSelectionModel *sel = selectionModel ();
701 
702  QList<QModelIndex> indices = sel->selectedIndexes ();
703 
704  if (! indices.isEmpty ())
705  {
706  menu->addSeparator ();
707 
708  QSignalMapper *plot_mapper = make_plot_mapper (menu);
709 
710  connect (plot_mapper, SIGNAL (mapped (const QString&)),
711  this, SLOT (selected_command_requested (const QString&)));
712  }
713 
714  menu->exec (mapToGlobal (qpos));
715  }
716 }
717 
718 void
720 {
721  int index = horizontalHeader ()->logicalIndexAt (pt);
722 
723  if (index < 0 || index > model ()->columnCount ())
724  return;
725 
726  QList<int> coords = range_selected ();
727 
728  bool nothingSelected = coords.isEmpty ();
729 
730  bool whole_columns_selected
731  = (nothingSelected
732  ? false
733  : (coords[0] == 1 && coords[1] == model ()->rowCount ()));
734 
735  bool current_column_selected
736  = nothingSelected ? false : (coords[2] <= index+1 && coords[3] > index);
737 
738  int column_selection_count
739  = nothingSelected ? 0 : (coords[3] - coords[2] + 1);
740 
741  if (! whole_columns_selected || ! current_column_selected)
742  {
743  selectColumn (index);
744  column_selection_count = 1;
745  }
746 
747  QString column_string
748  = column_selection_count > 1 ? tr (" columns") : tr (" column");
749 
750  QMenu *menu = new QMenu (this);
751 
752  add_edit_actions (menu, column_string);
753 
754  menu->addSeparator ();
755 
756  QSignalMapper *plot_mapper = make_plot_mapper (menu);
757 
758  connect (plot_mapper, SIGNAL (mapped (const QString&)),
759  this, SLOT (selected_command_requested (const QString&)));
760 
761  QPoint menupos = pt;
762  menupos.setY (horizontalHeader ()->height ());
763 
764  menu->exec (mapToGlobal (menupos));
765 }
766 
767 void
769 {
770  int index = verticalHeader ()->logicalIndexAt (pt);
771 
772  if (index < 0 || index > model ()->columnCount ())
773  return;
774 
775  QList<int> coords = range_selected ();
776 
777  bool nothingSelected = coords.isEmpty ();
778 
779  bool whole_rows_selected
780  = (nothingSelected
781  ? false
782  : (coords[2] == 1 && coords[3] == model ()->columnCount ()));
783 
784  bool current_row_selected
785  = (nothingSelected ? false : (coords[0] <= index+1 && coords[1] > index));
786 
787  int rowselection_count = nothingSelected ? 0 : (coords[3] - coords[2] + 1);
788 
789  if (! whole_rows_selected || ! current_row_selected)
790  {
791  selectRow (index);
792  rowselection_count = 1;
793  }
794 
795  QString row_string = rowselection_count > 1 ? tr (" rows") : tr (" row");
796 
797  QMenu *menu = new QMenu (this);
798 
799  add_edit_actions (menu, row_string);
800 
801  menu->addSeparator ();
802 
803  QSignalMapper *plot_mapper = make_plot_mapper (menu);
804 
805  connect (plot_mapper, SIGNAL (mapped (const QString&)),
806  this, SLOT (selected_command_requested (const QString&)));
807 
808  QPoint menupos = pt;
809  menupos.setX (verticalHeader ()->width ());
810 
811  // FIXME: What was the intent here?
812  // setY (verticalHeader ()->sectionPosition (index+1) +
813  // verticalHeader ()->sectionSize (index));
814 
815  menu->exec (mapToGlobal (menupos));
816 }
817 
818 void
820 {
821  // FIXME: Create unnamed1..n if exist ('unnamed', 'var') is true.
822 
823  selected_command_requested ("create");
824 }
825 
826 void
828 {
829  if (! hasFocus ())
830  return;
831 
832  emit command_signal (QString ("%1 = %1';").arg (objectName ()));
833 }
834 
835 void
837 {
838  if (! hasFocus ())
839  return;
840 
841  QAbstractItemModel *mod = model ();
842  QList<int> coords = range_selected ();
843 
844  if (coords.isEmpty ())
845  return;
846 
847  bool whole_columns_selected
848  = coords[0] == 1 && coords[1] == mod->rowCount ();
849 
850  bool whole_rows_selected
851  = coords[2] == 1 && coords[3] == mod->columnCount ();
852 
853  // Must be deleting whole columns or whole rows, and not the whole thing.
854 
855  if (whole_columns_selected == whole_rows_selected)
856  return;
857 
858  if (whole_rows_selected)
859  mod->removeRows (coords[0], coords[1] - coords[0]);
860 
861  if (whole_columns_selected)
862  mod->removeColumns (coords[2], coords[3] - coords[2]);
863 }
864 
865 void
867 {
868  if (! hasFocus ())
869  return;
870 
871  if (m_var_model == nullptr)
872  return;
873 
874  QItemSelectionModel *sel = selectionModel ();
875  QList<QModelIndex> indices = sel->selectedIndexes ();
876 
877  // FIXME: Use [] for empty cells?
878 
879  for (const auto& idx : indices)
880  m_var_model->clear_content (idx);
881 }
882 
883 void
885 {
886  copyClipboard ();
887 
888  clearContent ();
889 }
890 
891 void
893 {
894  if (! hasFocus ())
895  return;
896 
897  QItemSelectionModel *sel = selectionModel ();
898  QList<QModelIndex> indices = sel->selectedIndexes ();
899  std::sort (indices.begin (), indices.end ());
900 
901  if (indices.isEmpty ())
902  return;
903 
904  // Convert selected items into TSV format and copy that.
905  // Spreadsheet tools should understand that.
906 
907  QAbstractItemModel *mod = model ();
908  QModelIndex previous = indices.first ();
909  QString copy = mod->data (previous).toString ();
910  indices.removeFirst ();
911  for (auto idx : indices)
912  {
913  copy.push_back (previous.row () != idx.row () ? '\n' : '\t');
914  copy.append (mod->data (idx).toString ());
915  previous = idx;
916  }
917 
918  QClipboard *clipboard = QApplication::clipboard ();
919  clipboard->setText (copy);
920 }
921 
922 void
924 {
925  if (! hasFocus ())
926  return;
927 
928  QAbstractItemModel *mod = model ();
929  QItemSelectionModel *sel = selectionModel ();
930  QList<QModelIndex> indices = sel->selectedIndexes ();
931 
932  QClipboard *clipboard = QApplication::clipboard ();
933  QString text = clipboard->text ();
934 
935  QPoint start, end;
936 
937  QPoint tabsize = QPoint (mod->rowCount (), mod->columnCount ());
938 
939  if (indices.isEmpty ())
940  {
941  start = QPoint (0, 0);
942  end = tabsize;
943  }
944  else if (indices.size () == 1)
945  {
946  start = QPoint (indices[0].row (), indices[0].column ());
947  end = tabsize;
948  }
949  else
950  {
951  end = QPoint (0, 0);
952  start = tabsize;
953 
954  for (int i = 0; i < indices.size (); i++)
955  {
956  if (indices[i].column () < start.y ())
957  start.setY (indices[i].column ());
958 
959  if (indices[i].column () > end.y ())
960  end.setY (indices[i].column ());
961 
962  if (indices[i].row () < start.x ())
963  start.setX (indices[i].column ());
964 
965  if (indices[i].row () > end.x ())
966  end.setX (indices[i].column ());
967  }
968  }
969 
970  int rownum = 0;
971  int colnum = 0;
972 
973  QStringList rows = text.split ('\n');
974  for (const auto& row : rows)
975  {
976  if (rownum > end.x () - start.x ())
977  continue;
978 
979  QStringList cols = row.split ('\t');
980  if (cols.isEmpty ())
981  continue;
982 
983  for (const auto& col : cols)
984  {
985  if (col.isEmpty ())
986  continue;
987  if (colnum > end.y () - start.y () )
988  continue;
989 
990  mod->setData (mod->index (rownum + start.x (),
991  colnum + start.y ()),
992  QVariant (col));
993 
994  colnum++;
995  }
996 
997  colnum = 0;
998  rownum++;
999  }
1000 }
1001 
1002 void
1004 {
1005  if (action == QAbstractSlider::SliderSingleStepAdd
1006  || action == QAbstractSlider::SliderPageStepAdd
1007  || action == QAbstractSlider::SliderToMaximum
1008  || action == QAbstractSlider::SliderMove)
1009  {
1010  if (m_var_model != nullptr)
1011  {
1012  QScrollBar *sb = horizontalScrollBar ();
1013 
1014  if (sb && sb->value () == sb->maximum ())
1015  {
1016  int new_cols = m_var_model->display_columns () + 16;
1017 
1018  m_var_model->maybe_resize_columns (new_cols);
1019  }
1020  }
1021  }
1022 }
1023 
1024 void
1026 {
1027  if (action == QAbstractSlider::SliderSingleStepAdd
1028  || action == QAbstractSlider::SliderPageStepAdd
1029  || action == QAbstractSlider::SliderToMaximum
1030  || action == QAbstractSlider::SliderMove)
1031  {
1032  if (m_var_model != nullptr)
1033  {
1034  QScrollBar *sb = verticalScrollBar ();
1035 
1036  if (sb && sb->value () == sb->maximum ())
1037  {
1038  int new_rows = m_var_model->display_rows () + 16;
1039 
1040  m_var_model->maybe_resize_rows (new_rows);
1041  }
1042  }
1043  }
1044 }
1045 
1046 // Gadgets for focus restoration
1047 
1049  : QToolButton (parent)
1050 {
1051  installEventFilter (this);
1052 }
1053 
1054 bool HoverToolButton::eventFilter (QObject *obj, QEvent *ev)
1055 {
1056  if (ev->type () == QEvent::HoverEnter)
1057  emit hovered_signal ();
1058  else if (ev->type () == QEvent::MouseButtonPress)
1059  emit popup_shown_signal ();
1060 
1061  return QToolButton::eventFilter (obj, ev);
1062 }
1063 
1065  : HoverToolButton (parent)
1066 {
1067  installEventFilter (this);
1068 }
1069 
1071 {
1072 
1073  if (ev->type () == QEvent::MouseButtonRelease && isDown ())
1074  {
1075  emit about_to_activate ();
1076 
1077  setDown (false);
1078  QAction *action = defaultAction ();
1079  if (action != nullptr)
1080  action->activate (QAction::Trigger);
1081 
1082  return true;
1083  }
1084 
1085  return HoverToolButton::eventFilter (obj, ev);
1086 }
1087 
1089  : QMenu (parent)
1090 {
1091  installEventFilter (this);
1092 }
1093 
1094 bool ReturnFocusMenu::eventFilter (QObject *obj, QEvent *ev)
1095 {
1096  if (ev->type () == QEvent::MouseButtonRelease && underMouse ())
1097  {
1098  emit about_to_activate ();
1099  }
1100 
1101  return QMenu::eventFilter (obj, ev);
1102 }
1103 
1104 // Variable editor.
1105 
1107  : octave_dock_widget ("VariableEditor", p, oct_qobj),
1108  m_main (new dw_main_window (oct_qobj)),
1109  m_tool_bar (new QToolBar (m_main)),
1110  m_default_width (30),
1111  m_default_height (100),
1112  m_add_font_height (0),
1113  m_use_terminal_font (true),
1114  m_alternate_rows (true),
1115  m_stylesheet (""),
1116  m_font (),
1117  m_sel_font (),
1118  m_table_colors (),
1119  m_current_focus_vname (""),
1120  m_hovered_focus_vname (""),
1121  m_plot_mapper (nullptr),
1122  m_focus_widget (nullptr),
1123  m_focus_widget_vdw (nullptr)
1124 {
1125  set_title (tr ("Variable Editor"));
1126  setStatusTip (tr ("Edit variables."));
1127  setAttribute (Qt::WA_AlwaysShowToolTips);
1128 
1129  m_main->setParent (this);
1130  // See Octave bug #53409 and https://bugreports.qt.io/browse/QTBUG-55357
1131 #if (QT_VERSION < 0x050601) || (QT_VERSION >= 0x050701)
1132  m_main->setDockOptions (QMainWindow::AnimatedDocks |
1133  QMainWindow::AllowNestedDocks |
1134  QMainWindow::VerticalTabs);
1135 #else
1136  m_main->setDockNestingEnabled (true);
1137 #endif
1138 
1139  // Tool Bar.
1140 
1141  construct_tool_bar ();
1142  m_main->addToolBar (m_tool_bar);
1143 
1144  // Colors.
1145 
1146  for (int i = 0; i < ve_colors_count; i++)
1147  m_table_colors.append (QColor (Qt::white));
1148 
1149  // Use an MDI area that is shrunk to nothing as the central widget.
1150  // Future feature might be to switch to MDI mode in which the dock
1151  // area is shrunk to nothing and the widgets live in the MDI window.
1152 
1153  QMdiArea *central_mdiarea = new QMdiArea (m_main);
1154  central_mdiarea->setMinimumSize (QSize (0, 0));
1155  central_mdiarea->setMaximumSize (QSize (0, 0));
1156  central_mdiarea->resize (QSize (0, 0));
1157  m_main->setCentralWidget (central_mdiarea);
1158 
1159  setWidget (m_main);
1160 
1161  if (! p)
1162  make_window ();
1163 }
1164 
1165 void variable_editor::focusInEvent (QFocusEvent *ev)
1166 {
1167  octave_dock_widget::focusInEvent (ev);
1168 
1169  // set focus to the current variable or most recent if still valid
1170  if (m_focus_widget != nullptr)
1171  {
1172  // Activating a floating window causes problems.
1173  if (! m_focus_widget_vdw->isFloating ())
1174  activateWindow ();
1175  m_focus_widget->setFocus ();
1176  }
1177  else
1178  {
1179  QWidget *fw = m_main->focusWidget ();
1180  if (fw != nullptr)
1181  {
1182  activateWindow ();
1183  fw->setFocus ();
1184  }
1185  else
1186  {
1187  QDockWidget *any_qdw = m_main->findChild<QDockWidget *> ();
1188  if (any_qdw != nullptr)
1189  {
1190  activateWindow ();
1191  any_qdw->setFocus ();
1192  }
1193  else
1194  setFocus();
1195  }
1196  }
1197 }
1198 
1200 {
1201  // FIXME: Maybe toolbar actions could be handled with signals and
1202  // slots so that deleting the toolbar here would disconnect all
1203  // toolbar actions and any other slots that might try to access the
1204  // toolbar would work properly (I'm looking at you,
1205  // handle_focus_change).
1206 
1207  delete m_tool_bar;
1208  m_tool_bar = nullptr;
1209 }
1210 
1211 void
1212 variable_editor::edit_variable (const QString& name, const octave_value& val)
1213 {
1215 
1216  if (m_stylesheet.isEmpty ())
1217  {
1218  gui_settings *settings = rmgr.get_settings ();
1220  }
1221 
1222  QDockWidget *existing_qdw = m_main->findChild<QDockWidget *> (name);
1223  if (existing_qdw)
1224  {
1225  // Already open.
1226 
1227  // Put current focused variable out of focus
1228  if (m_main->focusWidget () != nullptr)
1229  {
1230  QFocusEvent event (QEvent::FocusOut, Qt::OtherFocusReason);
1231  QApplication::sendEvent (m_main->focusWidget (), &event);
1232  }
1233 
1234  // Put existing variable in focus and raise
1235  m_main->parentWidget ()->show ();
1236  existing_qdw->show ();
1237  existing_qdw->raise ();
1238  existing_qdw->activateWindow ();
1239  tab_to_front ();
1240  existing_qdw->setFocus ();
1241 
1242  return;
1243  }
1244 
1245  variable_dock_widget *page
1246  = new variable_dock_widget (this, m_octave_qobj);
1247 
1248  page->setObjectName (name);
1249  m_main->addDockWidget (Qt::LeftDockWidgetArea, page);
1250 
1251  // The old-style signal/slot connection appears to be needed here to
1252  // prevent a crash when closing a variable_dock_widget object.
1253  connect (qApp, SIGNAL (focusChanged (QWidget *, QWidget *)),
1254  page, SLOT (handle_focus_change (QWidget *, QWidget *)));
1255 
1256  connect (this, &variable_editor::visibilityChanged,
1257  page, &variable_dock_widget::setVisible);
1258 
1259  // Notify the variable editor for page actions.
1260  connect (page, &variable_dock_widget::destroyed,
1264 
1265  // See Octave bug #53807 and https://bugreports.qt.io/browse/QTBUG-44813
1266 #if (QT_VERSION >= 0x050302) && (QT_VERSION <= QTBUG_44813_FIX_VERSION)
1267  connect (page, SIGNAL (queue_unfloat_float ()),
1268  page, SLOT (unfloat_float ()), Qt::QueuedConnection);
1269  connect (page, SIGNAL (queue_float ()),
1270  page, SLOT (refloat ()), Qt::QueuedConnection);
1271 #endif
1272 
1273  variable_editor_stack *stack
1274  = new variable_editor_stack (page, m_octave_qobj);
1275 
1276  stack->setObjectName (name);
1277  page->setWidget (stack);
1278  page->setFocusProxy (stack);
1279 
1280  // Any interpreter_event signal from a variable_editor_stack object is
1281  // handled the same as for the parent variable_editor object.
1284 
1287 
1290  connect (this, &variable_editor::level_up_signal,
1292  connect (this, &variable_editor::save_signal,
1293  stack, [=] () { stack->save (); });
1294 
1295  variable_editor_view *edit_view = stack->edit_view ();
1296 
1297  edit_view->setObjectName (name);
1298  edit_view->setFont (m_font);
1299  edit_view->setStyleSheet (m_stylesheet);
1300  edit_view->setAlternatingRowColors (m_alternate_rows);
1301  edit_view->verticalHeader ()->setDefaultSectionSize (m_default_height
1302  + m_add_font_height);
1303 
1304  connect (m_plot_mapper, SIGNAL (mapped (const QString&)),
1305  edit_view, SLOT (selected_command_requested (const QString&)));
1306  connect (m_save_mapper, SIGNAL (mapped (const QString&)),
1307  stack, SLOT (save (const QString&)));
1308 
1309  connect (edit_view, &variable_editor_view::command_signal,
1313  connect (this, &variable_editor::clear_content_signal,
1319  connect (edit_view->horizontalHeader (),
1320  &QHeaderView::customContextMenuRequested,
1322  connect (edit_view->verticalHeader (),
1323  &QHeaderView::customContextMenuRequested,
1325  connect (edit_view, &variable_editor_view::customContextMenuRequested,
1327  connect (edit_view->horizontalScrollBar (), &QScrollBar::actionTriggered,
1329  connect (edit_view->verticalScrollBar (), &QScrollBar::actionTriggered,
1331 
1332  variable_editor_model *model =
1333  new variable_editor_model (name, val, stack);
1334 
1337  connect (model, &variable_editor_model::dataChanged,
1339  connect (this, &variable_editor::refresh_signal,
1343 
1344  edit_view->setModel (model);
1345  connect (edit_view, &variable_editor_view::doubleClicked,
1347 
1348  // Any interpreter_event signal from a variable_editor_model object is
1349  // handled the same as for the parent variable_editor object.
1350 
1353 
1356 
1357  // Must supply a title for a QLabel to be created. Calling set_title()
1358  // more than once will add more QLabels. Could change octave_dock_widget
1359  // to always supply a QLabel (initially empty) and then simply update its
1360  // contents.
1361  page->set_title (name);
1362  if (page->titleBarWidget () != nullptr)
1363  {
1364  QLabel *existing_ql = page->titleBarWidget ()->findChild<QLabel *> ();
1365 
1366  // FIXME: What was the intent here? update_label_signal does
1367  // not seem to exist now.
1368  connect (model, SIGNAL (description_changed (const QString&)),
1369  existing_ql, SLOT (setText (const QString&)));
1370  existing_ql->setMargin (2);
1371  }
1372 
1373  model->update_data (val);
1374 
1375  if (m_tool_bar)
1376  {
1377  QList<QTableView *> viewlist = findChildren<QTableView *> ();
1378  if (viewlist.size () == 1 && m_tool_bar)
1379  m_tool_bar->setEnabled (true);
1380  }
1381 
1382  show ();
1383  page->show ();
1384  page->raise ();
1385  page->activateWindow ();
1386  tab_to_front ();
1387  page->setFocus ();
1388 }
1389 
1390 void
1392 {
1393  QWidget *parent = parentWidget ();
1394 
1395  if (parent)
1396  {
1397  QList<QTabBar *> barlist = parent->findChildren<QTabBar *> ();
1398 
1399  QVariant this_value (reinterpret_cast<quintptr> (this));
1400 
1401  for (auto *tbar : barlist)
1402  {
1403  for (int i = 0; i < tbar->count (); i++)
1404  {
1405  if (tbar->tabData (i) == this_value)
1406  {
1407  tbar->setCurrentIndex (i);
1408  return;
1409  }
1410  }
1411  }
1412  }
1413 }
1414 
1415 void
1417 {
1418  emit refresh_signal ();
1419 }
1420 
1421 void
1422 variable_editor::callUpdate (const QModelIndex&, const QModelIndex&)
1423 {
1424  emit updated ();
1425 }
1426 
1427 void
1429 {
1430  m_main->notice_settings (settings); // update settings in parent main win
1431 
1432  m_default_width = settings->value (ve_column_width).toInt ();
1433 
1434  m_default_height = settings->value (ve_row_height).toInt ();
1435 
1436  m_alternate_rows = settings->value (ve_alternate_rows).toBool ();
1437 
1438  m_use_terminal_font = settings->value (ve_use_terminal_font).toBool ();
1439 
1440  QString font_name;
1441  int font_size;
1442  QString default_font = settings->value (global_mono_font).toString ();
1443 
1444  if (m_use_terminal_font)
1445  {
1446  font_name = settings->value (cs_font.key, default_font).toString ();
1447  font_size = settings->value (cs_font_size).toInt ();
1448  }
1449  else
1450  {
1451  font_name = settings->value (ve_font_name.key, default_font).toString ();
1452  font_size = settings->value (ve_font_size).toInt ();
1453  }
1454 
1455  m_font = QFont (font_name, font_size);
1456 
1457  QFontMetrics fm (m_font);
1458 
1459  m_add_font_height = fm.height ();
1460 
1461  int mode = settings->value (ve_color_mode).toInt ();
1462 
1463  for (int i = 0; i < ve_colors_count; i++)
1464  {
1465  QColor setting_color = settings->color_value (ve_colors[i], mode);
1466  m_table_colors.replace (i, setting_color);
1467  }
1468 
1469  update_colors ();
1470 
1471  // Icon size in the toolbar.
1472 
1473  if (m_tool_bar)
1474  {
1475  int size_idx = settings->value (global_icon_size).toInt ();
1476  size_idx = (size_idx > 0) - (size_idx < 0) + 1; // Make valid index from 0 to 2
1477 
1478  QStyle *st = style ();
1479  int icon_size = st->pixelMetric (global_icon_sizes[size_idx]);
1480  m_tool_bar->setIconSize (QSize (icon_size, icon_size));
1481  }
1482 
1483  // Shortcuts (same as file editor)
1486 }
1487 
1488 void
1490 {
1491  emit finished ();
1492 
1494 }
1495 
1496 void
1498 {
1499  // Invalidate the focus-restoring widget pointer if currently active.
1500  if (m_focus_widget_vdw == obj)
1501  {
1502  m_focus_widget = nullptr;
1503  m_focus_widget_vdw = nullptr;
1504  }
1505 
1506  if (m_tool_bar)
1507  {
1508  // If no variable pages remain, deactivate the tool bar.
1509  QList<variable_dock_widget *> vdwlist = findChildren<variable_dock_widget *> ();
1510  if (vdwlist.isEmpty ())
1511  m_tool_bar->setEnabled (false);
1512  }
1513 
1514  QFocusEvent ev (QEvent::FocusIn);
1515  focusInEvent (&ev);
1516 }
1517 
1518 void
1520 {
1521  m_current_focus_vname = name;
1522 
1523  // focusWidget() appears lost in transition to/from main window
1524  // so keep a record of the widget.
1525 
1526  QWidget *current = QApplication::focusWidget ();
1527  m_focus_widget = nullptr;
1528  m_focus_widget_vdw = nullptr;
1529  if (current != nullptr)
1530  {
1531  QList<variable_dock_widget *> vdwlist = findChildren<variable_dock_widget *> ();
1532  for (int i = 0; i < vdwlist.size (); i++)
1533  {
1534  variable_dock_widget *vdw = vdwlist.at (i);
1535  if (vdw->isAncestorOf (current))
1536  {
1537  m_focus_widget = current;
1538  m_focus_widget_vdw = vdw;
1539  break;
1540  }
1541  }
1542  }
1543 }
1544 
1545 void
1547 {
1549 }
1550 
1551 void
1553 {
1554  variable_dock_widget *tofocus = findChild<variable_dock_widget *> (m_hovered_focus_vname);
1555  if (tofocus != nullptr)
1556  {
1557  // Note that this may be platform and window system dependent.
1558  // On a particular Linux system, activateWindow() alone didn't
1559  // immediately set the active window and there was a race
1560  // between the window focus and action signal. Setting the
1561  // active window via the QApplication route did work.
1562  QApplication::setActiveWindow(tofocus->window());
1563  tofocus->activateWindow ();
1564  tofocus->setFocus (Qt::OtherFocusReason);
1565  }
1566 }
1567 
1568 void
1570 {
1571  emit save_signal ();
1572 }
1573 
1574 void
1576 {
1577  copyClipboard ();
1578 
1579  emit clear_content_signal ();
1580 }
1581 
1582 void
1584 {
1585  emit copy_clipboard_signal ();
1586 }
1587 
1588 void
1590 {
1591  emit paste_clipboard_signal ();
1592 
1593  emit updated ();
1594 }
1595 
1596 void
1598 {
1599  emit level_up_signal ();
1600 }
1601 
1602 // Also updates the font.
1603 
1605 {
1606  m_stylesheet = "";
1607 
1608  if (m_table_colors.length () > 0)
1609  m_stylesheet += "QTableView::item{ color: "
1610  + m_table_colors[0].name () +" }";
1611 
1612  if (m_table_colors.length () > 1)
1613  m_stylesheet += "QTableView::item{ background-color: "
1614  + m_table_colors[1].name () +" }";
1615 
1616  if (m_table_colors.length () > 2)
1617  m_stylesheet += "QTableView::item{ selection-color: "
1618  + m_table_colors[2].name () +" }";
1619 
1620  if (m_table_colors.length () > 3)
1621  m_stylesheet += "QTableView::item:selected{ background-color: "
1622  + m_table_colors[3].name () +" }";
1623 
1624  if (m_table_colors.length () > 4 && m_alternate_rows)
1625  {
1626  m_stylesheet += "QTableView::item:alternate{ background-color: "
1627  + m_table_colors[4].name () +" }";
1628 
1629  m_stylesheet += "QTableView::item:alternate:selected{ background-color: "
1630  + m_table_colors[3].name () +" }";
1631  }
1632 
1633  QList<QTableView *> viewlist = findChildren<QTableView *> ();
1634  for (int i = 0; i < viewlist.size (); i++)
1635  {
1636  QTableView *view = viewlist.at (i);
1637 
1638  if (! view)
1639  continue;
1640 
1641  view->setAlternatingRowColors (m_alternate_rows);
1642  view->setStyleSheet (m_stylesheet);
1643  view->setFont (m_font);
1644  }
1645 
1646 }
1647 
1648 QAction *
1650  const QString& text,
1651  const QObject *receiver,
1652  const char *member)
1653 {
1654  QAction *action = new QAction (icon, text, this);
1655  connect(action, SIGNAL (triggered ()), receiver, member);
1657  button->setDefaultAction (action);
1658  button->setText (text);
1659  button->setToolTip (text);
1660  button->setIcon (icon);
1661  m_tool_bar->addWidget (button);
1662 
1663  return action;
1664 }
1665 
1666 void
1668 {
1669  m_tool_bar->setAllowedAreas (Qt::TopToolBarArea);
1670 
1671  m_tool_bar->setObjectName ("VariableEditorToolBar");
1672 
1673  m_tool_bar->setWindowTitle (tr ("Variable Editor Toolbar"));
1674 
1676 
1677  m_save_action = add_tool_bar_button (rmgr.icon ("document-save"), tr ("Save"),
1678  this, SLOT (save ()));
1679  addAction (m_save_action);
1680  m_save_action->setShortcutContext (Qt::WidgetWithChildrenShortcut);
1681  m_save_action->setStatusTip(tr("Save variable to a file"));
1682 
1683  QAction *action = new QAction (rmgr.icon ("document-save-as"), tr ("Save in format ..."), m_tool_bar);
1684 
1685  QToolButton *save_tool_button = new HoverToolButton (m_tool_bar);
1686  save_tool_button->setDefaultAction (action);
1687 
1688  save_tool_button->setText (tr ("Save in format ..."));
1689  save_tool_button->setToolTip (tr("Save variable to a file in different format"));
1690  save_tool_button->setIcon (rmgr.icon ("document-save-as"));
1691  save_tool_button->setPopupMode (QToolButton::InstantPopup);
1692 
1693  QMenu *save_menu = new ReturnFocusMenu (save_tool_button);
1694  save_menu->setTitle (tr ("Save in format ..."));
1695  save_menu->setSeparatorsCollapsible (false);
1696 
1697  m_save_mapper = new QSignalMapper (save_menu);
1698  for (int i = 0; i < ve_save_formats.length (); i++)
1699  m_save_mapper->setMapping
1700  (save_menu->addAction (ve_save_formats.at (i),
1701  m_save_mapper, SLOT (map ())),
1702  ve_save_formats.at (i));
1703 
1704  save_tool_button->setMenu (save_menu);
1705  m_tool_bar->addWidget (save_tool_button);
1706 
1707  m_tool_bar->addSeparator ();
1708 
1709  action = add_tool_bar_button (rmgr.icon ("edit-cut"), tr ("Cut"),
1710  this, SLOT (cutClipboard ()));
1711  action->setStatusTip(tr("Cut data to clipboard"));
1712 
1713  action = add_tool_bar_button (rmgr.icon ("edit-copy"), tr ("Copy"),
1714  this, SLOT (copyClipboard ()));
1715  action->setStatusTip(tr("Copy data to clipboard"));
1716 
1717  action = add_tool_bar_button (rmgr.icon ("edit-paste"), tr ("Paste"),
1718  this, SLOT (pasteClipboard ()));
1719  action->setStatusTip(tr("Paste clipboard into variable data"));
1720 
1721  m_tool_bar->addSeparator ();
1722 
1723  // FIXME: Add a print item?
1724  // QAction *print_action; /icons/fileprint.png
1725  // m_tool_bar->addSeparator ();
1726 
1727  action = new QAction (rmgr.icon ("plot-xy-curve"), tr ("Plot"), m_tool_bar);
1728  action->setToolTip (tr ("Plot Selected Data"));
1729  QToolButton *plot_tool_button = new HoverToolButton (m_tool_bar);
1730  plot_tool_button->setDefaultAction (action);
1731 
1732  plot_tool_button->setText (tr ("Plot"));
1733  plot_tool_button->setToolTip (tr ("Plot selected data"));
1734  plot_tool_button->setIcon (rmgr.icon ("plot-xy-curve"));
1735 
1736  plot_tool_button->setPopupMode (QToolButton::InstantPopup);
1737 
1738  QMenu *plot_menu = new ReturnFocusMenu (plot_tool_button);
1739  plot_menu->setTitle (tr ("Plot"));
1740  plot_menu->setSeparatorsCollapsible (false);
1741 
1742  m_plot_mapper = make_plot_mapper (plot_menu);
1743 
1744  plot_tool_button->setMenu (plot_menu);
1745 
1746  m_tool_bar->addWidget (plot_tool_button);
1747 
1748  m_tool_bar->addSeparator ();
1749 
1750  action = add_tool_bar_button (rmgr.icon ("go-up"), tr ("Up"), this,
1751  SLOT (levelUp ()));
1752  action->setStatusTip(tr("Go one level up in variable hierarchy"));
1753 
1754  // The QToolButton mouse-clicks change active window, so connect all
1755  // HoverToolButton and ReturnFocusToolButton objects to the mechanism
1756  // that restores active window and focus before acting.
1757  QList<HoverToolButton *> hbuttonlist
1758  = m_tool_bar->findChildren<HoverToolButton *> (""
1759  , Qt::FindDirectChildrenOnly
1760  );
1761  for (int i = 0; i < hbuttonlist.size (); i++)
1762  {
1763  connect (hbuttonlist.at (i), &HoverToolButton::hovered_signal,
1765  connect (hbuttonlist.at (i), &HoverToolButton::popup_shown_signal,
1767  }
1768 
1769  QList<ReturnFocusToolButton *> rfbuttonlist
1770  = m_tool_bar->findChildren<ReturnFocusToolButton *> (""
1771  , Qt::FindDirectChildrenOnly
1772  );
1773  for (int i = 0; i < rfbuttonlist.size (); i++)
1774  {
1775  connect (rfbuttonlist.at (i), &ReturnFocusToolButton::about_to_activate,
1777  }
1778 
1779  // Same for QMenu
1780  QList<ReturnFocusMenu *> menulist
1781  = m_tool_bar->findChildren<ReturnFocusMenu *> ();
1782  for (int i = 0; i < menulist.size (); i++)
1783  {
1784  connect (menulist.at (i), &ReturnFocusMenu::about_to_activate,
1786  }
1787 
1788  m_tool_bar->setAttribute(Qt::WA_ShowWithoutActivating);
1789  m_tool_bar->setFocusPolicy (Qt::NoFocus);
1790 
1791  // Disabled when no tab is present.
1792 
1793  m_tool_bar->setEnabled (false);
1794 }
1795 
OCTAVE_END_NAMESPACE(octave)
charNDArray max(char d, const charNDArray &m)
Definition: chNDArray.cc:230
charNDArray min(char d, const charNDArray &m)
Definition: chNDArray.cc:207
void hovered_signal(void)
void popup_shown_signal(void)
bool eventFilter(QObject *obj, QEvent *ev)
HoverToolButton(QWidget *parent=nullptr)
void about_to_activate(void)
bool eventFilter(QObject *obj, QEvent *ev)
ReturnFocusMenu(QWidget *parent=nullptr)
void about_to_activate(void)
bool eventFilter(QObject *obj, QEvent *ev)
ReturnFocusToolButton(QWidget *parent=nullptr)
Base class for Octave interfaces that use Qt.
resource_manager & get_resource_manager(void)
shortcut_manager & get_shortcut_manager(void)
void notice_settings(const gui_settings *)
base_qobject & m_octave_qobj
void set_title(const QString &)
bool event(QEvent *event)
virtual void closeEvent(QCloseEvent *e)
void make_window(bool widget_was_dragged=false)
gui_settings * get_settings(void) const
QIcon icon(const QString &icon_name, bool octave_only=false, const QString &icon_alt_name=QString())
void set_shortcut(QAction *action, const sc_pref &scpref, bool enable=true)
QAction * m_fullscreen_action
void variable_focused_signal(const QString &name)
virtual void closeEvent(QCloseEvent *e)
variable_dock_widget(QWidget *p, base_qobject &oct_qobj)
void queue_float(void)
void resizeEvent(QResizeEvent *event)
void handle_focus_change(QWidget *old, QWidget *now)
void queue_unfloat_float(void)
octave_idx_type data_rows(void) const
void set_editable_signal(bool)
void edit_variable_signal(const QString &name, const octave_value &val)
void interpreter_event(const fcn_callback &fcn)
void update_data(const octave_value &val)
void double_click(const QModelIndex &idx)
octave_idx_type data_columns(void) const
bool clear_content(const QModelIndex &idx)
variable_editor_view * edit_view(void)
void save(const QString &format=QString())
base_qobject & m_octave_qobj
variable_editor_stack(QWidget *p, base_qobject &oct_qobj)
void interpreter_event(const fcn_callback &fcn)
void do_save_signal(const QString &format, const QString &save_opts)
void set_editable(bool editable)
QTextEdit * make_disp_view(QWidget *parent)
variable_editor_view * m_edit_view
void do_save(const QString &format, const QString &save_opts)
void edit_variable_signal(const QString &name, const octave_value &val)
void command_signal(const QString &cmd)
variable_editor_model * m_var_model
variable_editor_view(QWidget *p, base_qobject &oct_qobj)
void createColumnMenu(const QPoint &pt)
base_qobject & m_octave_qobj
QList< int > range_selected(void)
void add_edit_actions(QMenu *menu, const QString &qualifier_string)
void setModel(QAbstractItemModel *model)
void handle_horizontal_scroll_action(int action)
void handle_vertical_scroll_action(int action)
void createContextMenu(const QPoint &pt)
void selected_command_requested(const QString &cmd)
void createRowMenu(const QPoint &pt)
variable_dock_widget * m_focus_widget_vdw
void paste_clipboard_signal(void)
void record_hovered_focus_variable(void)
QToolBar * m_tool_bar
QAction * m_save_action
void level_up_signal(void)
QString m_current_focus_vname
void callUpdate(const QModelIndex &, const QModelIndex &)
QAction * add_tool_bar_button(const QIcon &icon, const QString &text, const QObject *receiver, const char *member)
QWidget * m_focus_widget
void closeEvent(QCloseEvent *)
void variable_focused(const QString &name)
variable_editor(QWidget *parent, base_qobject &oct_qobj)
void notice_settings(const gui_settings *)
void pasteClipboard(void)
void copyClipboard(void)
dw_main_window * m_main
void copy_clipboard_signal(void)
QList< QColor > m_table_colors
QSignalMapper * m_save_mapper
void focusInEvent(QFocusEvent *ev)
void construct_tool_bar(void)
void variable_destroyed(QObject *obj)
void tab_to_front(void)
QString m_hovered_focus_vname
void cutClipboard(void)
void finished(void)
void delete_selected_signal(void)
void restore_hovered_focus_variable(void)
void interpreter_event(const fcn_callback &fcn)
QSignalMapper * m_plot_mapper
void command_signal(const QString &cmd)
void updated(void)
void save_signal(void)
void refresh_signal(void)
void edit_variable(const QString &name, const octave_value &val)
void update_colors(void)
void clear_content_signal(void)
OCTAVE_BEGIN_NAMESPACE(octave) static octave_value daspk_fcn
const gui_pref cs_font_size("terminal/fontSize", QVariant(10))
const gui_pref cs_font("terminal/fontName", QVariant())
const gui_pref global_mono_font("monospace_font", global_font_family)
const QStyle::PixelMetric global_icon_sizes[3]
const QStringList global_icon_paths
const gui_pref global_use_native_dialogs("use_native_file_dialogs", QVariant(true))
@ ICON_THEME_OCTAVE
const gui_pref global_icon_size("toolbar_icon_size", QVariant(0))
const sc_pref sc_edit_file_save(sc_edit_file+":save", QKeySequence::Save)
const gui_pref ve_color_mode("variable_editor/color_mode", QVariant(0))
const int ve_colors_count
const QStringList ve_save_formats
const gui_pref ve_column_width("variable_editor/column_width", QVariant(100))
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 QStringList ve_save_formats_ext
const gui_pref ve_colors[2 *ve_colors_count]
const gui_pref ve_row_height("variable_editor/row_height", QVariant(10))
T mod(T x, T y)
Definition: lo-mappers.h:294
OCTAVE_EXPORT octave_value_list Fsave(octave::interpreter &interp, const octave_value_list &args, int nargout)
Definition: load-save.cc:1811
OCTAVE_EXPORT octave_value_list Fsave_default_options(octave::interpreter &interp, const octave_value_list &args, int nargout)
Definition: load-save.cc:1951
std::complex< double > w(std::complex< double > z, double relerr=0)
QString fromStdString(const std::string &s)
return octave_value(v1.char_array_value() . concat(v2.char_array_value(), ra_idx),((a1.is_sq_string()||a2.is_sq_string()) ? '\'' :'"'))
octave_value_list ovl(const OV_Args &... args)
Construct an octave_value_list with less typing.
Definition: ovl.h:211
static std::string format_string("short")
int qt_fontmetrics_horizontal_advance(const QFontMetrics &fm, QChar ch)
Definition: qt-utils.h:48
class OCTAVE_API range
Definition: range-fwd.h:33
const QString key
std::size_t format(std::ostream &os, const char *fmt,...)
Definition: utils.cc:1473
#define DOCKED_FULLSCREEN_BUTTON_TOOLTIP
static QSignalMapper * make_plot_mapper(QMenu *menu)
#define UNDOCKED_FULLSCREEN_BUTTON_TOOLTIP
#define QTBUG_44813_FIX_VERSION