Mercurial > octave
diff libgui/graphics/Table.cc @ 26112:36e0e5b428e7
Implement uitable (patch #9084)
* libgui/graphics/Table.[cc/h]: Add QT toolkit Table object.
* libgui/graphics/module.mk: Add new files.
* libgui/graphics/Backend.cc (toolkitObjectProperty, Backend::initialize,
Backend::update): Add uitable.
* libgui/graphics/Canvas.cc (Canvas::select_object): Allow uitable to be
selected like uicontrol or uipanel.
* libgui/graphics/ObjectFactory.cc (ObjectFactory::createObject): Add uitable.
* libgui/graphics/QtHandlesUtils.cc (computeFont<uibuttongroup>): Make
computeFont template for uitable.
* libinterp/corefcn/gl-render.cc (opengl_renderer::draw): Add uitable.
* libinterp/corefcn/graphics.cc (default_table_position,
default_table_backgroundcolor): Add new static default values.
* libinterp/corefcn/graphics.cc (lookup_object_name,
make_graphics_object_from_type, property_list::set, property_list::lookup):
Add uipanel.
* libinterp/corefcn/graphics.cc (uitable::properties::get_boundingbox,
uitable::properties::set_columnformat, uitable::properties::set_columnwidth,
uitable::properties::set_units, uitable::properties::update_units,
uitable::properties::set_fontunits, uitable::properties::update_fontunits,
uitable::properties::get___fontsize_points__,
uitable::properties::get_fontsize_pixels,
uitable::properties::get_backgroundcolor_rgb,
uitable::properties::get_alternatebackgroundcolor_rgb,
uitable::properties::get_extent_matrix, uitable::properties::get_extent,
uitable::properties::set_selectedobject, F__go_uitable__): Add new functions.
* libinterp/corefcn/graphics.in.h: Add uitable.
* scripts/gui/uitable.m: Add new file.
* scripts/gui/module.mk: Add new file to list.
* scripts/help/__unimplemented__.m: Remove uitable from missing_functions.
* doc/interpreter/genpropdoc.m, doc/interpreter/gui.txi,
doc/interpreter/module.mk, doc/interpreter/plot.txi: Add documentation for
uitable.
* NEWS: Add to list of new functions.
author | Andrew Thornton <art27@cantab.net> |
---|---|
date | Tue, 20 Dec 2016 03:25:17 +0000 |
parents | |
children | 4d1c6c60f6b3 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libgui/graphics/Table.cc Tue Dec 20 03:25:17 2016 +0000 @@ -0,0 +1,1692 @@ +/* + +Copyright (C) 2016 Andrew Thornton + +This file is part of Octave. + +Octave is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation; either version 3 of the License, or (at your +option) any later version. + +Octave is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with Octave; see the file COPYING. If not, see +<http://www.gnu.org/licenses/>. + +*/ + +#if defined (HAVE_CONFIG_H) +# include "config.h" +#endif + +#include <QEvent> +#include <QFrame> +#include <QLabel> +#include <QMouseEvent> +#include <QTimer> + +#include <QCheckBox> +#include <QComboBox> +#include <QHeaderView> +#include <QLineEdit> +#include <QModelIndexList> +#include <QString> +#include <QStringList> +#include <QTableWidget> +#include <QTableWidgetItem> +#include <QHBoxLayout> + +#include "Container.h" +#include "ContextMenu.h" +#include "Table.h" +#include "QtHandlesUtils.h" + +#include "oct-stream.h" +#include "oct-string.h" +#include "oct-strstrm.h" +#include "ov-struct.h" + +namespace QtHandles +{ + + static const int AUTO_WIDTH = 75; + +#define AUTO_HEIGHT \ + tp.get_fontsize () * 2 - 1 + + static QSize realQSizeForTable(QTableWidget *t) + { + int w = t->verticalHeader ()->width () + 4; + for (int i = 0; i < t->columnCount (); i++) + w += t->columnWidth (i); + int h = t->horizontalHeader ()->height () + 4; + for (int i = 0; i < t->rowCount (); i++) + h += t->rowHeight (i); + return QSize(w, h); + } + +#define FORMATNUMBER(type) \ + static QString formatNumber (type d, \ + char format = 'f', \ + int precision = 4) \ + { \ + if (format == 'n') \ + { \ + if (d == floor (d)) \ + return QString::number(d, 'g', precision); \ + else if (d <= pow (10, precision - 1) && d > pow (10, 1 - precision)) \ + return QString::number(d, 'f', precision); \ + else \ + return QString::number(d, 'e', precision); \ + } \ + else if (format == 'F') \ + { \ + int exponent = floor (log10 (d) / 3) * 3; \ + d *= pow (10, -exponent); \ + return QString::number(d, 'f', precision) + "e" + \ + (exponent < 0 ? "-" : "+") + \ + QString("%1").arg(abs (exponent), 3, 10, QChar('0')); \ + } \ + else if (format == 'E') \ + { \ + int exponent = floor (log10 (d) / 3) * 3; \ + d *= pow (10, -exponent); \ + return QString::number(d, \ + 'f', \ + precision - floor (log10 (d)) - 1) + \ + "e" + (exponent < 0 ? "-" : "+") + \ + QString("%1").arg(abs (exponent), 3, 10, QChar('0')); \ + } \ + else \ + return QString::number(d, format, precision); \ + } + + FORMATNUMBER(double) + FORMATNUMBER(float) + +#undef FORMATNUMBER + + static QString formatComplex (Complex c, char format = 'f', int precision = 4) + { + return formatNumber(c.real(), format, precision) + + " + " + formatNumber(c.imag (), format, precision) + "i"; + } + +#define FORMAT_VALUE_EXCEPT_RAT(f,l) \ + if (format == "numeric" || format == "short") \ + text = formatNumber(value, 'n', f); \ + else if (format == "short f" || format == "shortf") \ + text = formatNumber(value, 'f', f); \ + else if (format == "short e" || format == "shorte") \ + text = formatNumber(value, 'e', f); \ + else if (format == "short eng" || format == "shorteng") \ + text = formatNumber(value, 'F', f); \ + else if (format == "short g" || format == "shortg") \ + text = formatNumber(value, 'g', f + 1); \ + else if (format == "long") \ + text = formatNumber(value, 'n', l); \ + else if (format == "long f" || format == "longf") \ + text = formatNumber(value, 'f', l); \ + else if (format == "long e" || format == "longe") \ + text = formatNumber(value, 'e', l); \ + else if (format == "long eng" || format == "longeng") \ + text = formatNumber(value, 'E', l); \ + else if (format == "long g" || format == "longg") \ + text = formatNumber(value, 'g', l + 1); \ + else if (format == "bank") \ + text = QString::number(value, 'f', 2); \ + else if (format == "+") \ + if (value > 0) \ + text = Utils::fromStdString("+"); \ + else if (value < 0) \ + text = Utils::fromStdString("-"); \ + else \ + text = Utils::fromStdString(""); + +#define FORMAT_VALUE(f,l) \ + FORMAT_VALUE_EXCEPT_RAT(f,l) \ + else if (format == "rat") \ + text = Utils::fromStdString(rational_approx(double (value), 0)); \ + else \ + { \ + text = formatNumber (value, 'n', f); \ + flag = Qt::AlignLeft ; \ + } + +#define FORMAT_UINT_VALUE() \ + text = QString::number(value); \ + if (format == "char" || format == "popup") \ + flag = Qt::AlignLeft; \ + else if (format == "+") \ + { \ + if (value > 0) \ + text = Utils::fromStdString("+"); \ + else \ + text = Utils::fromStdString(""); \ + } + +#define FORMAT_INT_VALUE() \ + text = QString::number(value); \ + if (format == "char" || format == "popup") \ + flag = Qt::AlignLeft ; \ + else if (format == "+") \ + { \ + if (value > 0) \ + text = Utils::fromStdString("+"); \ + else if (value < 0) \ + text = Utils::fromStdString("-"); \ + else \ + text = Utils::fromStdString(""); \ + } + + static std::pair <Qt::AlignmentFlag, QString> qStringValueFor ( + octave_value val, std::string format = "") + { + Qt::AlignmentFlag flag = Qt::AlignRight; + QString text; + if (val.is_string ()) + { + text = QtHandles::Utils::fromStdString (val.string_value ()); + flag = Qt::AlignLeft ; + } + else if (val.iscomplex ()) + { + // Matlab has mulitple complex types we only have double. + Complex c = val.complex_value (); + if (format == "short") + text = formatComplex(c, 'f', 4); + else if (format == "short e" || format == "shorte") + text = formatComplex(c, 'e', 4); + else if (format == "short eng" || format == "shorteng") + text = formatComplex(c, 'E', 4); + else if (format == "short g" || format == "shortg") + text = formatComplex(c, 'g', 5); + else if (format == "long") + text = formatComplex(c, 'f', 15); + else if (format == "long e" || format == "longe") + text = formatComplex(c, 'e', 15); + else if (format == "long eng" || format == "longeng") + text = formatComplex(c, 'E', 15); + else if (format == "long g" || format == "longg") + text = formatComplex(c, 'g', 16); + else if (format == "bank") + text = QString::number(c.real (), 'f', 2); + else if (format == "+") + { + if (c.real () > 0) + text = Utils::fromStdString("+"); + else if (c.real () < 0) + text = Utils::fromStdString("-"); + else + text = Utils::fromStdString(""); + } + else if (format == "rat") + text = Utils::fromStdString(rational_approx(c.real (), 0)) + " + " + + Utils::fromStdString(rational_approx(c.imag (), 0)) + "i"; + else if (format == "numeric") + text = QString::number (c.real (), 'g', 5) + " + " + + QString::number (c.imag (), 'g', 5) + "i"; + else + { + text = QString::number (c.real (), 'g', 5) + " + " + + QString::number (c.imag (), 'g', 5) + "i"; + flag = Qt::AlignLeft ; + } + } + else if (val.is_double_type () ) + { + double value = val.double_value (); + FORMAT_VALUE(4, 15) + } + else if (val.is_single_type ()) + { + float value = val.float_value (); + FORMAT_VALUE(4, 7) + } + else if (val.is_int8_type ()) + { + short int value = val.short_value (); + FORMAT_INT_VALUE() + } + else if (val.is_uint8_type ()) + { + unsigned short int value = val.ushort_value (); + FORMAT_UINT_VALUE() + } + else if (val.is_int16_type ()) + { + int value = val.int_value (); + FORMAT_INT_VALUE() + } + else if (val.is_uint16_type ()) + { + unsigned int value = val.uint_value (); + FORMAT_UINT_VALUE() + } + else if (val.is_int32_type ()) + { + long int value = val.long_value (); + FORMAT_INT_VALUE() + } + else if (val.is_uint32_type ()) + { + unsigned long int value = val.ulong_value (); + FORMAT_UINT_VALUE() + } + else if (val.is_int64_type ()) + { + int64_t value = val.int64_value (); + FORMAT_INT_VALUE() + } + else if (val.is_uint64_type ()) + { + uint64_t value = val.uint64_value (); + FORMAT_UINT_VALUE() + } + else if (val.islogical ()) + { + bool b = val.bool_value (); + if (format == "char" || format == "popup" || format == "") + { + text = Utils::fromStdString(b ? "true" : "false"); + flag = Qt::AlignLeft ; + } + else if (format == "+") + { + text = Utils::fromStdString(b ? "+" : ""); + flag = Qt::AlignLeft ; + } + else + text = Utils::fromStdString(b ? "1" : "0"); + } + else + { + std::stringstream warn_string; + warn_string << "Unknown conversion for datatype " << val.class_name () + << " to " << format << " for value " << val.string_value (true); + warning (warn_string.str().c_str()); + + text = Utils::fromStdString(val.string_value (true)); + } + + return std::make_pair(flag, text); + } + +#undef FORMAT_VALUE +#undef FORMAT_VALUE_EXCEPT_RAT +#undef FORMAT_UINT_VALUE +#undef FORMAT_INT_VALUE + + static QTableWidgetItem * itemFor(octave_value val, std::string format = "", + bool enabled = false) + { + QTableWidgetItem* retval = new QTableWidgetItem (); + std::pair <Qt::AlignmentFlag, QString> flag_and_text = qStringValueFor(val, format); + retval->setTextAlignment (flag_and_text.first); + retval->setText (flag_and_text.second); + + if (enabled) + retval->setFlags (retval->flags () | Qt::ItemIsEditable); + else + retval->setFlags (retval->flags () & ~Qt::ItemIsEditable); + + return retval; + } + + static octave_value + attempt_type_conversion (const octave_value& ov, const octave_value& old_value) + { + octave_value retval; + + // Define a macro to help with the conversion of strings to integers + // FIXME: these will happily integer overflow in the (u)int64 case + // - this probably doesn't matter. +#define SCANF_AND_CONVERT(name,ctype,format) \ + else if (old_value.is_ ## name ## _type ()) \ + { \ + ctype val; \ + int n; \ + const char* c_str = ov.string_value ().c_str (); \ + int error = sscanf (c_str, format, &val, &n); \ + if (error != 1 || c_str[n]) \ + { \ + val = 0; \ + } \ + retval = octave_value ( octave_ ## name (val)); \ + } + + if (old_value.is_string ()) + retval = ov; + SCANF_AND_CONVERT(int8, int64_t, "%jd %n") + SCANF_AND_CONVERT(uint8, uint64_t, "%ju %n") + SCANF_AND_CONVERT(int16, int64_t, "%jd %n") + SCANF_AND_CONVERT(uint16, uint64_t, "%ju %n") + SCANF_AND_CONVERT(int32, int64_t, "%jd %n") + SCANF_AND_CONVERT(uint32, uint64_t, "%ju %n") + SCANF_AND_CONVERT(int64, int64_t, "%jd %n") + SCANF_AND_CONVERT(uint64, uint64_t, "%ju %n") + + #undef SCANF_AND_CONVERT + + else if (old_value.isnumeric () && !old_value.isinteger ()) + { + // Basically need to do str2double + Complex complex = octave_str2double (ov.string_value ()); + if (old_value.is_single_type ()) + retval = octave_value (FloatComplex (complex)); + else + retval = octave_value (complex); + } + else if (old_value.islogical ()) + { + // Right: Matlab basically needs this to be true or false, we should + // accept 1 too. + if (ov.string_value () == "true" || ov.string_value () == "1") + retval = octave_value (true); + else + retval = octave_value (false); + } + else + retval = octave_value (octave_str2double (ov.string_value ())); + return retval; + } + + QWidget * + Table::checkBoxForLogical (octave_value val, bool enabled = false) + { + QWidget *retval = new QWidget(m_tableWidget); + QCheckBox *checkBox = new QCheckBox(); + QHBoxLayout *layout = new QHBoxLayout(retval); + layout->addWidget (checkBox); + layout->setAlignment (Qt::AlignCenter); + layout->setContentsMargins (0, 0, 0, 0); + retval->setLayout (layout); + + if ((val.islogical () || val.is_bool_scalar ()) && val.bool_value ()) + checkBox->setCheckState (Qt::Checked); + else + checkBox->setCheckState (Qt::Unchecked); + + checkBox->setAttribute (Qt::WA_TransparentForMouseEvents, true); + checkBox->setFocusPolicy (Qt::NoFocus); + checkBox->setProperty ("Enabled", QVariant (enabled)); + + return retval; + } + + Table* + Table::create (const graphics_object& go) + { + Object* parent = Object::parentObject (go); + + if (parent) + { + Container* container = parent->innerContainer (); + + if (container) + return new Table (go, new QTableWidget (container)); + } + + return 0; + } + + Table::Table (const graphics_object& go, QTableWidget* tableWidget) + : Object (go, tableWidget), m_tableWidget(tableWidget), m_curData(), + m_blockUpdates(false) + { + uitable::properties& tp = properties<uitable> (); + + m_curData = octave_value(tp.get_data ()); + Matrix bb = tp.get_boundingbox (false); + m_tableWidget->setObjectName ("UITable"); + m_tableWidget->setAutoFillBackground (true); + m_tableWidget->setGeometry (octave::math::round (bb(0)), octave::math::round (bb(1)), + octave::math::round (bb(2)), octave::math::round (bb(3))); + m_tableWidget->setFont (Utils::computeFont<uitable> (tp)) ; + m_tableWidget->setSelectionBehavior (QAbstractItemView::SelectItems); + updatePalette (); + m_keyPressHandlerDefined = ! tp.get_keypressfcn ().isempty (); + m_keyReleaseHandlerDefined = ! tp.get_keyreleasefcn ().isempty (); + updateData (); + updateRowname (); + updateColumnname (); + updateColumnwidth(); + updateEnable(); // Also does rearrangeableColumns + m_tableWidget->setToolTip (Utils::fromStdString (tp.get_tooltipstring ())); + m_tableWidget->setVisible (tp.is_visible ()); + updateExtent (); + m_tableWidget->installEventFilter (this); + + + connect (m_tableWidget, SIGNAL (itemChanged (QTableWidgetItem*)), + SLOT (itemChanged (QTableWidgetItem*))); + connect (m_tableWidget, SIGNAL (cellClicked (int, int)), + SLOT (cellClicked (int, int))); + connect (m_tableWidget, SIGNAL (itemSelectionChanged (void)), + SLOT (itemSelectionChanged (void))); + } + + Table::~Table (void) + { + } + + void + Table::itemSelectionChanged () + { + if (!(properties<uitable> ().get_cellselectioncallback ().isempty ())) + { + QModelIndexList modelIndexList = m_tableWidget->selectionModel()->selectedIndexes (); + int length = modelIndexList.size(); + Matrix indices = Matrix (length, 2); + for (int i = 0; i < length; i++) + { + indices (i, 0) = modelIndexList.value (i).row () + 1; + indices (i, 1) = modelIndexList.value (i).column () + 1; + } + octave_scalar_map eventData; + eventData.setfield ("Indices", indices); + octave_value cellSelectionCallbackEventObject = + octave_value (new octave_struct (eventData)); + gh_manager::post_callback(m_handle, + "cellselectioncallback", + cellSelectionCallbackEventObject); + } + } + + void + Table::cellClicked (int row, int col) + { + QCheckBox* checkBox = NULL; + QWidget* widget = qobject_cast<QWidget*> ( + m_tableWidget->cellWidget (row, col)); + if (widget && !widget->children ().isEmpty ()) + { + QHBoxLayout* layout = qobject_cast<QHBoxLayout*> ( + widget->children ().first ()); + if (layout && layout->count () > 0) + { + checkBox = qobject_cast<QCheckBox*> ( + layout->itemAt (0)-> widget ()); + } + } + if (checkBox && checkBox->property ("Enabled").toBool()) + checkBoxClicked(row, col, checkBox); + } + + void + Table::sendCellEditCallback (int row, + int col, + octave_value old_value, + octave_value new_value, + octave_value edit_data, + octave_value error) + { + + if (!(properties<uitable> ().get_celleditcallback ().isempty ())) + { + Matrix indices = Matrix (1, 2); + indices (0, 0) = row + 1; + indices (0, 1) = col + 1; + + octave_scalar_map eventData; + eventData.setfield ("Indices", indices); + eventData.setfield ("PreviousData", old_value); + eventData.setfield ("NewData", new_value); + eventData.setfield ("EditData", edit_data); + eventData.setfield ("Error", error); + + octave_value cellEditCallbackEventObject = + octave_value (new octave_struct (eventData)); + + gh_manager::post_callback (m_handle, + "celleditcallback", + cellEditCallbackEventObject); + } + else if (error.string_value ().length () > 0) + warning (error.string_value ().c_str ()); + } + + void + Table::comboBoxCurrentIndexChanged (const QString& value) + { + if (m_blockUpdates) + return; + m_blockUpdates = true; + gh_manager::auto_lock lock; + octave_value data = octave_value(m_curData); + bool ok = false; + + QComboBox* comboBox = qobject_cast<QComboBox*> (sender()); + int row = comboBox->property ("row").toInt (); + int col = comboBox->property ("col").toInt (); + + octave_value edit_data = octave_value(Utils::toStdString(value)); + + if (row < data.rows () && col < data.columns ()) + { + if (data.iscell ()) + { + Cell cell = data.cell_value (); + octave_value old_data = cell (row, col); + if (cell (row, col).is_string ()) + { + cell (row, col) = edit_data; + if (edit_data.string_value () != old_data.string_value ()) + { + m_curData = octave_value(cell); + gh_manager::post_set (m_handle, + "data", + octave_value (cell), + false); + } + + octave_value error = octave_value(""); + sendCellEditCallback(row, + col, + old_data, + edit_data, + edit_data, + error); + ok = true; + } + else + { + cell (row, col) = attempt_type_conversion(edit_data, old_data); + + // Inform the QTableWidget of our change + updateData(row, col, cell (row, col), columnformat (col), columneditable (col)); + + m_curData = octave_value(cell); + gh_manager::post_set (m_handle, "data", octave_value (cell), false); + + octave_value error = octave_value(""); + sendCellEditCallback(row, + col, + old_data, + cell (row, col), + edit_data, + error); + ok = true; + } + } + else + { + octave_value old_data = data.is_matrix_type () + ? data.fast_elem_extract(row + col * data.rows()) + : octave_value (); + data.fast_elem_insert (row + col * data.rows(), + attempt_type_conversion(edit_data, old_data)); + + // Inform the QTableWidget of our change + updateData(row, + col, + data.fast_elem_extract (row + col * data.rows ()), + columnformat (col), + columneditable (col)); + + m_curData = octave_value(data); + gh_manager::post_set (m_handle, "data", data, false); + + octave_value error = octave_value(""); + sendCellEditCallback(row, + col, + old_data, + data.fast_elem_extract(row + col * data.rows()), + edit_data, + error); + ok = true; + } + } + else + { + // Reset the QTableWidgetItem + updateData(row, col, octave_value (""), columnformat (col), columneditable (col)); + + octave_value error = octave_value("Table data is not editable at this location."); + sendCellEditCallback(row, + col, + octave_value(), + octave_value(), + edit_data, + error); + } + + if (!ok) + { + comboBox->setCurrentIndex (-1); + comboBox->setEditable (true); + comboBox->setEditText (comboBox->property ("original_value").toString ()); + comboBox->lineEdit ()->setReadOnly (true); + } + m_blockUpdates = false; + } + + void + Table::checkBoxClicked (int row, int col, QCheckBox* checkBox) + { + if (m_blockUpdates) + return; + m_blockUpdates = true; + gh_manager::auto_lock lock; + + bool new_value = !checkBox->isChecked (); + + octave_value data = octave_value(m_curData); + if (data.islogical ()) + { + // EASY WE JUST CONVERT + boolMatrix matrix = data.bool_matrix_value (); + if (row < matrix.rows () && col < matrix.columns ()) + { + bool old_value = matrix (row, col); + matrix (row, col) = new_value; + checkBox->setChecked(new_value); + if (new_value != old_value) + { + m_curData = octave_value(matrix); + gh_manager::post_set (m_handle, "data", octave_value(matrix), false); + } + + sendCellEditCallback(row, + col, + octave_value(old_value), + octave_value(new_value), + octave_value(new_value), + octave_value("")); + + } + else + { + sendCellEditCallback(row, + col, + octave_value(), + octave_value(), + octave_value(new_value), + octave_value("Table data is not editable at this location.")); + } + } + else if (data.iscell ()) + { + Cell cell = data.cell_value (); + if (row < cell.rows () && col < cell.columns ()) + { + if (cell (row, col).islogical ()) + { + bool old_value = cell (row, col).bool_value (); + cell (row, col) = octave_value(new_value); + checkBox->setChecked(new_value); + if (new_value != old_value) + { + m_curData = octave_value(cell); + gh_manager::post_set (m_handle, "data", octave_value (cell), false); + } + + sendCellEditCallback(row, + col, + octave_value(old_value), + octave_value(new_value), + octave_value(new_value), + octave_value("")); + } + else + { + sendCellEditCallback(row, + col, + cell (row, col), + octave_value(), + octave_value(new_value), + octave_value("Cannot convert logical edit to other type.")); + } + } + else + { + sendCellEditCallback(row, + col, + cell (row, col), + octave_value(), + octave_value(new_value), + octave_value("Table data is not editable at this location.")); + } + } + else if (data.is_matrix_type ()) + { + if (row < data.rows () && col < data.columns ()) + { + sendCellEditCallback(row, + col, + data.fast_elem_extract (row + col * data.rows()), + octave_value(), + octave_value(new_value), + octave_value("Cannot convert logical edit to other type.")); + } + else + { + sendCellEditCallback(row, + col, + data.fast_elem_extract (row + col * data.rows()), + octave_value(), + octave_value(new_value), + octave_value("Table data is not editable at this location.")); + } + } + m_blockUpdates = false; + } + + + void + Table::itemChanged (QTableWidgetItem* item) + { + if (m_blockUpdates) + return; + m_blockUpdates = true; + gh_manager::auto_lock lock; + octave_value data = octave_value(m_curData); + + int row = item->row (); + int col = item->column (); + octave_value edit_data = octave_value (Utils::toStdString(item->text ())); + octave_value new_value; + octave_value old_value; + octave_value new_data; + + if (row < data.rows () && col < data.columns ()) + { + if (data.iscell ()) + { + old_value = data.cell_value () (row, col); + } + else if (data.is_matrix_type ()) + { + old_value = data.fast_elem_extract (row + col * data.rows ()); + } + + // Now we need to coerce the new_value in to the type of the old_value + if (old_value.is_string ()) + new_value = edit_data; + else + { + new_value = attempt_type_conversion(edit_data, old_value); + std::pair <Qt::AlignmentFlag, QString> flag_and_text = + qStringValueFor(new_value, columnformat (col)); + item->setTextAlignment (flag_and_text.first); + item->setText (flag_and_text.second); + } + + if (data.iscell ()) + { + Cell cell = data.cell_value(); + cell (row, col) = new_value; + new_data = octave_value (cell); + } + else + { + data.fast_elem_insert (row + col * data.rows (), new_value); + new_data = data; + } + m_curData = octave_value(new_data); + gh_manager::post_set (m_handle, "data", new_data, false); + + sendCellEditCallback(row, + col, + octave_value(old_value), + octave_value(new_value), + octave_value(new_value), + octave_value("")); + } + else + { + item->setText (""); + + octave_value error = octave_value("Table data is not editable at this location."); + sendCellEditCallback(row, + col, + octave_value(), + octave_value(), + edit_data, + error); + } + + m_blockUpdates = false; + } + + void + Table::update (int pId) + { + uitable::properties& tp = properties<uitable> (); + + switch (pId) + { + case uitable::properties::ID_BACKGROUNDCOLOR: + case uitable::properties::ID_FOREGROUNDCOLOR: + updatePalette (); + break; + + case uitable::properties::ID_COLUMNNAME: + updateColumnname (); + updateColumnwidth (); + break; + + case uitable::properties::ID_COLUMNWIDTH: + updateColumnwidth (); + break; + + case uitable::properties::ID_COLUMNEDITABLE: + case uitable::properties::ID_COLUMNFORMAT: + case uitable::properties::ID_DATA: + m_blockUpdates = true; + m_curData = octave_value(tp.get_data ()); + updateData (); + updateRowname (); + updateColumnname (); + updateColumnwidth (); + updateEnable(); + m_blockUpdates = false; + break; + + case uitable::properties::ID_ENABLE: + updateEnable(); + break; + + case uitable::properties::ID_KEYPRESSFCN: + m_keyPressHandlerDefined = ! tp.get_keypressfcn ().isempty (); + break; + + case uitable::properties::ID_KEYRELEASEFCN: + m_keyReleaseHandlerDefined = ! tp.get_keyreleasefcn ().isempty (); + break; + + case uitable::properties::ID_FONTNAME: + case uitable::properties::ID_FONTSIZE: + case uitable::properties::ID_FONTWEIGHT: + case uitable::properties::ID_FONTANGLE: + if (m_tableWidget) + { + m_tableWidget->setFont (Utils::computeFont<uitable> (tp)); + for (int row = 0; row < m_tableWidget->rowCount (); row++) + { + m_tableWidget->setRowHeight (row, AUTO_HEIGHT); + } + } + break; + + case uitable::properties::ID_POSITION: + { + Matrix bb = tp.get_boundingbox (false); + m_tableWidget->setGeometry (octave::math::round (bb(0)), octave::math::round (bb(1)), + octave::math::round (bb(2)), octave::math::round (bb(3))); + updateExtent(); + } + break; + + case uitable::properties::ID_REARRANGEABLECOLUMNS: + updateRearrangeableColumns (); + break; + + case uitable::properties::ID_ROWNAME: + updateRowname (); + break; + + case uitable::properties::ID_ROWSTRIPING: + updatePalette(); + break; + + case uitable::properties::ID_TOOLTIPSTRING: + m_tableWidget->setToolTip (Utils::fromStdString (tp.get_tooltipstring ())); + break; + + case base_properties::ID_VISIBLE: + m_tableWidget->setVisible (tp.is_visible ()); + break; + + default: + break; + + } + } + + void + Table::updateColumnname (void) + { + uitable::properties& tp = properties<uitable> (); + + // Reset the Column Count + m_tableWidget->setColumnCount (tp.get_data ().columns ()); + + octave_value columnname = tp.get_columnname (); + QStringList l; + bool visible = true; + + if (columnname.is_string () && columnname.string_value (false) == "numbered") + for (int i = 0; i < m_tableWidget->columnCount (); i++) + l << QString::number(i + 1); + else if (columnname.is_string ()) + { + if (m_tableWidget->columnCount () > 0) + l << Utils::fromStdString(columnname.string_value ()); + for (int i = 1; i < m_tableWidget->columnCount (); i++) + l << ""; + } + else if (columnname.isempty ()) + { + for (int i = 0; i < m_tableWidget->columnCount (); i++) + l << ""; + + visible = false; + } + else if (columnname.iscell ()) + { + octave_idx_type n = columnname.numel(); + Cell cell_value = columnname.cell_value(); + + for (octave_idx_type i = 0; i < n; i++) + { + octave_value v = cell_value (i); + if (v.is_string ()) + l << Utils::fromStdString(v.string_value (true)); + else if (v.is_matrix_type ()) + { + Matrix data = v.matrix_value (); + + /* Now Matlab does something very strange here: + * If data is a row or column matrix, then each datapoint is added + * Otherwise nothing is set. + */ + if (data.rows () > 1 && data.cols () > 1) + l << ""; + else + for (octave_idx_type j = 0; j < data.numel (); j++) + l << QString::number(data (j)); + } + else if (v.isnumeric ()) + l << QString::number(v.double_value ()); + else + l << QString::number(v.double_value ()); + } + } + else if (columnname.is_matrix_type ()) + { + octave_idx_type n = columnname.numel (); + Matrix matrix_value = columnname.matrix_value (); + + for (octave_idx_type i = 0; i < n; i++) + l << QString::number(matrix_value (i)); + } + else + { + for (int i = 0; i < m_tableWidget->columnCount (); i++) + l << ""; + visible = false; + } + + l.replaceInStrings("|", "\n"); + + // Now add the columns as required + if (m_tableWidget->columnCount () < l.length()) + { + int oldColumnCount = m_tableWidget->columnCount (); + m_tableWidget->setColumnCount (l.length()); + for (int col = oldColumnCount; col < l.length (); col++) + { + std::string format = columnformat (col); + bool enabled = columneditable (col); + + for (int row = 0; row < m_tableWidget->rowCount (); row++) + updateData (row, col, octave_value (""), format, enabled); + } + } + + m_tableWidget->setHorizontalHeaderLabels (l); + m_tableWidget->horizontalHeader ()->setVisible (visible); + } + + void + Table::updateColumnwidth (void) + { + uitable::properties& tp = properties<uitable> (); + + octave_value columnwidth = tp.get_columnwidth (); + if (columnwidth.isempty () || + (columnwidth.is_string () && columnwidth.string_value (false) == "auto")) + for (int i = 0; i < m_tableWidget->columnCount (); i++) + m_tableWidget->setColumnWidth (i, AUTO_WIDTH); + else if (columnwidth.is_string () && columnwidth.string_value (false) == "preferred") + for (int i = 0; i < m_tableWidget->columnCount (); i++) + { + int column_size = + (qobject_cast<QAbstractItemView*> (m_tableWidget))->sizeHintForColumn (i); + int header_size = m_tableWidget->horizontalHeader ()->sectionSizeHint (i); + + if (column_size > header_size) + header_size = column_size; + m_tableWidget->setColumnWidth (i, header_size); + } + else if (columnwidth.iscell ()) + { + Cell cell_value = columnwidth.cell_value (); + int i = 0; + for (; i < m_tableWidget->columnCount () && i < cell_value.numel (); i++) + { + octave_value v = cell_value (i); + if (v.is_string () && v.string_value (false) == "auto") + m_tableWidget->setColumnWidth (i, AUTO_WIDTH); + else if (v.is_string () && v.string_value (false) == "preferred") + { + int column_size = + (qobject_cast<QAbstractItemView*> (m_tableWidget))->sizeHintForColumn (i); + int header_size = m_tableWidget->horizontalHeader ()->sectionSizeHint (i); + + if (column_size > header_size) + header_size = column_size; + m_tableWidget->setColumnWidth (i, header_size); + } + else + { + int w = int (v.double_value()); + m_tableWidget->setColumnWidth (i, w); + } + } + for (;i < m_tableWidget->columnCount (); i++) + { + int column_size = + (qobject_cast<QAbstractItemView*> (m_tableWidget))->sizeHintForColumn (i); + int header_size = m_tableWidget->horizontalHeader ()->sectionSizeHint (i); + + if (column_size > header_size) + header_size = column_size; + m_tableWidget->setColumnWidth (i, header_size); + } + } + else if (columnwidth.is_matrix_type ()) + { + Matrix matrix_value = columnwidth.matrix_value (); + int i = 0; + for (; i < m_tableWidget->columnCount () && i < matrix_value.numel (); i++) + { + octave_value v = matrix_value (i); + int w = int (v.double_value()); + m_tableWidget->setColumnWidth (i, w); + } + for (;i < m_tableWidget->columnCount (); i++) + m_tableWidget->setColumnWidth (i, AUTO_WIDTH); + } + } + + bool inline + Table::columneditable (int col) + { + uitable::properties& tp = properties<uitable> (); + boolNDArray columneditable = tp.get_columneditable ().bool_array_value (); + bool editable = false; + + if (!columneditable.isempty () && col < columneditable.numel ()) + editable = columneditable.xelem (col); + else if (!columneditable.isempty () && columneditable.numel () == 1) + editable = columneditable.xelem (0); + + return editable; + } + + std::string inline + Table::columnformat (int col) + { + uitable::properties& tp = properties<uitable> (); + std::string format = ""; + octave_value ov_columnformat = tp.get_columnformat (); + + if (ov_columnformat.iscell ()) + { + Cell columnformat = ov_columnformat.cell_value (); + if (!columnformat.isempty () && col < columnformat.numel ()) + { + octave_value format_value = columnformat.xelem (col); + + if (!format_value.isempty () && format_value.is_string()) + format = format_value.string_value (); + else if (!format_value.isempty () && format_value.iscell ()) + format = "popup"; + } + } + else if (ov_columnformat.is_string ()) + { + format = ov_columnformat.string_value (); + } + return format; + } + + void inline + Table::updateDataColumn (int col) + { + octave_value data = properties<uitable> ().get_data (); + + std::string format = columnformat (col); + bool is_editable = columneditable (col); + + for (octave_idx_type row = 0; row < data.rows (); row++) + updateData(row, + col, + data.iscell () + ? data.cell_value () (row, col) + : data.fast_elem_extract (row + col * data.rows ()), + format, + is_editable); + } + + void inline + Table::updateData (int row, int col) + { + octave_value data = properties<uitable> ().get_data (); + updateData (row, + col, + data.iscell () + ? data.cell_value () (row, col) + : data.fast_elem_extract (row + col * data.rows ()), + columnformat (col), + columneditable (col)); + } + + void inline + Table::updateData (int row, int col, octave_value value, + std::string format = "", bool enabled = false) + { + if (format == "logical" || (format == "" && value.islogical ())) + { + if (m_tableWidget->item (row, col)) + delete m_tableWidget->item (row, col); + + m_tableWidget->setCellWidget (row, col, checkBoxForLogical(value, enabled)); + m_tableWidget->cellWidget (row, col)->setProperty ("row", QVariant(row)); + m_tableWidget->cellWidget (row, col)->setProperty ("col", QVariant(col)); + } + else if (format == "popup" && enabled) + { + if (m_tableWidget->item (row, col)) + delete m_tableWidget->item (row, col); + + QString string_value = qStringValueFor(value, format).second; + uitable::properties& tp = properties<uitable> (); + octave_value format_value = tp.get_columnformat ().cell_value ().xelem (col); + + QComboBox* comboBox = new QComboBox (); + comboBox->setProperty("row", QVariant (row)); + comboBox->setProperty("col", QVariant (col)); + + int index = -1; + for (int k = 0; k < format_value.numel (); k++) + { + QString popup_item = Utils::fromStdString( + format_value.fast_elem_extract (k).string_value ()); + + comboBox->addItem (popup_item); + + if (popup_item == string_value) + index = k; + } + comboBox->setCurrentIndex (index); + + if (index < 0) + { + comboBox->setEditable (true); + comboBox->setEditText (string_value); + comboBox->lineEdit ()->setReadOnly (true); + } + + comboBox->setProperty ("original_value", QVariant (string_value)); + + comboBox->installEventFilter (this); + m_tableWidget->setCellWidget(row, col, comboBox); + connect(comboBox, SIGNAL(currentIndexChanged(const QString&)), + this, SLOT(comboBoxCurrentIndexChanged(const QString&))); + } + else + { + if (m_tableWidget->cellWidget (row, col)) + delete m_tableWidget->cellWidget (row, col); + m_tableWidget->setItem (row, col, itemFor(value, format, enabled)); + } + } + + void + Table::updateData () + { + uitable::properties& tp = properties<uitable> (); + + octave_value data = tp.get_data(); + + if (data.iscell () || data.is_matrix_type ()) + { + m_tableWidget->setRowCount (data.rows ()); + m_tableWidget->setColumnCount (data.columns ()); + + for (octave_idx_type col = 0; col < data.columns (); col++) + updateDataColumn (col); + } + + for (octave_idx_type row = 0; row < m_tableWidget->rowCount (); row++) + m_tableWidget->setRowHeight (row, AUTO_HEIGHT); + } + + void + Table::updateEnable (void) + { + uitable::properties& tp = properties<uitable> (); + m_tableWidget->setEnabled (!tp.enable_is ("off")); + + bool enabled = tp.enable_is ("on"); + bool rearrangeableColumns = tp.is_rearrangeablecolumns (); + + // Set selection mode + m_tableWidget->setSelectionMode (enabled + ? QAbstractItemView::ExtendedSelection + : QAbstractItemView::NoSelection); + + // Set rearrangeablecolumns + #if defined (HAVE_QT4) + m_tableWidget->horizontalHeader ()->setMovable (enabled && rearrangeableColumns); + #elif defined (HAVE_QT5) + m_tableWidget->horizontalHeader ()->setSectionsMovable (enabled && rearrangeableColumns); + #endif + m_tableWidget->horizontalHeader ()->setDragEnabled (enabled && rearrangeableColumns); + m_tableWidget->horizontalHeader ()->setDragDropMode (QAbstractItemView::InternalMove); + + // Turn off column editable + for (int col = 0; col < m_tableWidget->columnCount (); col++) + { + bool editable = columneditable (col); + + for (int row = 0; row < m_tableWidget->rowCount (); row++) + if (QTableWidgetItem* item = m_tableWidget->item (row, col)) + { + Qt::ItemFlags flags = item->flags (); + if (enabled && editable) + item->setFlags (flags | Qt::ItemIsEditable); + else + item->setFlags (flags & ~Qt::ItemIsEditable); + } + else if (QWidget* widget = m_tableWidget->cellWidget (row, col)) + { + QCheckBox* checkBox = NULL; + if (widget && !widget->children ().isEmpty ()) + { + QHBoxLayout* layout = qobject_cast<QHBoxLayout*> ( + widget->children ().first ()); + if (layout && layout->count () > 0) + { + checkBox = qobject_cast<QCheckBox*> ( + layout->itemAt (0)-> widget ()); + } + } + if (checkBox) + { + widget->setProperty ("Enabled", QVariant (enabled & editable)); + } + else + { + widget->setAttribute(Qt::WA_TransparentForMouseEvents, !(editable & enabled)); + widget->setFocusPolicy(Qt::NoFocus); + } + } + } + } + + void + Table::updateExtent (void) + { + QSize s = realQSizeForTable (m_tableWidget); + Matrix extent = Matrix (1, 4); + extent(0, 0) = 0; + extent(0, 1) = 0; + extent(0, 2) = s.width (); + extent(0, 3) = s.height () ; + graphics_object go = object (); + gh_manager::post_set (go.get_handle (), "extent", extent, false); + } + + void + Table::updatePalette (void) + { + uitable::properties& tp = properties<uitable> (); + + QPalette p = m_tableWidget->palette (); + p.setColor (QPalette::Text, + Utils::fromRgb (tp.get_foregroundcolor_rgb ())); + p.setColor (QPalette::Base, + Utils::fromRgb (tp.get_backgroundcolor_rgb ())); + p.setColor (QPalette::AlternateBase, + Utils::fromRgb (tp.get_alternatebackgroundcolor_rgb ())); + m_tableWidget->setPalette (p); + m_tableWidget->setAlternatingRowColors (tp.is_rowstriping ()); + // FIXME: Handle multiple alternating background colors + } + + void + Table::updateRowname (void) + { + uitable::properties& tp = properties<uitable> (); + + // Reset the row count + m_tableWidget->setRowCount (tp.get_data ().rows ()); + + octave_value rowname = tp.get_rowname (); + QStringList l; + bool visible = true; + + if (rowname.is_string () && rowname.string_value (false) == "numbered") + for (int i = 0; i < m_tableWidget->rowCount (); i++) + l << QString::number(i + 1); + else if (rowname.is_string ()) + { + if (m_tableWidget->rowCount () > 0) + l << Utils::fromStdString(rowname.string_value ()); + for (int i = 1; i < m_tableWidget->rowCount (); i++) + l << ""; + } + else if (rowname.isempty ()) + { + for (int i = 0; i < m_tableWidget->rowCount (); i++) + l << ""; + visible = false; + } + else if (rowname.iscell ()) + { + octave_idx_type n = rowname.numel(); + Cell cell_value = rowname.cell_value(); + + for (octave_idx_type i = 0; i < n; i++) + { + octave_value v = cell_value (i); + if (v.is_string ()) + l << Utils::fromStdString(v.string_value (true)); + else if (v.is_matrix_type ()) + { + Matrix data = v.matrix_value (); + + /* Now Matlab does something very strange here: + * If data is a row or column matrix, then each datapoint is added + * Otherwise nothing is set. + */ + if (data.rows () > 1 && data.cols () > 1) + l << ""; + else + for (octave_idx_type j = 0; j < data.numel (); j++) + l << QString::number(data (j)); + } + else if (v.isnumeric ()) + l << QString::number(v.double_value (true)); + else + l << QString::number(v.double_value (true)); + } + } + else if (rowname.is_matrix_type ()) + { + octave_idx_type n = rowname.numel(); + Matrix matrix_value = rowname.matrix_value(); + + for (octave_idx_type i = 0; i < n; i++) + l << QString::number(matrix_value (i)); + } + else + { + for (int i = 0; i < m_tableWidget->columnCount (); i++) + l << ""; + visible = false; + } + + // Add dummy rows as required + if (m_tableWidget->rowCount () < l.length()) + { + int oldRowCount = m_tableWidget->rowCount (); + m_tableWidget->setRowCount (l.length()); + + for (int col = 0; col < m_tableWidget->columnCount (); col++) + { + std::string format = columnformat (col); + bool enabled = columneditable (col); + + for (int row = oldRowCount; row < l.length (); row++) + { + m_tableWidget->setRowHeight (row, AUTO_HEIGHT); + + updateData (row, col, octave_value (""), format, enabled); + } + } + } + + m_tableWidget->setVerticalHeaderLabels (l); + m_tableWidget->verticalHeader ()->setVisible (visible); + } + + void + Table::updateRearrangeableColumns (void) + { + uitable::properties& tp = properties<uitable> (); + + bool rearrangeableColumns = tp.is_rearrangeablecolumns (); + bool enabled = tp.enable_is ("on"); + + #if defined (HAVE_QT4) + m_tableWidget->horizontalHeader ()->setMovable (enabled && rearrangeableColumns); + #elif defined (HAVE_QT5) + m_tableWidget->horizontalHeader ()->setSectionsMovable (enabled && rearrangeableColumns); + #endif + m_tableWidget->horizontalHeader ()->setDragEnabled (enabled && rearrangeableColumns); + m_tableWidget->horizontalHeader ()->setDragDropMode (QAbstractItemView::InternalMove); + } + + bool + Table::eventFilter (QObject* watched, QEvent* xevent) + { + //uitable::properties& tp = properties<uitable> (); + if (qobject_cast<QTableWidget*> (watched)) + { + switch (xevent->type ()) + { + case QEvent::Resize: + { + gh_manager::auto_lock lock; + graphics_object go = object (); + if (go.valid_object ()) + { + const uitable::properties& tp = + Utils::properties<uitable> (go); + if (tp.fontunits_is ("normalized")) + m_tableWidget->setFont (Utils::computeFont<uitable> (tp)); + } + } + break; + + case QEvent::MouseButtonPress: + { + gh_manager::auto_lock lock; + QMouseEvent* m = dynamic_cast<QMouseEvent*> (xevent); + graphics_object go = object (); + const uitable::properties& tp = + Utils::properties<uitable> (go); + graphics_object fig = go.get_ancestor ("figure"); + + if (m->button () != Qt::LeftButton || !tp.enable_is ("on")) + { + gh_manager::post_set (fig.get_handle (), "selectiontype", + Utils::figureSelectionType (m), false); + gh_manager::post_set (fig.get_handle (), "currentpoint", + Utils::figureCurrentPoint (fig, m), + false); + gh_manager::post_callback (fig.get_handle (), + "windowbuttondownfcn"); + gh_manager::post_callback (m_handle, "buttondownfcn"); + + if (m->button () == Qt::RightButton) + ContextMenu::executeAt (properties (), m->globalPos ()); + } + else + { + gh_manager::post_set (fig.get_handle (), "selectiontype", + octave_value ("normal"), false); + } + } + break; + + case QEvent::KeyPress: + { + QKeyEvent* k = dynamic_cast<QKeyEvent*> (xevent); + if (m_keyPressHandlerDefined) + { + gh_manager::auto_lock lock; + + octave_scalar_map keyData = Utils::makeKeyEventStruct (k); + graphics_object fig = object ().get_ancestor ("figure"); + + gh_manager::post_set (fig.get_handle (), "currentcharacter", + keyData.getfield ("Character"), false); + gh_manager::post_callback (m_handle, "keypressfcn", keyData); + } + int row = m_tableWidget->currentRow (); + int col = m_tableWidget->currentColumn (); + switch (k->key()) + { + case Qt::Key_Space: + { + QCheckBox* checkBox = NULL; + QWidget* widget = qobject_cast<QWidget*> ( + m_tableWidget->cellWidget (row, col)); + if (widget && !widget->children ().isEmpty ()) + { + QHBoxLayout* layout = qobject_cast<QHBoxLayout*> ( + widget->children ().first ()); + if (layout && layout->count () > 0) + { + checkBox = qobject_cast<QCheckBox*> ( + layout->itemAt (0)-> widget ()); + } + } + if (checkBox && checkBox->property ("Enabled").toBool()) + checkBoxClicked(row, col, checkBox); + + QComboBox* comboBox = qobject_cast<QComboBox*> ( + m_tableWidget->cellWidget (row, col)); + if (comboBox) + comboBox->showPopup (); + } + break; + + case Qt::Key_Return: + case Qt::Key_Enter: + { + if (k->modifiers() == Qt::NoModifier) + { + if (row + 1 < m_tableWidget->rowCount()) + m_tableWidget->setCurrentCell (row + 1, col); + else + { + if (col + 1 < m_tableWidget->columnCount()) + m_tableWidget->setCurrentCell (0, col + 1); + else + m_tableWidget->setCurrentCell (0, 0); + } + } + else if (k->modifiers() == Qt::ShiftModifier) + { + if (row - 1 >= 0) + m_tableWidget->setCurrentCell (row - 1, col); + else + { + if (col - 1 >= 0) + m_tableWidget->setCurrentCell + (m_tableWidget->rowCount() - 1, + col - 1); + else + m_tableWidget->setCurrentCell + (m_tableWidget->rowCount() - 1, + m_tableWidget->columnCount() - 1); + } + } + } + break; + + default: + break; + } + } + break; + + case QEvent::KeyRelease: + { + if (m_keyReleaseHandlerDefined) + { + gh_manager::auto_lock lock; + QKeyEvent* k = dynamic_cast<QKeyEvent*> (xevent); + + octave_scalar_map keyData = Utils::makeKeyEventStruct (k); + graphics_object fig = object ().get_ancestor ("figure"); + + gh_manager::post_set (fig.get_handle (), "currentcharacter", + keyData.getfield ("Character"), false); + gh_manager::post_callback (m_handle, "keyreleasefcn", keyData); + } + } + break; + + default: + break; + } + } + else if (qobject_cast<QComboBox*> (watched)) + { + switch (xevent->type ()) + { + case QEvent::MouseButtonPress: + { + gh_manager::auto_lock lock; + QMouseEvent* m = dynamic_cast<QMouseEvent*> (xevent); + graphics_object go = object (); + const uitable::properties& tp = + Utils::properties<uitable> (go); + graphics_object fig = go.get_ancestor ("figure"); + + if (m->button () != Qt::LeftButton || !tp.enable_is ("on")) + { + gh_manager::post_set (fig.get_handle (), "selectiontype", + Utils::figureSelectionType (m), false); + gh_manager::post_set (fig.get_handle (), "currentpoint", + Utils::figureCurrentPoint (fig, m), + false); + gh_manager::post_callback (fig.get_handle (), + "windowbuttondownfcn"); + gh_manager::post_callback (m_handle, "buttondownfcn"); + + if (m->button () == Qt::RightButton) + ContextMenu::executeAt (tp, m->globalPos ()); + } + else + { + gh_manager::post_set (fig.get_handle (), "selectiontype", + Utils::figureSelectionType (m), false); + + QComboBox* comboBox_0 = qobject_cast<QComboBox*> (watched); + for (int row = 0; row < m_tableWidget->rowCount (); row++) + { + for (int col = 0; col < m_tableWidget->columnCount (); col++) + { + QComboBox* comboBox_1 = qobject_cast<QComboBox*> ( + m_tableWidget->cellWidget (row, col)); + if (comboBox_0 == comboBox_1) + m_tableWidget->setCurrentCell (row, col); + } + } + } + } + break; + + default: + break; + } + } + return false; + } + +#undef AUTO_HEIGHT +}