GNU Octave  6.2.0
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
Table.cc
Go to the documentation of this file.
1 ////////////////////////////////////////////////////////////////////////
2 //
3 // Copyright (C) 2016-2021 The Octave Project Developers
4 //
5 // See the file COPYRIGHT.md in the top-level directory of this
6 // distribution or <https://octave.org/copyright/>.
7 //
8 // This file is part of Octave.
9 //
10 // Octave is free software: you can redistribute it and/or modify it
11 // under the terms of the GNU General Public License as published by
12 // the Free Software Foundation, either version 3 of the License, or
13 // (at your option) any later version.
14 //
15 // Octave is distributed in the hope that it will be useful, but
16 // WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 // GNU General Public License for more details.
19 //
20 // You should have received a copy of the GNU General Public License
21 // along with Octave; see the file COPYING. If not, see
22 // <https://www.gnu.org/licenses/>.
23 //
24 ////////////////////////////////////////////////////////////////////////
25 
26 #if defined (HAVE_CONFIG_H)
27 # include "config.h"
28 #endif
29 
30 #include <QCheckBox>
31 #include <QComboBox>
32 #include <QEvent>
33 #include <QFrame>
34 #include <QHBoxLayout>
35 #include <QHeaderView>
36 #include <QLabel>
37 #include <QLineEdit>
38 #include <QModelIndexList>
39 #include <QMouseEvent>
40 #include <QString>
41 #include <QStringList>
42 #include <QTableWidget>
43 #include <QTableWidgetItem>
44 #include <QTimer>
45 
46 #include "Container.h"
47 #include "ContextMenu.h"
48 #include "Table.h"
49 #include "QtHandlesUtils.h"
50 
51 #include "octave-qobject.h"
52 
53 #include "graphics.h"
54 #include "interpreter.h"
55 #include "oct-stream.h"
56 #include "oct-string.h"
57 #include "oct-strstrm.h"
58 #include "ov-struct.h"
59 
60 namespace QtHandles
61 {
62 
63  static const int AUTO_WIDTH = 75;
64 
65 #define AUTO_HEIGHT (tp.get_fontsize () * 2 - 1)
66 
67  static QSize realQSizeForTable (QTableWidget *t)
68  {
69  int w = t->verticalHeader ()->width () + 4;
70  for (int i = 0; i < t->columnCount (); i++)
71  w += t->columnWidth (i);
72  int h = t->horizontalHeader ()->height () + 4;
73  for (int i = 0; i < t->rowCount (); i++)
74  h += t->rowHeight (i);
75  return QSize (w, h);
76  }
77 
78 #define FORMATNUMBER(type) \
79  static QString formatNumber (type d, \
80  char format = 'f', \
81  int precision = 4) \
82  { \
83  if (format == 'n') \
84  { \
85  if (d == floor (d)) \
86  return QString::number (d, 'g', precision); \
87  else if (d <= pow (10, precision - 1) && d > pow (10, 1 - precision)) \
88  return QString::number (d, 'f', precision); \
89  else \
90  return QString::number (d, 'e', precision); \
91  } \
92  else if (format == 'F') \
93  { \
94  int exponent = floor (log10 (d) / 3) * 3; \
95  d *= pow (10, -exponent); \
96  return QString::number (d, 'f', precision) + "e" + \
97  (exponent < 0 ? "-" : "+") + \
98  QString ("%1").arg (abs (exponent), 3, 10, QChar ('0')); \
99  } \
100  else if (format == 'E') \
101  { \
102  int exponent = floor (log10 (d) / 3) * 3; \
103  d *= pow (10, -exponent); \
104  return QString::number (d, \
105  'f', \
106  precision - floor (log10 (d)) - 1) + \
107  "e" + (exponent < 0 ? "-" : "+") + \
108  QString ("%1").arg (abs (exponent), 3, 10, QChar ('0')); \
109  } \
110  else \
111  return QString::number (d, format, precision); \
112  }
113 
114  FORMATNUMBER(double)
116 
117 #undef FORMATNUMBER
118 
119  static QString formatComplex (Complex c, char format = 'f', int precision = 4)
120  {
121  return formatNumber (c.real (), format, precision) + " + "
122  + formatNumber (c.imag (), format, precision) + "i";
123  }
124 
125 #define FORMAT_VALUE_EXCEPT_RAT(f,l) \
126  if (format == "numeric" || format == "short") \
127  text = formatNumber (value, 'n', f); \
128  else if (format == "short f" || format == "shortf") \
129  text = formatNumber (value, 'f', f); \
130  else if (format == "short e" || format == "shorte") \
131  text = formatNumber (value, 'e', f); \
132  else if (format == "short eng" || format == "shorteng") \
133  text = formatNumber (value, 'F', f); \
134  else if (format == "short g" || format == "shortg") \
135  text = formatNumber (value, 'g', f + 1); \
136  else if (format == "long") \
137  text = formatNumber (value, 'n', l); \
138  else if (format == "long f" || format == "longf") \
139  text = formatNumber (value, 'f', l); \
140  else if (format == "long e" || format == "longe") \
141  text = formatNumber (value, 'e', l); \
142  else if (format == "long eng" || format == "longeng") \
143  text = formatNumber (value, 'E', l); \
144  else if (format == "long g" || format == "longg") \
145  text = formatNumber (value, 'g', l + 1); \
146  else if (format == "bank") \
147  text = QString::number (value, 'f', 2); \
148  else if (format == "+") \
149  if (value > 0) \
150  text = Utils::fromStdString ("+"); \
151  else if (value < 0) \
152  text = Utils::fromStdString ("-"); \
153  else \
154  text = Utils::fromStdString ("");
155 
156 #define FORMAT_VALUE(f,l) \
157  FORMAT_VALUE_EXCEPT_RAT(f,l) \
158  else if (format == "rat") \
159  text = Utils::fromStdString (rational_approx (double (value), 0)); \
160  else \
161  { \
162  text = formatNumber (value, 'n', f); \
163  flag = Qt::AlignLeft ; \
164  }
165 
166 #define FORMAT_UINT_VALUE() \
167  text = QString::number (value); \
168  if (format == "char" || format == "popup") \
169  flag = Qt::AlignLeft; \
170  else if (format == "+") \
171  { \
172  if (value > 0) \
173  text = Utils::fromStdString ("+"); \
174  else \
175  text = Utils::fromStdString (""); \
176  }
177 
178 #define FORMAT_INT_VALUE() \
179  text = QString::number (value); \
180  if (format == "char" || format == "popup") \
181  flag = Qt::AlignLeft ; \
182  else if (format == "+") \
183  { \
184  if (value > 0) \
185  text = Utils::fromStdString ("+"); \
186  else if (value < 0) \
187  text = Utils::fromStdString ("-"); \
188  else \
189  text = Utils::fromStdString (""); \
190  }
191 
192  static std::pair<Qt::AlignmentFlag, QString>
193  qStringValueFor (octave_value val, std::string format = "")
194  {
195  Qt::AlignmentFlag flag = Qt::AlignRight;
196  QString text;
197  if (val.is_string ())
198  {
200  flag = Qt::AlignLeft ;
201  }
202  else if (val.iscomplex ())
203  {
204  // Matlab has multiple complex types, we only have double.
205  Complex c = val.complex_value ();
206  if (format == "short")
207  text = formatComplex (c, 'f', 4);
208  else if (format == "short e" || format == "shorte")
209  text = formatComplex (c, 'e', 4);
210  else if (format == "short eng" || format == "shorteng")
211  text = formatComplex (c, 'E', 4);
212  else if (format == "short g" || format == "shortg")
213  text = formatComplex (c, 'g', 5);
214  else if (format == "long")
215  text = formatComplex (c, 'f', 15);
216  else if (format == "long e" || format == "longe")
217  text = formatComplex (c, 'e', 15);
218  else if (format == "long eng" || format == "longeng")
219  text = formatComplex (c, 'E', 15);
220  else if (format == "long g" || format == "longg")
221  text = formatComplex (c, 'g', 16);
222  else if (format == "bank")
223  text = QString::number (c.real (), 'f', 2);
224  else if (format == "+")
225  {
226  if (c.real () > 0)
227  text = Utils::fromStdString ("+");
228  else if (c.real () < 0)
229  text = Utils::fromStdString ("-");
230  else
231  text = Utils::fromStdString ("");
232  }
233  else if (format == "rat")
234  text = Utils::fromStdString (rational_approx (c.real (), 0)) + " + "
235  + Utils::fromStdString (rational_approx (c.imag (), 0)) + "i";
236  else if (format == "numeric")
237  text = QString::number (c.real (), 'g', 5) + " + "
238  + QString::number (c.imag (), 'g', 5) + "i";
239  else
240  {
241  text = QString::number (c.real (), 'g', 5) + " + "
242  + QString::number (c.imag (), 'g', 5) + "i";
243  flag = Qt::AlignLeft ;
244  }
245  }
246  else if (val.is_double_type () )
247  {
248  double value = val.double_value ();
249  FORMAT_VALUE(4, 15)
250  }
251  else if (val.is_single_type ())
252  {
253  float value = val.float_value ();
254  FORMAT_VALUE(4, 7)
255  }
256  else if (val.is_int8_type ())
257  {
258  short int value = val.short_value ();
260  }
261  else if (val.is_uint8_type ())
262  {
263  unsigned short int value = val.ushort_value ();
265  }
266  else if (val.is_int16_type ())
267  {
268  int value = val.int_value ();
270  }
271  else if (val.is_uint16_type ())
272  {
273  unsigned int value = val.uint_value ();
275  }
276  else if (val.is_int32_type ())
277  {
278  long int value = val.long_value ();
280  }
281  else if (val.is_uint32_type ())
282  {
283  unsigned long int value = val.ulong_value ();
285  }
286  else if (val.is_int64_type ())
287  {
288  int64_t value = val.int64_value ();
290  }
291  else if (val.is_uint64_type ())
292  {
293  uint64_t value = val.uint64_value ();
295  }
296  else if (val.islogical ())
297  {
298  bool b = val.bool_value ();
299  if (format == "char" || format == "popup" || format == "")
300  {
301  text = Utils::fromStdString (b ? "true" : "false");
302  flag = Qt::AlignLeft ;
303  }
304  else if (format == "+")
305  {
306  text = Utils::fromStdString (b ? "+" : "");
307  flag = Qt::AlignLeft ;
308  }
309  else
310  text = Utils::fromStdString (b ? "1" : "0");
311  }
312  else
313  {
314  std::stringstream warn_string;
315  warn_string << "Unknown conversion for datatype " << val.class_name ()
316  << " to " << format
317  << " for value " << val.string_value (true);
318  warning ("%s", warn_string.str ().c_str ());
319 
320  text = Utils::fromStdString (val.string_value (true));
321  }
322 
323  return std::make_pair (flag, text);
324  }
325 
326 #undef FORMAT_VALUE
327 #undef FORMAT_VALUE_EXCEPT_RAT
328 #undef FORMAT_UINT_VALUE
329 #undef FORMAT_INT_VALUE
330 
331  static QTableWidgetItem * itemFor (octave_value val, std::string format = "",
332  bool enabled = false)
333  {
334  QTableWidgetItem *retval = new QTableWidgetItem ();
335  std::pair<Qt::AlignmentFlag, QString> flag_and_text =
336  qStringValueFor (val, format);
337  retval->setTextAlignment (flag_and_text.first);
338  retval->setText (flag_and_text.second);
339 
340  if (enabled)
341  retval->setFlags (retval->flags () | Qt::ItemIsEditable);
342  else
343  retval->setFlags (retval->flags () & ~Qt::ItemIsEditable);
344 
345  return retval;
346  }
347 
348  static octave_value
350  const octave_value& old_value)
351  {
353 
354  // Define a macro to help with the conversion of strings to integers
355  // FIXME: these will happily integer overflow in the (u)int64 case
356  // - this probably doesn't matter.
357 #define SCANF_AND_CONVERT(name,ctype,format) \
358  else if (old_value.is_ ## name ## _type ()) \
359  { \
360  ctype val; \
361  int n; \
362  const std::string cxx_str = ov.string_value (); \
363  const char *c_str = cxx_str.c_str (); \
364  int error = sscanf (c_str, format, &val, &n); \
365  if (error != 1 || c_str[n]) \
366  { \
367  val = 0; \
368  } \
369  retval = octave_value ( octave_ ## name (val)); \
370  }
371 
372  if (old_value.is_string ())
373  retval = ov;
374  SCANF_AND_CONVERT(int8, int64_t, "%" PRId64 " %n")
375  SCANF_AND_CONVERT(uint8, uint64_t, "%" PRIu64 " %n")
376  SCANF_AND_CONVERT(int16, int64_t, "%" PRId64 " %n")
377  SCANF_AND_CONVERT(uint16, uint64_t, "%" PRIu64 " %n")
378  SCANF_AND_CONVERT(int32, int64_t, "%" PRId64 " %n")
379  SCANF_AND_CONVERT(uint32, uint64_t, "%" PRIu64 " %n")
380  SCANF_AND_CONVERT(int64, int64_t, "%" PRId64 " %n")
381  SCANF_AND_CONVERT(uint64, uint64_t, "%" PRIu64 " %n")
382 
383 #undef SCANF_AND_CONVERT
384 
385  else if (old_value.isnumeric () && ! old_value.isinteger ())
386  {
387  // Basically need to do str2double
389  if (old_value.is_single_type ())
390  retval = octave_value (FloatComplex (complex));
391  else
392  retval = octave_value (complex);
393  }
394  else if (old_value.islogical ())
395  {
396  // Right: Matlab basically needs this to be true or false, we should
397  // accept 1 too.
398  if (ov.string_value () == "true" || ov.string_value () == "1")
399  retval = octave_value (true);
400  else
401  retval = octave_value (false);
402  }
403  else
405  return retval;
406  }
407 
408  QWidget *
409  Table::checkBoxForLogical (octave_value val, bool enabled = false)
410  {
412  QCheckBox *checkBox = new QCheckBox ();
413  QHBoxLayout *layout = new QHBoxLayout (retval);
414  layout->addWidget (checkBox);
415  layout->setAlignment (Qt::AlignCenter);
416  layout->setContentsMargins (0, 0, 0, 0);
417  retval->setLayout (layout);
418 
419  if ((val.islogical () || val.is_bool_scalar ()) && val.bool_value ())
420  checkBox->setCheckState (Qt::Checked);
421  else
422  checkBox->setCheckState (Qt::Unchecked);
423 
424  checkBox->setAttribute (Qt::WA_TransparentForMouseEvents, true);
425  checkBox->setFocusPolicy (Qt::NoFocus);
426  checkBox->setProperty ("Enabled", QVariant (enabled));
427 
428  return retval;
429  }
430 
431  Table*
433  const graphics_object& go)
434  {
435  Object *parent = parentObject (interp, go);
436 
437  if (parent)
438  {
439  Container *container = parent->innerContainer ();
440 
441  if (container)
442  return new Table (oct_qobj, interp, go, new QTableWidget (container));
443  }
444 
445  return 0;
446  }
447 
449  const graphics_object& go, QTableWidget *tableWidget)
450  : Object (oct_qobj, interp, go, tableWidget), m_tableWidget (tableWidget),
451  m_curData (), m_blockUpdates (false)
452  {
453  qObject ()->setObjectName ("UItable");
454  uitable::properties& tp = properties<uitable> ();
455 
456  m_curData = octave_value (tp.get_data ());
457  Matrix bb = tp.get_boundingbox (false);
458  m_tableWidget->setObjectName ("UITable");
459  m_tableWidget->setAutoFillBackground (true);
460  m_tableWidget->setGeometry (octave::math::round (bb(0)),
461  octave::math::round (bb(1)),
462  octave::math::round (bb(2)),
463  octave::math::round (bb(3)));
465  m_tableWidget->setSelectionBehavior (QAbstractItemView::SelectItems);
466  updatePalette ();
467  m_keyPressHandlerDefined = ! tp.get_keypressfcn ().isempty ();
468  m_keyReleaseHandlerDefined = ! tp.get_keyreleasefcn ().isempty ();
469  updateData ();
470  updateRowname ();
471  updateColumnname ();
473  updateEnable (); // Also does rearrangeableColumns
474  m_tableWidget->setToolTip (Utils::fromStdString (tp.get_tooltipstring ()));
475  m_tableWidget->setVisible (tp.is_visible ());
476  updateExtent ();
477  m_tableWidget->installEventFilter (this);
478 
479 
480  connect (m_tableWidget, SIGNAL (itemChanged (QTableWidgetItem*)),
481  SLOT (itemChanged (QTableWidgetItem*)));
482  connect (m_tableWidget, SIGNAL (cellClicked (int, int)),
483  SLOT (cellClicked (int, int)));
484  connect (m_tableWidget, SIGNAL (itemSelectionChanged (void)),
485  SLOT (itemSelectionChanged (void)));
486  }
487 
488  Table::~Table (void) { }
489 
490  void
492  {
493  if (! (properties<uitable> ().get_cellselectioncallback ().isempty ()))
494  {
495  QModelIndexList modelIndexList =
496  m_tableWidget->selectionModel ()->selectedIndexes ();
497  int length = modelIndexList.size ();
498  Matrix indices = Matrix (length, 2);
499  for (int i = 0; i < length; i++)
500  {
501  indices(i, 0) = modelIndexList.value (i).row () + 1;
502  indices(i, 1) = modelIndexList.value (i).column () + 1;
503  }
504  octave_scalar_map eventData;
505  eventData.setfield ("Indices", indices);
506  octave_value cellSelectionCallbackEventObject =
507  octave_value (new octave_struct (eventData));
508  emit gh_callback_event (m_handle, "cellselectioncallback",
509  cellSelectionCallbackEventObject);
510  }
511  }
512 
513  void
514  Table::cellClicked (int row, int col)
515  {
516  QCheckBox *checkBox = nullptr;
517  QWidget *widget
518  = qobject_cast<QWidget *> (m_tableWidget->cellWidget (row, col));
519  if (widget && ! widget->children ().isEmpty ())
520  {
521  QHBoxLayout *layout
522  = qobject_cast<QHBoxLayout *> (widget->children ().first ());
523 
524  if (layout && layout->count () > 0)
525  checkBox = qobject_cast<QCheckBox *> (layout->itemAt (0)-> widget ());
526  }
527 
528  if (checkBox && checkBox->property ("Enabled").toBool ())
529  checkBoxClicked (row, col, checkBox);
530  }
531 
532  void
534  int col,
535  octave_value old_value,
536  octave_value new_value,
537  octave_value edit_data,
539  {
540 
541  if (!(properties<uitable> ().get_celleditcallback ().isempty ()))
542  {
543  Matrix indices = Matrix (1, 2);
544  indices(0, 0) = row + 1;
545  indices(0, 1) = col + 1;
546 
547  octave_scalar_map eventData;
548  eventData.setfield ("Indices", indices);
549  eventData.setfield ("PreviousData", old_value);
550  eventData.setfield ("NewData", new_value);
551  eventData.setfield ("EditData", edit_data);
552  eventData.setfield ("Error", error);
553 
554  octave_value cellEditCallbackEventObject =
555  octave_value (new octave_struct (eventData));
556 
557  emit gh_callback_event (m_handle, "celleditcallback",
558  cellEditCallbackEventObject);
559  }
560  else if (error.string_value ().length () > 0)
561  warning ("%s", error.string_value ().c_str ());
562  }
563 
564  void
565  Table::comboBoxCurrentIndexChanged (const QString& value)
566  {
567  if (m_blockUpdates)
568  return;
569 
570  m_blockUpdates = true;
571 
573 
574  octave::autolock guard (gh_mgr.graphics_lock ());
575 
577  bool ok = false;
578 
579  QComboBox *comboBox = qobject_cast<QComboBox *> (sender ());
580  int row = comboBox->property ("row").toInt ();
581  int col = comboBox->property ("col").toInt ();
582 
583  octave_value edit_data = octave_value (Utils::toStdString (value));
584 
585  if (row < data.rows () && col < data.columns ())
586  {
587  if (data.iscell ())
588  {
589  Cell cell = data.cell_value ();
590  octave_value old_data = cell(row, col);
591  if (cell(row, col).is_string ())
592  {
593  cell(row, col) = edit_data;
594  if (edit_data.string_value () != old_data.string_value ())
595  {
596  m_curData = octave_value (cell);
597  emit gh_set_event (m_handle, "data", octave_value (cell),
598  false);
599  }
600 
602  sendCellEditCallback (row, col,
603  old_data,
604  edit_data,
605  edit_data,
606  error);
607  ok = true;
608  }
609  else
610  {
611  cell(row, col) = attempt_type_conversion (edit_data, old_data);
612 
613  // Inform the QTableWidget of our change
614  updateData (row, col, cell(row, col),
615  columnformat (col), columneditable (col));
616 
617  m_curData = octave_value (cell);
618  emit gh_set_event (m_handle, "data", octave_value (cell),
619  false);
620 
623  col,
624  old_data,
625  cell(row, col),
626  edit_data,
627  error);
628  ok = true;
629  }
630  }
631  else
632  {
633  octave_value old_data = data.is_matrix_type ()
634  ? data.fast_elem_extract (row + col * data.rows ())
635  : octave_value ();
636  data.fast_elem_insert (row + col * data.rows (),
637  attempt_type_conversion (edit_data, old_data));
638 
639  // Inform the QTableWidget of our change
640  updateData (row,
641  col,
642  data.fast_elem_extract (row + col * data.rows ()),
643  columnformat (col),
644  columneditable (col));
645 
646  m_curData = octave_value (data);
647  emit gh_set_event (m_handle, "data", data, false);
648 
651  col,
652  old_data,
653  data.fast_elem_extract (row + col * data.rows ()),
654  edit_data,
655  error);
656  ok = true;
657  }
658  }
659  else
660  {
661  // Reset the QTableWidgetItem
662  updateData (row, col, octave_value (""), columnformat (col),
663  columneditable (col));
664 
666  octave_value ("Table data is not editable at this location.");
668  col,
669  octave_value (),
670  octave_value (),
671  edit_data,
672  error);
673  }
674 
675  if (! ok)
676  {
677  comboBox->setCurrentIndex (-1);
678  comboBox->setEditable (true);
679  comboBox->setEditText (comboBox->property ("original_value").toString ());
680  comboBox->lineEdit ()->setReadOnly (true);
681  }
682  m_blockUpdates = false;
683  }
684 
685  void
686  Table::checkBoxClicked (int row, int col, QCheckBox *checkBox)
687  {
688  if (m_blockUpdates)
689  return;
690  m_blockUpdates = true;
691 
693 
694  octave::autolock guard (gh_mgr.graphics_lock ());
695 
696  bool new_value = ! checkBox->isChecked ();
697 
699  if (data.islogical ())
700  {
701  // EASY WE JUST CONVERT
702  boolMatrix matrix = data.bool_matrix_value ();
703  if (row < matrix.rows () && col < matrix.columns ())
704  {
705  bool old_value = matrix(row, col);
706  matrix(row, col) = new_value;
707  checkBox->setChecked (new_value);
708  if (new_value != old_value)
709  {
710  m_curData = octave_value (matrix);
711  emit gh_set_event (m_handle, "data", octave_value (matrix),
712  false);
713  }
714 
715  sendCellEditCallback (row, col,
716  octave_value (old_value),
717  octave_value (new_value),
718  octave_value (new_value),
719  octave_value (""));
720 
721  }
722  else
723  {
725  col,
726  octave_value (),
727  octave_value (),
728  octave_value (new_value),
729  octave_value ("Table data is not editable at this location."));
730  }
731  }
732  else if (data.iscell ())
733  {
734  Cell cell = data.cell_value ();
735  if (row < cell.rows () && col < cell.columns ())
736  {
737  if (cell(row, col).islogical ())
738  {
739  bool old_value = cell(row, col).bool_value ();
740  cell(row, col) = octave_value (new_value);
741  checkBox->setChecked (new_value);
742  if (new_value != old_value)
743  {
744  m_curData = octave_value (cell);
745  emit gh_set_event (m_handle, "data", octave_value (cell),
746  false);
747  }
748 
750  col,
751  octave_value (old_value),
752  octave_value (new_value),
753  octave_value (new_value),
754  octave_value (""));
755  }
756  else
757  {
759  col,
760  cell(row, col),
761  octave_value (),
762  octave_value (new_value),
763  octave_value ("Cannot convert logical edit to other type."));
764  }
765  }
766  else
767  {
769  col,
770  cell(row, col),
771  octave_value (),
772  octave_value (new_value),
773  octave_value ("Table data is not editable at this location."));
774  }
775  }
776  else if (data.is_matrix_type ())
777  {
778  if (row < data.rows () && col < data.columns ())
779  {
781  col,
782  data.fast_elem_extract (row + col * data.rows ()),
783  octave_value (),
784  octave_value (new_value),
785  octave_value ("Cannot convert logical edit to other type."));
786  }
787  else
788  {
790  col,
791  data.fast_elem_extract (row + col * data.rows ()),
792  octave_value (),
793  octave_value (new_value),
794  octave_value ("Table data is not editable at this location."));
795  }
796  }
797  m_blockUpdates = false;
798  }
799 
800 
801  void
802  Table::itemChanged (QTableWidgetItem *item)
803  {
804  if (m_blockUpdates)
805  return;
806  m_blockUpdates = true;
807 
809 
810  octave::autolock guard (gh_mgr.graphics_lock ());
811 
813 
814  int row = item->row ();
815  int col = item->column ();
816  octave_value edit_data = octave_value (Utils::toStdString (item->text ()));
817  octave_value new_value;
818  octave_value old_value;
819  octave_value new_data;
820 
821  if (row < data.rows () && col < data.columns ())
822  {
823  if (data.iscell ())
824  {
825  old_value = data.cell_value () (row, col);
826  }
827  else if (data.is_matrix_type ())
828  {
829  old_value = data.fast_elem_extract (row + col * data.rows ());
830  }
831 
832  // Now we need to coerce the new_value in to the type of the old_value
833  if (old_value.is_string ())
834  new_value = edit_data;
835  else
836  {
837  new_value = attempt_type_conversion (edit_data, old_value);
838  std::pair<Qt::AlignmentFlag, QString> flag_and_text =
839  qStringValueFor (new_value, columnformat (col));
840  item->setTextAlignment (flag_and_text.first);
841  item->setText (flag_and_text.second);
842  }
843 
844  if (data.iscell ())
845  {
846  Cell cell = data.cell_value ();
847  cell(row, col) = new_value;
848  new_data = octave_value (cell);
849  }
850  else
851  {
852  data.fast_elem_insert (row + col * data.rows (), new_value);
853  new_data = data;
854  }
855  m_curData = octave_value (new_data);
856  emit gh_set_event (m_handle, "data", new_data, false);
857 
859  col,
860  octave_value (old_value),
861  octave_value (new_value),
862  octave_value (new_value),
863  octave_value (""));
864  }
865  else
866  {
867  item->setText ("");
868 
870  octave_value ("Table data is not editable at this location.");
872  col,
873  octave_value (),
874  octave_value (),
875  edit_data,
876  error);
877  }
878 
879  m_blockUpdates = false;
880  }
881 
882  void
884  {
885  update (uitable::properties::ID_POSITION);
886  }
887 
888  void
889  Table::update (int pId)
890  {
891  uitable::properties& tp = properties<uitable> ();
892 
893  switch (pId)
894  {
895  case uitable::properties::ID_BACKGROUNDCOLOR:
896  case uitable::properties::ID_FOREGROUNDCOLOR:
897  updatePalette ();
898  break;
899 
900  case uitable::properties::ID_COLUMNNAME:
901  updateColumnname ();
903  break;
904 
905  case uitable::properties::ID_COLUMNWIDTH:
907  break;
908 
909  case uitable::properties::ID_COLUMNEDITABLE:
910  case uitable::properties::ID_COLUMNFORMAT:
911  case uitable::properties::ID_DATA:
912  m_blockUpdates = true;
913  m_curData = octave_value (tp.get_data ());
914  updateData ();
915  updateRowname ();
916  updateColumnname ();
918  updateEnable ();
919  m_blockUpdates = false;
920  break;
921 
922  case uitable::properties::ID_ENABLE:
923  updateEnable ();
924  break;
925 
926  case uitable::properties::ID_KEYPRESSFCN:
927  m_keyPressHandlerDefined = ! tp.get_keypressfcn ().isempty ();
928  break;
929 
930  case uitable::properties::ID_KEYRELEASEFCN:
931  m_keyReleaseHandlerDefined = ! tp.get_keyreleasefcn ().isempty ();
932  break;
933 
934  case uitable::properties::ID_FONTNAME:
935  case uitable::properties::ID_FONTSIZE:
936  case uitable::properties::ID_FONTWEIGHT:
937  case uitable::properties::ID_FONTANGLE:
938  if (m_tableWidget)
939  {
941  for (int row = 0; row < m_tableWidget->rowCount (); row++)
942  {
943  m_tableWidget->setRowHeight (row, AUTO_HEIGHT);
944  }
945  }
946  break;
947 
948  case uitable::properties::ID_POSITION:
949  {
950  Matrix bb = tp.get_boundingbox (false);
951  m_tableWidget->setGeometry (octave::math::round (bb(0)),
952  octave::math::round (bb(1)),
953  octave::math::round (bb(2)),
954  octave::math::round (bb(3)));
955  updateExtent ();
956  }
957  break;
958 
959  case uitable::properties::ID_REARRANGEABLECOLUMNS:
961  break;
962 
963  case uitable::properties::ID_ROWNAME:
964  updateRowname ();
965  break;
966 
967  case uitable::properties::ID_ROWSTRIPING:
968  updatePalette ();
969  break;
970 
971  case uitable::properties::ID_TOOLTIPSTRING:
972  m_tableWidget->setToolTip (Utils::fromStdString (tp.get_tooltipstring ()));
973  break;
974 
975  case base_properties::ID_VISIBLE:
976  m_tableWidget->setVisible (tp.is_visible ());
977  break;
978 
979  default:
980  break;
981 
982  }
983  }
984 
985  void
987  {
988  uitable::properties& tp = properties<uitable> ();
989 
990  // Reset the Column Count
991  m_tableWidget->setColumnCount (tp.get_data ().columns ());
992 
993  octave_value columnname = tp.get_columnname ();
994  QStringList l;
995  bool visible = true;
996 
997  if (columnname.is_string () && columnname.string_value (false) == "numbered")
998  for (int i = 0; i < m_tableWidget->columnCount (); i++)
999  l << QString::number (i + 1);
1000  else if (columnname.is_string ())
1001  {
1002  if (m_tableWidget->columnCount () > 0)
1003  l << Utils::fromStdString (columnname.string_value ());
1004  for (int i = 1; i < m_tableWidget->columnCount (); i++)
1005  l << "";
1006  }
1007  else if (columnname.isempty ())
1008  {
1009  for (int i = 0; i < m_tableWidget->columnCount (); i++)
1010  l << "";
1011 
1012  visible = false;
1013  }
1014  else if (columnname.iscell ())
1015  {
1016  octave_idx_type n = columnname.numel ();
1017  Cell cell_value = columnname.cell_value ();
1018 
1019  for (octave_idx_type i = 0; i < n; i++)
1020  {
1021  octave_value v = cell_value (i);
1022  if (v.is_string ())
1023  l << Utils::fromStdString (v.string_value (true));
1024  else if (v.is_matrix_type ())
1025  {
1026  Matrix data = v.matrix_value ();
1027 
1028  /* Now Matlab does something very strange here:
1029  * If data is a row or column matrix,
1030  * then each datapoint is added.
1031  * Otherwise, nothing is set.
1032  */
1033  if (data.rows () > 1 && data.cols () > 1)
1034  l << "";
1035  else
1036  for (octave_idx_type j = 0; j < data.numel (); j++)
1037  l << QString::number (data(j));
1038  }
1039  else if (v.isnumeric ())
1040  l << QString::number (v.double_value ());
1041  else
1042  l << QString::number (v.double_value ());
1043  }
1044  }
1045  else if (columnname.is_matrix_type ())
1046  {
1047  octave_idx_type n = columnname.numel ();
1048  Matrix matrix_value = columnname.matrix_value ();
1049 
1050  for (octave_idx_type i = 0; i < n; i++)
1051  l << QString::number (matrix_value(i));
1052  }
1053  else
1054  {
1055  for (int i = 0; i < m_tableWidget->columnCount (); i++)
1056  l << "";
1057  visible = false;
1058  }
1059 
1060  l.replaceInStrings ("|", "\n");
1061 
1062  // Now add the columns as required
1063  if (m_tableWidget->columnCount () < l.length ())
1064  {
1065  int oldColumnCount = m_tableWidget->columnCount ();
1066  m_tableWidget->setColumnCount (l.length ());
1067  for (int col = oldColumnCount; col < l.length (); col++)
1068  {
1069  std::string format = columnformat (col);
1070  bool enabled = columneditable (col);
1071 
1072  for (int row = 0; row < m_tableWidget->rowCount (); row++)
1073  updateData (row, col, octave_value (""), format, enabled);
1074  }
1075  }
1076 
1077  m_tableWidget->setHorizontalHeaderLabels (l);
1078  m_tableWidget->horizontalHeader ()->setVisible (visible);
1079  }
1080 
1081  void
1083  {
1084  uitable::properties& tp = properties<uitable> ();
1085 
1086  octave_value columnwidth = tp.get_columnwidth ();
1087  if (columnwidth.isempty ()
1088  || (columnwidth.is_string ()
1089  && columnwidth.string_value (false) == "auto"))
1090  for (int i = 0; i < m_tableWidget->columnCount (); i++)
1091  m_tableWidget->setColumnWidth (i, AUTO_WIDTH);
1092  else if (columnwidth.is_string ()
1093  && columnwidth.string_value (false) == "preferred")
1094  for (int i = 0; i < m_tableWidget->columnCount (); i++)
1095  {
1096  int column_size =
1097  (qobject_cast<QAbstractItemView *> (m_tableWidget))->sizeHintForColumn (i);
1098  int header_size = m_tableWidget->horizontalHeader ()->sectionSizeHint (i);
1099 
1100  if (column_size > header_size)
1101  header_size = column_size;
1102  m_tableWidget->setColumnWidth (i, header_size);
1103  }
1104  else if (columnwidth.iscell ())
1105  {
1106  Cell cell_value = columnwidth.cell_value ();
1107  int i = 0;
1108  for (; i < m_tableWidget->columnCount () && i < cell_value.numel (); i++)
1109  {
1110  octave_value v = cell_value (i);
1111  if (v.is_string () && v.string_value (false) == "auto")
1112  m_tableWidget->setColumnWidth (i, AUTO_WIDTH);
1113  else if (v.is_string () && v.string_value (false) == "preferred")
1114  {
1115  int column_size =
1116  (qobject_cast<QAbstractItemView *> (m_tableWidget))->sizeHintForColumn (i);
1117  int header_size = m_tableWidget->horizontalHeader ()->sectionSizeHint (i);
1118 
1119  if (column_size > header_size)
1120  header_size = column_size;
1121  m_tableWidget->setColumnWidth (i, header_size);
1122  }
1123  else
1124  {
1125  int w = int (v.double_value ());
1126  m_tableWidget->setColumnWidth (i, w);
1127  }
1128  }
1129  for (; i < m_tableWidget->columnCount (); i++)
1130  {
1131  int column_size =
1132  (qobject_cast<QAbstractItemView *> (m_tableWidget))->sizeHintForColumn (i);
1133  int header_size = m_tableWidget->horizontalHeader ()->sectionSizeHint (i);
1134 
1135  if (column_size > header_size)
1136  header_size = column_size;
1137  m_tableWidget->setColumnWidth (i, header_size);
1138  }
1139  }
1140  else if (columnwidth.is_matrix_type ())
1141  {
1142  Matrix matrix_value = columnwidth.matrix_value ();
1143  int i = 0;
1144  for (; i < m_tableWidget->columnCount () && i < matrix_value.numel (); i++)
1145  {
1146  octave_value v = matrix_value(i);
1147  int w = int (v.double_value ());
1148  m_tableWidget->setColumnWidth (i, w);
1149  }
1150  for (; i < m_tableWidget->columnCount (); i++)
1151  m_tableWidget->setColumnWidth (i, AUTO_WIDTH);
1152  }
1153  }
1154 
1155  bool inline
1157  {
1158  uitable::properties& tp = properties<uitable> ();
1159  boolNDArray columneditable = tp.get_columneditable ().bool_array_value ();
1160  bool editable = false;
1161 
1162  if (! columneditable.isempty () && col < columneditable.numel ())
1163  editable = columneditable.xelem (col);
1164  else if (! columneditable.isempty () && columneditable.numel () == 1)
1165  editable = columneditable.xelem (0);
1166 
1167  return editable;
1168  }
1169 
1170  std::string inline
1172  {
1173  uitable::properties& tp = properties<uitable> ();
1174  std::string format = "";
1175  octave_value ov_columnformat = tp.get_columnformat ();
1176 
1177  if (ov_columnformat.iscell ())
1178  {
1179  Cell columnformat = ov_columnformat.cell_value ();
1180  if (! columnformat.isempty () && col < columnformat.numel ())
1181  {
1182  octave_value format_value = columnformat.xelem (col);
1183 
1184  if (! format_value.isempty () && format_value.is_string ())
1185  format = format_value.string_value ();
1186  else if (! format_value.isempty () && format_value.iscell ())
1187  format = "popup";
1188  }
1189  }
1190  else if (ov_columnformat.is_string ())
1191  {
1192  format = ov_columnformat.string_value ();
1193  }
1194  return format;
1195  }
1196 
1197  void inline
1199  {
1200  octave_value data = properties<uitable> ().get_data ();
1201 
1202  std::string format = columnformat (col);
1203  bool is_editable = columneditable (col);
1204 
1205  for (octave_idx_type row = 0; row < data.rows (); row++)
1206  updateData (row,
1207  col,
1208  data.iscell ()
1209  ? data.cell_value () (row, col)
1210  : data.fast_elem_extract (row + col * data.rows ()),
1211  format,
1212  is_editable);
1213  }
1214 
1215  void inline
1216  Table::updateData (int row, int col)
1217  {
1218  octave_value data = properties<uitable> ().get_data ();
1219  updateData (row,
1220  col,
1221  data.iscell ()
1222  ? data.cell_value () (row, col)
1223  : data.fast_elem_extract (row + col * data.rows ()),
1224  columnformat (col),
1225  columneditable (col));
1226  }
1227 
1228  void inline
1229  Table::updateData (int row, int col, octave_value value,
1230  std::string format = "", bool enabled = false)
1231  {
1232  if (format == "logical" || (format == "" && value.islogical ()))
1233  {
1234  if (m_tableWidget->item (row, col))
1235  delete m_tableWidget->item (row, col);
1236 
1237  m_tableWidget->setCellWidget (row, col, checkBoxForLogical (value, enabled));
1238  m_tableWidget->cellWidget (row, col)->setProperty ("row", QVariant (row));
1239  m_tableWidget->cellWidget (row, col)->setProperty ("col", QVariant (col));
1240  }
1241  else if (format == "popup" && enabled)
1242  {
1243  if (m_tableWidget->item (row, col))
1244  delete m_tableWidget->item (row, col);
1245 
1246  QString string_value = qStringValueFor (value, format).second;
1247  uitable::properties& tp = properties<uitable> ();
1248  octave_value format_value = tp.get_columnformat ().cell_value ().xelem (col);
1249 
1250  QComboBox *comboBox = new QComboBox ();
1251  comboBox->setProperty ("row", QVariant (row));
1252  comboBox->setProperty ("col", QVariant (col));
1253 
1254  int index = -1;
1255  for (int k = 0; k < format_value.numel (); k++)
1256  {
1257  QString popup_item
1258  = Utils::fromStdString (format_value.fast_elem_extract (k).string_value ());
1259 
1260  comboBox->addItem (popup_item);
1261 
1262  if (popup_item == string_value)
1263  index = k;
1264  }
1265  comboBox->setCurrentIndex (index);
1266 
1267  if (index < 0)
1268  {
1269  comboBox->setEditable (true);
1270  comboBox->setEditText (string_value);
1271  comboBox->lineEdit ()->setReadOnly (true);
1272  }
1273 
1274  comboBox->setProperty ("original_value", QVariant (string_value));
1275 
1276  comboBox->installEventFilter (this);
1277  m_tableWidget->setCellWidget (row, col, comboBox);
1278  connect (comboBox, SIGNAL(currentIndexChanged (const QString&)),
1279  this, SLOT(comboBoxCurrentIndexChanged (const QString&)));
1280  }
1281  else
1282  {
1283  if (m_tableWidget->cellWidget (row, col))
1284  delete m_tableWidget->cellWidget (row, col);
1285  m_tableWidget->setItem (row, col, itemFor (value, format, enabled));
1286  }
1287  }
1288 
1289  void
1291  {
1292  uitable::properties& tp = properties<uitable> ();
1293 
1294  octave_value data = tp.get_data ();
1295 
1296  if (data.iscell () || data.is_matrix_type ())
1297  {
1298  m_tableWidget->setRowCount (data.rows ());
1299  m_tableWidget->setColumnCount (data.columns ());
1300 
1301  for (octave_idx_type col = 0; col < data.columns (); col++)
1302  updateDataColumn (col);
1303  }
1304 
1305  for (octave_idx_type row = 0; row < m_tableWidget->rowCount (); row++)
1306  m_tableWidget->setRowHeight (row, AUTO_HEIGHT);
1307  }
1308 
1309  void
1311  {
1312  uitable::properties& tp = properties<uitable> ();
1313  bool enabled = tp.is_enable ();
1314  m_tableWidget->setEnabled (enabled);
1315 
1316  bool rearrangeableColumns = tp.is_rearrangeablecolumns ();
1317 
1318  // Set selection mode
1319  m_tableWidget->setSelectionMode (enabled
1320  ? QAbstractItemView::ExtendedSelection
1321  : QAbstractItemView::NoSelection);
1322 
1323  // Set rearrangeablecolumns
1324  #if defined (HAVE_QT4)
1325  m_tableWidget->horizontalHeader ()->setMovable (enabled && rearrangeableColumns);
1326  #elif defined (HAVE_QT5)
1327  m_tableWidget->horizontalHeader ()->setSectionsMovable (enabled && rearrangeableColumns);
1328  #endif
1329  m_tableWidget->horizontalHeader ()->setDragEnabled (enabled && rearrangeableColumns);
1330  m_tableWidget->horizontalHeader ()->setDragDropMode (QAbstractItemView::InternalMove);
1331 
1332  // Turn off column editable
1333  for (int col = 0; col < m_tableWidget->columnCount (); col++)
1334  {
1335  bool editable = columneditable (col);
1336 
1337  for (int row = 0; row < m_tableWidget->rowCount (); row++)
1338  if (QTableWidgetItem *item = m_tableWidget->item (row, col))
1339  {
1340  Qt::ItemFlags flags = item->flags ();
1341  if (enabled && editable)
1342  item->setFlags (flags | Qt::ItemIsEditable);
1343  else
1344  item->setFlags (flags & ~Qt::ItemIsEditable);
1345  }
1346  else if (QWidget *widget = m_tableWidget->cellWidget (row, col))
1347  {
1348  QCheckBox *checkBox = nullptr;
1349  if (widget && ! widget->children ().isEmpty ())
1350  {
1351  QHBoxLayout *layout
1352  = qobject_cast<QHBoxLayout *> (widget->children ().first ());
1353 
1354  if (layout && layout->count () > 0)
1355  checkBox = qobject_cast<QCheckBox *> (layout->itemAt (0)-> widget ());
1356  }
1357 
1358  if (checkBox)
1359  widget->setProperty ("Enabled", QVariant (enabled & editable));
1360  else
1361  {
1362  widget->setAttribute (Qt::WA_TransparentForMouseEvents,
1363  !(editable & enabled));
1364 
1365  widget->setFocusPolicy (Qt::NoFocus);
1366  }
1367  }
1368  }
1369  }
1370 
1371  void
1373  {
1374  QSize s = realQSizeForTable (m_tableWidget);
1375  Matrix extent = Matrix (1, 4);
1376  extent(0, 0) = 0;
1377  extent(0, 1) = 0;
1378  extent(0, 2) = s.width ();
1379  extent(0, 3) = s.height () ;
1380  graphics_object go = object ();
1381  emit gh_set_event (go.get_handle (), "extent", extent, false);
1382  }
1383 
1384  void
1386  {
1387  uitable::properties& tp = properties<uitable> ();
1388 
1389  QPalette p = m_tableWidget->palette ();
1390  p.setColor (QPalette::Text,
1391  Utils::fromRgb (tp.get_foregroundcolor_rgb ()));
1392  p.setColor (QPalette::Base,
1393  Utils::fromRgb (tp.get_backgroundcolor_rgb ()));
1394  p.setColor (QPalette::AlternateBase,
1395  Utils::fromRgb (tp.get_alternatebackgroundcolor_rgb ()));
1396  m_tableWidget->setPalette (p);
1397  m_tableWidget->setAlternatingRowColors (tp.is_rowstriping ());
1398  // FIXME: Handle multiple alternating background colors
1399  }
1400 
1401  void
1403  {
1404  uitable::properties& tp = properties<uitable> ();
1405 
1406  // Reset the row count
1407  m_tableWidget->setRowCount (tp.get_data ().rows ());
1408 
1409  octave_value rowname = tp.get_rowname ();
1410  QStringList l;
1411  bool visible = true;
1412 
1413  if (rowname.is_string () && rowname.string_value (false) == "numbered")
1414  for (int i = 0; i < m_tableWidget->rowCount (); i++)
1415  l << QString::number (i + 1);
1416  else if (rowname.is_string ())
1417  {
1418  if (m_tableWidget->rowCount () > 0)
1419  l << Utils::fromStdString (rowname.string_value ());
1420  for (int i = 1; i < m_tableWidget->rowCount (); i++)
1421  l << "";
1422  }
1423  else if (rowname.isempty ())
1424  {
1425  for (int i = 0; i < m_tableWidget->rowCount (); i++)
1426  l << "";
1427  visible = false;
1428  }
1429  else if (rowname.iscell ())
1430  {
1431  octave_idx_type n = rowname.numel ();
1432  Cell cell_value = rowname.cell_value ();
1433 
1434  for (octave_idx_type i = 0; i < n; i++)
1435  {
1436  octave_value v = cell_value (i);
1437  if (v.is_string ())
1438  l << Utils::fromStdString (v.string_value (true));
1439  else if (v.is_matrix_type ())
1440  {
1441  Matrix data = v.matrix_value ();
1442 
1443  /* Now Matlab does something very strange here:
1444  * If data is a row or column matrix,
1445  * then each datapoint is added.
1446  * Otherwise, nothing is set.
1447  */
1448  if (data.rows () > 1 && data.cols () > 1)
1449  l << "";
1450  else
1451  for (octave_idx_type j = 0; j < data.numel (); j++)
1452  l << QString::number (data(j));
1453  }
1454  else if (v.isnumeric ())
1455  l << QString::number (v.double_value (true));
1456  else
1457  l << QString::number (v.double_value (true));
1458  }
1459  }
1460  else if (rowname.is_matrix_type ())
1461  {
1462  octave_idx_type n = rowname.numel ();
1463  Matrix matrix_value = rowname.matrix_value ();
1464 
1465  for (octave_idx_type i = 0; i < n; i++)
1466  l << QString::number (matrix_value(i));
1467  }
1468  else
1469  {
1470  for (int i = 0; i < m_tableWidget->columnCount (); i++)
1471  l << "";
1472  visible = false;
1473  }
1474 
1475  // Add dummy rows as required
1476  if (m_tableWidget->rowCount () < l.length ())
1477  {
1478  int oldRowCount = m_tableWidget->rowCount ();
1479  m_tableWidget->setRowCount (l.length ());
1480 
1481  for (int col = 0; col < m_tableWidget->columnCount (); col++)
1482  {
1483  std::string format = columnformat (col);
1484  bool enabled = columneditable (col);
1485 
1486  for (int row = oldRowCount; row < l.length (); row++)
1487  {
1488  m_tableWidget->setRowHeight (row, AUTO_HEIGHT);
1489 
1490  updateData (row, col, octave_value (""), format, enabled);
1491  }
1492  }
1493  }
1494 
1495  m_tableWidget->setVerticalHeaderLabels (l);
1496  m_tableWidget->verticalHeader ()->setVisible (visible);
1497  }
1498 
1499  void
1501  {
1502  uitable::properties& tp = properties<uitable> ();
1503 
1504  bool rearrangeableColumns = tp.is_rearrangeablecolumns ();
1505  bool enabled = tp.is_enable ();
1506 
1507  #if defined (HAVE_QT4)
1508  m_tableWidget->horizontalHeader ()->setMovable (enabled && rearrangeableColumns);
1509  #elif defined (HAVE_QT5)
1510  m_tableWidget->horizontalHeader ()->setSectionsMovable (enabled && rearrangeableColumns);
1511  #endif
1512  m_tableWidget->horizontalHeader ()->setDragEnabled (enabled && rearrangeableColumns);
1513  m_tableWidget->horizontalHeader ()->setDragDropMode (QAbstractItemView::InternalMove);
1514  }
1515 
1516  bool
1517  Table::eventFilter (QObject *watched, QEvent *xevent)
1518  {
1520 
1521  //uitable::properties& tp = properties<uitable> ();
1522  if (qobject_cast<QTableWidget *> (watched))
1523  {
1524  switch (xevent->type ())
1525  {
1526  case QEvent::Resize:
1527  {
1528  octave::autolock guard (gh_mgr.graphics_lock ());
1529 
1530  graphics_object go = object ();
1531  if (go.valid_object ())
1532  {
1533  const uitable::properties& tp =
1534  Utils::properties<uitable> (go);
1535  if (tp.fontunits_is ("normalized"))
1537  }
1538  }
1539  break;
1540 
1541  case QEvent::MouseButtonPress:
1542  {
1543  octave::autolock guard (gh_mgr.graphics_lock ());
1544 
1545  QMouseEvent *m = dynamic_cast<QMouseEvent *> (xevent);
1546  graphics_object go = object ();
1547  const uitable::properties& tp =
1548  Utils::properties<uitable> (go);
1549  graphics_object fig = go.get_ancestor ("figure");
1550 
1551  if (m->button () != Qt::LeftButton || ! tp.is_enable ())
1552  {
1553  emit gh_set_event (fig.get_handle (), "selectiontype",
1554  Utils::figureSelectionType (m), false);
1555  emit gh_set_event (fig.get_handle (), "currentpoint",
1557  false);
1558  emit gh_callback_event (fig.get_handle (),
1559  "windowbuttondownfcn");
1560  emit gh_callback_event (m_handle, "buttondownfcn");
1561 
1562  if (m->button () == Qt::RightButton)
1564  m->globalPos ());
1565  }
1566  else
1567  {
1568  emit gh_set_event (fig.get_handle (), "selectiontype",
1569  octave_value ("normal"), false);
1570  }
1571  }
1572  break;
1573 
1574  case QEvent::KeyPress:
1575  {
1576  QKeyEvent *k = dynamic_cast<QKeyEvent *> (xevent);
1578  {
1579  octave::autolock guard (gh_mgr.graphics_lock ());
1580 
1582  graphics_object fig = object ().get_ancestor ("figure");
1583 
1584  emit gh_set_event (fig.get_handle (), "currentcharacter",
1585  keyData.getfield ("Character"), false);
1586  emit gh_callback_event (m_handle, "keypressfcn", keyData);
1587  }
1588  int row = m_tableWidget->currentRow ();
1589  int col = m_tableWidget->currentColumn ();
1590  switch (k->key ())
1591  {
1592  case Qt::Key_Space:
1593  {
1594  QCheckBox *checkBox = nullptr;
1595 
1596  QWidget *widget
1597  = qobject_cast<QWidget *> (m_tableWidget->cellWidget (row, col));
1598 
1599  if (widget && ! widget->children ().isEmpty ())
1600  {
1601  QHBoxLayout *layout
1602  = qobject_cast<QHBoxLayout *> (widget->children ().first ());
1603 
1604  if (layout && layout->count () > 0)
1605  checkBox = qobject_cast<QCheckBox *> (layout->itemAt (0)-> widget ());
1606  }
1607 
1608  if (checkBox && checkBox->property ("Enabled").toBool ())
1609  checkBoxClicked (row, col, checkBox);
1610 
1611  QComboBox *comboBox
1612  = qobject_cast<QComboBox *> (m_tableWidget->cellWidget (row, col));
1613 
1614  if (comboBox)
1615  comboBox->showPopup ();
1616  }
1617  break;
1618 
1619  case Qt::Key_Return:
1620  case Qt::Key_Enter:
1621  {
1622  if (k->modifiers () == Qt::NoModifier)
1623  {
1624  if (row + 1 < m_tableWidget->rowCount ())
1625  m_tableWidget->setCurrentCell (row + 1, col);
1626  else
1627  {
1628  if (col + 1 < m_tableWidget->columnCount ())
1629  m_tableWidget->setCurrentCell (0, col + 1);
1630  else
1631  m_tableWidget->setCurrentCell (0, 0);
1632  }
1633  }
1634  else if (k->modifiers () == Qt::ShiftModifier)
1635  {
1636  if (row - 1 >= 0)
1637  m_tableWidget->setCurrentCell (row - 1, col);
1638  else
1639  {
1640  if (col - 1 >= 0)
1641  m_tableWidget->setCurrentCell
1642  (m_tableWidget->rowCount () - 1,
1643  col - 1);
1644  else
1645  m_tableWidget->setCurrentCell
1646  (m_tableWidget->rowCount () - 1,
1647  m_tableWidget->columnCount () - 1);
1648  }
1649  }
1650  }
1651  break;
1652 
1653  default:
1654  break;
1655  }
1656  }
1657  break;
1658 
1659  case QEvent::KeyRelease:
1660  {
1662  {
1663  octave::autolock guard (gh_mgr.graphics_lock ());
1664 
1665  QKeyEvent *k = dynamic_cast<QKeyEvent *> (xevent);
1666 
1668  graphics_object fig = object ().get_ancestor ("figure");
1669 
1670  emit gh_set_event (fig.get_handle (), "currentcharacter",
1671  keyData.getfield ("Character"), false);
1672  emit gh_callback_event (m_handle, "keyreleasefcn", keyData);
1673  }
1674  }
1675  break;
1676 
1677  default:
1678  break;
1679  }
1680  }
1681  else if (qobject_cast<QComboBox *> (watched))
1682  {
1683  switch (xevent->type ())
1684  {
1685  case QEvent::MouseButtonPress:
1686  {
1687  octave::autolock guard (gh_mgr.graphics_lock ());
1688 
1689  QMouseEvent *m = dynamic_cast<QMouseEvent *> (xevent);
1690  graphics_object go = object ();
1691  const uitable::properties& tp = Utils::properties<uitable> (go);
1692  graphics_object fig = go.get_ancestor ("figure");
1693 
1694  if (m->button () != Qt::LeftButton || ! tp.is_enable ())
1695  {
1696  emit gh_set_event (fig.get_handle (), "selectiontype",
1697  Utils::figureSelectionType (m), false);
1698  emit gh_set_event (fig.get_handle (), "currentpoint",
1700  false);
1701  emit gh_callback_event (fig.get_handle (),
1702  "windowbuttondownfcn");
1703  emit gh_callback_event (m_handle, "buttondownfcn");
1704 
1705  if (m->button () == Qt::RightButton)
1706  ContextMenu::executeAt (m_interpreter, tp, m->globalPos ());
1707  }
1708  else
1709  {
1710  emit gh_set_event (fig.get_handle (), "selectiontype",
1711  Utils::figureSelectionType (m), false);
1712 
1713  QComboBox *comboBox_0 = qobject_cast<QComboBox *> (watched);
1714  for (int row = 0; row < m_tableWidget->rowCount (); row++)
1715  {
1716  for (int col = 0; col < m_tableWidget->columnCount (); col++)
1717  {
1718  QComboBox *comboBox_1
1719  = qobject_cast<QComboBox *> (m_tableWidget->cellWidget (row, col));
1720 
1721  if (comboBox_0 == comboBox_1)
1722  m_tableWidget->setCurrentCell (row, col);
1723  }
1724  }
1725  }
1726  }
1727  break;
1728 
1729  default:
1730  break;
1731  }
1732  }
1733  return false;
1734  }
1735 
1736 #undef AUTO_HEIGHT
1737 }
#define FORMAT_INT_VALUE()
Definition: Table.cc:178
#define AUTO_HEIGHT
Definition: Table.cc:65
#define SCANF_AND_CONVERT(name, ctype, format)
#define FORMAT_VALUE(f, l)
Definition: Table.cc:156
#define FORMAT_UINT_VALUE()
Definition: Table.cc:166
#define FORMATNUMBER(type)
Definition: Table.cc:78
octave_idx_type columns(void) const
Definition: Array.h:424
octave_idx_type numel(void) const
Number of elements in the array.
Definition: Array.h:377
octave_idx_type cols(void) const
Definition: Array.h:423
octave_idx_type rows(void) const
Definition: Array.h:415
Definition: Cell.h:43
Definition: dMatrix.h:42
RowVector row(octave_idx_type i) const
Definition: dMatrix.cc:416
ColumnVector column(octave_idx_type i) const
Definition: dMatrix.cc:422
static void executeAt(octave::interpreter &interp, const base_properties &props, const QPoint &pt)
Definition: ContextMenu.cc:123
graphics_object object(void) const
Definition: Object.cc:83
base_properties & properties(void)
Definition: Object.h:60
virtual Container * innerContainer(void)=0
octave::interpreter & m_interpreter
Definition: Object.h:140
static Object * parentObject(octave::interpreter &interp, const graphics_object &go)
Definition: Object.cc:201
void gh_callback_event(const graphics_handle &h, const std::string &name)
graphics_handle m_handle
Definition: Object.h:157
void gh_set_event(const graphics_handle &h, const std::string &name, const octave_value &value)
virtual QObject * qObject(void)
Definition: Object.h:82
void updateEnable(void)
Definition: Table.cc:1310
void updateDataColumn(int col)
Definition: Table.cc:1198
void updateData(void)
Definition: Table.cc:1290
bool m_keyPressHandlerDefined
Definition: Table.h:86
void updateColumnname(void)
Definition: Table.cc:986
QWidget * checkBoxForLogical(octave_value cal, bool enabled)
Definition: Table.cc:409
void sendCellEditCallback(int row, int col, octave_value old_value, octave_value new_value, octave_value edit_data, octave_value error)
Definition: Table.cc:533
std::string columnformat(int column)
Definition: Table.cc:1171
octave_value m_curData
Definition: Table.h:84
void update(int pId)
Definition: Table.cc:889
bool eventFilter(QObject *watched, QEvent *event)
Definition: Table.cc:1517
~Table(void)
Definition: Table.cc:488
void redraw(void)
Definition: Table.cc:883
void comboBoxCurrentIndexChanged(const QString &value)
Definition: Table.cc:565
void itemChanged(QTableWidgetItem *item)
Definition: Table.cc:802
void updateExtent(void)
Definition: Table.cc:1372
void updateRowname(void)
Definition: Table.cc:1402
Table(octave::base_qobject &oct_qobj, octave::interpreter &interp, const graphics_object &go, QTableWidget *tableWidget)
Definition: Table.cc:448
bool columneditable(int column)
Definition: Table.cc:1156
bool m_keyReleaseHandlerDefined
Definition: Table.h:87
void itemSelectionChanged(void)
Definition: Table.cc:491
void updateRearrangeableColumns(void)
Definition: Table.cc:1500
void cellClicked(int row, int col)
Definition: Table.cc:514
void updateColumnwidth(void)
Definition: Table.cc:1082
bool m_blockUpdates
Definition: Table.h:85
static Table * create(octave::base_qobject &oct_qobj, octave::interpreter &interp, const graphics_object &go)
Definition: Table.cc:432
QTableWidget * m_tableWidget
Definition: Table.h:83
void checkBoxClicked(int row, int col, QCheckBox *checkBox)
Definition: Table.cc:686
void updatePalette(void)
Definition: Table.cc:1385
octave::mutex graphics_lock(void)
Definition: graphics.in.h:6393
graphics_object get_ancestor(const std::string &type) const
Definition: graphics.cc:3905
graphics_handle get_handle(void) const
Definition: graphics.in.h:2815
bool valid_object(void) const
Definition: graphics.in.h:2847
Base class for Octave interfaces that use Qt.
gh_manager & get_gh_manager(void)
Definition: interpreter.h:295
void setfield(const std::string &key, const octave_value &val)
Definition: oct-map.cc:190
octave_value getfield(const std::string &key) const
Definition: oct-map.cc:183
uint64_t uint64_value(bool req_int=false, bool frc_str_conv=false) const
Definition: ov.h:788
bool iscell(void) const
Definition: ov.h:560
bool bool_value(bool warn=false) const
Definition: ov.h:838
bool fast_elem_insert(octave_idx_type n, const octave_value &x)
Assign the n-th element, aka 'val(n) = x'.
Definition: ov.h:1459
int int_value(bool req_int=false, bool frc_str_conv=false) const
Definition: ov.h:765
bool is_uint16_type(void) const
Definition: ov.h:674
bool is_bool_scalar(void) const
Definition: ov.h:578
int64_t int64_value(bool req_int=false, bool frc_str_conv=false) const
Definition: ov.h:784
short int short_value(bool req_int=false, bool frc_str_conv=false) const
Definition: ov.h:758
Complex complex_value(bool frc_str_conv=false) const
Definition: ov.h:818
bool is_int8_type(void) const
Definition: ov.h:659
octave_idx_type rows(void) const
Definition: ov.h:504
bool isnumeric(void) const
Definition: ov.h:703
octave_idx_type numel(void) const
Definition: ov.h:518
unsigned short int ushort_value(bool req_int=false, bool frc_str_conv=false) const
Definition: ov.h:762
bool is_string(void) const
Definition: ov.h:593
bool isinteger(void) const
Definition: ov.h:683
bool is_double_type(void) const
Definition: ov.h:648
Cell cell_value(void) const
std::string class_name(void) const
Definition: ov.h:1256
bool is_uint32_type(void) const
Definition: ov.h:677
octave_idx_type columns(void) const
Definition: ov.h:506
bool is_int64_type(void) const
Definition: ov.h:668
float float_value(bool frc_str_conv=false) const
Definition: ov.h:797
unsigned long int ulong_value(bool req_int=false, bool frc_str_conv=false) const
Definition: ov.h:780
boolMatrix bool_matrix_value(bool warn=false) const
Definition: ov.h:841
octave_value fast_elem_extract(octave_idx_type n) const
Extract the n-th element, aka 'val(n)'.
Definition: ov.h:1447
std::string string_value(bool force=false) const
Definition: ov.h:927
bool is_matrix_type(void) const
Definition: ov.h:700
bool is_int32_type(void) const
Definition: ov.h:665
bool is_uint64_type(void) const
Definition: ov.h:680
bool is_int16_type(void) const
Definition: ov.h:662
long int long_value(bool req_int=false, bool frc_str_conv=false) const
Definition: ov.h:776
bool isempty(void) const
Definition: ov.h:557
bool is_single_type(void) const
Definition: ov.h:651
bool is_uint8_type(void) const
Definition: ov.h:671
Matrix matrix_value(bool frc_str_conv=false) const
Definition: ov.h:806
bool iscomplex(void) const
Definition: ov.h:694
double double_value(bool frc_str_conv=false) const
Definition: ov.h:794
unsigned int uint_value(bool req_int=false, bool frc_str_conv=false) const
Definition: ov.h:769
bool islogical(void) const
Definition: ov.h:688
Matrix get_boundingbox(bool internal=false, const Matrix &parent_pix_size=Matrix()) const
Definition: graphics.cc:11421
void warning(const char *fmt,...)
Definition: error.cc:1050
void error(const char *fmt,...)
Definition: error.cc:968
T octave_idx_type m
Definition: mx-inlines.cc:773
octave_idx_type n
Definition: mx-inlines.cc:753
std::complex< double > w(std::complex< double > z, double relerr=0)
QColor fromRgb(const Matrix &rgb)
std::string figureSelectionType(QMouseEvent *event, bool isDoubleClick)
template QFont computeFont< uitable >(const uitable::properties &props, int height)
octave_scalar_map makeKeyEventStruct(QKeyEvent *event)
Matrix figureCurrentPoint(const graphics_object &fig, QMouseEvent *event)
QString fromStdString(const std::string &s)
std::string toStdString(const QString &s)
static QTableWidgetItem * itemFor(octave_value val, std::string format="", bool enabled=false)
Definition: Table.cc:331
static QString formatNumber(double d, char format='f', int precision=4)
Definition: Table.cc:114
static const int AUTO_WIDTH
Definition: Table.cc:63
static QString formatComplex(Complex c, char format='f', int precision=4)
Definition: Table.cc:119
static QSize realQSizeForTable(QTableWidget *t)
Definition: Table.cc:67
static std::pair< Qt::AlignmentFlag, QString > qStringValueFor(octave_value val, std::string format="")
Definition: Table.cc:193
static octave_value attempt_type_conversion(const octave_value &ov, const octave_value &old_value)
Definition: Table.cc:349
double round(double x)
Definition: lo-mappers.h:136
OCTAVE_API Complex str2double(const std::string &str_arg)
Definition: oct-string.cc:460
size_t format(std::ostream &os, const char *fmt,...)
Definition: utils.cc:1329
std::complex< double > Complex
Definition: oct-cmplx.h:33
std::complex< float > FloatComplex
Definition: oct-cmplx.h:34
std::string rational_approx(T val, int len)
Definition: oct-string.cc:612
return octave_value(v1.char_array_value() . concat(v2.char_array_value(), ra_idx),((a1.is_sq_string()||a2.is_sq_string()) ? '\'' :'"'))
octave_value::octave_value(const Array< char > &chm, char type) return retval
Definition: ov.cc:811