/* 
 * Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved.
 *
 * This program 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; version 2 of the
 * License.
 * 
 * This program 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 this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301  USA
 */

#include "stdafx.h"

#include "mforms/mforms.h"
#include "wf_view.h"
#include "wf_table.h"
#include "wf_label.h"

#include "ConvUtils.h"

using namespace MySQL;
using namespace MySQL::Forms;
using namespace MySQL::Utilities;

using namespace System::Drawing;

//--------------------------------------------------------------------------------------------------

/**
 * Applies the computed bounds to each control. Adjust position if the control does not fill the given
 * space depending on the fill flag.
 *
 * @param list The list of cell entries with their bounds to be applied.
 */
void apply_bounds(CellList% list)
{
  HDWP hdwp = BeginDeferWindowPos(list.Count);
  for each (CellEntry^ entry in list)
  {
    if (!entry->isVisible)
      continue;

    ViewImpl::remove_auto_resize(entry->control, AutoResizeMode::ResizeBoth);
    Drawing::Rectangle newBounds = entry->control->Bounds;

    if (entry->horizontalFill)
      newBounds.Width = entry->bounds.Width;
    if (entry->verticalFill)
      newBounds.Height = entry->bounds.Height;

    newBounds.X = entry->bounds.Left + (entry->bounds.Width - newBounds.Width) / 2;
    newBounds.Y = entry->bounds.Top + (entry->bounds.Height - newBounds.Height) / 2;

    // The WebBrowser class doesn't like being resized via DeferWindowPos, so we do it the straight way.
    // Also, if DeferWindowPos (or BeginDeferWindowPos for that matter) returned an empty handle
    // then we need to do the resizing that way for all controls.
    if (hdwp == 0  || is<Windows::Forms::WebBrowser>(entry->control))
      entry->control->Bounds = newBounds;
    else
    {
      HDWP new_hdwp = DeferWindowPos(hdwp, (HWND)entry->control->Handle.ToInt32(), 0, newBounds.X, newBounds.Y,
        newBounds.Width, newBounds.Height, SWP_NOZORDER);
      if (new_hdwp == 0)
      {
        // Something went wrong, so finish what we started so far and continue with straight resize.
        EndDeferWindowPos(hdwp);
      }

      hdwp = new_hdwp;
    }
  }

  if (hdwp != 0)
    EndDeferWindowPos(hdwp);
}

//--------------------------------------------------------------------------------------------------

/**
 * Helper method to compare two cell entries by their column span size.
 */
int CompareColumnSpan(CellEntry^ entry1, CellEntry^ entry2)
{
  if (entry1 == nullptr)
  {
    if (entry2 == nullptr)
      return 0; // Both entries are null, so they are equal.
    else
      return -1; // entry2 is greater since it is not null, but entry1 is.
  }
  else
  {
    if (entry2 == nullptr)
      return 1; // entry1 is not null, but entry2 is, so entry1 is greater.
    else
    {
      // Now to the real work. Both entries are valid.
      int difference= (entry1->rightAttachment - entry1->leftAttachment) - (entry2->rightAttachment - entry2->leftAttachment);
      return (difference > 0) ? 1 : ((difference < 0) ? -1 : 0);
    }
  }
}

//--------------------------------------------------------------------------------------------------

/**
* Helper method to compare two cell entries by their row span size.
*/
int CompareRowSpan(CellEntry^ entry1, CellEntry^ entry2)
{
  if (entry1 == nullptr)
  {
    if (entry2 == nullptr)
      return 0; // Both entries are null, so they are equal.
    else
      return -1; // entry2 is greater since it is not null, but entry1 is.
  }
  else
  {
    if (entry2 == nullptr)
      return 1; // entry1 is not null, but entry2 is, so entry1 is greater.
    else
    {
      // Now to the real work. Both entries are valid.
      int difference= (entry1->bottomAttachment - entry1->topAttachment) - (entry2->bottomAttachment - entry2->topAttachment);
      return (difference > 0) ? 1 : ((difference < 0) ? -1 : 0);
    }
  }
}

//----------------- GtkTableLayout ----------------------------------------------------------------

/**
 * This is the main layout method of the GTK table layout engine. It is triggered automatically
 * by the .NET framework, whenever a need appears to re-layout the tables content (usually when child controls
 * are added or removed, or when the table is resized, e.g. as part of the layout process of its parent).
 *
 * @param container The control which must be laid out.
 * @param arguments A number of arguments which are supposed to control the layouting process. Not used currently.
 *
 * @return True if the parent should re-layout itself (e.g. due to a size change of this table) or false, if not.
 */
bool GtkTableLayout::Layout(Object^ container, LayoutEventArgs^ arguments)
{
  Table^ table = (Table^) container;
  if (!ViewImpl::can_layout(table, arguments->AffectedProperty))
    return false;

  if (table->RowCount > 0 && table->ColumnCount > 0)
  {
    ViewImpl::adjust_auto_resize_from_docking(table);
    System::Drawing::Size tableSize = table->ComputeLayout(table->Size, true);

    bool parentLayoutNeeded= !table->Size.Equals(tableSize);
    if (parentLayoutNeeded)
      ViewImpl::resize_with_docking(table, tableSize);

    return parentLayoutNeeded;
  }
  return false;
}

//----------------- Table ---------------------------------------------------------------------------

Table::Table()
{
  homogeneous = false;
  columnSpacing = 0;
  rowSpacing = 0;
  columnCount = 0;
  rowCount = 0;
}

//--------------------------------------------------------------------------------------------------

/**
 * Computes the entire layout of the table. This includes size and position of client controls.
 * 
 * @param proposedSize The size to start from layouting. Since super ordinated controls may impose
 *                     a layout size we need to honor that (especially important for auto wrapping
 *                     labels).
 * @param resizeChildren Tells the function whether the computed client control bounds should be applied
 *                       (when doing a relayout) or not (when computing the preferred size).
 * @return The resulting size of the table.
 */
System::Drawing::Size Table::ComputeLayout(System::Drawing::Size proposedSize, bool resizeChildren)
{
  // Layouting the grid goes like this:
  // * Compute all row heights + column widths.
  // * Apply the resulting cell sizes to all attached children.
  // * Adjust the size of the container if necessary.

  // To compute all row heights and widths do:
  // 1) For each cell entry
  // 2)   Keep the expand state for all cells it covers.
  // 3)   Compute the base cell sizes for all cells it covers (e.g. for the width: take the control width and distribute
  //      it evenly over all cells covered from left to right attachment).
  // 4)   Compare the cell size with what has been computed overall so far. Replace any cell size for 
  //      the control which is larger than what is stored so far by that larger value.

  // If in homogeneous mode do:
  // Find the tallest row and apply its height to all other rows so they are all at the same height.
  // Similar for columns.

  // If the sum of all rows is smaller then the current container height distribute the difference evenly
  // over all children with expand flag set.
  // Similar for columns.

  // If not in homogeneous mode do:
  // 1) If the sum of all widths is smaller than the control width then distribute the remaining
  //     space over all columns for which the expand flag is set.
  // 2) Same for all rows.

  array<int>^ heights= gcnew array<int>(RowCount);
  for (int i= 0; i < RowCount; i++)
    heights[i]= 0;
  array<int>^ widths= gcnew array<int>(ColumnCount);
  for (int i= 0; i < ColumnCount; i++)
    widths[i]= 0;

  array<bool>^ verticalExpandState= gcnew array<bool>(RowCount);
  for (int i= 0; i < RowCount; i++)
    verticalExpandState[i]= false;
  array<bool>^ horizontalExpandState= gcnew array<bool>(ColumnCount);
  for (int i= 0; i < ColumnCount; i++)
    horizontalExpandState[i]= false;

  if (!HorizontalCenter)
    proposedSize.Width -= Padding.Horizontal;
  if (!VerticalCenter)
    proposedSize.Height -= Padding.Vertical;
  bool useHorizontalCentering = resizeChildren && HorizontalCenter;
  bool useVerticalCentering = resizeChildren && VerticalCenter;

  System::Drawing::Size newSize= System::Drawing::Size::Empty;

  // First round: sort list for increasing column span count, so we can process smallest entries first.
  content.Sort(gcnew Comparison<CellEntry^>(CompareColumnSpan));

  // Go for each cell entry and apply its preferred size to the proper cells,
  // after visibility state and bounds are set.
  // Keep expand states so we can apply them later.
  for each (CellEntry^ entry in content)
  {
    entry->isVisible = entry->control->Visible && (entry->rightAttachment > entry->leftAttachment)
      && (entry->bottomAttachment > entry->topAttachment) && !entry->control->IsDisposed && !entry->control->Disposing;
    if (!entry->isVisible)
      continue;

    // Check if the width of the entry is larger than what we have already.
    // While we are at it, keep the expand state in the associated cells.
    // However, if the current entry is expanding and covers a column which is already set to expand
    // then don't apply expansion, as we only want to expand those columns.
    int currentWidth = 0;
    bool doExpand = entry->horizontalExpand;
    if (doExpand)
    {
      useHorizontalCentering = false; // Expansion disables auto centering.
      for (int i = entry->leftAttachment; i < entry->rightAttachment; i++)
      {
        if (horizontalExpandState[i])
        {
          doExpand = false;
          break;
        }
      }
    }
    for (int i = entry->leftAttachment; i < entry->rightAttachment; i++)
    {
      currentWidth += widths[i];
      if (doExpand)
        horizontalExpandState[i] = true;
    }

    bool use_min_width = ViewImpl::use_min_width_for_layout(entry->control);
    bool use_min_height = ViewImpl::use_min_height_for_layout(entry->control);
    if (use_min_width && use_min_height)
      entry->bounds.Size = entry->control->MinimumSize;
    else
    {
      entry->bounds= System::Drawing::Rectangle(Point(0, 0),
        entry->control->GetPreferredSize(System::Drawing::Size(currentWidth, 0)));
      if (use_min_width)
        entry->bounds.Width= entry->control->MinimumSize.Width;
      if (use_min_height)
        entry->bounds.Height= entry->control->MinimumSize.Height;
    }

    // Weird behavior of combo boxes. They report a one pixel too small height (you cannot set them
    // to this smaller height, though). So this wrong value is messing up our computed overall height.
    if (entry->control->GetType() == ComboBox::typeid)
      entry->bounds.Height++;

    // Set all cells to the computed partial size if it is larger than what was found so far.
    // On the way apply the expand flag to all cells that are covered by that entry.

    // If the width of the entry is larger then distribute the difference to all cells it covers.
    if (entry->bounds.Width > currentWidth)
    {
      // The fraction is a per-cell value and computed by an integer div (we cannot add partial pixels).
      // Hence we might have a rest, which is less than the span size. Distribute this rest over all spanned cell too.
      int fraction= (entry->bounds.Width - currentWidth) / (entry->rightAttachment - entry->leftAttachment);
      int rest= (entry->bounds.Width - currentWidth) % (entry->rightAttachment - entry->leftAttachment);

      for (int i= entry->leftAttachment; i < entry->rightAttachment; i++)
      {
        widths[i] += fraction;
        if (rest > 0)
        {
          widths[i]++;
          rest--;
        }
      }
    }
  }

  // Once we got the minimal width we need to compute the real width as the height computation depends
  // on the final column widths (e.g. for wrapping labels that change their height depending on their width).
  // Handle homogeneous mode.
  if (Homogeneous)
  {
    int max= 0;
    for (int i= 0; i < ColumnCount; i++)
      if (widths[i] > max)
        max = widths[i];

    for (int i= 0; i < ColumnCount; i++)
      widths[i] = max;
  }

  // Compute overall width and handle expanded entries.
  for (int i= 0; i < ColumnCount; i++)
    newSize.Width += widths[i];
  newSize.Width += (ColumnCount - 1) * ColumnSpacing;

  // Do auto sizing the table if enabled. Apply minimal bounds in any case.
  if (newSize.Width > proposedSize.Width || (ViewImpl::can_auto_resize_horizontally(this) && !useHorizontalCentering))
  {
    proposedSize.Width = newSize.Width;
    if (proposedSize.Width < MinimumSize.Width - Padding.Horizontal)
      proposedSize.Width = MinimumSize.Width - Padding.Horizontal;
  }

  // Handle expansion of cells.
  if (resizeChildren)
  {
    if (!useHorizontalCentering && (newSize.Width < proposedSize.Width))
    {
      int expandCount= 0;
      for (int i= 0; i < ColumnCount; i++)
        if (horizontalExpandState[i])
          expandCount++;
      if (expandCount > 0)
      {
        int fraction= (proposedSize.Width - newSize.Width) / expandCount;
        for (int i= 0; i < ColumnCount; i++)
          if (horizontalExpandState[i])
            widths[i] += fraction;
      }
    }
  }

  // Second round: Now that we have all final widths compute the heights. Start with sorting the entries
  // list for increasing row span count, so we can process smallest entries first.
  content.Sort(gcnew Comparison<CellEntry^>(CompareRowSpan));

  // Go for each cell entry and apply its preferred size to the proper cells.
  // Keep expand states so we can apply them later.
  for each (CellEntry^ entry in content)
  {
    // Visibility state and bounds where already determined in the first round.
    if (!entry->isVisible)
      continue;

    // Set all cells to the computed partial size if it is larger than what was found so far.
    // On the way apply the expand flag to all cells that are covered by that entry.

    // Check if the height of the entry is larger than what we have already.
    // Same expansion handling here as for horizontal expansion.
    int currentHeight= 0;
    bool doExpand = entry->verticalExpand;
    if (doExpand)
    {
      useVerticalCentering = false; // Expansion disables auto centering.
      for (int i = entry->topAttachment; i < entry->bottomAttachment; i++)
      {
        if (verticalExpandState[i])
        {
          doExpand = false;
          break;
        }
      }
    }
    for (int i= entry->topAttachment; i < entry->bottomAttachment; i++)
    {
      currentHeight += heights[i];
      if (doExpand)
        verticalExpandState[i]= true;
    }

    // For controls that change height depending on their width we need another preferred size computation.
    // Currently this applies only for wrapping labels.
    if (entry->control->GetType() == WrapControlLabel::typeid)
    {
      int currentWidth = 0;
      for (int i= entry->leftAttachment; i < entry->rightAttachment; i++)
        currentWidth += widths[i];
      entry->bounds= System::Drawing::Rectangle(Point(0, 0),
        entry->control->GetPreferredSize(System::Drawing::Size(currentWidth, 0)));
    }

    // If the height of the entry is larger then distribute the difference to all cells it covers.
    if (entry->bounds.Height > currentHeight)
    {
      int fraction= (entry->bounds.Height - currentHeight) / (entry->bottomAttachment - entry->topAttachment);
      int rest= (entry->bounds.Height - currentHeight) % (entry->bottomAttachment - entry->topAttachment);

      for (int i= entry->topAttachment; i < entry->bottomAttachment; i++)
      {
        heights[i] += fraction;
        if (rest > 0)
        {
          heights[i]++;
          rest--;
        }
      }
    }
  }

  // Handle homogeneous mode.
  if (Homogeneous)
  {
    int max = 0;
    for (int i = 0; i < RowCount; i++)
      if (heights[i] > max)
        max = heights[i];

    for (int i = 0; i < RowCount; i++)
      heights[i] = max;
  }

  // Compute overall size and handle expanded entries.
  for (int i = 0; i < RowCount; i++)
    newSize.Height += heights[i];
  newSize.Height += (RowCount - 1) * RowSpacing;

  // Do auto sizing the table if enabled. Apply minimal bounds in any case.
  if (newSize.Height > proposedSize.Height || (ViewImpl::can_auto_resize_vertically(this) && !useVerticalCentering))
  {
    proposedSize.Height = newSize.Height;
    if (proposedSize.Height < MinimumSize.Height - Padding.Vertical)
      proposedSize.Height = MinimumSize.Height - Padding.Vertical;
  }

  // Handle expansion of cells (vertical case). Since this can happen only if the new size is
  // less than the proposed size it does not matter for pure size computation (in that case we
  // have already our target size). Hence do it only if we are actually layouting the table.
  if (resizeChildren)
  {
    if (!useVerticalCentering && (newSize.Height < proposedSize.Height))
    {
      int expandCount= 0;
      for (int i= 0; i < RowCount; i++)
        if (verticalExpandState[i])
          expandCount++;
      if (expandCount > 0)
      {
        int fraction= (proposedSize.Height - newSize.Height) / expandCount;
        for (int i= 0; i < RowCount; i++)
          if (verticalExpandState[i])
            heights[i] += fraction;
      }
    }

    // Compute target bounds from cell sizes. Compute one more column/row used as right/bottom border.
    array<int>^ rowStarts = gcnew array<int>(RowCount + 1);
    rowStarts[0] = Padding.Top;
    if (useVerticalCentering && (newSize.Height < proposedSize.Height))
      rowStarts[0] = (proposedSize.Height - newSize.Height) / 2;

    for (int i= 1; i <= RowCount; i++)
      rowStarts[i] = rowStarts[i - 1] + heights[i - 1] + RowSpacing;

    array<int>^ columnStarts= gcnew array<int>(ColumnCount + 1);
    columnStarts[0] = Padding.Left;
    if (useHorizontalCentering && (newSize.Width < proposedSize.Width))
      columnStarts[0] = (proposedSize.Width - newSize.Width) / 2;

    for (int i= 1; i <= ColumnCount; i++)
      columnStarts[i] = columnStarts[i - 1] + widths[i - 1] + ColumnSpacing;

    for each (CellEntry^ entry in content)
    {
      if (!entry->isVisible)
        continue;

      entry->bounds.X = columnStarts[entry->leftAttachment];
      entry->bounds.Y = rowStarts[entry->topAttachment];
      entry->bounds.Width = columnStarts[entry->rightAttachment] - columnStarts[entry->leftAttachment] - ColumnSpacing;
      entry->bounds.Height = rowStarts[entry->bottomAttachment] - rowStarts[entry->topAttachment] - RowSpacing;
    }

    // Apply target bounds to cell content.
    apply_bounds(content);
  }

  if (!HorizontalCenter)
    proposedSize.Width += Padding.Horizontal;
  if (!VerticalCenter)
    proposedSize.Height += Padding.Vertical;

  return proposedSize;
}

//--------------------------------------------------------------------------------------------------

/**
 * Returns the preferred size of the table (that is, the minimal size that can cover all content). 
 *
 * @param proposedSize The base size to be used for layout. The resulting size won't usually go
 *                     below this size.
 *
 * @return The preferred size of this table.
 */
System::Drawing::Size Table::GetPreferredSize(System::Drawing::Size proposedSize)
{
  return ComputeLayout(proposedSize, false);
}

//-------------------------------------------------------------------------------------------------

void Table::Add(Control^ control, int left, int right, int top, int bottom, int flags)
{
  if (left >= ColumnCount || right > ColumnCount)
    throw gcnew IndexOutOfRangeException("Adding control to table at an invalid column position");
  if (top >= RowCount || top > RowCount)
    throw gcnew IndexOutOfRangeException("Adding control to table at an invalid row position");

  ViewImpl::set_layout_dirty(this, true);

  CellEntry^ entry= gcnew CellEntry();
  entry->control= control;

  // Do some sanity checks here to avoid later trouble.
  // Upper limits are not checked as this can change with the number of columns,
  // even after the table was filled.
  if (left < 0)
    left= 0;
  if (left > right)
  {
    int temp= left;
    left= right;
    right= temp;
  }
  if (left == right)
    right++;
  if (top < 0)
    top= 0;
  if (top > bottom)
  {
    int temp = top;
    top = bottom;
    bottom = temp;
  }
  if (top == bottom)
    bottom++;
  entry->leftAttachment = left;
  entry->rightAttachment = right;
  entry->topAttachment = top;
  entry->bottomAttachment = bottom;
  entry->horizontalExpand = (flags & mforms::HExpandFlag) != 0;
  entry->verticalExpand = (flags & mforms::VExpandFlag) != 0;
  entry->horizontalFill = (flags & mforms::HFillFlag) != 0;
  entry->verticalFill = (flags & mforms::VFillFlag) != 0;
  content.Add(entry); // for us
  Controls->Add(control); // for Windows
}

//-------------------------------------------------------------------------------------------------

void Table::Remove(Control^ control)
{
  ViewImpl::set_layout_dirty(this, true);

  for each (CellEntry^ entry in content)
  {
    if (entry->control == control)
    {
      content.Remove(entry);
      break;
    }
  }

  Controls->Remove(control);
}

//--------------------------------------------------------------------------------------------------

Layout::LayoutEngine^ Table::LayoutEngine::get()
{
  if (layoutEngine == nullptr)
    layoutEngine = gcnew GtkTableLayout();

  return layoutEngine;
}

//--------------------------------------------------------------------------------------------------

bool Table::Homogeneous::get()
{
  return homogeneous;
}

//--------------------------------------------------------------------------------------------------

void Table::Homogeneous::set(bool value)
{
  if (homogeneous != value)
  {
    ViewImpl::set_layout_dirty(this, true);
    homogeneous = value;
    Refresh();
  }
}

//--------------------------------------------------------------------------------------------------

int Table::RowSpacing::get()
{
  return rowSpacing;
}

//--------------------------------------------------------------------------------------------------

void Table::RowSpacing::set(int value)
{
  if (rowSpacing != value)
  {
    ViewImpl::set_layout_dirty(this, true);
    rowSpacing= value;
    Refresh();
  }
}

//--------------------------------------------------------------------------------------------------

int Table::ColumnSpacing::get()
{
  return columnSpacing;
}

//--------------------------------------------------------------------------------------------------

void Table::ColumnSpacing::set(int value)
{
  if (columnSpacing != value)
  {
    ViewImpl::set_layout_dirty(this, true);
    columnSpacing= value;
    Refresh();
  }
}

//--------------------------------------------------------------------------------------------------

int Table::RowCount::get() 
{ 
  return rowCount;
}

//--------------------------------------------------------------------------------------------------

void Table::RowCount::set(int value)
{
  if (rowCount != value)
  {
    ViewImpl::set_layout_dirty(this, true);
    rowCount = value;
    Refresh();
  }
}

//--------------------------------------------------------------------------------------------------

int Table::ColumnCount::get() 
{ 
  return columnCount;
}

//--------------------------------------------------------------------------------------------------

void Table::ColumnCount::set(int value)
{
  if (columnCount != value)
  {
    ViewImpl::set_layout_dirty(this, true);
    columnCount = value;
    Refresh();
  }
}

//--------------------------------------------------------------------------------------------------

CellList% Table::Content::get()
{
  return content;
}

//----------------- TableImpl -----------------------------------------------------------------------

void TableImpl::set_padding(int left, int top, int right, int bottom)
{
  // Depending on what is specified as padding we apply a dynamic padding (centering so the content).
  Table ^table = get_control<Table>();
  table->HorizontalCenter = (left < 0 || right < 0);
  table->VerticalCenter = (top < 0 || bottom < 0);
  
  ViewImpl::set_padding(left, top, right, bottom);
}

//-------------------------------------------------------------------------------------------------

bool TableImpl::create(mforms::Table *self)
{
  TableImpl ^table= gcnew TableImpl(self);

  if (table != nullptr)
  {
    Table^ native_table= ViewImpl::create<Table>(self, table);
    return true;
  }
  return false;
}

//-------------------------------------------------------------------------------------------------

void TableImpl::add(mforms::Table *self, mforms::View *child, int left, int right, int top, int bottom, int flags)
{
  TableImpl^ table= (TableImpl^)ObjectImpl::FromUnmanaged(self);
  if (table != nullptr)
  {
    ViewImpl^ child_view= (ViewImpl^)ObjectImpl::FromUnmanaged(child);
    table->add(child_view->get_control<Control>(), left, right, top, bottom, flags);
    self->set_layout_dirty(true);
  }
}

//-------------------------------------------------------------------------------------------------

void TableImpl::remove(mforms::Table *self, mforms::View* child)
{
  TableImpl^ table= (TableImpl^)ObjectImpl::FromUnmanaged(self);
  if (table != nullptr)
  {
    ViewImpl^ child_view= (ViewImpl^)ObjectImpl::FromUnmanaged(child);
    table->remove(child_view->get_control<Control>());
  }
}

//-------------------------------------------------------------------------------------------------

void TableImpl::set_row_count(mforms::Table *self, int count)
{
  TableImpl^ table= (TableImpl^)ObjectImpl::FromUnmanaged(self);
  if (table != nullptr)
  {
    table->set_row_count(count);
    self->set_layout_dirty(true);
  }
}

//-------------------------------------------------------------------------------------------------

void TableImpl::set_column_count(mforms::Table *self, int count)
{
  TableImpl^ table= (TableImpl^)ObjectImpl::FromUnmanaged(self);
  if (table != nullptr)
  {
    table->set_column_count(count);
    self->set_layout_dirty(true);
  }
}

//-------------------------------------------------------------------------------------------------

void TableImpl::set_row_spacing(mforms::Table *self, int space)
{
  TableImpl^ table= (TableImpl^)ObjectImpl::FromUnmanaged(self);
  if (table != nullptr)
  {
    table->set_row_spacing(space);
    self->set_layout_dirty(true);
  }
}

//-------------------------------------------------------------------------------------------------

void TableImpl::set_column_spacing(mforms::Table *self, int space)
{
  TableImpl^ table= (TableImpl^)ObjectImpl::FromUnmanaged(self);
  if (table != nullptr)
  {
    table->set_column_spacing(space);
    self->set_layout_dirty(true);
  }
}

//-------------------------------------------------------------------------------------------------

void TableImpl::set_homogeneous(mforms::Table *self, bool value)
{
  TableImpl^ table= (TableImpl^)ObjectImpl::FromUnmanaged(self);
  if (table != nullptr)
  {
    table->set_homogeneous(value);
    self->set_layout_dirty(true);
  }
}

//-------------------------------------------------------------------------------------------------

void TableImpl::add(Windows::Forms::Control ^control, int left, int right, int top, int bottom, int flags)
{
  Table ^table= get_control<Table>();
  table->Add(control, left, right, top, bottom, flags);
}

//-------------------------------------------------------------------------------------------------

void TableImpl::remove(Windows::Forms::Control ^control)
{
  Table ^table= get_control<Table>();
  table->Remove(control);
}

//-------------------------------------------------------------------------------------------------

void TableImpl::set_homogeneous(bool homogeneous)
{
  get_control<Table>()->Homogeneous= homogeneous;
}

//-------------------------------------------------------------------------------------------------

void TableImpl::set_row_spacing(int space)
{
  get_control<Table>()->RowSpacing= space;
}

//-------------------------------------------------------------------------------------------------

void TableImpl::set_column_spacing(int space)
{
  get_control<Table>()->ColumnSpacing= space;
}

//-------------------------------------------------------------------------------------------------

void TableImpl::set_row_count(int count)
{
  get_control<Table>()->RowCount= count;
}

//-------------------------------------------------------------------------------------------------

void TableImpl::set_column_count(int count)
{
  get_control<Table>()->ColumnCount= count;
}

//-------------------------------------------------------------------------------------------------
