/****************************************************************************
**
** 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 "config.h"

#include <qapplication.h>
#include <qtimer.h>
#include <qfile.h>
#include <qpainter.h>
#include <qmessagebox.h>
#include <qfont.h>
#include <qwhatsthis.h>
#include <qpushbutton.h>
#include <qlayout.h>
#include <qsplitter.h>
#include <qsizegrip.h>

#include "ResolvDialogImpl.h"
#include "globals.h"
#include "FileTableImpl.h"

//FileTable ID's
const int LEFT = 0;
const int RIGHT = 1;
const int MERGE = 2;

//colors for the tables
const QColor sourceBg(255,255,255);//white ffffff
const QColor mergeBg(255,255,255);//white ffffff
const QColor numBg(140,140,140);//dark gray 8c8c8c
const QColor numSelect(0,255,0);//green 00ff00
const QColor conflict(255,80,60);//light red ff503c
const QColor conflictSelect(120,160,215);//light blue 78a0d7 | selected 78a0d7
const QColor merge(60,225,255);//light blue 3cffff
const QColor marker(140,140,140);//dark gray 8c8c8c
const QColor fillColor(240,240,240);//light gray dcdcdc

//The width of the splitter
const unsigned int ResolvDialogImpl::SplitterStyle::SPLITTER_WIDTH = 4;

ResolvDialogImpl::Block::Block() :
   from(-1),
   to(-1),
   mergeMap(NULL) {
   fileMap[LEFT] = NULL;
   fileMap[RIGHT] = NULL;
}
ResolvDialogImpl::Block::~Block() {
   delete [] fileMap[LEFT];
   delete [] fileMap[RIGHT];
   delete [] mergeMap;
}

ResolvDialogImpl::CBlock::CBlock(int _from, int _to) :
   from(_from), to(_to) {
}

ResolvDialogImpl::ResolvDialogImpl( QString fileName,
      const QIconSet & whatsThisIconSet,
      QWidget* parent,
      WFlags fl,
      QString revision)
   : ResolvDialog( LookAndFeel::g_b0AsParent ? 0 : parent, "ResolvDialog", fl)
{
   setCaption (tr("Resolve") + " - " + fileName.mid(fileName.findRev("/")+1) + " " + revision);

   Splitter->setStyle(new SplitterStyle(this));

   TextLabel1->setText(tr("Sandbox (partly merged)"));
   TextLabel3->setText(tr("Result (preview)"));

   init(whatsThisIconSet,fileName);

   if (setFile()) {
      show();
      render();
   } else close();
}

ResolvDialogImpl::~ResolvDialogImpl() {
}

void ResolvDialogImpl::init(const QIconSet & whatsThisIconSet,QString & fileName) {

   m_blockVector.setAutoDelete(false);//there are multiple references to the same object
   m_blockTracker.setAutoDelete(true);//so we got this
   m_conflictVector.setAutoDelete(true);

   m_currentBlock = NULL;

   m_pWhatsThis->setIconSet(whatsThisIconSet);
#ifdef Q_WS_MAC
  m_pWhatsThis->setMaximumWidth(m_pWhatsThis->height() * 2);
#else
  m_pWhatsThis->setMaximumWidth(m_pWhatsThis->height());
#endif
   ButtonLayout->addWidget(new QSizeGrip(this),0,Qt::AlignRight|Qt::AlignBottom);
   ButtonLabel->setAlignment(Qt::AlignHCenter|AlignVCenter);

   m_stop = false;
   m_fileName = fileName;
   installEventFilter( this);//get rid of the anoying whatsthis popup menu on a rightclick

   if (!Font::g_diff.isEmpty()) {
      QFont f;
      if (f.fromString(Font::g_diff)) {
	 m_FileLeft->setFont(f);
	 m_FileRight->setFont(f);
	 m_FileMerged->setFont(f);
      } else Font::g_diff = QString::null;
   }

   m_FileLeft->setBackground(sourceBg);
   m_FileRight->setBackground(sourceBg);
   m_FileMerged->setBackground(mergeBg);

   m_FileLeft->setID(LEFT);
   m_FileRight->setID(RIGHT);
   m_FileMerged->setID(MERGE);

   connect(m_FileLeft,SIGNAL(xScrollValueChanged(int)),m_FileRight,SLOT(setXScrollPos(int)));
   connect(m_FileLeft,SIGNAL(xScrollValueChanged(int)),m_FileMerged,SLOT(setXScrollPos(int)));
   connect(m_FileRight,SIGNAL(xScrollValueChanged(int)),m_FileLeft,SLOT(setXScrollPos(int)));
   connect(m_FileRight,SIGNAL(xScrollValueChanged(int)),m_FileMerged,SLOT(setXScrollPos(int)));
   connect(m_FileMerged,SIGNAL(xScrollValueChanged(int)),m_FileLeft,SLOT(setXScrollPos(int)));
   connect(m_FileMerged,SIGNAL(xScrollValueChanged(int)),m_FileRight,SLOT(setXScrollPos(int)));

   connect(m_FileLeft,SIGNAL(yScrollValueChanged(int)),m_FileRight,SLOT(setYScrollPos(int)));
   connect(m_FileRight,SIGNAL(yScrollValueChanged(int)),m_FileMerged,SLOT(setYScrollPos(int)));
   connect(m_FileMerged,SIGNAL(yScrollValueChanged(int)),m_FileLeft,SLOT(setYScrollPos(int)));

   connect(m_FileLeft,SIGNAL(focused(int,int)),m_FileRight,SLOT(setFocus(int,int)));
   connect(m_FileLeft,SIGNAL(focused(int,int)),m_FileMerged,SLOT(setFocus(int,int)));
   connect(m_FileRight,SIGNAL(focused(int,int)),m_FileLeft,SLOT(setFocus(int,int)));
   connect(m_FileRight,SIGNAL(focused(int,int)),m_FileMerged,SLOT(setFocus(int,int)));
   connect(m_FileMerged,SIGNAL(focused(int,int)),m_FileLeft,SLOT(setFocus(int,int)));
   connect(m_FileMerged,SIGNAL(focused(int,int)),m_FileRight,SLOT(setFocus(int,int)));

   connect(m_FileLeft,SIGNAL(unselected(int,int)),this,SLOT(unselected(int,int)));
   connect(m_FileLeft,SIGNAL(unSelectedBlock(int,int,int)),this,SLOT(unSelectedBlock(int,int,int)));
   connect(m_FileLeft,SIGNAL(selectionChanged(int,int,QString)),this,SLOT(selected(int,int,QString)));
   connect(m_FileLeft,SIGNAL(selectedBlock(int,int,int)),this,SLOT(selectedBlock(int,int,int)));

   connect(m_FileRight,SIGNAL(unselected(int,int)),this,SLOT(unselected(int,int)));
   connect(m_FileRight,SIGNAL(unSelectedBlock(int,int,int)),this,SLOT(unSelectedBlock(int,int,int)));
   connect(m_FileRight,SIGNAL(selectionChanged(int,int,QString)),this,SLOT(selected(int,int,QString)));
   connect(m_FileRight,SIGNAL(selectedBlock(int,int,int)),this,SLOT(selectedBlock(int,int,int)));

   connect(m_FileMerged,SIGNAL(clear(int,int)),this,SLOT(clear(int,int)));
   connect(m_FileMerged,SIGNAL(clicked(int,int)),this,SLOT(clear(int,int)));

   QWhatsThis::add( m_FileLeft,
	 tr( "This file holds the state the file was in before the conflict causing cvs call.\n"
	       "\n"
	       "Resolve Dialog howto:\n"
	       "\n"
	       "left click into the line-number bar:\n"
	       "-on a selected line: 	deselects the whole block\n"
	       "-on an unselected line: 	select the whole block\n"
	       "\n"
	       "left click into a conflict block:\n"
	       "select/unselect a line\n"
	       "\n"
	       "right click into a conflict block:\n"
	       "selects the region between the last left click and the current click position\n"
	       "\n"
	       "Keyboard:\n"
	       "move Focus:		tab/<shift>tab\n"
	       "select/unselect line:	spacebar\n"
	       "select region:		<shift>spacebar\n"
	       "goto next/previous line:	arrow down/up\n"
	       "goto next/previous block:	<ctrl>arrow down/up\n"
	       "scroll half page up/down:	page up/down\n"
	       "goto beginning/end:	Home/End\n"
	       "\n"
	       "Special feature for the merged file:\n"
	       "remove a line:	Delete\n"
	       "\n"
	       "=======================================\n"
	       "Colors:\n"
	       "\n"
	       "white: non-conflicting code segments\n"
	       "dark gray: conflict markers and line numbers\n"
	       "red: conflicting code parts\n"
	       "light gray: space added for clarity\n"
	       "blue: selected lines" ) );
   QWhatsThis::add( m_FileRight,
	 tr( "This file represents the part of the file that was merged in by the conflict-causing cvs call.\n"
	       "\n"
	       "Resolve Dialog howto:\n"
	       "\n"
	       "left click into the line-number bar:\n"
	       "-on a selected line: 	deselects the whole block\n"
	       "-on an unselected line: 	select the whole block\n"
	       "\n"
	       "left click into a conflict block:\n"
	       "select/unselect a line\n"
	       "\n"
	       "right click into a conflict block:\n"
	       "selects the region between the last left click and the current click position\n"
	       "\n"
	       "Keyboard:\n"
	       "move Focus:		tab/<shift>tab\n"
	       "select/unselect line:	spacebar\n"
	       "select region:		<shift>spacebar\n"
	       "goto next/previous line:	arrow down/up\n"
	       "goto next/previous block:	<ctrl>arrow down/up\n"
	       "scroll half page up/down:	page up/down\n"
	       "goto beginning/end:	Home/End\n"
	       "\n"
	       "Special feature for the merged file:\n"
	       "remove a line:	Delete\n"
	       "\n"
	       "=======================================\n"
	       "Colors:\n"
	       "\n"
	       "white: non-conflicting code segments\n"
	       "dark gray: conflict markers and line numbers\n"
	       "red: conflicting code parts\n"
	       "light gray: space added for clarity\n"
	       "blue: selected lines" ) );
   QWhatsThis::add( m_FileMerged,
	 tr( "This file holds the merged state. The conflict markers of solved conflicts will be removed when the file is saved.\n"
	       "\n"
	       "Resolve Dialog howto:\n"
	       "\n"
	       "left click into the line-number bar:\n"
	       "-on a selected line: 	deselects the whole block\n"
	       "-on an unselected line: 	select the whole block\n"
	       "\n"
	       "left click into a conflict block:\n"
	       "select/unselect a line\n"
	       "\n"
	       "right click into a conflict block:\n"
	       "selects the region between the last left click and the current click position\n"
	       "\n"
	       "Keyboard:\n"
	       "move Focus:		tab/<shift>tab\n"
	       "select/unselect line:	spacebar\n"
	       "select region:		<shift>spacebar\n"
	       "goto next/previous line:	arrow down/up\n"
	       "goto next/previous block:	<ctrl>arrow down/up\n"
	       "scroll half page up/down:	page up/down\n"
	       "goto beginning/end:	Home/End\n"
	       "\n"
	       "Special feature for the merged file:\n"
	       "remove a line:	Delete\n"
	       "\n"
	       "============================================\n"
	       "Colors:\n"
	       "\n"
	       "white: non-conflicting code segments\n"
	       "dark gray: conflict markers and line numbers (will not be saved if conflict was solved)\n"
	       "light blue: Preview of conflicting segment as it will be saved" ) );
}

bool ResolvDialogImpl::eventFilter( QObject *o, QEvent *e ) {
   if ( e->type() == QEvent::ContextMenu ) {
      return TRUE; // eat event
   } else {
      // standard event processing
      return QWidget::eventFilter( o, e );
   }
}

void ResolvDialogImpl::render() {
   m_FileLeft->render();
   m_FileRight->render();
   m_FileMerged->render();
   m_FileLeft->setXScrollPos(0);
   m_FileLeft->setYScrollPos(0);
}

void ResolvDialogImpl::cancelRead() {
   m_stop = true;
   m_FileLeft->cancelRead();
   m_FileRight->cancelRead();
   m_FileMerged->cancelRead();
}

bool ResolvDialogImpl::setFile() {

   QApplication::setOverrideCursor(waitCursor);
   QString fName = m_fileName.mid(m_fileName.findRev("/")+1);
   ProgressDialog* progressDlg = new ProgressDialog(this, "ProgressDialog", true,
	 Qt::WStyle_Customize | Qt::WStyle_NoBorder | WDestructiveClose);
   connect(progressDlg->CancelButton,SIGNAL(clicked()),this,SLOT(cancelRead()));
   progressDlg->m_InfoMessage->setText(tr("Please wait ...")+"\n");
   progressDlg->m_InfoText->setText(tr("Analyzing file: ")+fName);
   progressDlg->show();
   progressDlg->update();

   FileTableImpl* fL = m_FileLeft;
   FileTableImpl* fR = m_FileRight;
   FileTableImpl* fM = m_FileMerged;

   QString parse = "";
   int mode = 0;
   int lineCount = 0;
   int confCount = 0;

   QFile f;
   QString line;

   f.setName(m_fileName);
   if(f.open(IO_ReadOnly)) {
      QTextStream textStream(&f); 

      m_linesTotal = 0;
      int blockStart = 0;
      int lineNum = 0;
    
      int leftLineCount = 0;
      int rightLineCount = 0;
      int mergeLineCount = 0;
      QString leftBlock = "";
      QString rightBlock = "";
      QString mergeBlock = "";
      QString conflictSeparator;

      while(!textStream.atEnd()) {
	 line = textStream.readLine();
	 m_linesTotal++;
      }
      f.reset();
      fL->reserveLines(m_linesTotal);
      fR->reserveLines(m_linesTotal);
      fM->reserveLines(m_linesTotal);

      while(!textStream.atEnd()) {

	 qApp->processEvents();
	 if (m_stop) break;

	 line = textStream.readLine();
	 switch (mode) {
	    case 0: {
	       if (line.startsWith("<<<<<<<")) {
		  QString numString;
		  if (!parse.isEmpty()) {
		     numString = QString::number(lineNum+1);
		     fL->addLine(numString,parse,true,numSelect,numBg);
		     fR->addLine(numString,parse,true,numSelect,numBg);
		     fM->addLine(numString,parse,true,numSelect,numBg);
		  }
		  numString = QString::number(lineCount+1);
		  fL->addLine(numString,line+"\n",true,numSelect,numBg,marker,marker);
		  fM->addLine(numString,line+"\n",true,numSelect,numBg,marker,marker);

		  blockStart = lineCount+1;
		  addLine(lineCount);
		  initBlock();
		  mode = 1;
		  ++confCount;
		  parse = "";
	       } else {
		  parse += line+"\n";
		  addLine(lineCount);
	       }
	       break;
	    }
	    case 1: {
	       if (line.startsWith("=======")) {
		  QString numString = QString::number(lineCount+1);
		  conflictSeparator = line+"\n";
		  fR->addLine(numString,conflictSeparator,true,numSelect,numBg,marker,marker);
		  leftBlock += parse;
		  mergeBlock += parse+line+"\n";
		  mode = 2;
		  addLine(lineCount);
		  parse = "";
		  lineNum = lineCount+1;
	       } else {
		  parse += line+"\n";
		  addLine(lineCount);
		  leftLineCount++;
		  mergeLineCount++;
	       }
	       break;
	    }
	    case 2: {
	       if (line.startsWith(">>>>>>>")) {
		  TextLabel2->setText(tr("Revision: ")+line.mid(8));
		  rightBlock += parse;

		  if (leftBlock.isEmpty()) {
		     leftBlock += "\n";
		     leftLineCount++;
		  }
		  if (rightBlock.isEmpty()) {
		     rightBlock += "\n";
		     rightLineCount++;
		  }

		  QString numString = QString::number(blockStart+1);
		  QString xNumString = QString::number(lineNum+1);
		  fL->addLine(numString,leftBlock,false,numSelect,numBg,conflictSelect,conflict);
		  fR->addLine(xNumString,rightBlock,false,numSelect,numBg,conflictSelect,conflict);
		  fM->addLine(numString,mergeBlock,true,numSelect,numBg,merge,merge);

		  leftBlock = "";
		  rightBlock = "";

		  int i = 0;
		  int l = 0;
		  int r = 0;
		  for (i=leftLineCount;i<=mergeLineCount;i++) {
		     leftBlock += "\n";
		     l++;
		  }
		  for (i=rightLineCount;i<=mergeLineCount;i++) {
		     rightBlock += "\n";
		     r++;
		  }

		  fL->addLine("",leftBlock,true,numSelect,numBg,fillColor,fillColor);
		  fR->addLine("",rightBlock,true,numSelect,numBg,fillColor,fillColor);

		  numString = QString::number(lineCount+1);
		  xNumString = QString::number(lineNum);
		  fL->addLine(xNumString,conflictSeparator,true,numSelect,numBg,marker,marker);
		  fR->addLine(numString,line+"\n",true,numSelect,numBg,marker,marker);
		  fM->addLine(numString,line+"\n",true,numSelect,numBg,marker,marker);

		  setCurrentBlock(blockStart,lineCount-1);
		  m_currentBlock = NULL;
		  addLine(lineCount);

		  leftBlock = "";
		  rightBlock = "";
		  mergeBlock = "";
		  leftLineCount = 0;
		  rightLineCount = 0;
		  mergeLineCount = 0;
		  lineNum = lineCount+1;
		  mode = 0;
		  parse = "";
	       } else {
		  parse += line+"\n";
		  mergeBlock += line+"\n";
		  addLine(lineCount);
		  rightLineCount++;
		  mergeLineCount++;
	       }
	       break;
	    }
	 }
	 lineCount++;
      }

      if (!parse.isEmpty() ) {
	 QString numString = QString::number(lineNum+1);
	 fL->addLine(numString,parse,true,numSelect,numBg);
	 fR->addLine(numString,parse,true,numSelect,numBg);
	 fM->addLine(numString,parse,true,numSelect,numBg);
      }

      f.close();
   }
   ButtonLabel->setText(tr("conflicts")+" "+QString::number(confCount));
   progressDlg->hide();
   delete(progressDlg);

   update();
   QApplication::restoreOverrideCursor();

   if ( (m_linesTotal != lineCount) || (mode != 0) ) {//parser error
      QString msg = (mode!=0) ? tr("Unbalanced conflict markers, cannot resolve") : tr("Parser error");
      QMessageBox::critical(this, tr("Error"), msg,
	    QMessageBox::Ok,QMessageBox::NoButton,QMessageBox::NoButton);
      return false;
   }

   if (m_stop) return false;
   else return true;
}

void ResolvDialogImpl::addLine(unsigned int line) {
   if (line >= m_blockVector.size()) m_blockVector.resize(m_blockVector.size()+100);
   m_blockVector.insert(line,m_currentBlock);
}

void ResolvDialogImpl::initBlock() {
   m_currentBlock = new Block();
   m_blockTracker.push(m_currentBlock);
}

void ResolvDialogImpl::setCurrentBlock(int from, int to) {
   m_currentBlock->from = from;
   m_currentBlock->to = to;
  
   m_currentBlock->fileMap[LEFT] = new int[to-from+1];
   m_currentBlock->fileMap[RIGHT] = new int[to-from+1];
   m_currentBlock->mergeMap = new Reference[to-from+1];

   m_currentBlock->pos = 0;

   CBlock* cbl = new CBlock(from,to);
   if (m_conflictVector.count() >= m_conflictVector.size()) m_conflictVector.resize(m_conflictVector.size()+10);
   m_conflictVector.insert(m_conflictVector.count(),cbl);
}

int ResolvDialogImpl::appendLine(int fileTable, int line) {

   Block* hlpBlock = m_blockVector.at((unsigned int)line);
   int mergePos = hlpBlock->pos;

   hlpBlock->mergeMap[mergePos].fileTable = fileTable;
   hlpBlock->mergeMap[mergePos].line = line;

   hlpBlock->fileMap[fileTable][line-hlpBlock->from] = mergePos;
   hlpBlock->pos++;

   return mergePos;
}

void ResolvDialogImpl::clear(int, int row) {
   Block* hlp = m_blockVector.at((unsigned int)row);
   if (hlp) {
      if (row >= hlp->from+hlp->pos) return;
      int pos = row-hlp->from;
      if (hlp->mergeMap[pos].fileTable == LEFT) {
	 m_FileLeft->setSelected(hlp->mergeMap[pos].line,false);
      } else {
	 m_FileRight->setSelected(hlp->mergeMap[pos].line,false);
      }
      unselected(hlp->mergeMap[pos].fileTable,hlp->mergeMap[pos].line);
   }
}

void ResolvDialogImpl::selected(int id,int row,QString txt) {

   Block* hlp = m_blockVector.at((unsigned int)row);
   if (hlp) {
      int pos = appendLine(id,row);
      if (!pos) {//clear Block
	 int i = 0;
	 for (i=hlp->from;i<=hlp->to;i++) {
	    hlp->oriTxt += m_FileMerged->getText(i)+"\n";
	    m_FileMerged->setText(i,"");
	 }
      }
      m_FileMerged->setText(hlp->from+pos,txt);
   }
}

void ResolvDialogImpl::unselected(int id,int row) {

   Block* hlp = m_blockVector.at((unsigned int)row);
   if (hlp) {
      int pos = hlp->fileMap[id][row-hlp->from];
      int i = 0;
      int j = 0;
      m_FileMerged->move(pos+hlp->from+1,-1,hlp->pos-pos-1);
      for (i=pos+hlp->from,j=pos;j<hlp->pos-1;i++,j++) {
	 hlp->mergeMap[j] = hlp->mergeMap[j+1];
	 hlp->fileMap[hlp->mergeMap[j].fileTable][hlp->mergeMap[j].line-hlp->from]--;
      }
      hlp->pos--;
      if (hlp->pos == 0) {
	 i = hlp->from;
	 j = 0;
	 while ( (j = hlp->oriTxt.find("\n"))>-1) {
	    m_FileMerged->setText(i++,hlp->oriTxt.mid(0,j));
	    hlp->oriTxt = hlp->oriTxt.mid(j+1);
	 }
	 hlp->oriTxt = "";
      } else {
	 m_FileMerged->setText(i,"");
      }
   }
}

void ResolvDialogImpl::selectedBlock(int id, int from, int to) {

   QString txt;
   int range = to-from+1;

   Block* hlp = m_blockVector.at((unsigned int)from);
   if (!hlp->pos) {//clear
      int i = 0;
      for (i=hlp->from;i<hlp->from+range;i++) {
	 hlp->oriTxt += m_FileMerged->getText(i)+"\n";
      }
      for (i=hlp->from+range;i<=hlp->to;i++) {
	 hlp->oriTxt += m_FileMerged->getText(i)+"\n";
	 m_FileMerged->setText(i,"");
      }
   }

   int hFrom = hlp->from;

   switch( id) {
      case LEFT: {
	 for (int i = from;i<=to; i++) {
	    txt = m_FileLeft->getText(i);
	    int pos = appendLine(id,i);
	    m_FileMerged->setText(hFrom+pos,txt);
	 }
	 break;
      }
      case RIGHT: {
	 for (int i = from;i<=to; i++) {
	    txt = m_FileRight->getText(i);
	    int pos = appendLine(id,i);
	    m_FileMerged->setText(hFrom+pos,txt);
	 }
	 break;
      }
   }
}

void ResolvDialogImpl::unSelectedBlock(int id, int from, int to) {

   Block* hlp = m_blockVector.at((unsigned int)from);
   if (hlp) {

      int j;
      int blockPos;
      int lower = hlp->pos;

      for ( blockPos=from; blockPos <= to; blockPos++) {//reorganize mergeFile lines

	 int mappedMergePos = hlp->fileMap[id][blockPos-hlp->from];
	 if (mappedMergePos < lower) lower = mappedMergePos;//find startpoint for rewrite

	 for (j=mappedMergePos;j<hlp->pos-1;j++) {//update pointers
	    hlp->mergeMap[j] = hlp->mergeMap[j+1];
	    hlp->fileMap[hlp->mergeMap[j].fileTable][hlp->mergeMap[j].line-hlp->from]--;
	 }
	 hlp->pos--;
      }

      if (hlp->pos == 0) {//reset to unchanged
	 int i = hlp->from;
	 j = 0;
	 while ( (j = hlp->oriTxt.find("\n"))>-1) {
	    m_FileMerged->setText(i++,hlp->oriTxt.mid(0,j));
	    hlp->oriTxt = hlp->oriTxt.mid(j+1);
	 }
	 hlp->oriTxt = "";
      } else {//modify m_FileMerged

	 for (j=lower;j<hlp->pos;j++) {

	    switch(hlp->mergeMap[j].fileTable) {
	       case LEFT: {
		  m_FileMerged->setText(hlp->from+j,m_FileLeft->getText(hlp->mergeMap[j].line));
		  break;
	       }
	       case RIGHT: {
		  m_FileMerged->setText(hlp->from+j,m_FileRight->getText(hlp->mergeMap[j].line));
		  break;
	       }
	    }
	 }
	 for (j=hlp->from+hlp->pos;j<=hlp->from+hlp->pos+to-from;j++) {
	    m_FileMerged->setText(j,"");
	 }
      }
   }
}

void ResolvDialogImpl::writeFile() {

   QFile f(m_fileName);
   QString line;
   QString lf = getSystemLF();
   Block* hlp = NULL;
   int maxLines = m_blockVector.size();

   if(f.open(IO_WriteOnly)) {
      QTextStream textStream(&f); 
      int i = 0;
      int to = -1;
      int skipLines = 0;
      while (true) {

	 line = m_FileMerged->getText(i++);
	 if (line.isNull()) break;
	 if (i == to) {
	    continue;
	 }
	 if (i>to && (skipLines)) {
	    skipLines--;
	    continue;
	 }
	 if ( (i < maxLines) && (hlp = m_blockVector.at(i))) {//one in advance
	    if (hlp->pos) {
	       if (i == hlp->from) {
		  to = hlp->from+hlp->pos+1;
		  skipLines = hlp->to+2-to;//skip empty lines
		  continue;
	       }
	    }
	 }
	 textStream << line;
	 textStream << lf;
      }
      f.flush();
      f.close();

      close();

   } else {

      QString fName = m_fileName.mid(m_fileName.findRev("/")+1);
      QMessageBox::warning(this, tr("Warning"), tr("Cannot save:\n") +
	    fName +
	    tr(",\nthe file is write-protected.\nPlease run edit first."),
	    QMessageBox::Ok,QMessageBox::NoButton,QMessageBox::NoButton);
   }
}

void ResolvDialogImpl::SplitterStyle::drawPrimitive(PrimitiveElement pe, QPainter *p,
      const QRect &r, const QColorGroup &cg, SFlags flags /*= Style_Default*/,
      const QStyleOption &StyleOption /*= QStyleOption::Default*/) const
{ 
   if (pe != PE_Splitter) {
      QApplication::style().drawPrimitive(pe, p, r, cg, flags, StyleOption);
   } else if (dlg->m_linesTotal) {

      int top = dlg->TextLabel1->height();
      int h = 0;
      dlg->m_FileLeft->adjustScrollBarOffsets(top,h);

      double ppl = (double)h/(double)dlg->m_linesTotal;

      p->setPen(conflict);
      p->setBrush(conflict);
      unsigned int i;
      for (i=0;i<dlg->m_conflictVector.count();i++) {
	 CBlock* bl = dlg->m_conflictVector.at(i);
	 int y = (int)((bl->from)*ppl);
	 int height = (int)((bl->to - bl->from)*ppl);
	 if (height<1) {
	    height = 1;
	 }
	 p->drawRect(0,top+y,r.width(),height);
      }

   }
}

void ResolvDialogImpl::nextClicked() {
   int line = m_FileLeft->getFocusedLine();
   if (line == -1) line = m_FileLeft->getTopLine();
   int i;
   for (i=0;i<(int)m_conflictVector.count();i++) {
      CBlock* bl = m_conflictVector.at(i);
      if (bl->from <= line) continue;
      else {
	 m_FileLeft->setFocusRow(bl->from);
	 break;
      }
   }
}

void ResolvDialogImpl::previousClicked() {
   int line = m_FileLeft->getFocusedLine();
   if (line == -1) line = m_FileLeft->getTopLine();
   int i;
   for (i=(int)m_conflictVector.count()-1;i>=0;i--) {
      CBlock* bl = m_conflictVector.at(i);
      if (bl->from >= line) continue;
      else {
	 m_FileLeft->setFocusRow(bl->from);
	 break;
      }
   }
}

void ResolvDialogImpl::enterWhatsThisMode()
{
   QWhatsThis::enterWhatsThisMode();
}

