/* ====================================================================
 * Copyright (c) 2003-2006, 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 "PainterSetup.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 <qapplication.h>
#include <qtimer.h>
#include <qclipboard.h>
#include <qdragobject.h>


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



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;
  }
};


const QColor& 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);
    }
  }
}


TextWidget::TextWidget( QWidget *parent, const char *name )
: super( parent, name ), _columns(0), _lines(0), _xpos(0), _ypos(0),
  _enableRightColMark(true), _rightColMark(72),
  _shiftKey(false), _cntrlKey(false), _editable(false), _enableSelection(false)
{
  //setEditable(true);
  //setWFlags( Qt::WStyle_NormalBorder );
  //setMouseTracking(true);

  setSizePolicy( QSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding) );
  setBackgroundMode( Qt::NoBackground );

  setModel(&NullText,&NullText);

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

  _tCursor = new QTimer(this);
  connect( _tCursor, SIGNAL(timeout()), this, SLOT(timerCursor()) );

  setFocusPolicy( QWidget::WheelFocus );

  //setAcceptDrops(true);
}


TextWidget::~TextWidget()
{
}

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\n", pr.x(), pr.width() );

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

  PainterSetup ps(this,pr);
  QPainter& pp = ps.getPainter();
  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();

  for( int curLine = topLine; curLine <= botLine; curLine++ )
  {
    Line line = _model->getLine(curLine);
    Line diff = _diff->getLine(curLine);

    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
    pp.setBackgroundColor( getConflictBgColor(line.getType()) );
    pp.eraseRect( tpc.getLineX(), tpc.getLineY(curLine), pr.width(), tpc.getFontHeight() );

    // 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 <= maxLen * equalityFactor )
        {
          for( unsigned int pos = 0; pos < maxLen; pos++ )
          {
            if( str.at(pos) != strdiff.at(pos) )
            {
              pp.setBackgroundColor( getConflictBgColor(line.getType()).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 )
  {
    QPen pen( QColor(0,0,120) );
    pen.setWidth(1);
    pp.setPen( pen );
    pp.drawRect( calcBlockRect(tpc) );
  }

  if( isEditable() && _model->getCursor().isOn() )
  {
    pp.setPen( QColor(0,0,0) );
    pp.setRasterOp( Qt::NotXorROP );
    pp.drawRect( calcCursorRect(tpc,_model->getCursor()) );
  }
}

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);
}

void TextWidget::setScrollPosY( int ypos )
{
  ScrollPositionCalculator spc;
  int oy = _ypos;
  int ny = spc.calcPos( oy, ypos, height(), sizeHint().height() );

  if( oy == ny )
  {
    return;
  }

  _ypos = ny;
  super::scroll( 0, oy - _ypos );

  emit yChanged(_ypos);
}

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

  update( calcCursorRect(c) );
}

void TextWidget::timerSelection()
{
  _tSelCnt++;

 _drawSel = (_tSelCnt%2) == 1;

 int t = 80;
 if( _tSelCnt < 5 )
 {
  _tSel->start( t, true );
 }

 update( calcBlockRect() );
}

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() );
}


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:
    {
      //printf("key right\n");
      moveCursorRight();
      e->accept();
      return;
    }
  case Qt::Key_Left:
    {
      //printf("key left\n");
      moveCursorLeft();
      e->accept();
      return;
    }
  case Qt::Key_Up:
    {
      //printf("key up\n");
      moveCursorUp();
      e->accept();
      return;
    }
  case Qt::Key_Down:
    {
      //printf("key down\n");
      moveCursorDown();
      e->accept();
      return;
    }
  case Qt::Key_C:
    {
      if( e->state() == Qt::ControlButton )
      {
        QString hl = _model->getHighlightedText().getStr();
#if 1
        QTextDrag* qtd = new QTextDrag( QString::fromUtf8(hl) );
#else
        // this doesn't work on windows.. :(
        QTextDrag* qtd = new QTextDrag( hl );
        qtd->setSubtype( "utf8" );
#endif
        QApplication::clipboard()->setData( qtd );

        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 )
      {
        QCString plain("plain");
        QString  paste = QApplication::clipboard()->text( plain, QClipboard::Clipboard );

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

        e->accept();
        return;
      }
    }
  default:
    {
      QCString s = e->text().utf8();
      if( s.size() == 1 && ! QChar(s[0]).isPrint() )
      {
        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()) );
    }
    else
    {
      // shift key
      update( calcHighlightedRect() );
      _model->setCursor2( nc );
      update( calcHighlightedRect() );
    }
  }
  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() );

      _tSelCnt = 0;
      _tSel->start( 250, true );
    }
  }
}

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() );
  }
}

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

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

  update( calcBlockRect() );
}

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 )
{
  return QRect(
    calc.getTextX(), calc.getLineY(_selBlock.getStart()),
    std::max(width(),sizeHint().width()), calc.getHeight(_selBlock.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)
{
  e->accept( QUriDrag::canDecode(e) || QTextDrag::canDecode(e) );
}

void TextWidget::dropEvent(QDropEvent* e)
{
  if( e->type() == QEvent::Drop )
  {
    QDropEvent* de = (QDropEvent*)e;

    QStringList drop;
    if( QUriDrag::decodeLocalFiles(de,drop) )
    {
      QString dropped = drop.first();
      emit fileDropped(dropped);
      return;
    }

    QString dropped;
    if( QTextDrag::decode(de, dropped) )
    {
      emit fileDropped(dropped);
      return;
    }
  }
}


syntax highlighted by Code2HTML, v. 0.9.1