/****************************************************************************
**
** Copyright (C) 2002-2006 Frank Hemer <frank@hemer.org>,
**                         Tilo Riemer <riemer@crossvc.com>
**
**
**----------------------------------------------------------------------------
**
**----------------------------------------------------------------------------
**
** CrossVC is available under two different licenses:
**
** If CrossVC is linked against the GPLed version of Qt 
** CrossVC is released under the terms of GPL also.
**
** If CrossVC is linked against a nonGPLed version of Qt 
** CrossVC is released under the terms of the 
** CrossVC License for non-Unix platforms (CLNU)
**
**
** CrossVC License for non-Unix platforms (CLNU):
**
** Redistribution and use in binary form, without modification, 
** are permitted provided that the following conditions are met:
**
** 1. Redistributions in binary form must reproduce the above copyright
**    notice, this list of conditions and the following disclaimer in the
**    documentation and/or other materials provided with the distribution.
** 2. It is not permitted to distribute the binary package under a name
**    different than CrossVC.
** 3. The name of the authors may not be used to endorse or promote
**    products derived from this software without specific prior written
**    permission.
** 4. The source code is the creative property of the authors.
**    Extensions and development under the terms of the Gnu Public License
**    are limited to the Unix platform. Any distribution or compilation of 
**    the source code against libraries licensed other than gpl requires 
**    the written permission of the authors.
**
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR 
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
** WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 
** DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 
** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 
** GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
** INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 
** NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
** SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**
**
**
** CrossVC License for Unix platforms:
**
** 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 version 2 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
**
*****************************************************************************/

#include <FileTableImpl.h>

#include <qapplication.h>
#include <qtable.h>
#include <qtextedit.h>
#include <qlineedit.h>
#include <qregexp.h>
#include <qstyle.h>
#include <qpainter.h>
#include <qcommonstyle.h>
#include <assert.h>

class NoFocusStyle : public QCommonStyle {//private style to disable focusRect

 public:
   NoFocusStyle() {}
   ~NoFocusStyle() {}
   
   void drawPrimitive( PrimitiveElement pe,
	 QPainter *p,
	 const QRect & r,
	 const QColorGroup & cg,
	 SFlags flags,
	 const QStyleOption & opt) const {
      
      if (pe != QStyle::PE_FocusRect) {
	 QApplication::style().drawPrimitive( pe, p, r, cg, flags, opt );
      }
   }
   void polishPopupMenu ( QPopupMenu * p) {QApplication::style().polishPopupMenu(p);}
 private:
   // Disabled copy constructor and operator=
   NoFocusStyle( const NoFocusStyle & );
   NoFocusStyle& operator=( const NoFocusStyle & );
};


FileTableImpl::FileTableItem::FileTableItem(QTable* tbl,
      QTableItem::EditType type,
      QString txt,
      const QColor& selectedColor,
      const QColor& unselectedColor,
      bool unchangeable,
      int id)
   : QTableItem(tbl,type,txt),
     m_selectedColor(selectedColor),
     m_unselectedColor(unselectedColor),
     m_colorGroup(QColorGroup()),
     m_unchangeable(unchangeable),
     m_id(id) {

   m_state = false;
   m_colorGroup.setColor(QColorGroup::Base,unselectedColor);
}

FileTableImpl::FileTableItem::~FileTableItem() {}

void FileTableImpl::FileTableItem::paint( QPainter* p, const QColorGroup&, const QRect& r, bool) {
    p->fillRect( 0, 0, r.width(), r.height(), m_colorGroup.brush( QColorGroup::Base ) );
    p->setPen( m_colorGroup.text() );
    p->drawText( 2, 0, r.width() - 4, r.height(), Qt::AlignLeft | Qt::ExpandTabs, text() );
}

void FileTableImpl::FileTableItem::setColorGroup(QColorGroup& grp) {
   m_colorGroup = QColorGroup(grp);
}

void FileTableImpl::FileTableItem::toggle() {
   if (m_unchangeable) return;
   if (m_state^=true) {
//    qDebug("toggle on row:"+QString::number(row())+", col: "+QString::number(col()));
      m_colorGroup.setColor(QColorGroup::Base,m_selectedColor);
   } else {
//    qDebug("toggle off row:"+QString::number(row())+", col: "+QString::number(col()));
      m_colorGroup.setColor(QColorGroup::Base,m_unselectedColor);
   }
}

//============================================FileTableImpl============================================

FileTableImpl::FileTableImpl( QWidget* parent,
      const char* name)
   : QTable(0,0,parent, name)
{
   horizontalHeader()->hide();
   verticalHeader()->hide();
   insertColumns(0,2);
   setShowGrid(false);
   setTopMargin(0);
   setLeftMargin(0);
   setSelectionMode(QTable::MultiRow);
   setFocusStyle(QTable::FollowStyle);
   setHScrollBarMode(QScrollView::AlwaysOn);
   setVScrollBarMode(QScrollView::AlwaysOn);
   items.setAutoDelete(true);
   setStyle(new NoFocusStyle());

   m_stop = false;
   m_rows = 0;
   m_lines = 0;
   m_blocks = 0;
   m_selectedRow = -1;
   m_focusedRowPos = -1;
   m_wasBlockSelect = false;
   m_lastMarked = -1;
   m_Partner = 0;
   m_maxWidth0 = 0;
   m_maxWidth1 = 0;
   fm = new QFontMetrics(font());
   m_lineSpacing = fm->lineSpacing();
   m_ScrollBarMinPos = -1;
   m_blockScrollPos = false;
//   history = "";

   setCornerWidget(new QWidget(this));
   cornerWidget()->setBackgroundColor("gray");

   connect( horizontalScrollBar(), SIGNAL(valueChanged(int)),
	 this,SLOT(xScrollPosChanged(int)) );
   connect( horizontalScrollBar(), SIGNAL(sliderMoved(int)),
	 this,SLOT(xScrollPosChanged(int)) );
   connect( verticalScrollBar(), SIGNAL(valueChanged(int)),
	 this,SLOT(yScrollPosChanged(int)) );
   connect( verticalScrollBar(), SIGNAL(sliderMoved(int)),
	 this,SLOT(yScrollPosChanged(int)) );

   setUpdatesEnabled(false);
}

FileTableImpl::~FileTableImpl() {
}

int FileTableImpl::rowHeight(int p) const {
   if (p < 0) return 0;
   if (p > numRows()-1) return 0;
   return m_lineSpacing;
}

int FileTableImpl::rowPos(int p) const {
   if (p<0) return 0;
   if (p > numRows()-1) return 0;
   return m_lineSpacing*p;
}

int FileTableImpl::rowAt(int p) const {
   int ra = p/m_lineSpacing;
   if (ra < 0) return 0;
   if (ra > numRows()-1) return numRows()-1;
   return(ra);
}

void FileTableImpl::paintCell(QPainter * p, int row, int col, const QRect & r, bool selected, const QColorGroup & g) {
   QTableItem * t;
   if ( (t = items.find(indexOf(row,col))) ) t->paint(p,g,r,selected);
}

void FileTableImpl::setItem(int row, int col, QTableItem * item) {
   items.replace( indexOf(row,col),item);
}

QTableItem * FileTableImpl::item(int row, int col) const {
   return items.find( indexOf( row, col) );
}

void FileTableImpl::clearCell(int row, int col) {
   items.remove( indexOf( row, col) );
}

void FileTableImpl::takeItem(QTableItem * item) {
   items.setAutoDelete(false);
   items.remove( indexOf(item->row(),item->col()) );
   items.setAutoDelete(true);
}

void FileTableImpl::swapCells(int row1, int col1, int row2, int col2) {
   int idx1 = indexOf(row1,col1);
   int idx2 = indexOf(row2,col2);
   QTableItem * item1 = items.take(idx1);
   QTableItem * item2 = items.take(idx2);
   items.replace(idx1,item2);
   items.replace(idx2,item1);
}

void FileTableImpl::setFont ( const QFont & font) {
   if (fm) delete fm;
   fm = new QFontMetrics(font);
   m_lineSpacing = fm->lineSpacing();
   QTable::setFont(font);
}

void FileTableImpl::cancelRead() {
   m_stop = true;
}

void FileTableImpl::setID( int id) {
   m_ID = id;
}

void FileTableImpl::xScrollPosChanged(int val) {
   int w = horizontalScrollBar()->maxValue() - horizontalScrollBar()->minValue();
   int value = (int)(100*((double)val/(double)w));
   if (!m_blockScrollPos) emit xScrollValueChanged(value);
}

void FileTableImpl::yScrollPosChanged(int val) {
   emit yScrollValueChanged(val);
}

void FileTableImpl::setXScrollPos(int val) {
   int w = horizontalScrollBar()->maxValue() - horizontalScrollBar()->minValue();
   int value = (int)((double)w/100*val);
   m_blockScrollPos = true;
   horizontalScrollBar()->setValue(value);
   m_blockScrollPos = false;
}

void FileTableImpl::setYScrollPos(int val) {
   verticalScrollBar()->setValue(val);
}

void FileTableImpl::render() {
   m_maxWidth0 += 4;
   setColumnWidth(0,m_maxWidth0);
   int tableWidth = horizontalScrollBar()->width();
   if ( tableWidth > (m_maxWidth0 + m_maxWidth1) ) setColumnWidth( 1,tableWidth-m_maxWidth0 );
   else setColumnWidth(1,m_maxWidth1);
   qApp->processEvents();
   setUpdatesEnabled(true);
   updateContents();
}

void FileTableImpl::focusInEvent( QFocusEvent*) {
   cornerWidget()->setBackgroundColor("red");
}

void FileTableImpl::focusOutEvent( QFocusEvent*) {
   cornerWidget()->setBackgroundColor("gray");
}

void FileTableImpl::keyPressEvent( QKeyEvent* e) {


   switch (e->key()) {

      case Qt::Key_Delete: {
	 emit clear( m_ID, m_selectedRow);
	 break;
      }
      case Qt::Key_Down: {
	 if (m_wasBlockSelect) {
	    m_wasBlockSelect = false;
	    m_lastMarked = m_selectedRow;
	 }
	 if (e->state() & Qt::ControlButton || !verticalScrollBar()->isVisible()) {
	    if (m_selectedRow==-1) setFocusRow(0);
	    else if (m_selectedRow<m_rows-1) {
	       int i = 0;
	       FileTableItem* tableItem = (FileTableItem*)item(m_selectedRow,1);
	       if (tableItem) {
		  int id = tableItem->id();
		  for (i = m_selectedRow+1;i<m_rows;i++) {
		     tableItem = (FileTableItem*)item(i,1);
		     if (tableItem && tableItem->id()!=id) {
			setFocusRow(i);
			return;
		     }
		  }
		  setFocusRow(m_rows-1);
	       }
	    }
	 } else {
	    if (m_selectedRow==-1) setFocusRow(0);
	    else setFocusPos(m_focusedRowPos+m_lineSpacing);
	 }
	 break;
      }
      case Qt::Key_Up: {
	 if (m_wasBlockSelect) {
	    m_wasBlockSelect = false;
	    m_lastMarked = m_selectedRow;
	 }
	 if (e->state() & Qt::ControlButton || !verticalScrollBar()->isVisible()) {
	    if (m_selectedRow==-1) setFocusRow(0);
	    else if (m_selectedRow>0) {
	       int i = 0;
	       FileTableItem* tableItem = (FileTableItem*)item(m_selectedRow,1);
	       if (tableItem) {
		  int id = tableItem->id();
		  for (i = m_selectedRow-1;i>=0;i--) {
		     tableItem = (FileTableItem*)item(i,1);
		     if (tableItem && tableItem->id()!=id) {
			setFocusRow(i);
			return;
		     }
		  }
		  setFocusRow(0);
	       }
	    }
	 } else {
	    if (m_selectedRow==-1) setFocusRow(0);
	    else setFocusPos(m_focusedRowPos-m_lineSpacing);
	 }
	 break;
      }
      case Qt::Key_Space: {
	 if (m_selectedRow==-1) setFocusRow(0);
	 if (e->state() & Qt::ShiftButton) {

	    FileTableItem*tableItem = (FileTableItem*)item(m_selectedRow,1);
	    if (tableItem && !tableItem->changeable()) return;

	    tableItem = (FileTableItem*)item(m_lastMarked,1);
	    bool state = false;
	    if (tableItem) {
	       state = tableItem->isSelected();
	       if (m_wasBlockSelect && (tableItem = (FileTableItem*)item(m_selectedRow,1)) && tableItem) state = !tableItem->isSelected();
	       else if (state == ((FileTableItem*)item(m_selectedRow,1))->isSelected()) return;//start and end marker need different state
	    }
	    if (m_selectedRow<m_lastMarked) {
	       int i = 0;
	       m_wasBlockSelect = true;
	       int to = setBlockState(m_selectedRow+1-1,m_lastMarked-1,state);
	       for (i=m_selectedRow+1;i<=to;i++) {
		  setSelected(i,state);
	       }
	       if (to != m_lastMarked-1) return;//different block, don't select
	    } else if (m_lastMarked<m_selectedRow) {
	       int i = 0;
	       m_wasBlockSelect = true;
	       int to = setBlockState(m_lastMarked+1,m_selectedRow-1+1,state);
	       for (i=m_lastMarked+1;i<to;i++) {
		  setSelected(i,state);
	       }
	       if (to != m_selectedRow-1+1) return;//different block, don't select
	    }

	 } else {
	    m_wasBlockSelect = false;
	 }
	 FileTableItem*tableItem = (FileTableItem*)item(m_selectedRow,1);
	 if (tableItem) {
	    tableItem->toggle();
	    updateCell(m_selectedRow,1);
	    if (!m_wasBlockSelect) {
	       m_lastMarked = m_selectedRow;
	    }
	    if (!tableItem->changeable()) return;
	    if (!m_wasBlockSelect) {
	       if (tableItem->isSelected()) {
		  emit selectionChanged(m_ID,m_selectedRow,tableItem->text());
	       } else {
		  emit unselected(m_ID,m_selectedRow);
	       }
	    }
	 }
	 break;
      }
      case Qt::Key_Prior: {
	 if (m_wasBlockSelect) {
	    m_wasBlockSelect = false;
	    m_lastMarked = m_selectedRow;
	 }
	 setFocusPos(m_focusedRowPos-(visibleHeight()/2));
	 break;
      }
      case Qt::Key_Next: {
	 if (m_wasBlockSelect) {
	    m_wasBlockSelect = false;
	    m_lastMarked = m_selectedRow;
	 }
	 setFocusPos(m_focusedRowPos+(visibleHeight()/2));
	 break;
      }
      case Qt::Key_Home: {
	 if (m_wasBlockSelect) {
	    m_wasBlockSelect = false;
	    m_lastMarked = m_selectedRow;
	 }
	 setFocusPos(0);
	 break;
      }
      case Qt::Key_End: {
	 if (m_wasBlockSelect) {
	    m_wasBlockSelect = false;
	    m_lastMarked = m_selectedRow;
	 }
	 setFocusPos(contentsHeight());
	 break;
      }
      case Qt::Key_Right: {
	 horizontalScrollBar()->setValue( horizontalScrollBar()->value()+horizontalScrollBar()->lineStep() );
	 break;
      }
      case Qt::Key_Left: {
	 horizontalScrollBar()->setValue( horizontalScrollBar()->value() - horizontalScrollBar()->lineStep() );
	 break;
      }
      default:
	 QTable::keyPressEvent( e );
	 return;
   }
   e->accept();
}

void FileTableImpl::setBackground(QColor color) {
   QPalette pal = palette();
   setPaletteBackgroundColor(color);
   pal.setColor(QColorGroup::Highlight,color);
   setPalette(pal);
   setPaletteBackgroundColor(color);
}

void FileTableImpl::contentsMousePressEvent(QMouseEvent * e) {
   int col = columnAt(e->x());
   int row = rowAt(e->y());
   tableClicked(row,col,e->button(),e->pos());
}


void FileTableImpl::tableClicked(int row, int col, int button, const QPoint&) {

//   switch(button) {
//     case 1: {
//       history += "\nL,"+QString::number(row)+","+((col==0) ? "Full" : "Line");
//       break;
//     }
//     case 2: {
//       history += "\nR,"+QString::number(row)+","+((col==0) ? "Full" : "Line");
//       break;
//     }
//   }
//   qDebug("###############################"+history+"\n------------------------------------");

   emit clicked(m_ID,row);

   FileTableItem* tableItem = NULL;
   if (row != m_selectedRow) {//new row not jet focused
      tableItem = (FileTableItem*)item(row,0);
      if (tableItem) {//set focus pointer to new row
	 tableItem->setSelected(true);
	 updateCell(row,0);
	 emit focused(m_ID,row);
      }
      else return;
      tableItem = (FileTableItem*)item(m_selectedRow,0);
      if (tableItem) {//reset old focus
	 tableItem->setSelected(false);
	 updateCell(m_selectedRow,0);
      }
      if (m_wasBlockSelect) {
	 m_wasBlockSelect = false;
	 m_lastMarked = m_selectedRow;
      }
      m_selectedRow = row;
      m_focusedRowPos = rowPos(m_selectedRow);
   }

   if (col == 1) {

      if (button == 2) {
      
	 if (m_lastMarked == -1) return;

	 tableItem = (FileTableItem*)item(m_selectedRow,1);
	 if (tableItem && !tableItem->changeable()) return;

	 tableItem = (FileTableItem*)item(m_lastMarked,1);
	 bool state = false;
	 if (tableItem) {
	    state = tableItem->isSelected();
	    if (m_wasBlockSelect && (tableItem = (FileTableItem*)item(m_selectedRow,1)) && tableItem) state = !tableItem->isSelected();
	    else if (state == ((FileTableItem*)item(m_selectedRow,1))->isSelected()) return;//start and end marker need different state
	 }
	 if (m_selectedRow<m_lastMarked) {
	    int i = 0;
	    m_wasBlockSelect = true;
	    int to = setBlockState(m_selectedRow+1-1,m_lastMarked-1,state);
	    for (i=m_selectedRow+1;i<=to;i++) {
	       setSelected(i,state);
	    }
	    if (to != m_lastMarked-1) return;//different block, don't select
	 } else if (m_lastMarked<m_selectedRow) {
	    int i = 0;
	    m_wasBlockSelect = true;
	    int to = setBlockState(m_lastMarked+1,m_selectedRow-1+1,state);
	    for (i=m_lastMarked+1;i<to;i++) {
	       setSelected(i,state);
	    }
	    if (to != m_selectedRow-1+1) return;//different block, don't select
	 }

      } else {
	 m_wasBlockSelect = false;
      }

      tableItem = (FileTableItem*)item(row,1);
      if (tableItem) tableItem->toggle();
      updateCell(row,1);
      if (!m_wasBlockSelect) {
	 m_lastMarked = m_selectedRow;
      }
      if (tableItem) {
	 if (!tableItem->changeable()) return;
	 if (!m_wasBlockSelect) {
	    if (tableItem->isSelected()) {
	       emit selectionChanged(m_ID,row,tableItem->text());
	    } else {
	       emit unselected(m_ID,row);
	    }
	 }
      }

   } else if (col == 0) {

      tableItem = (FileTableItem*)item(row,1);
      if (tableItem && tableItem->changeable()) {
	 setBlock(tableItem->id(),!tableItem->isSelected());
      }
   }
}

void FileTableImpl::setBlock( int id, bool state) {

   int pos = m_selectedRow;
   int i = 0;
   FileTableItem* tableItem = NULL;
   for (i = pos-1;i>=0;i--) {
      tableItem = (FileTableItem*)item(i,1);
      if (!tableItem) return;
      if (tableItem->id()!=id) break;
   }

   int from = i+1;
   for (i=i+1;i<pos;i++) {
      tableItem = (FileTableItem*)item(i,1);
      if (tableItem->isSelected() == state) {
	 if (from != i) {
	    if (state) {
	       emit selectedBlock(m_ID,from,i-1);
	    } else {
	       emit unSelectedBlock(m_ID,from,i-1);
	    }
	 }
	 from = i+1;
      }
      setSelected(i,state);
   }
   for (i = pos;i<m_rows;i++) {
      tableItem = (FileTableItem*)item(i,1);
      if (tableItem && tableItem->id()==id) {
	 if (tableItem->isSelected() == state) {
	    if (from != i) {
	       if (state) {
		  emit selectedBlock(m_ID,from,i-1);
	       } else {
		  emit unSelectedBlock(m_ID,from,i-1);
	       }
	    }
	    from = i+1;
	 }
	 setSelected(i,state);
      } else {
	 if (from <= i-1) {
	    if (state) {
	       emit selectedBlock(m_ID,from,i-1);
	    } else {
	       emit unSelectedBlock(m_ID,from,i-1);
	    }
	 }
	 break;
      }
   }
}

int FileTableImpl::setBlockState( int from, int to, bool state) {

   FileTableItem* tableItem = NULL;
   int i;
   int end = from;
   for (i = from;i<=to;i++) {
      tableItem = (FileTableItem*)item(i,1);
      if (!tableItem->changeable()) return end;//return end of allowed state change range (don't leave block)
      if (tableItem->isSelected() == state) {
	 if (from != i) {
	    end = i-1;
	    if (state) {
	       emit selectedBlock(m_ID,from,i-1);
	    } else {
	       emit unSelectedBlock(m_ID,from,i-1);
	    }
	 }
	 from = i+1;
      }
   }

   if (from <= to) {
      if (state) {
	 emit selectedBlock(m_ID,from,to);
      } else {
	 emit unSelectedBlock(m_ID,from,to);
      }
   }
   return to;
}

void FileTableImpl::setFocusRow(int row) {

   FileTableItem* tableItem = NULL;
   if (row != m_selectedRow) {
      tableItem = (FileTableItem*)item(row,0);
      if (tableItem) {
	 tableItem->setSelected(true);
	 updateCell(row,0);
	 emit focused(m_ID,row);
      }
      else return;
      tableItem = (FileTableItem*)item(m_selectedRow,0);
      if (tableItem) {
	 tableItem->setSelected(false);
	 updateCell(m_selectedRow,0);
      }
      m_selectedRow = row;
      setContentsPos(contentsX(),rowPos(m_selectedRow)-(visibleHeight()/2)+(m_lineSpacing/2));
      m_focusedRowPos = rowPos(m_selectedRow);
   }
}

void FileTableImpl::setFocusPos(int pos) {

   if (pos<0) pos = 0;
   if (pos>=contentsHeight()) pos = contentsHeight()-1;
   m_focusedRowPos = pos;

   int row = rowAt(pos);
   FileTableItem* tableItem = NULL;
   if (row != m_selectedRow) {
      tableItem = (FileTableItem*)item(row,0);
      if (tableItem) {
	 tableItem->setSelected(true);
	 updateCell(row,0);
	 emit focused(m_ID,row);
      }
      else return;
      tableItem = (FileTableItem*)item(m_selectedRow,0);
      if (tableItem) {
	 tableItem->setSelected(false);
	 updateCell(m_selectedRow,0);
      }
      m_selectedRow = row;
   }
   setContentsPos(contentsX(),pos-(visibleHeight()/2)+(m_lineSpacing/2));
}

void FileTableImpl::setFocus(int,int row) {

   FileTableItem* tableItem = NULL;
   if (row != m_selectedRow) {
      tableItem = (FileTableItem*)item(row,0);
      if (tableItem) {
	 tableItem->setSelected(true);
	 updateCell(row,0);
      }
      else return;
      tableItem = (FileTableItem*)item(m_selectedRow,0);
      if (tableItem) {
	 tableItem->setSelected(false);
	 updateCell(m_selectedRow,0);
      }
      m_selectedRow = row;
      m_focusedRowPos = rowPos(m_selectedRow);
   }

}

void FileTableImpl::setSelected(int row,bool state) {

   FileTableItem* tableItem =(FileTableItem*)item(row,1);
   if (tableItem && (tableItem->isSelected() != state)) {
      tableItem->setSelected(state);
      updateCell(row,1);
      if (!tableItem->changeable()) return;
//    if (state) {
//       emit selectionChanged(m_ID,row,tableItem->text());
//    } else {
//       emit unselected(m_ID,row);
//    }
   }
}

void FileTableImpl::move(int from, int offset, int count) {
   int i;
   for (i = 0; i<count; i++) {
      swapCells(from+i,1,from+offset+i,1);
   }
   updateContents();
}

void FileTableImpl::reserveLines(int count) {
   if ( (unsigned int)count > items.size()) {
      items.resize(count);
   }
   insertRows( m_rows,items.size() );
}

void FileTableImpl::addLine(QString numString,
      QString txt,
      bool unchangeable,
      const QColor selectedNumColor,
      const QColor unselectedNumColor,
      const QColor selectedColor) {
   addLine(numString,txt,unchangeable,selectedNumColor,unselectedNumColor,selectedColor,paletteBackgroundColor());
}

void FileTableImpl::addLine(QString numString,
      QString txt,
      bool unchangeable,
      const QColor selectedNumColor,
      const QColor unselectedNumColor,
      const QColor selectedColor,
      const QColor unselectedColor) {

   int from = 0;
   int to = 0;
   int loop = 100;
   to=txt.find("\n",to);
   bool isNum;
   int lineNum = numString.toInt(&isNum);
   if (isNum) numString += ":";
   do {
      if (loop == 0) {
	 qApp->processEvents(1);//ProgressDialog needs processing
	 loop = 100;
      }
      QString tmpTxt = txt.mid((unsigned int)from,(unsigned int)(to-from));
      QString numTxt = isNum ? QString::number(lineNum++)+":" : numString;
      FileTableItem* numItem = new FileTableItem(this,QTableItem::Never,
	    numTxt,
	    selectedNumColor,unselectedNumColor,false,m_blocks);
      FileTableItem* txtItem = new FileTableItem(this,QTableItem::Never,tmpTxt,selectedColor,unselectedColor,unchangeable,m_blocks);

      setItem( m_rows,0,numItem);
      setItem( m_rows,1,txtItem);

      m_rows++;
      m_lines++;
      from = to+1;
      loop--;
      int width = fm->width(numTxt);
      if (width > m_maxWidth0) m_maxWidth0 = width;
      width = fm->size(Qt::ExpandTabs,tmpTxt).width();
      if (width > m_maxWidth1) m_maxWidth1 = width;
   } while ( ((to=txt.find("\n",from))>-1) && (!m_stop));

   m_blocks++;
}

void FileTableImpl::addBlock(QString txt,
      bool unchangeable,
      const QColor selectedNumColor,
      const QColor unselectedNumColor,
      const QColor selectedColor) {
   addBlock(txt,unchangeable,selectedNumColor,unselectedNumColor,selectedColor,paletteBackgroundColor());
}

void FileTableImpl::addBlock(QString txt,
      bool unchangeable,
      const QColor selectedNumColor,
      const QColor unselectedNumColor,
      const QColor selectedColor,
      const QColor unselectedColor) {

   int from = 0;
   int to = 0;
   int count = txt.contains("\n");
   int loop = 100;
   if (count == 0) return;
   insertRows( m_rows,count);
   to=txt.find("\n",to);
   do {
      if (loop == 0) {
	 qApp->processEvents(1);//ProgressDialog needs processing
	 loop = 100;
      }
      QString tmpTxt = txt.mid((unsigned int)from,(unsigned int)(to-from));

      QString numString = QString::number(m_lines+1)+":";
      FileTableItem* numItem = new FileTableItem(this,QTableItem::Never,numString,selectedNumColor,unselectedNumColor,false,m_blocks);
      FileTableItem* txtItem = new FileTableItem(this,QTableItem::Never,tmpTxt,selectedColor,unselectedColor,unchangeable,m_blocks);

//       setRowHeight(m_rows, m_lineSpacing);
      setItem( m_rows,0,numItem);
      setItem( m_rows,1,txtItem);

      m_rows++;
      m_lines++;
      from = to+1;
      loop--;
      int width = fm->width(numString);
      if (width > m_maxWidth0) m_maxWidth0 = width;
      width = fm->size(Qt::ExpandTabs,tmpTxt).width();
      if (width > m_maxWidth1) m_maxWidth1 = width;
   } while ( ((to=txt.find("\n",from))>-1) && (!m_stop));

   m_blocks++;
}

void FileTableImpl::addParagraph(QString txt,
      bool unchangeable,
      const QColor selectedNumColor,
      const QColor unselectedNumColor,
      const QColor selectedColor) {
   addParagraph(txt,unchangeable,selectedNumColor,unselectedNumColor,selectedColor,paletteBackgroundColor());
}

void FileTableImpl::addParagraph(QString txt,
      bool unchangeable,
      const QColor selectedNumColor,
      const QColor unselectedNumColor,
      const QColor selectedColor,
      const QColor unselectedColor) {

   QString numString = QString::number(m_lines+1)+":";
   int i=0;
   int addLinesCount = txt.contains('\n') + 1;
   int from = m_lines + 2;
   int to = m_lines + addLinesCount;
   for (i=from;i<=to;i++) {
      numString += "\n"+QString::number(i)+":";
   }
   FileTableItem* numItem = new FileTableItem(this,QTableItem::Never,numString,selectedNumColor,unselectedNumColor,false,m_blocks);
   FileTableItem* txtItem = new FileTableItem(this,QTableItem::Never,txt,selectedColor,unselectedColor,unchangeable,m_blocks);

   insertRows( m_rows,1);
   setRowHeight(m_rows, addLinesCount*m_lineSpacing);
   setItem( m_rows,0,numItem);
   setItem( m_rows,1,txtItem);

   m_rows++;
   m_lines += addLinesCount;
   m_blocks++;
   int width = fm->width(numString);
   if (width > m_maxWidth0) m_maxWidth0 = width;
   width = fm->size(Qt::ExpandTabs,txt).width();
   if (width > m_maxWidth1) m_maxWidth1 = width;
}

void FileTableImpl::setText(int row, const QString& txt) {
   QTable::setText(row,1,txt);
}

const QString FileTableImpl::getText(int row) {
   if (row<m_lines) {
      return text(row,1);
   } else {
      return QString::null;
   }
}

int FileTableImpl::getTopLine() {
   return rowAt(contentsY());
}

int FileTableImpl::getFocusedLine() {
   return m_selectedRow;
}

void FileTableImpl::resizeEvent( QResizeEvent *e) {
   QTable::resizeEvent(e);
   updateScrollBars();
   if (verticalScrollBar()->isVisible()) {
      QRect rect = verticalScrollBar()->style().querySubControlMetrics(QStyle::CC_ScrollBar,
      verticalScrollBar(),
      QStyle::SC_ScrollBarGroove);
      m_ScrollBarMinPos = rect.top();
      m_ScrollBarMaxPos = rect.height();
   }
   
   int tableWidth = horizontalScrollBar()->width();
   if ( tableWidth > (m_maxWidth0 + m_maxWidth1) ) setColumnWidth( 1,tableWidth-m_maxWidth0 );
   else setColumnWidth(1,m_maxWidth1);
}

void FileTableImpl::adjustScrollBarOffsets(int &from, int &height) {
   updateScrollBars();
   
   if (verticalScrollBar()->isVisible()) {
      if (m_ScrollBarMinPos == -1) {
         QRect rect = verticalScrollBar()->style().querySubControlMetrics(QStyle::CC_ScrollBar,
         verticalScrollBar(),
         QStyle::SC_ScrollBarGroove);
         m_ScrollBarMinPos = rect.top();
         m_ScrollBarMaxPos = rect.height();
      }
      from += m_ScrollBarMinPos;
      height = m_ScrollBarMaxPos;
   }
}

