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