void PageItem_Table::mergeCells(int row, int column, int numRows, int numCols)
{
	ASSERT_VALID();

	if (!validCell(row, column) || !validCell(row + numRows - 1, column + numCols - 1))
		return;

	CellArea newArea(row, column, numCols, numRows);

	// Unite intersecting areas.
	QMutableListIterator<CellArea> areaIt(m_cellAreas);
	while (areaIt.hasNext())
	{
		CellArea oldArea = areaIt.next();
		if (newArea.intersects(oldArea))
		{
			// The two areas intersect, so unite them.
			newArea = newArea.united(oldArea);

			// Reset row/column span of old spanning cell, then remove old area.
			TableCell oldSpanningCell = cellAt(oldArea.row(), oldArea.column());
			oldSpanningCell.setRowSpan(1);
			oldSpanningCell.setColumnSpan(1);
			areaIt.remove();
		}
	}

	// Set row/column span of new spanning cell, and add new area.
	TableCell newSpanningCell = cellAt(newArea.row(), newArea.column());
	newSpanningCell.setRowSpan(newArea.height());
	newSpanningCell.setColumnSpan(newArea.width());
	m_cellAreas.append(newArea);

	// Update cells. TODO: Not for entire table.
	updateCells();

	// If merged area covers active position, move to the spanning cell.
	if (newArea.contains(m_activeRow, m_activeColumn))
		moveTo(newSpanningCell);

	// Remove all cells covered by the merged area from the selection.
	QMutableSetIterator<TableCell> cellIt(m_selection);
	while (cellIt.hasNext())
	{
		TableCell cell = cellIt.next();
		if (newArea.contains(cell.row(), cell.column()) &&
			!(cell.row() == newArea.row() && cell.column() == newArea.column()))
			cellIt.remove();
	}

	emit changed();

	ASSERT_VALID();
}
void PageItem_Table::updateCells(int startRow, int startColumn, int endRow, int endColumn)
{
	if (startRow > endRow || startColumn > endColumn)
		return; // Invalid area.

	if (!validCell(startRow, startColumn) || !validCell(endRow, endColumn))
		return; // Invalid area.

	foreach (const QList<TableCell>& cellRow, m_cellRows)
		foreach (TableCell cell, cellRow)
			cell.updateContent();
}
void PageItem_Table::selectCell(int row, int column)
{
	if (!validCell(row, column))
		return;

	m_selection.insert(cellAt(row, column));
	emit selectionChanged();
}
void PageItem_Table::assertValid() const
{
	// Check list sizes.
	Q_ASSERT(rows() == m_rowPositions.size());
	Q_ASSERT(rows() == m_rowHeights.size());
	Q_ASSERT(columns() == m_columnPositions.size());
	Q_ASSERT(columns() == m_columnWidths.size());
	Q_ASSERT(rows() == m_cellRows.size());
	foreach (QList<TableCell> cellRow, m_cellRows)
		Q_ASSERT(columns() == cellRow.size());

	for (int row = 0; row < rows(); ++row)
	{
		for (int col = 0; col < columns(); ++col)
		{
			TableCell cell = m_cellRows[row][col];

			// Check that the cell reports correct row and column.
			Q_ASSERT(cell.row() == row);
			Q_ASSERT(cell.column() == col);

			// Check that the row and column span is sane.
			Q_ASSERT(cell.rowSpan() >= 1 && cell.columnSpan() >= 1);

			if (cell.rowSpan() > 1 || cell.columnSpan() > 1)
			{
				// Check that there's exactly one matching cell area.
				CellArea expectedArea(cell.row(), cell.column(), cell.columnSpan(), cell.rowSpan());
				Q_ASSERT(m_cellAreas.count(expectedArea) == 1);
			}
		}
	}

	// Check that the active position is in this table.
	Q_ASSERT(validCell(m_activeRow, m_activeColumn));

	// Check that the active cell is valid.
	Q_ASSERT(m_activeCell.isValid());
	Q_ASSERT(validCell(m_activeCell.row(), m_activeCell.column()));

	// Check that selected cells are valid.
	foreach (const TableCell& cell, m_selection)
	{
		Q_ASSERT(cell.isValid());
		Q_ASSERT(validCell(cell.row(), cell.column()));
	}
void PageItem_Table::selectCells(int startRow, int startColumn, int endRow, int endColumn)
{
	if (!validCell(startRow, startColumn) || !validCell(endRow, endColumn))
		return;

	const TableCell startCell = cellAt(startRow, startColumn);
	const TableCell endCell = cellAt(endRow, endColumn);

	const int topRow = qMin(startCell.row(), endCell.row());
	const int bottomRow = qMax(startCell.row() + startCell.rowSpan() - 1,
		endCell.row() + endCell.rowSpan() - 1);

	const int leftCol = qMin(startCell.column(), endCell.column());
	const int rightCol = qMax(startCell.column() + startCell.columnSpan() - 1,
		endCell.column() + endCell.columnSpan() - 1);

	for (int row = topRow; row <= bottomRow; ++row)
		for (int col = leftCol; col <= rightCol; ++col)
			selectCell(row, col);
	emit selectionChanged();
}
void PageItem_Table::activateCell(const TableCell& cell)
{
	ASSERT_VALID();

	TableCell newActiveCell = validCell(cell.row(), cell.column()) ? cell : cellAt(0, 0);

	// Deselect previous active cell and its text.
	m_activeCell.textFrame()->setSelected(false);
	m_activeCell.textFrame()->itemText.deselectAll();

	// Set the new active cell and select it.
	m_activeCell = newActiveCell;
	m_activeCell.textFrame()->setSelected(true);
	m_Doc->currentStyle = m_activeCell.textFrame()->currentStyle();
	m_activeRow = m_activeCell.row();
	m_activeColumn = m_activeCell.column();
	emit selectionChanged();

	ASSERT_VALID();
}
TableCell PageItem_Table::cellAt(int row, int column) const
{
	if (!validCell(row, column))
		return TableCell();

	TableCell cell = m_cellRows[row][column];

	QList<CellArea>::const_iterator areaIt;
	for (areaIt = m_cellAreas.begin(); areaIt != m_cellAreas.end(); ++areaIt)
	{
		CellArea area = (*areaIt);
		if (area.contains(row, column))
		{
			// Cell was contained in merged area, so use spanning cell.
			cell = m_cellRows[area.row()][area.column()];
			break;
		}
	}

	return cell;
}
/**
* Prompts user for input
* @param response Pointer to store address user inputted
* @param value    Pointer to store value user inputted
*/
bool prompt(char *response, char *value, int socket) {
   // printf("In %s\n",__func__);
   SOCKET_WRITE(socket, "Enter row and column you would like to manipulate or enter 'exit' to exit program: ", 0);
   read(socket, response, IN_BUF_LIMIT);
   printf("%s\n", response);
   if (strcmp(response, "exit") == 0)
      return false;

   response[0] = toupper(response[0]);

   while(!validCell(response) || !response)
   {
      SOCKET_WRITE(socket, "Invalid cell. Enter cell to edit ('exit' to quit): ", 0);
      read(socket, response, IN_BUF_LIMIT);
      if (strcmp(response, "exit") == 0)
         return false;
      response[0] = toupper(response[0]);
   }
   SOCKET_WRITE(socket, "Enter value: ", 0);
   read(socket, value, IN_BUF_LIMIT);

   return true;
}