// SPDX-FileCopyrightText: Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
// SPDX-FileCopyrightText: Copyright 2008 Sandia Corporation
// SPDX-License-Identifier: LicenseRef-BSD-3-Clause-Sandia-USGov
#include "vtkQtTableModelAdapter.h"

#include "vtkConvertSelection.h"
#include "vtkDataSetAttributes.h"
#include "vtkDoubleArray.h"
#include "vtkIdList.h"
#include "vtkIdTypeArray.h"
#include "vtkIntArray.h"
#include "vtkSelection.h"
#include "vtkSelectionNode.h"
#include "vtkSmartPointer.h"
#include "vtkTable.h"
#include "vtkUnsignedCharArray.h"
#include "vtkVariant.h"

#include <QColor>
#include <QHash>
#include <QIcon>
#include <QImage>
#include <QMap>
#include <QMimeData>
#include <QPainter>
#include <QPair>
#include <QPixmap>

#include <set>
#include <sstream>

//------------------------------------------------------------------------------
VTK_ABI_NAMESPACE_BEGIN
class vtkQtTableModelAdapter::vtkInternal
{
public:
  vtkInternal() = default;
  ~vtkInternal() = default;

  QHash<QModelIndex, QVariant> IndexToDecoration;
  QHash<int, QPair<vtkIdType, int>> ModelColumnToTableColumn;
  QHash<int, QString> ModelColumnNames;
  QHash<vtkIdType, vtkSmartPointer<vtkDoubleArray>> MagnitudeColumns;
};

//------------------------------------------------------------------------------
vtkQtTableModelAdapter::vtkQtTableModelAdapter(QObject* p)
  : vtkQtAbstractModelAdapter(p)
{
  this->Internal = new vtkInternal;
  this->Table = nullptr;
  this->SplitMultiComponentColumns = false;
  this->DecorationLocation = vtkQtTableModelAdapter::HEADER;
  this->DecorationStrategy = vtkQtTableModelAdapter::NONE;
  this->ColorColumn = -1;
  this->IconIndexColumn = -1;
  this->IconSheetSize[0] = this->IconSheetSize[1] = 0;
  this->IconSize[0] = this->IconSize[1] = 0;
}

//------------------------------------------------------------------------------
vtkQtTableModelAdapter::vtkQtTableModelAdapter(vtkTable* t, QObject* p)
  : vtkQtAbstractModelAdapter(p)
  , Table(t)
{
  this->Internal = new vtkInternal;
  this->SplitMultiComponentColumns = false;
  this->DecorationLocation = vtkQtTableModelAdapter::HEADER;
  this->DecorationStrategy = vtkQtTableModelAdapter::NONE;
  this->ColorColumn = -1;
  this->IconIndexColumn = -1;
  this->IconSheetSize[0] = this->IconSheetSize[1] = 0;
  this->IconSize[0] = this->IconSize[1] = 0;
  if (this->Table != nullptr)
  {
    this->Table->Register(nullptr);
  }
}

//------------------------------------------------------------------------------
vtkQtTableModelAdapter::~vtkQtTableModelAdapter()
{
  if (this->Table != nullptr)
  {
    this->Table->Delete();
  }
  delete this->Internal;
}

//------------------------------------------------------------------------------
void vtkQtTableModelAdapter::SetColorColumnName(const char* name)
{
  int color_column = this->ColorColumn;
  if (name == nullptr || !this->Table)
  {
    this->ColorColumn = -1;
  }
  else if (this->SplitMultiComponentColumns)
  {
    this->ColorColumn = -1;
    int color_index = 0;
    Q_FOREACH (QString columnname, this->Internal->ModelColumnNames)
    {
      if (columnname == name)
      {
        this->ColorColumn = color_index;
        break;
      }
      color_index++;
    }
  }
  else
  {
    this->ColorColumn = -1;
    for (int i = 0; i < static_cast<int>(this->Table->GetNumberOfColumns()); i++)
    {
      if (!strcmp(name, this->Table->GetColumn(i)->GetName()))
      {
        this->ColorColumn = i;
        break;
      }
    }
  }
  if (this->ColorColumn != color_column)
  {
    Q_EMIT this->reset();
  }
}

//------------------------------------------------------------------------------
void vtkQtTableModelAdapter::SetIconIndexColumnName(const char* name)
{
  int color_column = this->IconIndexColumn;
  if (name == nullptr || !this->Table)
  {
    this->IconIndexColumn = -1;
  }
  else if (this->SplitMultiComponentColumns)
  {
    this->IconIndexColumn = -1;
    int color_index = 0;
    Q_FOREACH (QString columnname, this->Internal->ModelColumnNames)
    {
      if (columnname == name)
      {
        this->IconIndexColumn = color_index;
        break;
      }
      color_index++;
    }
  }
  else
  {
    this->IconIndexColumn = -1;
    for (int i = 0; i < static_cast<int>(this->Table->GetNumberOfColumns()); i++)
    {
      if (!strcmp(name, this->Table->GetColumn(i)->GetName()))
      {
        this->IconIndexColumn = i;
        break;
      }
    }
  }
  if (this->IconIndexColumn != color_column)
  {
    Q_EMIT this->reset();
  }
}

//------------------------------------------------------------------------------
void vtkQtTableModelAdapter::SetKeyColumnName(const char* name)
{
  int key_column = this->KeyColumn;
  if (name == nullptr || !this->Table)
  {
    this->KeyColumn = -1;
  }
  else if (this->SplitMultiComponentColumns)
  {
    this->KeyColumn = -1;
    int key_index = 0;
    Q_FOREACH (QString columnname, this->Internal->ModelColumnNames)
    {
      if (columnname == name)
      {
        this->KeyColumn = key_index;
        break;
      }
      key_index++;
    }
  }
  else
  {
    this->KeyColumn = -1;
    for (int i = 0; i < static_cast<int>(this->Table->GetNumberOfColumns()); i++)
    {
      if (!strcmp(name, this->Table->GetColumn(i)->GetName()))
      {
        this->KeyColumn = i;
        break;
      }
    }
  }
  if (this->KeyColumn != key_column)
  {
    Q_EMIT this->reset();
  }
}

//------------------------------------------------------------------------------
void vtkQtTableModelAdapter::SetVTKDataObject(vtkDataObject* obj)
{
  vtkTable* t = vtkTable::SafeDownCast(obj);
  if (obj && !t)
  {
    qWarning("vtkQtTableModelAdapter needs a vtkTable for SetVTKDataObject");
    return;
  }

  // Okay it's a table so set it :)
  this->setTable(t);
}

//------------------------------------------------------------------------------
vtkDataObject* vtkQtTableModelAdapter::GetVTKDataObject() const
{
  return this->Table;
}

//------------------------------------------------------------------------------
void vtkQtTableModelAdapter::updateModelColumnHashTables()
{
  // Clear the hash tables
  this->Internal->ModelColumnToTableColumn.clear();
  this->Internal->ModelColumnNames.clear();
  this->Internal->MagnitudeColumns.clear();

  // Do not continue if SplitMultiComponentColumns is false
  // or our table is null.
  if (!this->SplitMultiComponentColumns || !this->Table)
  {
    return;
  }

  // Get the start and end columns.
  int startColumn = 0;
  int endColumn = this->Table->GetNumberOfColumns() - 1;
  if (this->GetViewType() == DATA_VIEW)
  {
    startColumn = this->DataStartColumn;
    endColumn = this->DataEndColumn;
  }

  // Double check to make sure startColumn and endColumn are within bounds
  int maxColumn = this->Table->GetNumberOfColumns() - 1;
  if ((startColumn < 0 || startColumn > maxColumn) || (endColumn < 0 || endColumn > maxColumn))
  {
    return;
  }

  // For each column in the vtkTable, iterate over the column's number of
  // components to construct a mapping from qt model columns to
  // vtkTable columns-component pairs.  Also generate qt model column names.
  int modelColumn = 0;
  for (int tableColumn = startColumn; tableColumn <= endColumn; ++tableColumn)
  {
    const int nComponents = this->Table->GetColumn(tableColumn)->GetNumberOfComponents();
    for (int c = 0; c < nComponents; ++c)
    {
      QString columnName = this->Table->GetColumnName(tableColumn);
      if (nComponents != 1)
      {
        columnName = QString("%1 (%2)").arg(columnName).arg(c);
      }
      this->Internal->ModelColumnNames[modelColumn] = columnName;
      this->Internal->ModelColumnToTableColumn[modelColumn++] =
        QPair<vtkIdType, int>(tableColumn, c);
    }

    // If number of components is greater than 1, create a new column for Magnitude
    vtkDataArray* dataArray = vtkArrayDownCast<vtkDataArray>(this->Table->GetColumn(tableColumn));
    if (nComponents > 1 && dataArray)
    {
      vtkSmartPointer<vtkDoubleArray> magArray = vtkSmartPointer<vtkDoubleArray>::New();
      magArray->SetNumberOfComponents(1);
      const vtkIdType nTuples = dataArray->GetNumberOfTuples();
      for (vtkIdType i = 0; i < nTuples; ++i)
      {
        double mag = 0;
        for (int j = 0; j < nComponents; ++j)
        {
          double tmp = dataArray->GetComponent(i, j);
          mag += tmp * tmp;
        }
        mag = sqrt(mag);
        magArray->InsertNextValue(mag);
      }

      // Create a name for this column and add it to the ModelColumnNames map
      QString columnName = this->Table->GetColumnName(tableColumn);
      columnName = QString("%1 (Magnitude)").arg(columnName);
      this->Internal->ModelColumnNames[modelColumn] = columnName;

      // Store the magnitude column mapped to its corresponding column in the vtkTable
      this->Internal->MagnitudeColumns[tableColumn] = magArray;

      // Add a pair that has the vtkTable column and component, but use a component value
      // that is out of range to signal that this column represents magnitude
      this->Internal->ModelColumnToTableColumn[modelColumn++] =
        QPair<vtkIdType, int>(tableColumn, nComponents);
    }
  }
}

//------------------------------------------------------------------------------
void vtkQtTableModelAdapter::setTable(vtkTable* t)
{
  if (this->Table != nullptr)
  {
    this->Table->Delete();
  }
  this->Table = t;
  if (this->Table != nullptr)
  {
    this->Table->Register(nullptr);

    // When setting a table, update the QHash tables for column mapping.
    // If SplitMultiComponentColumns is disabled, this call will just clear
    // the tables and return.
    this->updateModelColumnHashTables();

    // We will assume the table is totally
    // new and any views should update completely
    Q_EMIT this->reset();
  }
}

//------------------------------------------------------------------------------
bool vtkQtTableModelAdapter::noTableCheck() const
{
  if (this->Table == nullptr)
  {
    // It's not necessarily an error to have a null pointer for the
    // table.  It just means that the model is empty.
    return true;
  }
  if (this->Table->GetNumberOfRows() == 0)
  {
    return true;
  }
  return false;
}

//------------------------------------------------------------------------------
// Description:
// Selection conversion from VTK land to Qt land
vtkSelection* vtkQtTableModelAdapter::QModelIndexListToVTKIndexSelection(QModelIndexList qmil) const
{
  // Create vtk index selection
  vtkSelection* IndexSelection = vtkSelection::New(); // Caller needs to delete
  vtkSmartPointer<vtkSelectionNode> node = vtkSmartPointer<vtkSelectionNode>::New();
  node->SetContentType(vtkSelectionNode::INDICES);
  node->SetFieldType(vtkSelectionNode::ROW);
  vtkSmartPointer<vtkIdTypeArray> index_arr = vtkSmartPointer<vtkIdTypeArray>::New();
  node->SetSelectionList(index_arr);
  IndexSelection->AddNode(node);

  // Run through the QModelIndexList pulling out vtk indexes
  std::set<int> unique_ids;
  for (int i = 0; i < qmil.size(); i++)
  {
    unique_ids.insert(qmil.at(i).internalId());
  }
  std::set<int>::iterator iter;
  for (iter = unique_ids.begin(); iter != unique_ids.end(); ++iter)
  {
    index_arr->InsertNextValue(*iter);
  }

  return IndexSelection;
}

//------------------------------------------------------------------------------
QItemSelection vtkQtTableModelAdapter::VTKIndexSelectionToQItemSelection(vtkSelection* vtksel) const
{

  QItemSelection qis_list;
  vtkSelectionNode* node = vtksel->GetNode(0);
  if (node)
  {
    vtkIdTypeArray* arr = vtkArrayDownCast<vtkIdTypeArray>(node->GetSelectionList());
    if (arr)
    {
      for (vtkIdType i = 0; i < arr->GetNumberOfTuples(); i++)
      {
        vtkIdType vtk_index = arr->GetValue(i);
        QModelIndex qmodel_index = this->createIndex(vtk_index, 0, static_cast<int>(vtk_index));
        qis_list.select(qmodel_index, qmodel_index);
      }
    }
  }
  return qis_list;
}

//------------------------------------------------------------------------------
bool vtkQtTableModelAdapter::GetSplitMultiComponentColumns() const
{
  return this->SplitMultiComponentColumns;
}

//------------------------------------------------------------------------------
void vtkQtTableModelAdapter::SetSplitMultiComponentColumns(bool value)
{
  if (value != this->SplitMultiComponentColumns)
  {
    this->SplitMultiComponentColumns = value;
    this->updateModelColumnHashTables();
  }
}

//------------------------------------------------------------------------------
void vtkQtTableModelAdapter::SetDecorationStrategy(int s)
{
  if (s != this->DecorationStrategy)
  {
    this->DecorationStrategy = s;
  }
}

//------------------------------------------------------------------------------
void vtkQtTableModelAdapter::SetDecorationLocation(int s)
{
  if (s != this->DecorationLocation)
  {
    this->DecorationLocation = s;
  }
}

//------------------------------------------------------------------------------
QVariant vtkQtTableModelAdapter::data(const QModelIndex& idx, int role) const
{
  if (this->noTableCheck())
  {
    return QVariant();
  }
  if (!idx.isValid())
  {
    return QVariant();
  }

  // Map the qt model column to a column in the vtk table and
  // get the value from the table as a vtkVariant
  vtkVariant v;
  this->getValue(idx.row(), idx.column(), v);

  // Return a string if they ask for a display role
  if (role == Qt::DisplayRole)
  {
    bool ok;
    double value = v.ToDouble(&ok);
    if (ok)
    {
      return QVariant(value);
    }
    else
    {
      return QString::fromUtf8(v.ToString().c_str()).trimmed();
    }
  }

  // Return a byte array if they ask for a decorate role
  if (role == Qt::DecorationRole)
  {
    if (this->DecorationStrategy == vtkQtTableModelAdapter::COLORS &&
      this->DecorationLocation == vtkQtTableModelAdapter::ITEM && this->ColorColumn >= 0)
    {
      return this->getColorIcon(idx.row());
    }
    else if (this->DecorationStrategy == vtkQtTableModelAdapter::ICONS &&
      this->DecorationLocation == vtkQtTableModelAdapter::ITEM && this->IconIndexColumn >= 0)
    {
      return this->getIcon(idx.row());
    }
    return this->Internal->IndexToDecoration[idx];
  }

  // Return a raw value if they ask for a user role
  if (role == Qt::UserRole)
  {
    if (v.IsNumeric())
    {
      return QVariant(v.ToDouble());
    }
    return QVariant(v.ToString().c_str());
  }

  // Hmmm... if the role isn't decorate, user or display
  // then just punt and return a empty qvariant
  return QVariant();
}

//------------------------------------------------------------------------------
bool vtkQtTableModelAdapter::setData(const QModelIndex& idx, const QVariant& value, int role)
{
  if (role == Qt::DecorationRole)
  {
    this->Internal->IndexToDecoration[idx] = value;
    Q_EMIT this->dataChanged(idx, idx);
    return true;
  }
  return false;
}

//------------------------------------------------------------------------------
Qt::ItemFlags vtkQtTableModelAdapter::flags(const QModelIndex& idx) const
{
  if (!idx.isValid())
  {
    return Qt::ItemIsEnabled | Qt::ItemIsDropEnabled;
  }

  return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDropEnabled | Qt::ItemIsDragEnabled;
}

//------------------------------------------------------------------------------
QVariant vtkQtTableModelAdapter::headerData(
  int section, Qt::Orientation orientation, int role) const
{
  if (this->noTableCheck())
  {
    return QVariant();
  }

  // For horizontal headers, try to convert the column names to double.
  // If it doesn't work, return a string.
  if (orientation == Qt::Horizontal && (role == Qt::DisplayRole || role == Qt::UserRole))
  {

    QString columnName;
    if (this->GetSplitMultiComponentColumns())
    {
      columnName = this->Internal->ModelColumnNames[section];
    }
    else
    {
      int column = this->ModelColumnToFieldDataColumn(section);
      columnName = this->Table->GetColumnName(column);
    }

    QVariant svar(columnName);
    bool ok;
    double value = svar.toDouble(&ok);
    if (ok)
    {
      return QVariant(value);
    }
    return svar;
  }

  // For vertical headers, return values in the first column if
  // KeyColumn is valid.
  if (orientation == Qt::Vertical)
  {
    if (role == Qt::DisplayRole || role == Qt::UserRole)
    {
      if (this->KeyColumn >= 0)
      {
        vtkVariant v;
        this->getValue(section, this->KeyColumn, v);
        if (v.IsNumeric())
        {
          return QVariant(v.ToDouble());
        }
        return QVariant(v.ToString().c_str());
      }
    }
    else if (role == Qt::DecorationRole &&
      this->DecorationStrategy == vtkQtTableModelAdapter::ICONS &&
      this->DecorationLocation == vtkQtTableModelAdapter::ITEM && this->IconIndexColumn >= 0)
    {
      return this->getIcon(section);
    }
  }

  return QVariant();
}

//------------------------------------------------------------------------------
void vtkQtTableModelAdapter::getValue(int row, int in_column, vtkVariant& v) const
{
  // Map the qt model column to a column in the vtk table
  int column;
  if (this->GetSplitMultiComponentColumns())
  {
    column = this->Internal->ModelColumnToTableColumn[in_column].first;
  }
  else
  {
    column = this->ModelColumnToFieldDataColumn(in_column);
  }

  // Get the value from the table as a vtkVariant
  // We don't use vtkTable::GetValue() since for multi-component arrays, it can
  // be slow due to the use of vtkDataArray in the vtkVariant.
  vtkAbstractArray* array = this->Table->GetColumn(column);
  if (!array)
  {
    return;
  }

  const int nComponents = array->GetNumberOfComponents();

  // Special case- if the variant is an array it means the column data
  // has multiple components
  if (nComponents == 1)
  {
    v = array->GetVariantValue(row);
  }
  else if (nComponents > 1)
  {
    if (this->GetSplitMultiComponentColumns())
    {
      // Map the qt model column to the corresponding component in the vtkTable column
      const int component = this->Internal->ModelColumnToTableColumn[in_column].second;
      if (component < nComponents)
      {
        // If component is in range, then fetch the component's value
        v = array->GetVariantValue(nComponents * row + component);
      }
      else
      {
        // If component is out of range this signals that we should return the
        // value from the magnitude column
        v = this->Internal->MagnitudeColumns[column]->GetValue(row);
      }
    }
    else // don't split columns.
    {
      QString strValue;
      for (int i = 0; i < nComponents; ++i)
      {
        strValue.append(
          QString("%1, ").arg(array->GetVariantValue(row * nComponents + i).ToString().c_str()));
      }
      strValue = strValue.remove(strValue.size() - 2, 2); // remove the last comma

      // Reconstruct the variant using this string value
      v = vtkVariant(strValue.toUtf8().data());
    }
  }
}

//------------------------------------------------------------------------------
QModelIndex vtkQtTableModelAdapter::index(
  int row, int column, const QModelIndex& vtkNotUsed(parentIdx)) const
{
  return createIndex(row, column, row);
}

//------------------------------------------------------------------------------
QModelIndex vtkQtTableModelAdapter::parent(const QModelIndex& vtkNotUsed(idx)) const
{
  return QModelIndex();
}

//------------------------------------------------------------------------------
int vtkQtTableModelAdapter::rowCount(const QModelIndex& mIndex) const
{
  if (this->noTableCheck())
  {
    return 0;
  }
  if (mIndex == QModelIndex())
  {
    return this->Table->GetNumberOfRows();
  }
  return 0;
}

//------------------------------------------------------------------------------
int vtkQtTableModelAdapter::columnCount(const QModelIndex&) const
{
  if (this->noTableCheck())
  {
    return 0;
  }

  // If we are splitting multi-component columns, then just return the
  // number of entries in the QHash map that stores column names.
  if (this->GetSplitMultiComponentColumns())
  {
    return this->Internal->ModelColumnNames.size();
  }

  // The number of columns in the qt model depends on the current ViewType
  switch (this->ViewType)
  {
    case FULL_VIEW:
      return this->Table->GetNumberOfColumns();
    case DATA_VIEW:
      return this->DataEndColumn - this->DataStartColumn + 1;
    default:
      vtkGenericWarningMacro("vtkQtTreeModelAdapter: Bad view type.");
  };
  return 0;
}

bool vtkQtTableModelAdapter::dropMimeData(const QMimeData* d, Qt::DropAction action,
  int vtkNotUsed(row), int vtkNotUsed(column), const QModelIndex& vtkNotUsed(parent))
{
  if (action == Qt::IgnoreAction)
    return true;

  if (!d->hasFormat("vtk/selection"))
    return false;

  void* temp = nullptr;
  std::istringstream buffer(d->data("vtk/selection").data());
  buffer >> temp;
  vtkSelection* s = reinterpret_cast<vtkSelection*>(temp);

  Q_EMIT this->selectionDropped(s);

  return true;
}

QStringList vtkQtTableModelAdapter::mimeTypes() const
{
  QStringList types;
  types << "vtk/selection";
  return types;
}

Qt::DropActions vtkQtTableModelAdapter::supportedDropActions() const
{
  return Qt::CopyAction;
}

QMimeData* vtkQtTableModelAdapter::mimeData(const QModelIndexList& indexes) const
{
  // Only supports dragging single item right now ...

  if (indexes.empty())
  {
    return nullptr;
  }

  vtkSmartPointer<vtkSelection> indexSelection =
    vtkSmartPointer<vtkSelection>::Take(QModelIndexListToVTKIndexSelection(indexes));
  // vtkSmartPointer<vtkSelection> pedigreeIdSelection =
  // vtkSmartPointer<vtkSelection>::Take(vtkConvertSelection::ToSelectionType(indexSelection,
  // this->Table, vtkSelectionNode::PEDIGREEIDS));

  // This is a memory-leak, we need to serialize its contents as a string, instead of serializing a
  // pointer to the object
  vtkSelection* pedigreeIdSelection = vtkConvertSelection::ToSelectionType(
    indexSelection, this->Table, vtkSelectionNode::PEDIGREEIDS);

  if (pedigreeIdSelection->GetNode(0) == nullptr ||
    pedigreeIdSelection->GetNode(0)->GetSelectionList()->GetNumberOfTuples() == 0)
  {
    return nullptr;
  }

  std::ostringstream buffer;
  buffer << pedigreeIdSelection;

  QMimeData* mime_data = new QMimeData();
  mime_data->setData("vtk/selection", buffer.str().c_str());

  return mime_data;
}

QVariant vtkQtTableModelAdapter::getColorIcon(int row) const
{
  int column;
  if (this->GetSplitMultiComponentColumns())
  {
    column = this->Internal->ModelColumnToTableColumn[this->ColorColumn].first;
  }
  else
  {
    column = this->ModelColumnToFieldDataColumn(this->ColorColumn);
  }
  vtkUnsignedCharArray* colors =
    vtkArrayDownCast<vtkUnsignedCharArray>(this->Table->GetColumn(column));
  if (!colors)
  {
    return QVariant();
  }

  const int nComponents = colors->GetNumberOfComponents();
  if (nComponents >= 3)
  {
    unsigned char rgba[4];
    colors->GetTypedTuple(row, rgba);
    int rgb[3];
    rgb[0] = static_cast<int>(0x0ff & rgba[0]);
    rgb[1] = static_cast<int>(0x0ff & rgba[1]);
    rgb[2] = static_cast<int>(0x0ff & rgba[2]);

    QPixmap pixmap(16, 16);
    pixmap.fill(QColor(0, 0, 0, 0));
    QPainter painter(&pixmap);
    painter.setRenderHint(QPainter::Antialiasing);
    painter.setPen(Qt::NoPen);
    painter.setBrush(QBrush(QColor(rgb[0], rgb[1], rgb[2])));
    painter.drawEllipse(4, 4, 7, 7);
    return QVariant(pixmap);
  }

  return QVariant();
}

QVariant vtkQtTableModelAdapter::getIcon(int row) const
{
  int column;
  if (this->GetSplitMultiComponentColumns())
  {
    column = this->Internal->ModelColumnToTableColumn[this->IconIndexColumn].first;
  }
  else
  {
    column = this->ModelColumnToFieldDataColumn(this->IconIndexColumn);
  }
  vtkIntArray* icon_indices = vtkArrayDownCast<vtkIntArray>(this->Table->GetColumn(column));
  if (!icon_indices)
  {
    return QVariant();
  }

  int icon_idx = icon_indices->GetValue(row);
  int x, y;
  int dimX = this->IconSheetSize[0] / this->IconSize[0];
  x = (icon_idx >= dimX) ? icon_idx % dimX : icon_idx;
  x *= this->IconSize[0];
  y = (icon_idx >= dimX) ? (icon_idx / dimX) : 0;
  y *= this->IconSize[1];

  return this->IconSheet.copy(x, y, this->IconSize[0], this->IconSize[1]);
}

void vtkQtTableModelAdapter::SetIconSheet(QImage sheet)
{
  this->IconSheet = sheet;
}
void vtkQtTableModelAdapter::SetIconSheetSize(int w, int h)
{
  this->IconSheetSize[0] = w;
  this->IconSheetSize[1] = h;
}
void vtkQtTableModelAdapter::SetIconSize(int w, int h)
{
  this->IconSize[0] = w;
  this->IconSize[1] = h;
}
VTK_ABI_NAMESPACE_END
