/* ====================================================================
 * Copyright (c) 2003-2008  Martin Hauner
 *                          http://subcommander.tigris.org
 *
 * Subcommander is licensed as described in the file doc/COPYING, which
 * you should have received as part of this distribution.
 * ====================================================================
 */

// sc
#include "TextWidget.h"
#include "LinePainter.h"
#include "TextPositionCalculator.h"
#include "ScrollPositionCalculator.h"
#include "DiffInfoModel.h"
#include "NullDiffInfoModel.h"
#include "Pair.h"
#include "ColorId.h"
#include "sublib/Tab.h"
#include "sublib/Line.h"
#include "sublib/TextModel.h"
#include "sublib/NullTextModel.h"
#include "sublib/PaintLine.h"
#include "sublib/PaintLineFactory.h"
#include "sublib/LineConfig.h"
#include "sublib/ColorStorage.h"

// sys
#include <stdio.h>
#include <algorithm>
#include <assert.h>
#include "util/max.h"

// qt
#include <QtCore/QTimer>
#include <QtCore/QUrl>
#include <QtGui/QApplication>
#include <QtGui/QClipboard>
#include <QtGui/QPaintEvent>
#include <QtGui/QRubberBand>
#include <QtGui/QPainter>


static const int ScrollBorder = 2;           // chars
static const int LinePad      = 2;           // pixel
static NullTextModel NullText;
static NullDiffInfoModel NullInfo;

////////////////////////////////////////////////////////////////////////////////

class TextHighlightCalculator
{
public:
  TextHighlightCalculator()
  {
  }

  bool calcHighlight( const CursorPair& cp, int curLine, int maxcol, IntPair& pos )
  {
    const Cursor& topCursor = cp.getOne();
    const Cursor& botCursor = cp.getTwo();

    int col1 = 0;
    int col2 = 0;

    if( curLine > topCursor.line() && curLine < botCursor.line() )
    {
      // completely highlighted line
      col1 = 0;
      col2 = maxcol;
    }
    else if( curLine == topCursor.line() && curLine == botCursor.line() )
    {
      // possibly only partially highlighted line
      col1 = topCursor.column();
      col2 = botCursor.column();
    }
    else if( curLine == topCursor.line() && curLine != botCursor.line() )
    {
      // partially highlighted first line
      col1 = topCursor.column();
      col2 = maxcol;
    }
    else if( curLine == botCursor.line() && curLine != topCursor.line() )
    {
      // partially highlighted last line
      col1 = 0;
      col2 = botCursor.column();
    }

    pos.one = col1;
    pos.two = col2;

    if( pos.equal() )
    {
      return false;
    }
    return true;
  }
};

////////////////////////////////////////////////////////////////////////////////

TextWidget::TextWidget( QWidget *parent, Position pos )
: super(parent,NULL), _columns(0), _lines(0), _xpos(0), _ypos(0),
  _enableRightColMark(true), _rightColMark(72), _shiftKey(false), _cntrlKey(false),
  _editable(false), _enableSelection(false), _pos(pos)
{
  //setEditable(true);
  //setWindowFlags( (Qt::WindowFlags)(windowFlags() | Qt::WA_PaintOnScreen) );
  //setMouseTracking(true);

  setSizePolicy( QSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding) );

  setModel(&NullText,&NullText);
  setModel(&NullInfo);

  _drawSel    = false;
  _selBlockNr = -1;
  _tSel = new QTimer(this);
  _tSel->setSingleShot(true);
  connect( _tSel, SIGNAL(timeout()), this, SLOT(timerSelection()) );

  _tCursor = new QTimer(this);
  _tCursor->setInterval(750);
  _tCursor->setSingleShot(false);
  connect( _tCursor, SIGNAL(timeout()), this, SLOT(timerCursor()) );

  connect(
    this, SIGNAL(customContextMenuRequested(const QPoint&)),
    this, SLOT(contextMenu(const QPoint&)) );
  
  setFocusPolicy( Qt::WheelFocus );
}

TextWidget::~TextWidget()
{
}

void TextWidget::setModel( DiffInfoModel* info )
{
  _info = info;
}

void TextWidget::setModel( TextModel* model, TextModel* diff )
{
  _model   = model;
  _lines   = _model->getLineCnt();
  _columns = _model->getColumnCnt();

  _diff    = diff;

  updateGeometry();
  update();
}

TextModel* TextWidget::getModel() const
{
  return _model;
}

void TextWidget::setModelSize( sc::Size cols, sc::Size lines )
{
  _lines   = lines;
  _columns = cols;

  updateGeometry();

  emit sizeChanged();
}

void TextWidget::setEditable( bool editable )
{
  _editable = editable;
}

bool TextWidget::isEditable() const
{
  return _editable;
}

void TextWidget::setRightColMark( int column )
{
  _rightColMark = column;
}

void TextWidget::enableRightColMark( bool b )
{
  _enableRightColMark = b;
}

void TextWidget::enableSelection( bool enable )
{
  _enableSelection = enable;
}

#if 0
void TextWidget::enableCursor( bool enable )
{
  _enableCursor = enable;
}
#endif

void TextWidget::paintEvent( QPaintEvent *e )
{
  QRect pr = e->rect();
  //printf("prect: %d %d %d %d\n", pr.x(), pr.y(), pr.width(), pr.height() );

  // Qt/X11 does crash when pr is null, todo: why is it null?
  if( pr.isNull() )
    return;

  QPainter pp(this);
  pp.setClipping(true);
  pp.setClipRect(pr);
  pp.setPen(ColorStorage::getColor(ColorTextFg));

  // prepare cursors for highlighted text repaint
  CursorPair cp( _model->getCursor(), _model->getCursor2() );
  cp.order();


  LineConfig lcfg;
  Tab tab(_model->getTabWidth());
  QFontMetrics m(font());
  TextPositionCalculator tpc( m, pr, _xpos, _ypos );

  // get first and last line we need to repaint
  int topLine = tpc.getTopLine();
  int botLine = tpc.getBottomLine();
  //printf("top/bot %d %d\n",topLine,botLine);
  
  for( int curLine = topLine; curLine <= botLine; curLine++ )
  {
    Line line = _model->getLine(curLine);
    Line diff = _diff->getLine(curLine);
    DiffInfo& info = _info->getInfo( line.getBlockNr() );

    IntPair pos0(0,0); // result
    TextHighlightCalculator thc;
    bool highlight = thc.calcHighlight( cp, curLine, tab.calcColumns(line.getStr()), pos0 );

    const PaintLine* pline = PaintLineFactory::create( line.getLine(), pos0.one, pos0.two, lcfg );
    QString          str   = QString::fromUtf8( pline->getPaint() );

    // draw diff/merge background
    QColor background = getBgColor(line.getType(), info.getMergeType());
    pp.setBackgroundColor( background );
    pp.eraseRect( tpc.getLineX(), tpc.getLineY(curLine), pr.width(), tpc.getFontHeight() );
    //printf("erase %d \n", curLine);

    // draw single character diff background
    if( line.isDifference() && ! diff.isEmpty() )
    {
      const PaintLine* plinediff = PaintLineFactory::create( diff.getLine(), pos0.one, pos0.two, lcfg );
      QString          strdiff   = QString::fromUtf8( plinediff->getPaint() );

      unsigned int maxLen  = std::max(str.length(),strdiff.length());
      unsigned int minLen  = std::min(str.length(),strdiff.length());
      
      // we only care for character differences if both lines are nearly equal.
      double equalityFactor = 10.0 / 100.0;  // in percent
      if( maxLen - minLen < maxLen * equalityFactor )
      {
        unsigned int cntDiff = 0;
        for( unsigned int pos = 0; pos < minLen; pos++ )
        {
          if( str.at(pos) != strdiff.at(pos) )
          {
            cntDiff++;
          }
        }

        if( cntDiff <= minLen * equalityFactor )
        {
          for( unsigned int pos = 0; pos < minLen; pos++ )
          {
            if( str.at(pos) != strdiff.at(pos) )
            {
              pp.setBackgroundColor( background.dark(110) );

              int left  = tpc.getCursorX( pos,   str, LinePad );
              int right = tpc.getCursorX( pos+1, str, LinePad );

              pp.eraseRect( left, tpc.getLineY(curLine), right-left, tpc.getFontHeight() );
            }
          }
        }
      }
    }

    // draw highlighted background
    if( highlight )
    {
      int left  = tpc.getCursorX( pos0.one, str, LinePad );
      int right = tpc.getCursorX( pos0.two, str, LinePad );

      pp.setBackgroundColor( ColorStorage::getColor(ColorHighlightedBg) );
      pp.eraseRect( left, tpc.getLineY(curLine), right-left, tpc.getFontHeight() );
    }

    // draw right column marker
    if( _enableRightColMark )
    {
      // fonth width with proportional font??
      int dashX = tpc.getFontWidth() * _rightColMark + 2 - _xpos;
      pp.setPen( ColorStorage::getColor(ColorDashBg) );
      pp.drawLine( dashX, pr.y(), dashX, pr.y()+pr.height() );

      pp.setPen( ColorStorage::getColor(ColorDashFg) );
      for( int d = pr.y()+(_ypos + pr.y())%2; d < pr.y()+pr.height(); d+=2 )
      {
        pp.drawPoint( dashX, d );
      }
    }

    // draw text if any.
    if( ! line.isEmpty() )
    {
      LinePainter p( ColorStorage::getColor(ColorTextFg), ColorStorage::getColor(ColorWhitespaceFg),
        ColorStorage::getColor(ColorHighlightedTextFg), ColorStorage::getColor(ColorHighlightedWhitespaceFg) );
      p.drawLine( pp, tpc.getTextX(LinePad), tpc.getTextY(curLine), pline );
    }

    delete pline;
  }

  if( _enableSelection && _drawSel )
  {
    pp.setBrush(Qt::NoBrush);
    pp.setPen(QColor(0,0,120));
    
    QRect rect = calcBlockRect(tpc);
    rect.adjust( 0, 0, -1, -1 );
    pp.drawRect(rect);
  }

  if( isEditable() && _model->getCursor().isOn() )
  {
    pp.setBrush( Qt::SolidPattern );
    pp.setBrush( QColor(0,0,0) );
    pp.setPen( QColor(0,0,0) );
    pp.setPen( Qt::NoPen );

    QRect rect = calcCursorRect(tpc,_model->getCursor());
    pp.drawRect(rect);
  }
}

QSize TextWidget::sizeHint() const
{
  // fixme: _columns does not include tab adjustments...

  QFontMetrics m(font());
  sc::Size h = m.height()   * _lines;
  sc::Size w = m.maxWidth() * _columns + 2*LinePad;
  return QSize( (int)w, (int)h );
}

void TextWidget::setScrollPosX( int xpos )
{
  ScrollPositionCalculator spc;
  int ox = _xpos;
  int nx = spc.calcPos( ox, xpos, width(), sizeHint().width() );

  if( ox == nx )
  {
    return;
  }

  _xpos = nx;
  super::scroll( ox - _xpos, 0 );

  emit xChanged(_xpos);
  update();
}

void TextWidget::setScrollPosY( int ypos )
{
  ScrollPositionCalculator spc;
  int oy = _ypos;
  int ny = spc.calcPos( oy, ypos, height(), sizeHint().height() );
  _ypos = ny;
  
  if( oy != _ypos )
  {
    super::scroll( 0, oy - _ypos );
    emit yChanged(_ypos);
  }
  update();
}

void TextWidget::timerCursor()
{
  Cursor c = _model->getCursor();
  c.toggle();
  _model->setCursor(c);
  
  update( calcCursorRect(c) );
}

void TextWidget::timerSelection()
{
  _tSelCnt++;
  
 _drawSel = (_tSelCnt%2) == 1;
  
 if( _tSelCnt < 5 )
 {
   _tSel->start(80);
 }  
  
 update( calcBlockRect() );
}

void TextWidget::contextMenu( const QPoint & pos )
{
}

void TextWidget::focusInEvent( QFocusEvent* e )
{
  if( isEditable() )
  {
    _tCursor->start(750);

    Cursor c = _model->getCursor();
    c.setOn();
    _model->setCursor(c);

    update( calcCursorRect(c) );
  }
}

void TextWidget::focusOutEvent( QFocusEvent* e )
{
  if( isEditable() )
  {
    _tCursor->stop();

    Cursor c = _model->getCursor();
    c.setOff();
    _model->setCursor( c );

    update( calcCursorRect(_model->getCursor()) );
  }
}

void TextWidget::setScrollPosCursor( const Cursor& oc, const Cursor& nc )
{
  QFontMetrics m(font());
  TextPositionCalculator tpc( m, rect(), _xpos, _ypos );
  int topLine  = tpc.getTopLine();
  int botLine  = tpc.getBottomLine();
  int leftCol  = tpc.getLeftColumn();
  int rightCol = tpc.getRightColumn();

  int dx = nc.column() - oc.column();
  int dy = nc.line()   - oc.line();

  //printf( "oc(%d) ol(%d) nc(%d) nl(%d)\n", oc.column(), oc.line(), nc.column(), nc.line() );
  //printf( "l(%d) r(%d) t(%d) b(%d)\n", leftCol, rightCol, topLine, botLine );
  //printf( "xpos(%d) ypos(%d) dx(%d) dy(%d)\n", _xpos, _ypos, dx, dy );

  // right
  if( dx > 0 && nc.column() > (rightCol - ScrollBorder) )
  {
    if( dy != 0 ) // line up by cursor left
    {
      setScrollPosX( _xpos + tpc.getFontWidth()*(nc.column()-rightCol+ScrollBorder) );
    }
    else
    {
      setScrollPosX( _xpos + tpc.getFontWidth()*dx );
    }
  }

  // left
  if( dx < 0 && nc.column() <= (leftCol + ScrollBorder) )
  {
    setScrollPosX( _xpos + tpc.getFontWidth()*dx );
  }

  // down
  if( dy > 0 && nc.line() >= (botLine - ScrollBorder) )
  {
    setScrollPosY( _ypos + tpc.getFontHeight()*dy );
  }

  // up
  if( dy < 0 && nc.line() < (topLine + ScrollBorder) )
  {
    setScrollPosY( _ypos + tpc.getFontHeight()*dy );
  }
}

void TextWidget::moveCursorRight()
{
  updateCursor();

  Cursor oc = _model->getCursor();
  Cursor nc = _model->moveCursorRight(!_shiftKey);
  setScrollPosCursor( oc, nc );

  updateCursor();
}

void TextWidget::moveCursorLeft()
{
  updateCursor();

  Cursor oc = _model->getCursor();
  Cursor nc = _model->moveCursorLeft(!_shiftKey);
  setScrollPosCursor( oc, nc );

  updateCursor();
}

void TextWidget::moveCursorDown()
{
  updateCursor();

  Cursor oc = _model->getCursor();
  Cursor nc = _model->moveCursorDown(!_shiftKey);
  setScrollPosCursor( oc, nc );

  updateCursor();
}

void TextWidget::moveCursorUp()
{
  updateCursor();

  Cursor oc = _model->getCursor();
  Cursor nc = _model->moveCursorUp(!_shiftKey);
  setScrollPosCursor( oc, nc );

  updateCursor();
}


//TODO hmm, add cursor parameter?
void TextWidget::updateCursor()
{
  update( calcCursorRect(_model->getCursor()) );
  update( calcHighlightedRect() );
  update();
}


bool TextWidget::event( QEvent* e )
{
  if( e->type() == QEvent::KeyPress )
  {
    QKeyEvent* key = (QKeyEvent*)e;
    switch( key->key() )
    {
    case Qt::Key_Tab:
      {
        keyPressEvent( key );
        return true;
      }
    }
  }
  return super::event(e);
}

void TextWidget::keyPressEvent( QKeyEvent* e )
{
  //printf("key press\n");

  switch( e->key() )
  {
  case Qt::Key_Shift:
    {
      _shiftKey = true;
      e->accept();
      return;
    }
  case Qt::Key_Control:
    {
      _cntrlKey = true;
      e->accept();
      return;
    }
  case Qt::Key_Z: // undo
    {
      if( ! _cntrlKey )
      {
        break;
      }

      _model->undo();
      setModelSize( _model->getColumnCnt(),_model->getLineCnt() );
      update();

      e->accept();
      return;
    }
  case Qt::Key_Y: // redo
    {
      if( ! _cntrlKey )
      {
        break;
      }

      _model->redo();
      setModelSize( _model->getColumnCnt(),_model->getLineCnt() );
      update();

      e->accept();
      return;
    }
  case Qt::Key_Right:
    {
      moveCursorRight();
      e->accept();
      return;
    }
  case Qt::Key_Left:
    {
      moveCursorLeft();
      e->accept();
      return;
    }
  case Qt::Key_Up:
    {
      moveCursorUp();
      e->accept();
      return;
    }
  case Qt::Key_Down:
    {
      moveCursorDown();
      e->accept();
      return;
    }
  case Qt::Key_C:
    {
      if( e->state() == Qt::ControlButton )
      {
        QString hl = _model->getHighlightedText().getStr();
        QApplication::clipboard()->setText( QString::fromUtf8(hl) );
        e->accept();
        return;
      }
      break;
    }
  default:
    {
      break;
    }
  }


  if( ! isEditable() )
  {
    e->ignore();
    return;
  }


  switch( e->key() )
  {
    case Qt::Key_Backspace:
    {
      _model->removeTextLeft();
      setModelSize( _model->getColumnCnt(),_model->getLineCnt() );
      update();

      e->accept();
      return;
    }
  case Qt::Key_Delete:
    {
      _model->removeTextRight();
      setModelSize( _model->getColumnCnt(),_model->getLineCnt() );
      update();

      e->accept();
      return;
    }
  case Qt::Key_V:
    {
      if( e->state() == Qt::ControlButton )
      {
        QString plain("plain");
        QString paste = QApplication::clipboard()->text(plain);

        _model->addText( sc::String(paste.utf8()) );
        setModelSize( _model->getColumnCnt(),_model->getLineCnt() );
        update();

        e->accept();
        return;
      }
    }
  default:
    {
      QByteArray s = e->text().utf8();
      QChar c(s[0]);
      
      // ignore unprintable chars
      if( (s.size() == 1 && !(c.isPrint() || c.isSpace())) )
      {
        break;
      }
      
      sc::String t(s);
      _model->addText(t);
      setModelSize( _model->getColumnCnt(),_model->getLineCnt() );
      update();

      e->accept();
      return;
    }
  }

  e->ignore();
  //printf( "cursor l(%d) c(%d)\n", _model->getCursor().line(), _model->getCursor().column() );
}

void TextWidget::keyReleaseEvent( QKeyEvent* e )
{
  switch( e->key() )
  {
  case Qt::Key_Shift:
    {
      _shiftKey = false;
      break;
    }
  case Qt::Key_Control:
    {
      _cntrlKey = false;
      break;
    }
  default:
    {
      e->ignore();
      return;
    }
  }
  e->accept();
}

void TextWidget::mousePressEvent( QMouseEvent* e )
{
  if( e->button() == Qt::LeftButton )
  {
    Cursor c  = calcCursorPos( e->x(), e->y() );
    Cursor nc = _model->calcNearestCursorPos(c);

    if( ! _shiftKey )
    {
      // no shift key, clear highlighted
      update( calcCursorRect(_model->getCursor()) );
      update( calcHighlightedRect() );
      _model->setCursor( nc );
      _model->setCursor2( nc );
      update( calcCursorRect(_model->getCursor()) );
      update();
    }
    else
    {
      // shift key
      update( calcHighlightedRect() );
      _model->setCursor2( nc );
      update( calcHighlightedRect() );
      update();
    }
  }
  else if( e->button() == Qt::RightButton )
  {
    Cursor c = calcCursorPos( e->x(), e->y() );
    emit mouseLine( c.line() );
  }
}

void TextWidget::mouseReleaseEvent( QMouseEvent* e )
{
  super::mouseReleaseEvent(e);
}

void TextWidget::mouseDoubleClickEvent( QMouseEvent* e )
{
  if( e->button() == Qt::LeftButton )
  {
    Cursor c = calcCursorPos( e->x(), e->y() );
    const Line& l = _model->getLine(c.line());

    if( ! _shiftKey )
    {
      // is it a non selectable block?
      if( l.getType() == ctCommon || l.getType() == ctNop )
      {
        return;
      }

      // select block
      setBlockSelection( l.getBlockNr() );
      emit blockChanged( l.getBlockNr() );
    }
  }
}

void TextWidget::mouseMoveEvent( QMouseEvent* e )
{
  // TODO handle scroll pos when the cursor is moved outside of the scroll rect

  QFontMetrics m(font());
  TextPositionCalculator tpc( m, rect(), _xpos, _ypos );
  int topLine  = tpc.getTopLine();
  int botLine  = tpc.getBottomLine();
  int leftCol  = tpc.getLeftColumn();
  int rightCol = tpc.getRightColumn();


  if( e->state() == Qt::LeftButton )
  {
    Cursor c = calcCursorPos( e->x(), e->y() );

    Tab tab(_model->getTabWidth());
    c.maxColumn( tab.calcColumns(_model->getLine(c.line()).getStr()) );  // hmm

    if( c.column() > rightCol-ScrollBorder )
    {
      setScrollPosX( _xpos+tpc.getFontWidth() );
    }
    if( c.column() < leftCol+ScrollBorder )
    {
      setScrollPosX( _xpos-tpc.getFontWidth() );
    }
    if( c.line() > botLine-ScrollBorder )
    {
      setScrollPosY( _ypos+tpc.getFontHeight() );
    }
    if( c.line() < topLine+ScrollBorder )
    {
      setScrollPosY( _ypos-tpc.getFontHeight() );
    }

    update( calcHighlightedRect() );
    _model->setCursor2( c );
    update( calcHighlightedRect() );
    update();
  }
}

// todo store selection info in "model" (DiffInfo!?)
void TextWidget::setBlockSelection( int block )
{
  update( calcBlockRect() );

  _drawSel    = true;
  _selBlock   = _model->getBlockInfo( block );
  _selBlockNr = block;

  update( calcBlockRect() );
  
  if(_enableSelection)
  {
    //_tSelCnt = 0;
    //_tSel->start(100);
  }
}

void TextWidget::clearBlockSelection()
{
  _drawSel = false;

  update( calcBlockRect() );
}

int TextWidget::getBlockSelection() const
{
  return _selBlockNr;
}

QRect TextWidget::calcCursorRect( const TextPositionCalculator& calc, const Cursor& c )
{
  const Line&      line  = _model->getLine(c.line());
  const PaintLine* pline = PaintLineFactory::create( line.getLine(), c.column(),
    c.column(), LineConfig() );

  int x = calc.getCursorX( c.column(), QString::fromUtf8(pline->getPaint()), LinePad );
  int y = calc.getLineY(c.line());
  int w = 2;
  int h = calc.getFontHeight();
  
  return QRect( x, y, w, h );
}

QRect TextWidget::calcCursorRect( const Cursor& c )
{
  QFontMetrics m(font());
  TextPositionCalculator tpc( m, rect(), _xpos, _ypos );
  return calcCursorRect(tpc,c);
}

QRect TextWidget::calcBlockRect( const TextPositionCalculator& calc )
{
  const BlockInfo& info = _model->getBlockInfo( _selBlockNr );

  return QRect(
    calc.getTextX(), calc.getLineY(info.getStart()),
    std::max(width(),sizeHint().width()), calc.getHeight(info.getLength()) );
}

QRect TextWidget::calcBlockRect()
{
  QFontMetrics m(font());
  TextPositionCalculator tpc( m, rect(), _xpos, _ypos );
  return calcBlockRect(tpc);
}

QRect TextWidget::calcHighlightedRect()
{
  QFontMetrics m(font());
  TextPositionCalculator tpc( m, rect(), _xpos, _ypos );

  CursorPair cp( _model->getCursor(), _model->getCursor2() );
  cp.order();

  int top = tpc.getLineY( cp.getOne().line()-1 );
  int bot = tpc.getLineY( cp.getTwo().line()+1 );

  return QRect( tpc.getTextX(), top, sizeHint().width(), bot-top );
}

int TextWidget::calcLineY( int line, bool optimize )
{
  // we pass as scroll position 0,0 because we want absolute values
  QFontMetrics m(font());
  TextPositionCalculator tpc( m, rect(), 0, 0 );

  // let's have some space between the widgets top and the target
  // line, as long as we have space for it...
  if( optimize && (height() > (2*ScrollBorder+1)*m.height()) )
  {
    line -= ScrollBorder;
  }

  return tpc.getLineY( line );
}

Cursor TextWidget::calcCursorPos( int x, int y )
{
  QFontMetrics m( font() );
  TextPositionCalculator tpc( m, rect(), _xpos, _ypos );

  int lineno = ( y + _ypos ) / m.height();

  const Line&      line  = _model->getLine(lineno);
  const PaintLine* pline = PaintLineFactory::create( line.getLine(), 0, 0, LineConfig() );

  int colno  = tpc.getColumn( x + _xpos, QString::fromUtf8(pline->getPaint()) );

  //printf( "c l(%d) c(%d)\n", lineno, colno );

  return Cursor( lineno, colno );
}

void TextWidget::dragEnterEvent(QDragEnterEvent* e)
{
  if( e->mimeData()->hasText() || e->mimeData()->hasUrls() )
  {
    e->acceptProposedAction();
  }
}

void TextWidget::dropEvent(QDropEvent* e)
{
  if( e->type() == QEvent::Drop )
  {
    if( e->mimeData()->hasUrls() )
    {
      QList<QUrl> urls = e->mimeData()->urls();
      emit fileDropped( urls.first().toLocalFile() );
    }
    else if( e->mimeData()->hasText() )
    {
      emit fileDropped( e->mimeData()->text() );
    }
  }
}

const QColor& TextWidget::getConflictBgColor( ConflictType t )
{
  switch( t )
  {
    case ctConflictAll:
    case ctConflictAllEmpty:
    {
      return ColorStorage::getColor(ColorConflictBgAll);
    }
    case ctConflictLatestModified:
    case ctConflictLatestModifiedEmpty:
    {
      return ColorStorage::getColor(ColorConflictBgLatestModified);
    }
    case ctConflictModifiedLatest:
    case ctConflictModifiedLatestEmpty:
    {
      return ColorStorage::getColor(ColorConflictBgModifiedLatest);
    }
    case ctConflictOriginal:
    case ctConflictOriginalEmpty:
    {
      return ColorStorage::getColor(ColorConflictBgOriginal);
    }
    case ctNop:
    {
      return ColorStorage::getColor(ColorNopBg);
    }
    case ctConflictAllMerged:
    case ctConflictLatestModifiedMerged:
    case ctConflictModifiedLatestMerged:
    case ctConflictOriginalMerged:
    case ctConflictAllEmptyMerged:
    case ctConflictLatestModifiedEmptyMerged:
    case ctConflictModifiedLatestEmptyMerged:
    case ctConflictOriginalEmptyMerged:
    {
      return ColorStorage::getColor(ColorMergedBg);
    }
    default:
    {
      return ColorStorage::getColor(ColorNormalBg);
    }
  }
}

const QColor& TextWidget::getMergeBgColor( MergeType t )
{
  return ColorStorage::getColor(ColorMergedBg);
}

const QColor& TextWidget::getBgColor( ConflictType ct, MergeType mt )
{
  if( ct == ctNop )
    return getConflictBgColor(ct);
    
  if( mt == msModified && _pos == Left )
    return getMergeBgColor(mt);
  else if( mt == msLatest && _pos == Right ) 
    return getMergeBgColor(mt);
  else
    return getConflictBgColor(ct);
}
