/* massXpert - the true massist's program.
   --------------------------------------
   Copyright(C) 2006,2007 Filippo Rusconi

   http://www.massxpert.org/massXpert

   This file is part of the massXpert project.

   The massxpert project is the successor to the "GNU polyxmass"
   project that is an official GNU project package(see
   www.gnu.org). The massXpert project is not endorsed by the GNU
   project, although it is released ---in its entirety--- under the
   GNU General Public License. A huge part of the code in massXpert
   is actually a C++ rewrite of code in GNU polyxmass. As such
   massXpert was started at the Centre National de la Recherche
   Scientifique(FRANCE), that granted me the formal authorization to
   publish it under this Free Software License.

   This software is free software; you can redistribute it and/or
   modify it under the terms of the GNU  General Public
   License version 3, as published by the Free Software Foundation.
   

   This software 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 for more details.

   You should have received a copy of the GNU General Public License
   along with this software; if not, write to the

   Free Software Foundation, Inc.,

   51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
*/

/////////////////////// Qt includes

#include<QScrollBar>
#include<QGraphicsItem>

/////////////////////// Local includes
#include "sequenceEditorGraphicsView.hpp"
#include "chemEntVignette.hpp"
#include "chemEntVignetteRenderer.hpp"
#include "polChemDef.hpp"
#include "application.hpp"
#include "coordinates.hpp"
#include <cmath>


namespace massXpert
{

  SequenceEditorGraphicsView::SequenceEditorGraphicsView  
 (SequenceEditorWnd *editorWnd) : mp_editorWnd(editorWnd)
  {
    m_kbdShiftDown = false;
    m_kbdCtrlDown = false;
    m_kbdAltDown = false;

    m_ongoingMouseMultiSelection = false;
    m_ongoingKeyboardMultiSelection = false;
    
    m_sequenceDrawn = false;
    
    m_requestedVignetteSize = 32;
  
    m_xScaleFactor = 1;
    m_yScaleFactor = 1;
      
    // These are pointers, and have to be 0ed.
    mpa_cursorRenderer = 0;
    mpa_cursorVignette = 0;
  
    mpa_selection = new SequenceSelection(this);
    
    mpa_monomerCodeEvaluator = 0;
  
    m_lastClickedVignette = 0;
  
    m_leftMargin = 64;

    // m_columns is often used a fractional denominator, cannot be 0.
    m_columns = 1;
      
    setMouseTracking(true);
    setFocusPolicy(Qt::StrongFocus);

    QScrollBar *vScrollBar = verticalScrollBar();
    connect(vScrollBar, SIGNAL(actionTriggered(int)),
            this, SLOT(vScrollBarActionTriggered(int)));
    
    setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
    //setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
  }


  SequenceEditorGraphicsView::~SequenceEditorGraphicsView()
  {
    // Make sure we first remove all selection items from the view.
    delete mpa_selection;
    delete mpa_cursorRenderer;
    delete mpa_monomerCodeEvaluator;
    
    return;
  }


  int 
  SequenceEditorGraphicsView::requestedVignetteSize()
  {
    return m_requestedVignetteSize;
  }


  int 
  SequenceEditorGraphicsView::columns()
  {
    return m_columns;
  }
  

  int 
  SequenceEditorGraphicsView::rows()
  {
    return m_rows;
  }
  

  int 
  SequenceEditorGraphicsView::leftMargin()
  {
    return m_leftMargin;
  }
  

  void
  SequenceEditorGraphicsView::setPolymer(Polymer *polymer)
  {
    Q_ASSERT(polymer);
  
    mp_polymer = polymer;
  }


  const Polymer *
  SequenceEditorGraphicsView::polymer() const
  {
    return mp_polymer;
  }


  void
  SequenceEditorGraphicsView::setEditorWnd(SequenceEditorWnd *editorWnd)
  {
    Q_ASSERT(editorWnd);
  
    mp_editorWnd = editorWnd;
  }


  void
  SequenceEditorGraphicsView::setMonomerCodeEvaluator 
 (MonomerCodeEvaluator *evaluator)
  {
    Q_ASSERT(evaluator);
  
    mpa_monomerCodeEvaluator = evaluator;
  }


  void 
  SequenceEditorGraphicsView::updateColumns()
  {
    QRect viewRect = rect();

    m_columns = static_cast<int>((viewRect.width() 
				   - m_leftMargin - m_requestedVignetteSize) / 
				  m_requestedVignetteSize);

//     qDebug() << __FILE__ << __LINE__ 
// 	      << "viewRect.width:" << viewRect.width() 
// 	      << "columns:" << m_columns;
  }
  
  
  void
  SequenceEditorGraphicsView::updateVScrollBar()
  {
    QScrollBar *scrollBar = verticalScrollBar();
    scrollBar->setSingleStep(m_requestedVignetteSize);

    QRect viewRect = QWidget::rect();

    scrollBar->setPageStep(viewRect.height());
  }


  void
  SequenceEditorGraphicsView::updateSceneRect()
  {
    QRect viewRect = rect();
  
    scene()->setSceneRect(0, 0, 
			    viewRect.width(),
			    ++m_rows * m_requestedVignetteSize);
  }


  int
  SequenceEditorGraphicsView::vignetteIndex(const QPointF &point)
  {
    QPointF local = point;

//     qDebug() << __FILE__ << __LINE__
// 	      << "Enter in vignetteIndex"
// 	      << point;
      
    if (static_cast<int>(local.x()) < m_leftMargin)
      {
// 	qDebug() << __FILE__ << __LINE__
// 	      << "static_cast<int>(local.x()) < m_leftMargin";
	
	return -1;
      }
    
      if (local.x() > m_leftMargin + m_columns * m_requestedVignetteSize)
	{
// 	  qDebug() << __FILE__ << __LINE__
// 		    << "local.x() > m_leftMargin + "
// 	    "m_columns * m_requestedVignetteSize";
	  
	  return -1;
	}
          
    if (local.y() < 0)
      {
// 	qDebug() << __FILE__ << __LINE__
// 	      << "local.y() < 0";
	
	return -1;
      }
    
    if (local.y() > m_rows * m_requestedVignetteSize)
      {
// 	qDebug() << __FILE__ << __LINE__
// 	      << "local.y() > m_rows * m_requestedVignetteSize"
// 		  << "local.y():" << local.y()
// 		  << "m_rows * m_requestedVignetteSize" 
// 		  << m_rows * m_requestedVignetteSize;
		
	return -1;
      }
        
    int row = static_cast<int>(local.y() / m_requestedVignetteSize);
    int yCount = row * m_columns;

//     qDebug() << __FILE__ << __LINE__
// 	      << "x--y:" 
// 	      << local.x() << "--" << local.y();

//     qDebug() << __FILE__ << __LINE__
// 	      << "m_requestedVignetteSize: m_columns:" 
// 	      << m_requestedVignetteSize << m_columns;

//     qDebug() << __FILE__ << __LINE__
// 	      << "row:" << row;

//     qDebug() << __FILE__ << __LINE__
// 	      << "yCount:" << yCount;

  
    double xRatio =(local.x() - m_leftMargin) / m_requestedVignetteSize;

    //     qDebug() << __FILE__ << __LINE__
    // 	      << "xRatio:" << xRatio;
    
    double intPart;
  
    double fracPart = modf(xRatio, &intPart);
    
//       qDebug() << __FILE__ << __LINE__
// 	      << "intPart -- fracPart" << intPart << fracPart;


    if (fracPart > 0.9)
      ++intPart;
  
//     qDebug() << __FILE__ << __LINE__
// 	      << "intPart:" << intPart;
    
    int idx = static_cast<int>(intPart + yCount);

//     qDebug() << __FILE__ << __LINE__
// 	      << "idx:" << idx;

    if (idx < 0)
      return -1;
  
    if (idx > mp_polymer->size())
      return -1;
  
//     qDebug() << __FILE__ << __LINE__
// 	      << "Exit from vignetteIndex";
    
    return idx;
  }


  QPointF 
  SequenceEditorGraphicsView::vignetteLocation(int index, 
						MxtCardinalDirection cardDir)
  {
//     qDebug() << __FILE__ << __LINE__
// 	      << "Enter in vignetteLocation with index:" << index;

    int localIndex = 0;
  
    if (index < 0)
      localIndex = 0;
    else if (index > mp_polymer->size())
      localIndex = mp_polymer->size() - 1;
    else
      localIndex = index;
  
//     qDebug() << __FILE__ << __LINE__
// 	      << "localIndex:" << localIndex;
    
    Q_ASSERT(m_columns);
    
    // row is an index
    double rowIndex = localIndex / m_columns;
  
    // column is a position
    int column = localIndex -(static_cast <int>(rowIndex) * m_columns);

    //   qDebug() << "localIndex -- column -- rowIndex"
    //  	    << localIndex << "--" << column << "--" << rowIndex;
  
    double x = 0;
    double y = 0;

 
    if (cardDir == MXT_CENTER)
      {
	x = m_leftMargin +((column + 0.5 ) * m_requestedVignetteSize);
	y = rowIndex * m_requestedVignetteSize +
	 (m_requestedVignetteSize / 2);
      }
    else if (cardDir == MXT_NORTH_WEST)
      {
	x = m_leftMargin +(column * m_requestedVignetteSize);
	y = rowIndex * m_requestedVignetteSize;
      }
    else if (cardDir == MXT_NORTH)
      {
	x = m_leftMargin + 
	 (column * m_requestedVignetteSize) + 
	 (m_requestedVignetteSize / 2);
	y = rowIndex * m_requestedVignetteSize;
      }
    else if (cardDir == MXT_NORTH_EAST)
      {
	x = m_leftMargin + 
	 (column * m_requestedVignetteSize) +
	  m_requestedVignetteSize;
	y = rowIndex * m_requestedVignetteSize;
      }
    else if (cardDir == MXT_EAST)
      {
	x = m_leftMargin + 
	 (column * m_requestedVignetteSize) +
	  m_requestedVignetteSize;
	y = rowIndex * m_requestedVignetteSize +
	 (m_requestedVignetteSize / 2);
      }
    else if (cardDir == MXT_SOUTH_EAST)
      {
	x = m_leftMargin + 
	 (column * m_requestedVignetteSize) +
	  m_requestedVignetteSize;
	y = rowIndex * m_requestedVignetteSize +
	  m_requestedVignetteSize;
      }
    else if (cardDir == MXT_SOUTH)
      {
	x = m_leftMargin + 
	 (column * m_requestedVignetteSize) + 
	 (m_requestedVignetteSize / 2);
	y = rowIndex * m_requestedVignetteSize +
	  m_requestedVignetteSize;
      }
    else if (cardDir == MXT_SOUTH_WEST)
      {
	x = m_leftMargin +(column * m_requestedVignetteSize);
	y = rowIndex * m_requestedVignetteSize +
	  m_requestedVignetteSize;
      }
    else if (cardDir == MXT_WEST)
      {
	x = m_leftMargin + 
	 (column * m_requestedVignetteSize) + 
	 (m_requestedVignetteSize / 2);
	y = rowIndex * m_requestedVignetteSize +
	 (m_requestedVignetteSize / 2);
      }
  

    if (localIndex >= mp_polymer->size() - 1)
      {
// 	qDebug() << __FILE__ << __LINE__ 
// 		  << "vignetteLocation:"
// 		  << "m_columns:" << m_columns
// 		  << "m_rows:" << m_rows;

	// In this case, IF the vignette is located at the right
	// margin of the last row(that is the sequence vignettes
	// cover a perfect rectangle and the vignette is the bottom
	// right one), then y cannot be greater than 
	//
	//(m_rows * m_requestedVignetteSize).
	// 
	// The point is that when selecting a sequence with the
	// keyboard, the selection goes right to the beginning of the
	// next line, and thus, if asking MXT_CENTER, y becomes too
	// big(see && column == 0 below to indicate that the cursor
	// passes to next line).
	// 
	// We thus have to track the case of the vignette at 'index'
	// being at the right bottom corner.
	// 
	// Note that this is only meaningful if the vignette is for
	// the last monomer in the polymer sequence, otherwise it
	// could not be the last vignette in the sequence and located
	// at the right bottom corner of the sequence rendering area.

// 	qDebug() << __FILE__ << __LINE__ 
// 		  << "vignetteLocation"
// 		  << "rowIndex:" << rowIndex
// 		  << "column:" << column;
	
	if(rowIndex == m_rows && column == 0)
	  {
	    // The vignette is actually at the last row of the display
	    // and at the last column. That is, it is located at the
	    // bottom right corner of a rectangular sequence display
	    // area.
	    
	    if (cardDir == MXT_CENTER)
	      {
		y = m_rows * m_requestedVignetteSize;

// 		qDebug() << "vignetteLocation - QPointF's y set to:" << y;
	      }
	  }
      }
    
 
    QPointF pointF(x,y);
  
    //  qDebug() << "vignettePosition:" << pointF; 
  
    return pointF;
  }


  void
  SequenceEditorGraphicsView::setSelection(int start, int end,
					    bool multiRegionSelection,
					    bool multiSelectionRegion)
  {
    int localStart;
    int localEnd;
  
    if (start >= end)
      {
	localStart = end;
	localEnd = start;
      }
    else
      {
	localStart = start;
	localEnd = end;
      }
    
    if (localStart < 0)
      localStart = 0;
  
    if (localEnd >= mp_polymer->size())
      localEnd = mp_polymer->size();

    if (!multiRegionSelection)
      mpa_selection->deselectRegions();

    mpa_selection->selectRegion(localStart, localEnd,
				 multiRegionSelection, multiSelectionRegion);
    
    m_lastClickedVignette = localEnd + 1;
    
    positionCursor();
    
    QScrollBar *vScrollBar = verticalScrollBar();
    
    int visibleRows = rect().height() / m_requestedVignetteSize;
    int lines = m_lastClickedVignette / m_columns;
    
    vScrollBar->setValue((lines + 1) * m_requestedVignetteSize -
			  visibleRows * m_requestedVignetteSize);
    
    mp_editorWnd->updateSelectedSequenceMasses();  

    // Finally update the selection clipboard
    mp_editorWnd->clipboardCopy(QClipboard::Selection);
  }


  void
  SequenceEditorGraphicsView::setSelection(const Coordinates &coordinates,
					    bool multiRegionSelection,
					    bool multiSelectionRegion)
  {
    setSelection(coordinates.start(), coordinates.end(),
		 multiRegionSelection, multiSelectionRegion);
  }
  

  void
  SequenceEditorGraphicsView::setSelection(const CoordinateList &coordinateList,
					    bool multiRegionSelection,
					    bool multiSelectionRegion)
  {
    for (int iter = 0; iter < coordinateList.size(); ++iter)
      {
	Coordinates *coordinates = coordinateList.at(iter);
	
	setSelection(coordinates->start(), coordinates->end(),
		     multiRegionSelection, multiSelectionRegion);
      }
  }
  

  void
  SequenceEditorGraphicsView::resetSelection()
  {
    mpa_selection->deselectRegions();

    m_selectionFirstIndex = -1;
    m_selectionSecondIndex = -1;

    m_selectionFirstPoint = QPointF(0,0);
    m_selectionSecondPoint = QPointF(0,0);

    mp_editorWnd->updateSelectedSequenceMasses();
  }


  void 
  SequenceEditorGraphicsView::resetSelectionButLastRegion()
  {
    // Deselect all the selected regions, but the lastly selected
    // region.
    mpa_selection->deselectRegionsButLast();

    if (mpa_selection->regionSelectionCount())
      {
	// If there was actually one selection left, then set the cursor
	// to be at the end of that selection.
	
	positionCursor(*(mpa_selection->regionSelectionList().last()));
      }
}
  
  
  void 
  SequenceEditorGraphicsView::resetMultiSelectionRegionsButFirstSelection()
  {
    // Deselect all the selected regions, but the lastly selected
    // region.
    mpa_selection->deselectMultiSelectionRegionsButFirstSelection();
  }
  
  
  bool
  SequenceEditorGraphicsView::selectionIndices(CoordinateList *coordList)
  {
    // The coordList should be empty. If not we empty it right away.

    if (coordList && !coordList->isEmpty())
      coordList->empty();
        
    // At this point we should ask the selection to provide the info.

    bool res = mpa_selection->selectionIndices(coordList);

    if (!res)
      {
	// There is no real selection, thus, all we have to do is
	// construct a fake Coordinates with the pseudo-selection,
	// that is a pseudo-region from start to last clicked
	// vignette. Remember, however, that the lastClickedVignette
	// index is one monomer vignette right of the actual vignette
	//(because the selection mark drawing functions require
	// this).
	Coordinates *coordinates = 
	  new Coordinates(0, lastClickedVignette() - 1);
	
	if(coordList)
	  coordList->append(coordinates);
      }
    
    // However, return the real result so that caller knows if there
    // was a real selection or if the returned Coordinates instance is
    // for the pseudo-selection from start of sequence to the last
    // clicked vignette.

    return res;
  }
    

   
  
  void 
  SequenceEditorGraphicsView::setOngoingMouseMultiSelection(bool value)
  {
    m_ongoingMouseMultiSelection = value;
  }
  

  // SCENE-DRAWING FUNCTIONS /////////////////////////////////////////////
  bool
  SequenceEditorGraphicsView::renderCursor()
  {
    //   qDebug() << __FILE__ << __LINE__ << "Entering renderCursor()";
  
    QString filePath = mp_editorWnd->polChemDef()->dirPath() + "/cursor.svg";

    //   qDebug() << __FILE__ << __LINE__ << "filePath" << filePath;

    mpa_cursorRenderer = new QSvgRenderer(filePath);
  
    if (!mpa_cursorRenderer->isValid())
      return false;
  
    mpa_cursorVignette = new QGraphicsSvgItem();
  
    mpa_cursorVignette->setSharedRenderer(mpa_cursorRenderer);
  
    scene()->addItem(mpa_cursorVignette);
    
    QRectF itemRectF = mpa_cursorVignette->boundingRect();
      
    // QMatrix(double m11, double m12, double m21, double m22, double
    // dx, double dy). A QMatrix object contains a 3 x 3 matrix. The
    // dx and dy elements specify horizontal and vertical
    // translation. The m11 and m22 elements specify horizontal and
    // vertical scaling. And finally, the m21 and m12 elements
    // specify horizontal and vertical shearing.
  
    double xScale = m_requestedVignetteSize / itemRectF.width();
    double yScale = m_requestedVignetteSize / itemRectF.height();
  
    //       qDebug() << "xScale - yScale:"
    // 		<< xScale << "-" << yScale;
  
    QMatrix matrix(xScale, 0, 0, yScale, 0, 0);
  
    mpa_cursorVignette->setMatrix(matrix);
  
    scene()->update();

    //   qDebug() << __FILE__ << __LINE__ << "Exiting renderCursor()";

    return true;
  }


  bool
  SequenceEditorGraphicsView::scaleCursor()
  {
    if (!mpa_cursorVignette)
      return renderCursor();
  
    QRectF itemRectF = mpa_cursorVignette->boundingRect();
  
    // QMatrix(double m11, double m12, double m21, double m22, double
    // dx, double dy). A QMatrix object contains a 3 x 3 matrix. The
    // dx and dy elements specify horizontal and vertical
    // translation. The m11 and m22 elements specify horizontal and
    // vertical scaling. And finally, the m21 and m12 elements
    // specify horizontal and vertical shearing.
  
    double xScale = m_requestedVignetteSize / itemRectF.width();
    double yScale = m_requestedVignetteSize / itemRectF.height();
  
    //       qDebug() << "xScale - yScale:"
    // 		<< xScale << "-" << yScale;
  
    QMatrix matrix(xScale, 0, 0, yScale, 0, 0);
  
    mpa_cursorVignette->setMatrix(matrix);
  
    scene()->update();
  
    return true;
  }


  bool 
  SequenceEditorGraphicsView::positionCursor(const QPointF &pos, 
					      MxtCursorLinePosition linePos)
  {
    if (!mpa_cursorVignette)
      if (!renderCursor())
	return false;
  
    if (linePos == MXT_END_OF_LINE)
      {
	if(pos.x() == m_leftMargin && pos.y() >= m_requestedVignetteSize)
	  {
	    // Do not go to the left of first vignette of next row simply
	    // because we are right of the last vignette in the row. That's
	    // not intuitive.
	    mpa_cursorVignette->setPos(m_leftMargin + 
					m_columns * m_requestedVignetteSize 
					-(m_requestedVignetteSize / 2),
					pos.y() - m_requestedVignetteSize);
	    return true;
	  }
      }
  
    mpa_cursorVignette->setPos(pos.x() -(m_requestedVignetteSize / 2),
				pos.y());

    return true;
  }


  bool 
  SequenceEditorGraphicsView::positionCursor(double x, double y, 
					      MxtCursorLinePosition linePos)
  {
    return positionCursor(QPointF(x, y), linePos);
  }


  bool 
  SequenceEditorGraphicsView::positionCursor(int index, 
					      MxtCursorLinePosition linePos)
  {
    return positionCursor(vignetteLocation(index), linePos);
  }


  bool 
  SequenceEditorGraphicsView::positionCursor(MxtCursorLinePosition linePos)
  {
    return positionCursor(vignetteLocation(m_lastClickedVignette), linePos);
  }


  bool 
  SequenceEditorGraphicsView::positionCursor(const RegionSelection &regionSelection,
					      MxtCursorLinePosition linePos)
  {
    // We should place the cursor at the m_endIndex of the
    // regionSelection. Note that because we want the cursor to be
    // located RIGHT of the last vignette of the selection, we
    // increment the endIndex by one.
    m_lastClickedVignette = regionSelection.endIndex() + 1;
    return positionCursor();
   
  }




  ChemEntVignette *
  SequenceEditorGraphicsView::renderVignette(const Monomer &monomer)
  {
    ChemEntVignetteRenderer *monomerRenderer = 0;
    ChemEntVignetteRenderer *modifRenderer = 0;

    QSize momentSize(0, 0);
  
    Q_ASSERT(mp_polymer);
    PolChemDef *polChemDef = mp_editorWnd->polChemDef();
    Q_ASSERT(polChemDef);
 
  
    QString monomerCode = monomer.code();
    Q_ASSERT(!monomerCode.isEmpty());

    //   qDebug() << __FILE__ << __LINE__ 
    // 	    << "Asking renderer for monomer code:" << monomerCode;
        
    monomerRenderer = polChemDef->chemEntVignetteRenderer(monomerCode);
      
    if (!monomerRenderer)
      {
	//       qDebug() << __FILE__ << __LINE__ 
	// 		<< "Newing renderer for monomer code:" << monomerCode;
      
	monomerRenderer = 
	  polChemDef->newMonomerVignetteRenderer(monomerCode);
	  
	if(!monomerRenderer)
	  {
	    qDebug() << __FILE__ << __LINE__
		      << "Failed to make svg renderer for monomer."
		      << monomerCode;
	    abort();
	  }
      
	//       qDebug() << __FILE__ << __LINE__ 
	// 		<< "Got new renderer:" << monomerRenderer;
      }
    else
      {
	//       qDebug() << __FILE__ << __LINE__ 
	// 		<< "Got renderer from hash:" << monomerRenderer;
      }
        
    // Ok, we have done the main work.

    ChemEntVignette *monomerVignette = new ChemEntVignette();
    monomerVignette->setSharedRenderer(monomerRenderer);
    monomerVignette->setZValue(1);

    scene()->addItem(monomerVignette);

    // Now, make sure that we test if the monomer is modified, if so
    // render the modif(s).
  
    if (monomer.isModified())
      {
	// Iterate in the list of modifications...
	for(int iter = 0; iter < monomer.modifList()->size(); ++iter)
	  {
	    Modif *modif = monomer.modifList()->at(iter);
	  
	    QString modifName = modif->name();
	  
	    // qDebug() << __FILE__ << __LINE__ 
	    // << "Asking renderer for modif name:" << modifName;
	  
	    modifRenderer = polChemDef->chemEntVignetteRenderer(modifName);
	  
	    if (!modifRenderer)
	      {
		// qDebug() << __FILE__ << __LINE__ 
		// << "Newing renderer for modif name:" << modifName;
	  
		modifRenderer = polChemDef->newModifVignetteRenderer(modifName);
	      
		if(!modifRenderer)
		  {
		    qDebug() << __FILE__ << __LINE__
			      << "Failed to make svg renderer for modif."
			      << modifName;
		  
		    abort();
		  }

		// qDebug() << __FILE__ << __LINE__ 
		// << "Got new renderer:" << modifRenderer;
	      }
	    else
	      {
		// qDebug() << __FILE__ << __LINE__ 
		// << "Got renderer from hash:" << modifRenderer;
	      }

	    // At this point we actually have a modifRenderer for the
	    // currently iterated modif.
	    ChemEntVignette *modifVignette = 
	      new ChemEntVignette(monomerVignette);
	    modifVignette->setSharedRenderer(modifRenderer);
	  
	    modifVignette->setName(QString("MODIF"));      
	    modifVignette->setOwner(modif);
	    modifVignette->setZValue(2);
	  }
      }

    // Finally we can update the scene.
  
    scene()->update(monomerVignette->boundingRect());
  
    return monomerVignette;
  }


  bool 
  SequenceEditorGraphicsView::modifyVignetteAt(int index, 
						Modif *modif)
  {
    Q_ASSERT(mp_polymer);
    PolChemDef *polChemDef = mp_editorWnd->polChemDef();
    Q_ASSERT(polChemDef);

    Q_ASSERT(index >= 0 && index < m_monomerVignetteList.size());
 
    Q_ASSERT(modif);

    // Get a pointer to the vignette to modify at index 'index'.
    ChemEntVignette *monomerVignette = m_monomerVignetteList.at(index);
    Q_ASSERT(monomerVignette);
  
    QString name = modif->name();
    Q_ASSERT(!name.isEmpty());
  
    // Use the entity name to create a renderer for that entity.
    ChemEntVignetteRenderer *renderer = 
      polChemDef->chemEntVignetteRenderer(name);
  
    if (!renderer)
      {
	// A renderer for that modification did not exist already. We
	// have to create one proper.
	renderer = polChemDef->newModifVignetteRenderer(name);
      
	if(!renderer)
	  {
	    qDebug() << __FILE__ << __LINE__
		      << "Failed to make svg renderer for modif:"
		      << name;
	  
	    abort();
	  }
      }
  
    ChemEntVignette *modifVignette = 
      new ChemEntVignette(monomerVignette);
    modifVignette->setSharedRenderer(renderer);

    modifVignette->setName(QString("MODIF"));
    modifVignette->setOwner(modif);
  
    modifVignette->setZValue(2);

    scene()->update(monomerVignette->boundingRect());

    return true;
  }


  bool 
  SequenceEditorGraphicsView::unmodifyVignetteAt(int index,
						  Modif *modif)
  {
    Q_ASSERT(modif);
  
    Q_ASSERT(index >= 0 && index < m_monomerVignetteList.size());
  
    ChemEntVignette *monomerVignette = m_monomerVignetteList.at(index);
    Q_ASSERT(monomerVignette);
  
    QList<QGraphicsItem *> childrenList = 
      static_cast<QGraphicsItem *>(monomerVignette)->childItems();

    for (int iter = 0; iter < childrenList.size(); ++iter)
      {
	ChemEntVignette *item = 
	  static_cast<ChemEntVignette *>(childrenList.at(iter));
      
	if(item->name() == "MODIF" && item->owner() == modif)
	  {
	    scene()->removeItem(item);
	  
	    delete(item);
	  }
      }
  
    return true;
  }


  bool 
  SequenceEditorGraphicsView::crossLinkVignetteAt(int index, 
						   CrossLinker *crossLinker)
  {
    Q_ASSERT(mp_polymer);
    PolChemDef *polChemDef = mp_editorWnd->polChemDef();
    Q_ASSERT(polChemDef);

    Q_ASSERT(index >= 0 && index < m_monomerVignetteList.size());
 
    Q_ASSERT(crossLinker);

    // Get a pointer to the vignette to crossLink at index 'index'.
    ChemEntVignette *monomerVignette = m_monomerVignetteList.at(index);
    Q_ASSERT(monomerVignette);
  
    QString name = crossLinker->name();
    Q_ASSERT(!name.isEmpty());
  
    // Use the crossLinker name to create a renderer for that entity.
    ChemEntVignetteRenderer *renderer = 
      polChemDef->chemEntVignetteRenderer(name);
  
    if (!renderer)
      {
	// A renderer for that crossLink did not exist already. We
	// have to create one proper.
	renderer = polChemDef->newCrossLinkerVignetteRenderer(name);
      
	if(!renderer)
	  {
	    qDebug() << __FILE__ << __LINE__
		      << "Failed to make svg renderer for cross-link:"
		      << name;
	  
	    abort();
	  }
      }
  
    ChemEntVignette *crossLinkVignette = 
      new ChemEntVignette(monomerVignette);
    crossLinkVignette->setSharedRenderer(renderer);

    crossLinkVignette->setName(QString("CROSS_LINK"));
    crossLinkVignette->setOwner(crossLinker);
  
    crossLinkVignette->setZValue(2);

    scene()->update(monomerVignette->boundingRect());

    return true;
  }


  bool 
  SequenceEditorGraphicsView::uncrossLinkVignetteAt(int index,
						     CrossLinker *crossLinker)
  {
    Q_ASSERT(crossLinker);
  
    Q_ASSERT(index >= 0);
    Q_ASSERT(index < m_monomerVignetteList.size());
  
    ChemEntVignette *monomerVignette = m_monomerVignetteList.at(index);
    Q_ASSERT(monomerVignette);
  
    QList<QGraphicsItem *> childrenList = 
      static_cast<QGraphicsItem *>(monomerVignette)->childItems();

    for (int iter = 0; iter < childrenList.size(); ++iter)
      {
	ChemEntVignette *item = 
	  static_cast<ChemEntVignette *>(childrenList.at(iter));
      
	if(item->name() == "CROSS_LINK" && item->owner() == crossLinker)
	  {
	    scene()->removeItem(item);
	    
// 	    qDebug() << __FILE__ << __LINE__
// 		      << "Removed cross-linker from scene:"
// 		      << crossLinker->name();
	    
	    delete(item);
	  }
      }
  
    return true;
  }



  bool
  SequenceEditorGraphicsView::removeVignetteAt(int index)
  {
    Q_ASSERT(index >= 0 && index < m_monomerVignetteList.size());
  
    ChemEntVignette *monomerVignette = m_monomerVignetteList.takeAt(index);
    Q_ASSERT(monomerVignette);
  
    scene()->removeItem(monomerVignette); // And all the children.

    delete(monomerVignette);

    return true;
  }


  bool
  SequenceEditorGraphicsView::renderVignettes()
  {
    int iter = 0;
  
    mp_editorWnd->statusBar()->showMessage(tr("Rendering vignettes"));

    if (mp_polymer->size())
      mp_editorWnd->progressBar()->setRange(1, mp_polymer->size());
    else 
      mp_editorWnd->progressBar()->setRange(1, 1);
  
    for (iter = 0; iter < mp_polymer->size(); ++iter)
      {
	mp_editorWnd->progressBar()->setValue(iter + 1);
      
	const Monomer *monomer = mp_polymer->monomerList().at(iter);
	
	mp_editorWnd->statusBar()->showMessage(tr("Rendering vignettes: "
						     "%1 - %2")
						 .arg(monomer->name())
						 .arg(iter + 1));
      
	ChemEntVignette *monomerVignette = renderVignette(*monomer);
      
	Q_ASSERT(monomerVignette);
      
	m_monomerVignetteList.append(monomerVignette);
      
	//       qDebug() << "Rendered vignette for monomer:" 
	// 		<< monomer->name().toAscii();
      }

    // At this point we have to render all the crossLinks of the
    // sequence... We know the polymer sequence has the list of
    // crossLinks.

    const CrossLinkList &crossLinkList = mp_polymer->crossLinkList();
  
    for (int iter = 0; iter < crossLinkList.size(); ++iter)
      {
	CrossLink *crossLink = crossLinkList.at(iter);
      
	// For each monomer in the cross-link, make a vignette.

	for(int jter = 0; jter < crossLink->monomerList()->size(); ++jter)
	  {
	    const Monomer *monomer = crossLink->monomerList()->at(jter);
	  
	    int index = mp_polymer->monomerIndex(monomer);
	  
	    if (!crossLinkVignetteAt(index, crossLink))
	      {
		qDebug() << __FILE__ << __LINE__ 
			  << "Failed to render vignette for cross-linker:"
			  << crossLink->name();
	      
		abort();
	      }
	  }
      }
  
    mp_editorWnd->statusBar()->showMessage(tr("Done rendering vignettes"));
  
    mp_editorWnd->progressBar()->setValue(1);
    mp_editorWnd->statusBar()->clearMessage();
  
    return true;
  }


  bool
  SequenceEditorGraphicsView::scaleVignette(ChemEntVignette *vignette)
  {
    Q_ASSERT(vignette);
  
    QRectF itemRectF = vignette->boundingRect();
      
    // QMatrix(double m11, double m12, double m21, double m22, double
    // dx, double dy). A QMatrix object contains a 3 x 3 matrix. The
    // dx and dy elements specify horizontal and vertical
    // translation. The m11 and m22 elements specify horizontal and
    // vertical scaling. And finally, the m21 and m12 elements
    // specify horizontal and vertical shearing.

    double xScale = m_requestedVignetteSize / itemRectF.width();
    double yScale = m_requestedVignetteSize / itemRectF.height();
      
    //       qDebug() << "xScale - yScale:"
    // 		<< xScale << "-" << yScale;
      
    QMatrix matrix(xScale, 0, 0, yScale, 0, 0);

    vignette->setMatrix(matrix);

    return true;
  }


  bool
  SequenceEditorGraphicsView::scaleVignettes()
  {

    if (m_monomerVignetteList.size() < 1)
      return true;
  
    mp_editorWnd->statusBar()->showMessage(tr("Scaling vignettes"));

    int vignetteListSize = m_monomerVignetteList.size();

    if (vignetteListSize)
      mp_editorWnd->progressBar()->setRange(1, vignetteListSize);
    else 
      mp_editorWnd->progressBar()->setRange(1, 1);
  
    for (int iter = 0 ; iter < vignetteListSize; ++iter)
      {
	mp_editorWnd->progressBar()->setValue(iter + 1);

	mp_editorWnd->statusBar()->showMessage(tr("Scaling vignettes: "
						     "%1")
						 .arg(iter + 1));
      
	ChemEntVignette *monomerVignette = m_monomerVignetteList.at(iter);
	scaleVignette(monomerVignette);
      }
  
    scene()->update();
  
    mp_editorWnd->statusBar()->showMessage(tr("Done scaling vignettes"));
    mp_editorWnd->progressBar()->setValue(1);
  
    return true;
  }



  bool
  SequenceEditorGraphicsView::positionVignette(ChemEntVignette *vignette,
						int index)
  {
    Q_ASSERT(vignette);
    Q_ASSERT(index >= 0 && index < mp_polymer->size());
  
    // row is an index
    double rowIndex = index / m_columns;
  
    // column is a position
    int column = index -(static_cast <int>(rowIndex) * m_columns);
  
    double x = 0;
    double y = 0;
  
    x = m_leftMargin +(column * m_requestedVignetteSize);
    y = rowIndex * m_requestedVignetteSize;
  
    vignette->setPos(x, y);
  
    return true;
  }


  bool
  SequenceEditorGraphicsView::positionVignettes()
  {
    double x = m_leftMargin;
    double y = 0;

    while(!m_labelList.isEmpty())
      {
	QGraphicsTextItem *label = m_labelList.takeFirst();
  
	scene()->removeItem(label);
	delete(label);
      }
  
    int vignetteListSize = m_monomerVignetteList.size();

    if (vignetteListSize < 1)
      {
	return true;
      }

//     qDebug() << __FILE__ << __LINE__ 
// 	      << "Enter positionVignettes:"
// 	      << "m_leftMargin:" << m_leftMargin
// 	      << "Number of vignettes in the list:" << vignetteListSize;
  
    m_rows = 0;
  
    updateColumns();

    // Vignette positioning proper.

    int column = 0;
  
    for (int iter = 0 ; iter < vignetteListSize; ++iter)
      {
	ChemEntVignette *monomerVignette = m_monomerVignetteList.at(iter);

	if(column < m_columns)
	  {
	    if (!iter)
	      {
		// We are at the beginning of first line.
		QString text = QString("%1").arg(iter + 1);
	      
		QGraphicsTextItem *label = 
		  new QGraphicsTextItem(text, 0);

                // Now add the new item to the scene.
                scene()->addItem(label);
                
		label->setPos(0,y);
		
// 		qDebug() << __FILE__ << __LINE__ 
// 			  << "Positioned label at:" << QPointF(0,y);
		
		m_labelList.append(label);
	      }

	    monomerVignette->setPos(x, y);
	  }
	else
	  {
	    column = 0;
	    m_rows ++;
	  
	    x = m_leftMargin;
	    y += m_requestedVignetteSize;

	    monomerVignette->setPos(x, y);
	  
	    // We are at the beginning of a new line.
	    QString text = QString("%1").arg(iter + 1);
	  
	    QGraphicsTextItem *label = 
	      new QGraphicsTextItem(text, 0);

            scene()->addItem(label);
            
	    label->setPos(0,y);
	  
// 	    qDebug() << __FILE__ << __LINE__ 
// 		      << "Positioned label at:" << QPointF(0,y);
		
	    m_labelList.append(label);
	  }
      
	x += m_requestedVignetteSize;
	++column;
      }
    
//     qDebug() << __FILE__ << __LINE__ 
// 	      << "Exit positionVignettes.";
    
    return true;
  }


  int
  SequenceEditorGraphicsView::drawSequence(bool reset)
  {
    // We have a pointer to the polymer for which we have to draw the
    // sequence in the scene. This is the first time the sequence is
    // drawn. So we must go through the creation of all the monomer
    // graphics items. However, we might have already a polymer
    // chemistry definition that holds a number of usable vignette renderer
    // instances.

    setCursor(Qt::WaitCursor);

    Q_ASSERT(mp_polymer);

    if (reset)
      {
	// We are asked to reset all the GraphicsSvgItem in the scene.

	QList<QGraphicsItem *> itemList = scene()->items();
      
	while(!itemList.isEmpty())
	  delete(itemList.takeFirst());

	m_monomerVignetteList.clear();
	m_labelList.clear();
      }
    
    bool ret = renderVignettes();
    Q_ASSERT(ret);
  
    ret = scaleVignettes();
    if (ret == 0)
      qFatal("Fatal error at %s@%d. Aborting.",__FILE__, __LINE__);

    mp_editorWnd->statusBar()->showMessage(tr("Positioning vignettes"));
    ret = positionVignettes();
    if (ret == 0)
      qFatal("Fatal error at %s@%d. Aborting.",__FILE__, __LINE__);
    
    mp_editorWnd->statusBar()->showMessage(tr("Done positioning vignettes"));

    renderCursor();
    m_selectionFirstIndex = -1;
    m_selectionFirstPoint = QPointF(0, 0);
    m_selectionSecondIndex = -1;
    m_selectionSecondPoint = QPointF(0, 0);
    positionCursor(0);
    
    updateSceneRect();
  
    updateVScrollBar();

    m_sequenceDrawn = true;

    setFocus();

    mp_editorWnd->updateWholeSequenceMasses();
    mp_editorWnd->updateSelectedSequenceMasses();

    mp_editorWnd->statusBar()->clearMessage();
    mp_editorWnd->statusBar()->showMessage(tr("Done drawing sequence"),
					     3000);
    mp_editorWnd->progressBar()->setValue(1);

    setCursor(Qt::ArrowCursor);

    return 0;
  }


  bool
  SequenceEditorGraphicsView::updateSequence()
  {
    setCursor(Qt::WaitCursor);

    positionVignettes();
  
    positionCursor();
  
    updateSceneRect();
  
    updateVScrollBar();

    setCursor(Qt::ArrowCursor);

    return true;
  }
  


  // POLYMER SEQUENCE-MODIFYING FUNCTIONS ///////////////////////////////
  int 
  SequenceEditorGraphicsView::insertMonomerAtPoint(const Monomer *monomer,
						    bool freeze)
  {
    Q_ASSERT(monomer);
  
    int res = insertMonomerAt(monomer, m_lastClickedVignette, freeze);
    mp_editorWnd->setWindowModified(true);
    mp_editorWnd->updateWindowTitle();
  
    return res;
  }


  int 
  SequenceEditorGraphicsView::insertMonomerAt(const Monomer *monomer,
					       int index, bool freeze)
  {
    Q_ASSERT(monomer);
  
    Q_ASSERT(index > -1 && index <= mp_polymer->size());
  
    ChemEntVignette *monomerVignette = renderVignette(*monomer);
  
    Q_ASSERT(monomerVignette);
  
    bool ret = false;
    ret = mp_polymer->insertMonomerAt(monomer, index);
    if (ret == 0)
      qFatal("Fatal error at %s@%d. Aborting.",__FILE__, __LINE__);

    m_monomerVignetteList.insert(index, monomerVignette);  
    scaleVignette(monomerVignette);

    ++m_lastClickedVignette;

    if (!freeze)
      {
	mp_editorWnd->setWindowModified(true);
	mp_editorWnd->updateWindowTitle();
	
	mp_editorWnd->updateWholeSequenceMasses();
	mp_editorWnd->updateSelectedSequenceMasses();
      
	updateSequence();
      
	QPointF pointF = vignetteLocation(m_lastClickedVignette);
	centerOn(pointF);
      
	scene()->update();
      }
  
//     qDebug() << __FILE__ << __LINE__
// 	      << "hScrollBar.";
    QScrollBar *hScrollBar = horizontalScrollBar();
    hScrollBar->setMinimum(0);
    hScrollBar->setValue(0);

    // Return the number of monomers added.
    return 1;
  }


  int
  SequenceEditorGraphicsView::insertSequenceAtPoint(Sequence &sequence)
  {
    bool freeze = sequence.monomerList().size() > 10 ? true : false;
    int count = 0;
  
    while(!sequence.monomerList().isEmpty())
      {
	// Reuse the monomer, we take it out of the list and we use it
	// to put into the sequence.
	const Monomer *monomer = 
	  const_cast<QList<const Monomer *> &> 
	 (sequence.monomerList()).takeFirst();
      
	insertMonomerAtPoint(monomer, freeze);
	++count;
      }

    if (freeze)
      {
	if(!mp_editorWnd->isWindowModified())
	  {
	    mp_editorWnd->setWindowModified(true);
	    mp_editorWnd->updateWindowTitle();
	  }
      
	mp_editorWnd->updateWholeSequenceMasses();
	mp_editorWnd->updateSelectedSequenceMasses();
      
	updateSequence();
      
	QPointF pointF = vignetteLocation(m_lastClickedVignette);
	centerOn(pointF);
      
	scene()->update();
      }

//     qDebug() << __FILE__ << __LINE__
// 	      << "hScrollBar.";
    QScrollBar *hScrollBar = horizontalScrollBar();
    hScrollBar->setMinimum(0);
    hScrollBar->setValue(0);
    
    return count;
  }


  bool
  SequenceEditorGraphicsView::prepareMonomerRemovalAt(int index)
  {
    int polymerSize = mp_polymer->size();
    if (index >= polymerSize)
      qFatal("Fatal error at %s@%d. Program aborted.",
	      __FILE__, __LINE__);
    if (index <= -1)
      qFatal("Fatal error at %s@%d. Program aborted.",
	      __FILE__, __LINE__);
    
    // We have to check if the monomer is cross-linked, if so first
    // uncross-link all its partners.
  
//     qDebug() << __FILE__ << __LINE__ << "Going to call at() with value"
// 	      << index;
        
    const Monomer *monomer = mp_polymer->at(index);
  
//     qDebug() << __FILE__ << __LINE__
// 	      << "prepareMonomerRemovalAt:" << index
// 	      << "Monomer is:" << monomer->name();
    
    const CrossLinkList &crossLinkList = mp_polymer->crossLinkList();
  
    QList<int> theList;

    // One monomer might be involved in more than one cross-link. Get
    // the list of the cross-links in which the monomer is involved.

    if (!crossLinkList.crossLinksInvolvingMonomer(monomer, &theList))
      return true;
  
    // At this point we know that the monomer is involved at least in
    // one crossLink. For each of the cross-links involving the
    // monomer, make sure we uncross-link all its partners.
  
    for (int iter = 0; iter < theList.size(); ++iter)
      {
	CrossLink *crossLink = crossLinkList.at(theList.at(iter));
      
// 	qDebug() << __FILE__ << __LINE__
// 		  << "Monomer is involved in cross-link:" << crossLink->name()
// 		  << "at index of crossLinkList:" << iter;
	
	for(int jter = 0; jter < crossLink->monomerList()->size(); ++jter)
	  {
	    const Monomer *iterMonomer = crossLink->monomerList()->at(jter);
	
	    int monomerIndex = mp_polymer->monomerIndex(iterMonomer);
	  
// 	    qDebug() << __FILE__ << __LINE__
// 		      << "Uncross-linking monomer:" << iterMonomer->name()
// 		      << "at:" << monomerIndex;
	    
	    bool result = uncrossLinkVignetteAt(monomerIndex, crossLink);
	    
	    if (!result)
	      {
// 		qDebug() << __FILE__ << __LINE__
// 			  << "Failed to uncross-link monomer:"
// 			  << iterMonomer->name() 
// 			  << "at index:" << monomerIndex;
		
		return false;
	      }
	    else
	      {
// 		qDebug() << __FILE__ << __LINE__
// 			  << "Successfully uncross-linked monomer";
	      }
	  }
      }
    
    return true;
  }


  int 
  SequenceEditorGraphicsView::removeMonomerAt(int index, bool freeze)
  {
    int polymerSize = mp_polymer->size();
  
    Q_ASSERT(index < polymerSize);
    Q_ASSERT(index > -1);
  
    if (polymerSize < 1)
      return 0;

    bool result = prepareMonomerRemovalAt(index);
    if (result == 0)
      qFatal("Fatal error at %s@%d. Aborting.",__FILE__, __LINE__);

    result = mp_polymer->removeMonomerAt(index);
    if (result == 0)
      qFatal("Fatal error at %s@%d. Aborting.",__FILE__, __LINE__);

        // Get the pointer to the corresponding monomer vignette, so that we
    // can remove it from the scene.

    removeVignetteAt(index);
  
    if (!freeze)
      {
	if(!mp_editorWnd->isWindowModified())
	  {
	    mp_editorWnd->setWindowModified(true);
	    mp_editorWnd->updateWindowTitle();
	  }
      
	mp_editorWnd->updateWholeSequenceMasses();
	mp_editorWnd->updateSelectedSequenceMasses();
      }
  
    // Return the number of monomers removed.
    return 1;
  }


  int 
  SequenceEditorGraphicsView::removeSelectedOligomer()
  {
    // We only can remove a selected oligomer in non-multi region
    // selection mode. However, just to make sure, we use the lastly
    // selected region.

    CoordinateList coordList;
    
    bool selectionPresent = selectionIndices(&coordList);
    
    if (!selectionPresent)
      return 0;
    
    Coordinates *coord = coordList.last();
    
    return removeSequenceRegion(coord->start(),
				 coord->end());
  }
  

  int 
  SequenceEditorGraphicsView::removeSequenceRegion(int start, int end)
  {
//     qDebug() << __FILE__ << __LINE__
// 	      << "removeSequenceRegion with indices start--end:"
// 	      << start << "--" << end;
    
    int count = 0;
    int localStart = 0;
    int localEnd = 0;
  
    if (start > end)
      {
	localStart = end;
	localEnd = start;
      }
    else
      {
	localStart = start;
	localEnd = end;
      }
    
    Q_ASSERT(localStart > -1);
    Q_ASSERT(localEnd < mp_polymer->size());
  
    // If the number of monomers to remove is greater than 10, then
    // freeze the sequence display.
    bool freeze = abs(localEnd - localStart) > 10 ? true : false;

    int iter = localEnd;
  
    while(iter >= localStart)
      {
// 	qDebug() << __FILE__ << __LINE__
// 		  << "Going to remove monomer at:" << iter;
	
	count += removeMonomerAt(iter, freeze);
	--iter;
      }
  
    resetSelection();
  
    m_lastClickedVignette = localStart;
  
    mp_editorWnd->updateWholeSequenceMasses();
    mp_editorWnd->updateSelectedSequenceMasses();

    mp_editorWnd->setWindowModified(true);
    mp_editorWnd->updateWindowTitle();
  
    updateSequence();
  
    QPointF pointF = vignetteLocation(m_lastClickedVignette);
    centerOn(pointF);
  
    // Return the number of monomers removed.  
    return count;
  }



  ////////////////////////////// SLOTS ///////////////////////////////
  void
  SequenceEditorGraphicsView::vScrollBarActionTriggered(int action)
  {
    // Actions are QAbstractSlider::SliderSingleStepAdd,
    // SliderSingleStepSub, SliderPageStepAdd, SliderPageStepSub,
    // SliderToMinimum, SliderToMaximum, and SliderMove.

    QScrollBar *vScrollBar = verticalScrollBar();
    int curPos = vScrollBar->sliderPosition();

    if (action == QAbstractSlider::SliderSingleStepAdd)
      vScrollBar->setSliderPosition(curPos + m_requestedVignetteSize);
    else if (action == QAbstractSlider::SliderPageStepAdd)
      vScrollBar->setSliderPosition(curPos + rect().height());

    if (action == QAbstractSlider::SliderSingleStepSub)
      vScrollBar->setSliderPosition(curPos - m_requestedVignetteSize);
    else if (action == QAbstractSlider::SliderPageStepSub)
      vScrollBar->setSliderPosition(curPos - rect().height());
  }


  bool
  SequenceEditorGraphicsView::requestVignetteSize(int size)
  {
    // Be careful that this function can be called by the parent
    // sequence editor window, while the sequence editor is being
    // initialized(readSettings()). If the sequence was never drawn,
    // we just return avec having set the m_requestedVignetteSize value
    // that will be used during the sequence drawing.

    m_requestedVignetteSize = size;
  
    if (!m_sequenceDrawn)
      return true;
  
    if (!scaleVignettes())
      return false;
  
    if (!positionVignettes())
      return false;
  
    scaleCursor();
  
    positionCursor();
  
    mpa_selection->reselectRegions();

    updateSceneRect();
  
    updateVScrollBar();
  
    QPointF pointF = vignetteLocation(m_lastClickedVignette);
    centerOn(pointF);

    return true;
  }


  int 
  SequenceEditorGraphicsView::lastClickedVignette()
  {
    return m_lastClickedVignette;
  }



  //////////////////// EVENT HANDLING FUNCTIONS ///////////////////

  void 
  SequenceEditorGraphicsView::focusOutEvent(QFocusEvent *event)
  {
//     qDebug() << __FILE__ << __LINE__
// 	      << "focusOutEvent";

    m_kbdShiftDown = false;
    m_kbdCtrlDown = false;
    m_kbdAltDown = false;
    m_ongoingKeyboardMultiSelection = false;
  }
  

  void
  SequenceEditorGraphicsView::resizeEvent(QResizeEvent *event)
  {
    // Be careful that this function can be called during setting up of
    // the widget, even if no sequence was drawn, and thus a number of
    // variables are not properly initialized. Thus, if no sequence was
    // drawn, we have to return.

    if (!m_sequenceDrawn)
      return;
  
    QGraphicsView::resizeEvent(event);

    positionVignettes();
  
    mpa_selection->reselectRegions();
    
    positionCursor();
  
    updateSceneRect();
  
    updateVScrollBar();

    QPointF pointF = vignetteLocation(m_lastClickedVignette);
    centerOn(pointF);
  }



  void
  SequenceEditorGraphicsView::paintEvent(QPaintEvent *event)
  {
    QGraphicsView::paintEvent(event);
  }


  void 
  SequenceEditorGraphicsView::mousePressEvent(QMouseEvent *event)
  {
    mp_editorWnd->getsFocus();

    // We have to map to the whole scene coordinates
    QPointF pointF = mapToScene(event->pos());
  
    int index = vignetteIndex(pointF);
  
    if (index <= - 1)
      {
	event->accept();
	return;
      }

    if (index > mp_polymer->size())
      {
	event->accept();
	return;
      }

    m_lastClickedVignette = index;
  
    bool multiRegionSelection = mp_editorWnd->isMultiRegionSelection();
    bool multiSelectionRegion = mp_editorWnd->isMultiSelectionRegion();
        
    if (event->buttons() & Qt::LeftButton)
      {
	if(!m_kbdCtrlDown || !multiRegionSelection)
	  mpa_selection->deselectRegions();

	if(m_kbdShiftDown)
	  {
	    // Usually, in text editors, when the cursor is somewhere
	    // and that the user presses the shift key while clicking
	    // the mouse in some other place, it is considered that the
	    // user wanted to select the text between the first cursor
	    // position and the newly pointed position.  Thus we set the
	    // second selection index to the present value.
	  
	    m_selectionSecondPoint = pointF;

	    // Immediately draw the selection rectangle and set the
	    // pressed position to be the next round's first selection
	    // point.

            // qDebug() << __FILE__ << __LINE__
            //          << "m_selectionFirstPoint: "
            //          << m_selectionFirstPoint
            //          << "m_selectionSecondPoint: "
            //          << m_selectionSecondPoint;
            
	    
	    mpa_selection->selectRegion(m_selectionFirstPoint, 
					 m_selectionSecondPoint,
					 multiRegionSelection,
					 multiSelectionRegion);
	  }
	
	m_selectionFirstPoint = pointF;
	m_selectionSecondPoint = pointF;
	      
	positionCursor(index);

	// Not required, really, seems.
	// m_mouseDragging = true;
      }

    if (event->buttons() & Qt::MidButton)
      {
	mpa_selection->deselectRegions();
    
	mp_editorWnd->clipboardPaste(QClipboard::Selection);
      }
  
    //   qDebug() <<__FILE__ << __LINE__
    //      << QTime::currentTime()
    //      << "mousePressEvent"
    // 	    << "selectionFirstIndex:" << m_selectionFirstIndex
    // 	    << "selectionSecondIndex:" << m_selectionSecondIndex
    // 	    << "m_selectionFirstPoint" << m_selectionFirstPoint
    // 	    << "m_selectionSecondPoint" << m_selectionSecondPoint;
  
    mp_editorWnd->updateSelectedSequenceMasses();

    event->accept();
  }


  void 
  SequenceEditorGraphicsView::mouseReleaseEvent(QMouseEvent *event)
  {
    mp_editorWnd->getsFocus();

    // We have to map to the whole scene coordinates
    QPointF pointF = mapToScene(event->pos());

    int index = vignetteIndex(pointF);
  
    if (index > -1)
      m_lastClickedVignette = index;
    else
      {
	event->accept();
	return;
      }

    m_ongoingMouseMultiSelection = false;

    //   qDebug() <<__FILE__ << __LINE__
    // << "m_ongoingMouseMultiSelection set to:" <<
    //       m_ongoingMouseMultiSelection;
    
  
    //   qDebug() << "mouseReleaseEvent"
    // 	    << QTime::currentTime()
    //      << "selectionFirstIndex:" << m_selectionFirstIndex
    // 	    << "selectionSecondIndex:" << m_selectionSecondIndex
    // 	    << "m_selectionFirstPoint" << m_selectionFirstPoint
    // 	    << "m_selectionSecondPoint" << m_selectionSecondPoint;

    //    mpa_selection->debugSelectionPutStdErr();
    
    event->accept();
  }

  void 
  SequenceEditorGraphicsView::mouseMoveEvent(QMouseEvent *event)
  {
    mp_editorWnd->getsFocus();

    // If there is no sequence, then nothing to do.
    if (!mp_polymer->size())
      {
	event->accept();
	return;
      }
    
    // We have to map to the whole scene coordinates
    QPointF pointF = mapToScene(event->pos());
  
    int index = vignetteIndex(pointF);

//     qDebug() << __FILE__ << __LINE__ 
// 	      << QTime::currentTime()
// 	      << "mouseMoveEvent" << "index:" << index;
    
    if (index < 0)
      {
	event->accept();
	return;
      }

    if (index > mp_polymer->size())
      {
	event->accept();
	return;
      }

    if (index + 1 < mp_polymer->size())
      mp_editorWnd->updateMonomerPosition(index + 1);
    else
      mp_editorWnd->updateMonomerPosition(mp_polymer->size());
    
    bool multiRegionSelection = mp_editorWnd->isMultiRegionSelection();
    bool multiSelectionRegion = mp_editorWnd->isMultiSelectionRegion();
  
    if (event->buttons() & Qt::LeftButton)
      {
	m_selectionSecondPoint = pointF;
      

	////////////// Debugging ///////////////
// 	int firstPointIndex = vignetteIndex(m_selectionFirstPoint);
// 	int secondPointIndex = vignetteIndex(m_selectionSecondPoint);
	
// 	qDebug() << __FILE__ << __LINE__ 
// 		  << "selectRegion with indices:"
// 		  << firstPointIndex << m_selectionFirstPoint
// 		  << secondPointIndex << m_selectionSecondPoint;
	////////////// Debugging ///////////////

	
	
	if(!m_kbdCtrlDown)
	  {
	    mpa_selection->deselectRegions();
	    
	    mpa_selection->selectRegion(m_selectionFirstPoint, 
					 m_selectionSecondPoint,
					 multiRegionSelection,
					 multiSelectionRegion);
	  }
	else
	  {
	    // We should be incrementing the last selected region, and
	    // not adding larger regions on top of the ones previously
	    // selected. Thus remove the lastly selected region and
	    // update the selected region.

	    if (m_ongoingMouseMultiSelection)
	      {
		mpa_selection->deselectLastRegion();
	      }
	    
	    mpa_selection->selectRegion(m_selectionFirstPoint, 
					 m_selectionSecondPoint,
					 multiRegionSelection,
					 multiSelectionRegion);
	    
	    m_ongoingMouseMultiSelection = true;
	  }
	
// 	qDebug() << __FILE__ << __LINE__ 
// 		  << "positioning cursor at index:" << index;
	
	positionCursor(index);

	if(event->pos().y() >= rect().height() - 
	   (m_requestedVignetteSize / 4))
	  vScrollBarActionTriggered(QAbstractSlider::SliderSingleStepAdd);
	else if (event->pos().y() <=(m_requestedVignetteSize / 4))
	  vScrollBarActionTriggered(QAbstractSlider::SliderSingleStepSub);

	mp_editorWnd->updateSelectedSequenceMasses();

        // Finally update the selection clipboard
        mp_editorWnd->clipboardCopy(QClipboard::Selection);
      }
  
    event->accept();
  }

} // namespace massXpert
