
// -*- C++ -*-
//
// This file is part of the Coriolis Software.
// Copyright (c) UPMC/LIP6 2008-2010, All Rights Reserved
//
// ===================================================================
//
// $Id$
//
// x-----------------------------------------------------------------x 
// |                                                                 |
// |                   C O R I O L I S                               |
// |     V L S I   B a c k e n d   D a t a - B a s e                 |
// |                                                                 |
// |  Author      :                    Jean-Paul CHAPUT              |
// |  E-mail      :       Jean-Paul.Chaput@asim.lip6.fr              |
// | =============================================================== |
// |  C++ Module  :       "./CellWidget.cpp"                         |
// | *************************************************************** |
// |  U p d a t e s                                                  |
// |                                                                 |
// x-----------------------------------------------------------------x


#include <sys/resource.h>
#include <ctime>
#include <cmath>

#include <QApplication>
#include <QMouseEvent>
#include <QKeyEvent>
#include <QAction>
#include <QPainter>
#include <QStylePainter>
#include <QBitmap>
#include <QLabel>

#include "hurricane/SharedName.h"
#include "hurricane/DataBase.h"
#include "hurricane/Technology.h"
#include "hurricane/BasicLayer.h"
#include "hurricane/Cell.h"
#include "hurricane/Instance.h"
#include "hurricane/Slice.h"
#include "hurricane/Segment.h"
#include "hurricane/Horizontal.h"
#include "hurricane/Vertical.h"
#include "hurricane/Contact.h"
#include "hurricane/Pad.h"
#include "hurricane/RoutingPad.h"
#include "hurricane/ExtensionGo.h"

#include "hurricane/viewer/Graphics.h"
#include "hurricane/viewer/PaletteItem.h"
#include "hurricane/viewer/PaletteWidget.h"
// #include "MapView.h"
#include "hurricane/viewer/Command.h"
#include "hurricane/viewer/CellWidget.h"


namespace {


  using namespace std;


  string  getTime ( const char* format )
  {
    time_t     t  = time(NULL);
    struct tm* bt = localtime(&t);
    char       formatted[1024];
    
    strftime ( formatted, 1024, format, bt );

    return formatted;
  }


}  // End of anonymous namespace.


namespace Hurricane {


// -------------------------------------------------------------------
// Collections.


  typedef Hurricane::Filter<Occurrence>     OccurrenceHF;
  typedef Hurricane::Locator<Occurrence>    OccurrenceHL;
  typedef Hurricane::Collection<Occurrence> OccurrenceHC;


// -------------------------------------------------------------------
// Class  :  "Occurrences_IsSelectable".


  class Occurrences_IsSelectable : public OccurrenceHF {
    public:
      inline                    Occurrences_IsSelectable ( const CellWidget* );
      inline                    Occurrences_IsSelectable ( const Occurrences_IsSelectable& );
      virtual OccurrenceHF*     getClone                 () const;
      virtual bool              accept                   ( Occurrence ) const;
      virtual string            _getString               () const;
    private:
              const CellWidget* _cellWidget;    
  };


  inline Occurrences_IsSelectable::Occurrences_IsSelectable ( const CellWidget* widget )
    : OccurrenceHF()
    , _cellWidget(widget)
  { }


  inline Occurrences_IsSelectable::Occurrences_IsSelectable ( const Occurrences_IsSelectable& rhs )
    : OccurrenceHF()
    , _cellWidget(rhs._cellWidget)
  { }


  OccurrenceHF* Occurrences_IsSelectable::getClone () const
  {
    return new Occurrences_IsSelectable(_cellWidget);
  }


  bool  Occurrences_IsSelectable::accept ( Occurrence occurrence ) const
  {
    Entity* entity = occurrence.getEntity();
    if ( !entity ) return false;

    ExtensionGo* eGo = dynamic_cast<ExtensionGo*>(entity);
    if ( eGo )
      return _cellWidget->isSelectable ( eGo->getName() );

    Component* component = dynamic_cast<Component*>(entity);
    if ( component ) {
      return _cellWidget->isSelectable ( component->getLayer() );
    }

    return true;
  }


  string  Occurrences_IsSelectable::_getString () const
  {
    return "<Occurrences_IsSelectable>";
  }


// -------------------------------------------------------------------
// Class :  "Hurricane::CellWidget::Spot".


  CellWidget::Spot::Spot ( CellWidget* cw )
    : _cellWidget(cw)
    , _spotPoint()
    , _restore(false)
    , _showSpot(true)
  { }


  void  CellWidget::Spot::restore ()
  {
    if ( _restore ) {
      _cellWidget->getDrawingPlanes().copyToScreen ( _spotPoint.x()-5, _spotPoint.y()-5, 10, 10 );
      _restore = false;
    }
  }


  void  CellWidget::Spot::setRestore ( bool state )
  { _restore = state; }


  QPoint  CellWidget::Spot::computeSpotPoint ( const QPoint& screenPoint )
  {
    Point mousePoint = _cellWidget->screenToDbuPoint ( screenPoint );
    Point  spotPoint = Point ( _cellWidget->_onCursorGrid(mousePoint.getX())
                             , _cellWidget->_onCursorGrid(mousePoint.getY())
                             );

    return _cellWidget->dbuToScreenPoint(spotPoint);
  }


  void  CellWidget::Spot::moveTo ( const QPoint& screenPoint )
  {
    restore ();
    if ( !_showSpot ) return;

    _restore = true;

    QPainter& screenPainter = _cellWidget->getDrawingPlanes().painter(PlaneId::Widget);
    _spotPoint = computeSpotPoint ( screenPoint );

    screenPainter.setPen ( Graphics::getPen("spot") );
    screenPainter.drawRect ( _spotPoint.x()-3, _spotPoint.y()-3, 6, 6 );
  }


// -------------------------------------------------------------------
// Class :  "Hurricane::CellWidget::RedrawEvent".


  CellWidget::RedrawEvent::RedrawEvent ( EventType type, int shift, CellWidget* widget )
    : _type(type)
    , _shift(shift)
  { }


// -------------------------------------------------------------------
// Class :  "Hurricane::CellWidget::RedrawManager".


  CellWidget::RedrawManager::RedrawManager ( CellWidget* widget )
    : _widget        (widget)
    , _events        ()
    , _refreshSession(0)
    , _processing    (false)
    , _interrupted   (false)
  { }


  CellWidget::RedrawManager::~RedrawManager ()
  {
    while ( !_events.empty() ) {
      delete _events.front ();
      _events.pop_front ();
    }
  }


  void  CellWidget::RedrawManager::goLeft ( int shift )
  {
#ifdef ALLOW_REQUEST_INTERRUPT
    list<RedrawEvent*>::iterator ievent = _events.end();
    if ( !_events.empty() && (_events.back()->getType() == RedrawEvent::Refresh) )
      --ievent;
    _events.insert ( ievent, new RedrawEvent(RedrawEvent::GoLeft,shift,_widget) );
#else
    _events.push_back ( new RedrawEvent(RedrawEvent::GoLeft,shift,_widget) );
#endif

    if ( !_processing ) process ();
  }


  void  CellWidget::RedrawManager::goRight ( int shift )
  {
#ifdef ALLOW_REQUEST_INTERRUPT
    list<RedrawEvent*>::iterator ievent = _events.end();
    if ( !_events.empty() && (_events.back()->getType() == RedrawEvent::Refresh) )
      --ievent;
    _events.insert ( ievent, new RedrawEvent(RedrawEvent::GoRight,shift,_widget) );
#else
    _events.push_back ( new RedrawEvent(RedrawEvent::GoRight,shift,_widget) );
#endif

    if ( !_processing ) process ();
  }


  void  CellWidget::RedrawManager::goUp ( int shift )
  {
#ifdef ALLOW_REQUEST_INTERRUPT
    list<RedrawEvent*>::iterator ievent = _events.end();
    if ( !_events.empty() && (_events.back()->getType() == RedrawEvent::Refresh) )
      --ievent;
    _events.insert ( ievent, new RedrawEvent(RedrawEvent::GoUp,shift,_widget) );
#else
    _events.push_back ( new RedrawEvent(RedrawEvent::GoUp,shift,_widget) );
#endif

    if ( !_processing ) process ();
  }


  void  CellWidget::RedrawManager::goDown ( int shift )
  {
#ifdef ALLOW_REQUEST_INTERRUPT
    list<RedrawEvent*>::iterator ievent = _events.end();
    if ( !_events.empty() && (_events.back()->getType() == RedrawEvent::Refresh) )
      --ievent;
    _events.insert ( ievent, new RedrawEvent(RedrawEvent::GoDown,shift,_widget) );
#else
    _events.push_back ( new RedrawEvent(RedrawEvent::GoDown,shift,_widget) );
#endif

    if ( !_processing ) process ();
  }


  void  CellWidget::RedrawManager::refresh ()
  {
#ifdef ALLOW_REQUEST_INTERRUPT
    if ( !_events.empty() && (_events.back()->getType() == RedrawEvent::Refresh) ) {
      _interrupted = true;
    } else if ( _events.empty() ) {
      _events.push_back ( new RedrawEvent(RedrawEvent::Refresh,0,_widget) );

      if ( !_processing ) process ();
    }
#else
    bool addRefresh = true;
    if ( _refreshSession ) {
      list<RedrawEvent*>::iterator ievent = _events.begin();
      for ( ; ievent != _events.end() ; ievent++ ) {
        if ( (_events.back()->getType() == RedrawEvent::Refresh) ) {
          addRefresh = false;
          break;
        }
      }
    }
    if ( addRefresh )
      _events.push_back ( new RedrawEvent(RedrawEvent::Refresh,0,_widget) );

    if ( !_processing && (_refreshSession == 0) ) process ();
#endif
  }


  void  CellWidget::RedrawManager::process ()
  {
    _processing = true;

    while ( !_events.empty() ) {
      RedrawEvent* event = _events.front ();

      switch ( event->getType() ) {
        case RedrawEvent::GoLeft:  _widget->_goLeft  ( event->getShift() ); break;
        case RedrawEvent::GoRight: _widget->_goRight ( event->getShift() ); break;
        case RedrawEvent::GoUp:    _widget->_goUp    ( event->getShift() ); break;
        case RedrawEvent::GoDown:  _widget->_goDown  ( event->getShift() ); break;
        case RedrawEvent::Refresh: _widget->_refresh (); break;
      }

#ifdef ALLOW_REQUEST_INTERRUPT
      if ( event->getType() == RedrawEvent::Refresh ) {
        _events.pop_back ();
      } else
        _events.pop_front ();

      delete event;

      if ( _events.empty() && _interrupted ) {
        _events.push_back ( new RedrawEvent(RedrawEvent::Refresh,0,_widget) );
        _interrupted = false;
      }
#else
      delete _events.front ();
      _events.pop_front ();
#endif
    }

    _processing = false;
  }


// -------------------------------------------------------------------
// Class :  "Hurricane::CellWidget::DrawingPlanes".


  const int  CellWidget::DrawingPlanes::_cartoucheWidth  = 743;
  const int  CellWidget::DrawingPlanes::_cartoucheHeight = 80;
  const int  CellWidget::DrawingPlanes::_titleHeight     = 60;


  CellWidget::DrawingPlanes::DrawingPlanes ( const QSize& size, CellWidget* cw )
    : _cellWidget(cw)
    , _printer(NULL)
    , _image(NULL)
    , _normalPen()
    , _linePen()
    , _workingPlane(0)
    , _lineMode(false)
  {
    for ( size_t i=0 ; i<2 ; i++ )
      _planes[i] = new QPixmap ( size );
  }


  CellWidget::DrawingPlanes::~DrawingPlanes ()
  {
    for ( size_t i=0 ; i<2 ; i++ ) {
      if ( _painters[i].isActive() ) _painters[i].end();
      delete _planes[i];
    }
  }


  void  CellWidget::DrawingPlanes::setPen ( const QPen& pen )
  {
    _normalPen = pen;
    _linePen   = pen;
    _linePen.setStyle ( Qt::SolidLine );
    _linePen.setWidth ( 1 );
 
    if ( _lineMode ) painter().setPen ( _linePen );
    else             painter().setPen ( _normalPen );
  }


  void  CellWidget::DrawingPlanes::setBrush ( const QBrush& brush )
  {
    painter().setBrush ( brush );
  }


  void  CellWidget::DrawingPlanes::setBackground ( const QBrush& brush )
  {
    painter().setBackground ( brush );
    painter().setBackground ( brush );
  }


  void  CellWidget::DrawingPlanes::setBackgroundMode ( Qt::BGMode mode )
  {
    painter().setBackgroundMode ( mode );
    painter().setBackgroundMode ( mode );
  }


  void  CellWidget::DrawingPlanes::setLineMode ( bool mode )
  {
    if ( _lineMode != mode ) {
      _lineMode = mode;
      if ( _lineMode ) painter().setPen ( _linePen );
      else             painter().setPen ( _normalPen );
    }
  }


  void  CellWidget::DrawingPlanes::resize ( const QSize& size )
  {
    for ( size_t i=0 ; i<2 ; i++ ) {
      bool activePainter = _painters[i].isActive();

      if ( activePainter ) _painters[i].end();
      delete _planes[i];

      _planes[i] = new QPixmap ( size );
      if ( activePainter ) _painters[i].begin ( _planes[i] );
    }
  }


  void  CellWidget::DrawingPlanes::shiftLeft ( int dx )
  {
    buffersBegin ();
    _painters[PlaneId::Normal   ].drawPixmap ( dx, 0, *_planes[PlaneId::Normal   ], 0, 0, width()-dx, height() );
    _painters[PlaneId::Selection].drawPixmap ( dx, 0, *_planes[PlaneId::Selection], 0, 0, width()-dx, height() );
    buffersEnd ();
  }


  void  CellWidget::DrawingPlanes::shiftRight ( int dx )
  {
    buffersBegin ();
    _painters[PlaneId::Normal   ].drawPixmap ( 0, 0, *_planes[PlaneId::Normal   ], dx, 0, width()-dx, height() );
    _painters[PlaneId::Selection].drawPixmap ( 0, 0, *_planes[PlaneId::Selection], dx, 0, width()-dx, height() );
    buffersEnd ();
  }


  void  CellWidget::DrawingPlanes::shiftUp ( int dy )
  {
    buffersBegin ();
    _painters[PlaneId::Normal   ].drawPixmap ( 0, dy, *_planes[PlaneId::Normal   ], 0, 0, width(), height()-dy );
    _painters[PlaneId::Selection].drawPixmap ( 0, dy, *_planes[PlaneId::Selection], 0, 0, width(), height()-dy );
    buffersEnd ();
  }


  void  CellWidget::DrawingPlanes::shiftDown ( int dy )
  {
    buffersBegin ();
    _painters[PlaneId::Normal   ].drawPixmap ( 0, 0, *_planes[PlaneId::Normal   ], 0, dy, width(), height()-dy );
    _painters[PlaneId::Selection].drawPixmap ( 0, 0, *_planes[PlaneId::Selection], 0, dy, width(), height()-dy );
    buffersEnd ();
  }


  void  CellWidget::DrawingPlanes::copyToSelect ( int sx, int sy, int w, int h )
  {
    begin ( PlaneId::Selection );
    _painters[PlaneId::Selection].setPen        ( Qt::NoPen );
    _painters[PlaneId::Selection].setBackground ( Graphics::getBrush("background") );
    _painters[PlaneId::Selection].eraseRect     ( sx, sy, w, h );
  //_painters[PlaneId::Selection].setOpacity    ( 0.5 );
    _painters[PlaneId::Selection].drawPixmap    ( sx, sy, *_planes[PlaneId::Normal], sx, sy, w, h );
    end ( PlaneId::Selection );
  }


  void  CellWidget::DrawingPlanes::copyToScreen ( int sx, int sy, int w, int h )
  {
    if ( _cellWidget->showSelection() )
      _painters[PlaneId::Widget].drawPixmap
        ( sx, sy
        , *_planes[PlaneId::Selection]
        , _cellWidget->getOffsetVA().rx()+sx, _cellWidget->getOffsetVA().ry()+sy
        , w, h
        );
    else
      _painters[PlaneId::Widget].drawPixmap
        ( sx, sy
        , *_planes[PlaneId::Normal]
        , _cellWidget->getOffsetVA().rx()+sx, _cellWidget->getOffsetVA().ry()+sy
        , w, h
        );
  }


  void  CellWidget::DrawingPlanes::drawCartouche ( int right
                                                 , int bottom
                                                 , const string& title
                                                 , const string& area
                                                 )
  {
    QFont font ( "Bitstream Vera Sans", 18 );
    font.setWeight ( QFont::Bold );

    string user = getenv ( "USER" );
    if ( user.empty() ) user = "unkown";
    else {
      size_t equal = user.find('=');
      if ( equal != string::npos )
        user.erase ( 0, equal );
    }

    string date = getTime ( "%d %b %Y" );

    QPen cartouchePen = QPen ( QBrush(QColor("black")), 1.0 );
    _painters[PlaneId::Printer].setPen ( cartouchePen );

    QRect cartoucheRect = QRect ( right  - _cartoucheWidth
                                , bottom - _cartoucheHeight
                                , _cartoucheWidth
                                , _cartoucheHeight
                                );
    QRect titleRect = cartoucheRect;
    titleRect.adjust ( 0, 0, 0,  _titleHeight - _cartoucheHeight );

    cartouchePen.setWidth ( 2 );
    _painters[PlaneId::Printer].setPen   ( cartouchePen );
    _painters[PlaneId::Printer].drawRect ( cartoucheRect );
    cartouchePen.setWidth ( 1 );
    _painters[PlaneId::Printer].setPen   ( cartouchePen );
    _painters[PlaneId::Printer].drawLine ( titleRect.bottomLeft(), titleRect.bottomRight() );
    _painters[PlaneId::Printer].setFont  ( font );
    _painters[PlaneId::Printer].drawText ( titleRect, Qt::AlignVCenter|Qt::AlignHCenter, title.c_str() );

    QRect fieldRect = QRect ( cartoucheRect.x()
                            , cartoucheRect.y() + _titleHeight
                            , 100
                            , _cartoucheHeight - _titleHeight
                            );
    font.setPointSize ( 11 );
    _painters[PlaneId::Printer].setFont  ( font );
    _painters[PlaneId::Printer].drawLine ( fieldRect.topRight(), fieldRect.bottomRight() );
    _painters[PlaneId::Printer].drawText ( fieldRect, Qt::AlignVCenter|Qt::AlignHCenter, user.c_str() );

    fieldRect = QRect ( cartoucheRect.x() + 100
                      , cartoucheRect.y() + _titleHeight
                      , 120
                      , _cartoucheHeight - _titleHeight
                      );
    font.setWeight ( QFont::Normal );
    _painters[PlaneId::Printer].setFont  ( font );
    _painters[PlaneId::Printer].drawLine ( fieldRect.topRight(), fieldRect.bottomRight() );
    _painters[PlaneId::Printer].drawText ( fieldRect, Qt::AlignVCenter|Qt::AlignHCenter, date.c_str() );

    fieldRect = QRect ( cartoucheRect.x() + 220
                      , cartoucheRect.y() + _titleHeight
                      , 300
                      , _cartoucheHeight - _titleHeight
                      );
    _painters[PlaneId::Printer].setFont  ( font );
    _painters[PlaneId::Printer].drawLine ( fieldRect.topRight(), fieldRect.bottomRight() );
    _painters[PlaneId::Printer].drawText ( fieldRect, Qt::AlignVCenter|Qt::AlignHCenter, area.c_str() );
  }


  void  CellWidget::DrawingPlanes::copyToPrinter ( int sx, int sy, int w, int h, QPrinter* printer, bool imageOnly )
  {
    if ( !printer ) return;
    _printer = printer;
    _printer->setPageMargins ( 0.0, 0.0, 0.0, 0.0, QPrinter::DevicePixel );

    int paperWidth    = _printer->width ();
    int paperHeight   = _printer->height ();
    int frameMargin   = 25;
    int drawingWidth  = paperWidth  - (frameMargin<<1);
    int drawingHeight = paperHeight - (frameMargin<<1);
    int ximage        = 0;
    int yimage        = 0;

  // Substract the cartouche size only for A4 format.
    if ( _printer->paperSize  () == QPrinter::A4 ) {
      if ( _printer->orientation() == QPrinter::Landscape ) {
        drawingWidth -= _cartoucheHeight;
      } else {
        drawingHeight -= _cartoucheHeight;
      }
    }

    if ( imageOnly ) {
      _printer->setPaperSize ( QSizeF(w,h), QPrinter::DevicePixel );
    } else {
      ximage = frameMargin + ((drawingWidth  > w) ? (drawingWidth -w)/2 : 0);
      yimage = frameMargin + ((drawingHeight > h) ? (drawingHeight-h)/2 : 0);
    }

    begin ( PlaneId::Printer );

    if ( _cellWidget->showSelection() )
      _painters[PlaneId::Printer].drawPixmap
        ( ximage, yimage
        , *_planes[PlaneId::Selection]
        , _cellWidget->getOffsetVA().rx()+sx, _cellWidget->getOffsetVA().ry()+sy
        , w, h
        );
    else
      _painters[PlaneId::Printer].drawPixmap
        ( ximage, yimage
        , *_planes[PlaneId::Normal]
        , _cellWidget->getOffsetVA().rx()+sx, _cellWidget->getOffsetVA().ry()+sy
        , w, h
        );

    if ( !imageOnly ) {
      DbU::Unit x1 = _cellWidget->displayToDbuX ( sx );
      DbU::Unit x2 = _cellWidget->displayToDbuX ( sx+w );
      DbU::Unit y1 = _cellWidget->displayToDbuY ( sy );
      DbU::Unit y2 = _cellWidget->displayToDbuY ( sy+h );

      string title = getString(_cellWidget->getCell()->getName());
      string area  = "Area: [" + DbU::getValueString(x1)
                   + " "       + DbU::getValueString(y1)
                   + "] ["     + DbU::getValueString(x2)
                   + " "       + DbU::getValueString(y2)
                   + "]";

      QPen framePen = QPen ( QBrush(QColor("black")), 1.0 );
      _painters[PlaneId::Printer].setPen ( framePen );
      _painters[PlaneId::Printer].drawRect ( frameMargin
                                           , frameMargin
                                           , paperWidth  - (frameMargin<<1)
                                           , paperHeight - (frameMargin<<1)
                                           );

      if (    (_printer->paperSize  () == QPrinter::A4)
           && (_printer->orientation() == QPrinter::Landscape) ) {
        _painters[PlaneId::Printer].translate ( paperWidth - frameMargin, frameMargin );
        _painters[PlaneId::Printer].rotate ( -90 );
      } else
        _painters[PlaneId::Printer].translate ( paperWidth - frameMargin, paperHeight - frameMargin );

      drawCartouche ( 0, 0, title , area );
    }

    end ( PlaneId::Printer );
    _printer = NULL;
  }


  void  CellWidget::DrawingPlanes::copyToImage ( int sx, int sy, int w, int h, QImage* image, bool noScale )
  {
    int   ximage    = 0;
    int   yimage    = 0;

    if ( !image ) return;
    _image = image;

    begin ( PlaneId::Image );

    _painters[PlaneId::Image].setRenderHint ( QPainter::Antialiasing, false );
    if ( _cellWidget->showSelection() ) {
        _painters[PlaneId::Image].drawPixmap
          ( ximage, yimage
          , *_planes[PlaneId::Selection]
          , _cellWidget->getOffsetVA().rx()+sx, _cellWidget->getOffsetVA().ry()+sy
          , w, h
          );
    } 
    else {
        _painters[PlaneId::Image].drawPixmap
          ( ximage, yimage
          , *_planes[PlaneId::Normal]
          , _cellWidget->getOffsetVA().rx()+sx, _cellWidget->getOffsetVA().ry()+sy
          , w, h
          );
    }

    if ( !noScale ) {
        int xGradient = (w-510)/2;
        _painters[PlaneId::Image].setPen(Qt::white);
        _painters[PlaneId::Image].drawRect(xGradient-1, h+9, 512, 31);
        _painters[PlaneId::Image].setPen(Qt::NoPen);
        for ( unsigned i = 0 ; i < 256 ; i++ ) {
            _painters[PlaneId::Image].setBrush(Graphics::getColorScale(ColorScale::Fire).getBrush(i,100) );
            _painters[PlaneId::Image].drawRect(xGradient+(i*2), h+10, 2, 30);
            if ( i==0 || i==51 || i==102 || i==153 || i==204 || i==255 ) {
                QRect tArea (xGradient+(i*2)-15, h+44, 30, 12);
                std::ostringstream oss;
                oss << (float)(i)/255;
                _painters[PlaneId::Image].setPen(Qt::white);
                _painters[PlaneId::Image].drawLine(xGradient+(i*2), h+38, xGradient+(i*2), h+42);
                _painters[PlaneId::Image].drawText(tArea, Qt::AlignCenter, oss.str().c_str());
                _painters[PlaneId::Image].setPen(Qt::NoPen);
            }
        }
    }

    end ( PlaneId::Image );
    _image = NULL;
  }


// -------------------------------------------------------------------
// Class :  "Hurricane::CellWidget::DrawingQuery".


  CellWidget::DrawingQuery::DrawingQuery ( CellWidget* widget )
    : Query()
    , _cellWidget(widget)
    , _drawExtensionGo(NULL)
    , _goCount(0)
    , _extensionGoCount(0)
    , _instanceCount(0)
  { }


  void  CellWidget::DrawingQuery::setDrawExtensionGo ( const Name& name )
  {
    map<Name,pair<InitExtensionGo_t*,DrawExtensionGo_t*> >::iterator idraw
      = _drawExtensionGos.find ( name );

    if ( idraw != _drawExtensionGos.end() ) {
      _drawExtensionGo = idraw->second.second;
      if ( idraw->second.first )
        idraw->second.first ( _cellWidget );
    } else
      _drawExtensionGo = NULL;
  }


  bool  CellWidget::DrawingQuery::hasMasterCellCallback () const
  { return true; }


  void  CellWidget::DrawingQuery::masterCellCallback ()
  {
    _instanceCount++;

    drawMasterCell ( getMasterCell(), getTransformation() );
  }

  void CellWidget::DrawingQuery::drawMasterCell ( const Cell*            cell
                                                , const Transformation&  transformation
                                                )
  {
    Box bbox = transformation.getBox(cell->getAbutmentBox());
    _cellWidget->drawBox ( bbox );
  }


  bool  CellWidget::DrawingQuery::hasGoCallback () const
  {
    return true;
  }


  void  CellWidget::DrawingQuery::goCallback ( Go* go )
  {
    drawGo ( go, getBasicLayer(), getArea(), getTransformation() );
  }


  void  CellWidget::DrawingQuery::drawGo ( const Go*              go
                                         , const BasicLayer*      basicLayer
                                         , const Box&             area
                                         , const Transformation&  transformation
                                         )
  {
  //cerr << "DrawingQuery::drawGo() - " << go << endl;

    static QRect         rectangle;
    static unsigned int  state;

    const Component* component = dynamic_cast<const Component*>(go);
    if ( component ) {
      _goCount++;
      Box bb = transformation.getBox(component->getBoundingBox(basicLayer));
      rectangle = _cellWidget->dbuToDisplayRect ( bb );
      state     = ( (rectangle.width() > 2) ? 1:0) | ( (rectangle.height() > 2) ? 2:0); 
      switch ( state ) {
        case 0: break;
        case 1: _cellWidget->drawScreenLine ( rectangle.bottomLeft(), rectangle.bottomRight() ); break;
        case 2: _cellWidget->drawScreenLine ( rectangle.bottomLeft(), rectangle.topLeft    () ); break;
        case 3: _cellWidget->drawScreenRect ( rectangle ); break;
      }

      if ( _cellWidget->isDrawable("text.component")
         and (getDepth() < 2)
         and (rectangle.width () > 30)
         and (rectangle.height() > 30) ) {
        const Net* net = component->getNet();
        if ( not net->isAutomatic() ) {
          const char* netName = net->getName()._getSharedName()->_getSString().c_str();
          _cellWidget->drawDisplayText ( rectangle, netName, BigFont|Bold|Center|Frame );
        }
      }
    }
  }


  bool  CellWidget::DrawingQuery::hasExtensionGoCallback () const
  {
    return true;
  }


  void  CellWidget::DrawingQuery::extensionGoCallback ( Go* go )
  {
    drawExtensionGo ( _cellWidget, go, getBasicLayer(), getArea(), getTransformation() );
  }


  void  CellWidget::DrawingQuery::drawExtensionGo ( CellWidget*           widget
                                                  , const Go*             go
                                                  , const BasicLayer*     basicLayer
                                                  , const Box&            area
                                                  , const Transformation& transformation
                                                  )
  {
    if ( _drawExtensionGo ) {
      _extensionGoCount++;
      _drawExtensionGo ( widget, go, basicLayer, area, transformation );
    }
  }


  bool  CellWidget::DrawingQuery::hasMarkerCallback () const
  { return true; }


  void  CellWidget::DrawingQuery::markerCallback ( Marker* marker )
  { drawMarker ( marker, getArea(), getTransformation() ); }


  void  CellWidget::DrawingQuery::drawMarker ( const Marker*          marker
                                             , const Box&             area
                                             , const Transformation&  transformation
                                             )
  {
    static QRect  rectangle;

    const Reference* reference = dynamic_cast<const Reference*>(marker);
    if ( reference and _cellWidget->isDrawable("text.reference") and (getDepth() < 2) ) {
      _goCount++;
      unsigned int flags = BigFont|Bold|Frame;

      Box bb = transformation.getBox ( reference->getBoundingBox() );
      rectangle = _cellWidget->dbuToDisplayRect ( bb );
    //rectangle.adjust ( 10, 10, 10, 10 );

      if ( reference->getType() == Reference::Position ) {
        QPoint point = _cellWidget->dbuToDisplayPoint ( reference->getPoint() );
        rectangle.translate ( point.x() - rectangle.x(), point.y() - rectangle.y() );

        flags |= Left;
      } else {
        flags |= Center/*|Rounded*/;
      }

      const char* refName = reference->getName()._getSharedName()->_getSString().c_str();
      _cellWidget->drawDisplayText ( rectangle, refName, flags );

      if ( reference->getType() == Reference::Position ) {
        QPoint losange [5] = { QPoint(rectangle.x()  ,rectangle.y()-6)
                             , QPoint(rectangle.x()-6,rectangle.y()  )
                             , QPoint(rectangle.x()  ,rectangle.y()+6)
                             , QPoint(rectangle.x()+6,rectangle.y()  )
                             , QPoint(rectangle.x()  ,rectangle.y()-6)
                             };
        _cellWidget->drawScreenPolyline ( losange, 5, 2 );
      }
    }
  }


  bool  CellWidget::DrawingQuery::hasRubberCallback () const
  { return true; }


  void  CellWidget::DrawingQuery::rubberCallback ( Rubber* rubber )
  { drawRubber ( rubber, getArea(), getTransformation() ); }


  void  CellWidget::DrawingQuery::drawRubber ( const Rubber*          rubber
                                             , const Box&             area
                                             , const Transformation&  transformation
                                             )
  {
    static QPoint center;
    static QPoint extremity;
    static QPoint steiner;

    switch ( _cellWidget->getRubberShape() ) {
      case CellWidget::Steiner:
        center = _cellWidget->dbuToDisplayPoint(transformation.getPoint(rubber->getBarycenter()));
        forEach ( Hook*, hook, rubber->getHooks() ) {
          extremity = _cellWidget->dbuToDisplayPoint
            ( transformation.getPoint(hook->getComponent()->getCenter()) );
          steiner = QPoint ( extremity.x(), center.y() );
          _cellWidget->drawScreenLine ( center , steiner  , PlaneId::Working, false );
          _cellWidget->drawScreenLine ( steiner, extremity, PlaneId::Working, false );
        }
        break;
      case CellWidget::Barycentric:
        center = _cellWidget->dbuToDisplayPoint(transformation.getPoint(rubber->getBarycenter()));
        forEach ( Hook*, hook, rubber->getHooks() ) {
          extremity = _cellWidget->dbuToDisplayPoint
            ( transformation.getPoint(hook->getComponent()->getCenter()) );
          _cellWidget->drawScreenLine ( center, extremity, PlaneId::Working, false );
        }
        break;
      case CellWidget::Centric:
      default:
        center = _cellWidget->dbuToDisplayPoint(transformation.getPoint(rubber->getCenter()));
        forEach ( Hook*, hook, rubber->getHooks() ) {
          extremity = _cellWidget->dbuToDisplayPoint
            ( transformation.getPoint(hook->getComponent()->getCenter()) );
          _cellWidget->drawScreenLine ( center, extremity, PlaneId::Working, false );
        }
        break;
    }
  }


// -------------------------------------------------------------------
// Class :  "Hurricane::CellWidget::TextDrawingQuery".


  CellWidget::TextDrawingQuery::TextDrawingQuery ( CellWidget* widget )
    : Query()
    ,_cellWidget(widget)
  {
    setBasicLayer ( NULL );
    setFilter     ( Query::DoMasterCells|Query::DoTerminalCells );
    setStartLevel ( 0 );
    setStopLevel  ( 1 );
  }


  bool  CellWidget::TextDrawingQuery::hasMasterCellCallback () const
  { return true; }


  void  CellWidget::TextDrawingQuery::masterCellCallback ()
  {
    Box bbox = getTransformation().getBox(getMasterCell()->getAbutmentBox());
    if ( getDepth() == 2 )
      _cellWidget->drawText ( Point(bbox.getXMin(),bbox.getYMin())
                            , getString(getInstance()->getName()).c_str()
                            , Reverse|Top
                            , -90
                            );
  }


  bool  CellWidget::TextDrawingQuery::hasGoCallback () const
  { return false; }


  void  CellWidget::TextDrawingQuery::goCallback ( Go* )
  { }


  bool  CellWidget::TextDrawingQuery::hasRubberCallback () const
  { return false; }


  void  CellWidget::TextDrawingQuery::rubberCallback ( Rubber* )
  { }


  bool  CellWidget::TextDrawingQuery::hasExtensionGoCallback () const
  { return false; }


  void  CellWidget::TextDrawingQuery::extensionGoCallback ( Go* )
  { }


// -------------------------------------------------------------------
// Class :  "Hurricane::CellWidget::SelectorCriterions".


  CellWidget::SelectorCriterions::SelectorCriterions ()
    : _cellWidget(NULL)
    , _criterions()
  { }


  CellWidget::SelectorCriterions::~SelectorCriterions ()
  {
    clear ();
  }


  bool  CellWidget::SelectorCriterions::add ( const Net* net )
  {
    if ( !_cellWidget ) return false;
    if ( !_cellWidget->isSelected(Occurrence(net)) ) {
      _criterions.push_back ( new NetSelectorCriterion(net) );
      _criterions.back()->doSelection ( _cellWidget );
      return true;
    }
    return false;
  }


  bool  CellWidget::SelectorCriterions::add ( Box area )
  {
    if ( !_cellWidget ) return false;
    _criterions.push_back ( new AreaSelectorCriterion(area) );
    _criterions.back()->doSelection ( _cellWidget );
    return true;
  }


  bool  CellWidget::SelectorCriterions::remove (  const Net* net )
  {
    if ( !_cellWidget ) return false;
    if ( !_cellWidget->isSelected(Occurrence(net)) ) return false;

    size_t i=0;
    for ( ; i<_criterions.size() ; i++ )
      if ( _criterions[i]->getNet() == net ) break;

    if ( i < _criterions.size() ) {
      swap ( _criterions[i], *(_criterions.end()-1) );
      _criterions.back()->undoSelection ( _cellWidget );
      _criterions.pop_back ();
    } else
      return false;

    return true;
  }


  void  CellWidget::SelectorCriterions::clear ()
  {
    for ( size_t i=0 ; i<_criterions.size() ; i++ ) {
      delete _criterions[i];
    }
    _criterions.clear ();
  }


  void  CellWidget::SelectorCriterions::revalidate ()
  {
    if ( !_cellWidget ) return;

    size_t i    = 0;
    size_t last = _criterions.size ();
    while ( i < last ) {
      if ( _criterions[i]->isValid(_cellWidget) ) {
        _criterions[i]->doSelection ( _cellWidget );
        ++i;
      } else
        swap ( _criterions[i], _criterions[--last] );
    }
    _criterions.erase ( _criterions.begin()+last, _criterions.end() );
  }


// -------------------------------------------------------------------
// Class :  "Hurricane::CellWidget::State".


  void  CellWidget::State::setScale ( float scale )
  {
    _scaleHistory.erase ( _scaleHistory.begin()+_ihistory+1,_scaleHistory.end() );
    if ( _historyEnable ) {
      _scaleHistory.push_back ( ScaleEntry(scale,Point(0,0)) );
      _ihistory++;
    } else {
      _scaleHistory[_ihistory]._scale   = scale;
      _scaleHistory[_ihistory]._topLeft = Point(0,0);
    }
  }


  bool  CellWidget::State::scaleHistoryUp ()
  {
    if ( _ihistory == 0 ) return false;

    _ihistory--;

    return true;
  }


  bool  CellWidget::State::scaleHistoryDown ()
  {
    if ( _ihistory+2 > _scaleHistory.size() ) return false;

    _ihistory++;

    return true;
  }


  const Name& CellWidget::State::getName () const
  {
    static const Name noCell = "None";

    if ( !_cell ) return noCell;
    return _cell->getName();
  }


// -------------------------------------------------------------------
// Class :  "Hurricane::CellWidget".


  const int  CellWidget::_stripWidth  = 10;
  const int  CellWidget::_initialSide = (400/_stripWidth)*_stripWidth;


  CellWidget::CellWidget ( QWidget* parent )
    : QWidget               (parent)
    , _technology           (NULL)
    , _palette              (NULL)
    , _displayArea          (0,0,_initialSide+2*_stripWidth,_initialSide+2*_stripWidth)
    , _visibleArea          (_stripWidth,_stripWidth,_initialSide,_initialSide)
    , _offsetVA             (_stripWidth,_stripWidth)
    , _redrawManager        (this)
    , _drawingPlanes        (QSize(_initialSide+2*_stripWidth,_initialSide+2*_stripWidth),this)
    , _drawingQuery         (this)
    , _textDrawingQuery     (this)
    , _darkening            (100)
    , _mousePosition        (0,0)
    , _spot                 (this)
    , _state                (new State(NULL))
    , _cellChanged          (true)
    , _selectionHasChanged  (false)
    , _delaySelectionChanged(0)
    , _cellModificated      (true)
    , _enableRedrawInterrupt(false)
    , _selectors            ()
    , _activeCommand        (NULL)
    , _commands             ()
    , _redrawRectCount      (0)
    , _textFontHeight       (20)
  {
  //setBackgroundRole ( QPalette::Dark );
  //setAutoFillBackground ( false );
    setAttribute     ( Qt::WA_OpaquePaintEvent );
    setAttribute     ( Qt::WA_NoSystemBackground );
    setAttribute     ( Qt::WA_PaintOnScreen );
    setAttribute     ( Qt::WA_StaticContents );
    setSizePolicy    ( QSizePolicy::Expanding, QSizePolicy::Expanding );
    setFocusPolicy   ( Qt::StrongFocus );
    setMouseTracking ( true );

  //_mapView = new MapView ( this );
    DataBase* database = DataBase::getDB();
    if ( database )
      _technology = database->getTechnology ();

    QFont font = Graphics::getNormalFont();
    _textFontHeight = QFontMetrics(font).ascent();
  }


  CellWidget::~CellWidget ()
  {
    unselectAll ();

    for ( size_t i=0 ; i<_commands.size() ; i++ )
      unbindCommand ( _commands[i] );
  }


  void  CellWidget::setStyle ( int id )
  {
    Graphics::setStyle ( (size_t)id );
    refresh ();

    emit styleChanged ();
  }


  void  CellWidget::updatePalette ()
  {
    emit updatePalette(getCell());
  }


  void  CellWidget::bindToPalette ( PaletteWidget* palette )
  {
    detachFromPalette ();
    _palette = palette;

    connect ( _palette, SIGNAL(paletteChanged())    , this    , SLOT(refresh()) );
    connect ( this    , SIGNAL(cellChanged(Cell*))  , _palette, SLOT(updateExtensions(Cell*)) );
    connect ( this    , SIGNAL(updatePalette(Cell*)), _palette, SLOT(updateExtensions(Cell*)) );
    connect ( this    , SIGNAL(styleChanged())      , _palette, SLOT(changeStyle()) );
  }


  void  CellWidget::detachFromPalette ()
  {
    if ( _palette ) {
      disconnect ( _palette, SIGNAL(paletteChanged())    , this    , SLOT(refresh()) );
      disconnect ( this    , SIGNAL(cellChanged(Cell*))  , _palette, SLOT(updateExtensions(Cell*)) );
      disconnect ( this    , SIGNAL(updatePalette(Cell*)), _palette, SLOT(updateExtensions(Cell*)) );
      disconnect ( this    , SIGNAL(styleChanged(void*)) , _palette, SLOT(styleChange(void*)) );
      _palette = NULL;
    }
  }


  void  CellWidget::bindCommand ( Command* command )
  {
    for ( size_t i=0 ; i<_commands.size() ; i++ )
      if ( _commands[i] == command ) return;

    command->setCellWidget ( this );
    _commands.push_back ( command );
  }


  void  CellWidget::unbindCommand ( Command* command )
  {
    size_t i = 0;
    for ( ; i<_commands.size() ; i++ )
      if ( _commands[i] == command ) break;

    if ( i < _commands.size() ) _commands[i]->setCellWidget ( NULL );

    for ( ; i+1<_commands.size() ; i++ )
      _commands[i] = _commands[i+1];

    _commands.pop_back ();
  }


  void  CellWidget::pushCursor ( Qt::CursorShape cursor )
  {
    setCursor ( cursor );
    _cursors.push_back ( cursor );
  }


  void  CellWidget::popCursor ()
  {
    _cursors.pop_back ();
    if ( !_cursors.empty() )
      setCursor ( _cursors.back() );
    else
      unsetCursor ();
  }


  QSize  CellWidget::minimumSizeHint () const
  {
    return QSize(_initialSide,_initialSide);
  }


  void  CellWidget::rubberChange ()
  {
    switch ( getRubberShape() ) {
      case Centric:     setRubberShape(Barycentric); break;
      case Barycentric: setRubberShape(Steiner    ); break;
      case Steiner:     setRubberShape(Centric    ); break;
    }
  }


  void  CellWidget::changeDbuMode ( unsigned int mode, DbU::UnitPower p )
  {
    if ( (_state->getDbuMode() != mode) or (_state->getUnitPower() != p) ) {
      _state->setDbuMode   ( mode );
      _state->setUnitPower ( p );
      
      DbU::setStringMode ( mode, p );

      updateMousePosition ();
      refresh ();

      emit dbuModeChanged ( _state->getDbuMode(), _state->getUnitPower() );
    }
  }


  void  CellWidget::setShowSelection ( bool state )
  {
    if ( state != _state->showSelection() ) {
      _state->setShowSelection ( state );
      _selectionHasChanged = false;
      refresh ();

      emit selectionModeChanged ();
    }
  }


  void  CellWidget::setCumulativeSelection ( bool state )
  {
    if ( state != _state->cumulativeSelection() ) {
      _state->setCumulativeSelection ( state );
      emit selectionModeChanged ();
    }
  }


  void  CellWidget::setShowBoundaries ( bool state )
  {
    if ( _state->showBoundaries() != state ) {
      _state->setShowBoundaries ( state );
      _redrawManager.refresh ();

      emit showBoundariesToggled ( state );
    }
  }


  void  CellWidget::changeQueryFilter ()
  {
    _redrawManager.refresh ();
    emit queryFilterChanged ();
  }


  void  CellWidget::_redraw ( QRect redrawArea )
  {
//     cerr << "CellWidget::redraw() - start "
//          << _selectionHasChanged << " filter:"
//          << _state->getQueryFilter() << endl;

//     static bool  timedout;
//     static Timer timer;

    if ( !isVisible() ) return;

//     timer.start ();
//     timedout         = false;
    _cellChanged     = false;
    _redrawRectCount = 0;

    pushCursor ( Qt::BusyCursor );

    if ( ! ( _selectionHasChanged && _state->showSelection() ) || _cellModificated ) {
      _spot.setRestore ( false );
    //_drawingPlanes.copyToSelect ( redrawArea );
      _drawingPlanes.select ( PlaneId::Normal );
      _drawingPlanes.begin  ();
      _drawingPlanes.painter().setPen        ( Qt::NoPen );
      _drawingPlanes.painter().setBackground ( Graphics::getBrush("background") );
      _drawingPlanes.painter().setClipRect   ( redrawArea );
      _drawingPlanes.painter().eraseRect     ( redrawArea );

      setDarkening ( (_state->showSelection()) ? Graphics::getDarkening() : 100 );

      if ( getCell() ) {

        Box redrawBox = displayToDbuBox ( redrawArea );

        _drawingQuery.resetGoCount      ();
        _drawingQuery.resetExtensionGoCount();
        _drawingQuery.resetInstanceCount();
        _drawingQuery.setExtensionMask  ( 0 );
        _drawingQuery.setArea           ( redrawBox );
        _drawingQuery.setTransformation ( Transformation() );

        forEach ( BasicLayer*, iLayer, _technology->getBasicLayers() ) {
          _drawingPlanes.setPen   ( Graphics::getPen  ((*iLayer)->getName(),getDarkening()) );
          _drawingPlanes.setBrush ( Graphics::getBrush((*iLayer)->getName(),getDarkening()) );

          if ( isDrawable((*iLayer)->getName()) ) {
            _drawingQuery.setBasicLayer ( *iLayer );
            _drawingQuery.setFilter     ( getQueryFilter().unset(Query::DoMasterCells|Query::DoRubbers|Query::DoMarkers) );
            _drawingQuery.doQuery       ();
          }
          if ( _enableRedrawInterrupt ) QApplication::processEvents();
          if ( _redrawManager.interrupted() ) {
          //cerr << "CellWidget::redraw() - interrupt after " << (*iLayer)->getName() << endl;
            break;
          }
        //if ( timeout("redraw [layer]",timer,10.0,timedout) ) break;
        }

        if ( /*!timeout("redraw [boundaries]",timer,10.0,timedout) &&*/ (!_redrawManager.interrupted()) ) {
          if ( isDrawable("boundaries") ) {
             _drawingPlanes.setPen   ( Graphics::getPen  ("boundaries",getDarkening()) );
             _drawingPlanes.setBrush ( Graphics::getBrush("boundaries",getDarkening()) );

             _drawingQuery.setBasicLayer ( NULL );
             _drawingQuery.setFilter     ( getQueryFilter().unset(Query::DoComponents|Query::DoRubbers|Query::DoMarkers) );
             _drawingQuery.doQuery       ();
          }
        }

        if ( /*!timeout("redraw [markers]",timer,10.0,timedout) &&*/ (!_redrawManager.interrupted()) ) {
          if ( isDrawable("text.reference") ) {
             _drawingPlanes.setPen   ( Graphics::getPen  ("text.reference",getDarkening()) );
             _drawingPlanes.setBrush ( Graphics::getBrush("text.reference",getDarkening()) );

             _drawingQuery.setBasicLayer ( NULL );
             _drawingQuery.setFilter     ( getQueryFilter().unset(Query::DoComponents|Query::DoMasterCells) );
             _drawingQuery.doQuery       ();
          }
        }

        if ( /*!timeout("redraw [rubbers]",timer,10.0,timedout) &&*/ (!_redrawManager.interrupted()) ) {
          if ( isDrawable("rubber") ) {
             _drawingPlanes.setPen   ( Graphics::getPen  ("rubber",getDarkening()) );
             _drawingPlanes.setBrush ( Graphics::getBrush("rubber",getDarkening()) );

             _drawingQuery.setBasicLayer ( NULL );
             _drawingQuery.setFilter     ( getQueryFilter().unset(Query::DoComponents|Query::DoMasterCells|Query::DoMarkers) );
             _drawingQuery.doQuery       ();
          }
        }

        if ( _enableRedrawInterrupt ) QApplication::processEvents();
        if ( /*!timeout("redraw [text.instances]",timer,10.0,timedout) &&*/ (!_redrawManager.interrupted()) ) {
          if ( isDrawable("text.instance") ) {
            _drawingPlanes.setPen               ( Graphics::getPen  ("text.instance",getDarkening()) );
            _drawingPlanes.setBrush             ( Graphics::getBrush("text.instance",getDarkening()) );
            _drawingPlanes.setBackground        ( Graphics::getBrush("boundaries"   ,getDarkening()) );
            _textDrawingQuery.setArea           ( redrawBox );
            _textDrawingQuery.setTransformation ( Transformation() );
            _textDrawingQuery.doQuery           ();
          }
        }

      //_drawingQuery.setFilter ( getQueryFilter() & ~Query::DoMasterCells );
        forEach ( ExtensionSlice*, islice, getCell()->getExtensionSlices() ) {
          if ( _enableRedrawInterrupt ) QApplication::processEvents();
          if ( /*timeout("redraw [extension]",timer,10.0,timedout) ||*/ (_redrawManager.interrupted()) ) break;

          if ( isDrawableExtension((*islice)->getName()) ) {
            _drawingQuery.setExtensionMask   ( (*islice)->getMask() );
            _drawingQuery.setDrawExtensionGo ( (*islice)->getName() );
            _drawingQuery.setFilter          ( Query::DoExtensionGos );
            _drawingQuery.doQuery            ();
          }
        }
      }

      _drawingPlanes.end ();
      _cellModificated = false;
    }

    if ( isDrawable("grid")       ) drawGrid   ( redrawArea );
    if ( isDrawable("text.ruler") ) drawRulers ( redrawArea );

    setDarkening ( 100 );
    if ( _state->showSelection() )
      redrawSelection ( redrawArea );

    popCursor ();
    repaint ();

//     timer.stop ();
//     cerr << "CellWidget::redraw() - " << _redrawRectCount
//          << " in " << timer.getCombTime() << "s ("
//          << setprecision(3) << (timer.getCombTime()/_redrawRectCount) << " s/r)";
//     if ( _drawingQuery.getGoCount() )
//       cerr << " " << _drawingQuery.getGoCount()
//            << " (" << setprecision(3) << (timer.getCombTime()/_drawingQuery.getGoCount()) << " s/go)";
//     else
//       cerr << " 0 Gos";
//     if ( _drawingQuery.getExtensionGoCount() )
//       cerr << " " << _drawingQuery.getExtensionGoCount()
//            << " (" << setprecision(3) << (timer.getCombTime()/_drawingQuery.getExtensionGoCount()) << " s/ego)";
//     else
//       cerr << " 0 eGos";
//     if ( _drawingQuery.getInstanceCount() )
//       cerr << " " << _drawingQuery.getInstanceCount()
//            << " (" << setprecision(3) << (timer.getCombTime()/_drawingQuery.getInstanceCount()) << " s/inst)";
//     else
//       cerr << " 0 Instances";
//     cerr << endl;
  }


  void  CellWidget::redrawSelection ( QRect redrawArea )
  {
    _drawingPlanes.copyToSelect ( redrawArea.x()
                                , redrawArea.y()
                                , redrawArea.width()
                                , redrawArea.height()
                                );

    _drawingPlanes.select ( PlaneId::Selection );
    _drawingPlanes.begin  ();
    _drawingPlanes.painter().setPen        ( Qt::NoPen );
    _drawingPlanes.painter().setBackground ( Graphics::getBrush("background") );
    _drawingPlanes.painter().setClipRect   ( redrawArea );

    if ( getCell() ) {
      Box                    redrawBox = displayToDbuBox ( redrawArea );
      SelectorSet::iterator  iselector;

      forEach ( BasicLayer*, basicLayer, _technology->getBasicLayers() ) {
      //if ( !isDrawableLayer(basicLayer->getName()) ) continue;

        _drawingPlanes.setPen   ( Graphics::getPen  (basicLayer->getName()) );
        _drawingPlanes.setBrush ( Graphics::getBrush(basicLayer->getName()) );

        iselector = _selectors.begin();
        for ( ; iselector != _selectors.end() ; iselector++ ) {
          Occurrence occurrence = (*iselector)->getOccurrence();
          Component* component  = dynamic_cast<Component*>(occurrence.getEntity());

          if ( !component ) break;
          if ( !component->getLayer() ) continue;
          if ( !component->getLayer()->contains(*basicLayer) ) continue;

          Transformation  transformation = occurrence.getPath().getTransformation();
          _drawingQuery.drawGo ( dynamic_cast<Go*>(occurrence.getEntity())
                               , *basicLayer
                               , redrawBox
                               , transformation
                               );
        }
      }

      _drawingPlanes.setPen   ( Graphics::getPen  ("boundaries") );
      _drawingPlanes.setBrush ( Graphics::getBrush("boundaries") );

      for ( ; iselector != _selectors.end() ; iselector++ ) {
        Occurrence  occurrence = (*iselector)->getOccurrence();
        Instance*   instance   = dynamic_cast<Instance*>(occurrence.getEntity());
        if ( instance ) {
          Transformation  transformation
            = occurrence.getPath().getTransformation().getTransformation(instance->getTransformation());
          _drawingQuery.drawMasterCell ( instance->getMasterCell(), transformation );
        }
      }

      _drawingPlanes.setPen   ( Graphics::getPen  ("rubber") );
      _drawingPlanes.setBrush ( Graphics::getBrush("rubber") );

      for ( ; iselector != _selectors.end() ; iselector++ ) {
        Occurrence occurrence = (*iselector)->getOccurrence();
        Rubber*    rubber     = dynamic_cast<Rubber*>(occurrence.getEntity());

        if ( !rubber ) break;

        Transformation  transformation = occurrence.getPath().getTransformation();
        _drawingQuery.drawRubber ( rubber, redrawBox, transformation );
      }

      Name extensionName = "";
      for ( ; iselector != _selectors.end() ; iselector++ ) {
        Occurrence   occurrence = (*iselector)->getOccurrence();
        ExtensionGo* eGo        = dynamic_cast<ExtensionGo*>(occurrence.getEntity());

        if ( !eGo ) break;

        Transformation transformation = occurrence.getPath().getTransformation();
        if ( eGo->getName() != extensionName ) {
          extensionName = eGo->getName();
          _drawingQuery.setDrawExtensionGo ( extensionName );
        }

        if ( isDrawable(extensionName) )
          _drawingQuery.drawExtensionGo ( this, eGo, NULL, redrawBox, transformation );
      }
    }

    _drawingPlanes.end ();
    _selectionHasChanged = false;
  }


  void  CellWidget::setLayerVisible ( const Name& layer, bool visible )
  {
    if ( !_palette ) return;
    _palette->setItemVisible ( layer, visible ); 
  }


  bool  CellWidget::isDrawable ( const Name& name )
  {
    PaletteItem* item  = (_palette) ? _palette->find(name) : NULL;
  //DbU::Unit    unity = symbolicMode() ? DbU::lambda(1.0) : DbU::grid(10.0);
    DbU::Unit    unity = DbU::lambda(1.0);

    return (!item || item->isItemVisible())
      && ( Graphics::getThreshold(name) < getScale()*unity );
  }


  bool  CellWidget::isDrawableLayer ( const Name& layerName )
  {
    PaletteItem* item = (_palette) ? _palette->find(layerName) : NULL;

    return !item || item->isItemVisible();
  }


  bool  CellWidget::isDrawableExtension ( const Name& extensionName )
  {
    PaletteItem* item = (_palette) ? _palette->find(extensionName) : NULL;

    return (!item || item->isItemVisible());
  }


  bool  CellWidget::isSelectable ( const Name& name ) const
  {
    PaletteItem* item = (_palette) ? _palette->find(name) : NULL;

    return (!item || item->isItemSelectable());
  }


  bool  CellWidget::isSelectable ( const Layer* layer ) const
  {
    PaletteItem* item = NULL;
    forEach ( BasicLayer*, ilayer, layer->getBasicLayers() ) {
      item = (_palette) ? _palette->find(ilayer->getName()) : NULL;
      if ( item && item->isItemSelectable() )
        return true;
    }
    return false;
  }


  void  CellWidget::drawBox ( DbU::Unit x1, DbU::Unit y1, DbU::Unit x2, DbU::Unit y2 )
  {
    _redrawRectCount++;
    _drawingPlanes.setLineMode ( false );
    _drawingPlanes.painter().drawRect ( dbuToDisplayRect(x1,y1,x2,y2) );
  }


  void  CellWidget::drawBox ( const Box& box )
  {
    _redrawRectCount++;
    _drawingPlanes.setLineMode ( false );
    _drawingPlanes.painter().drawRect ( dbuToDisplayRect(box) );
  }


  void  CellWidget::drawText ( const Point& point, const char* text, unsigned int flags, int angle )
  {
    drawDisplayText ( dbuToDisplayPoint(point), text, flags, angle );
  }


  void  CellWidget::drawDisplayText ( const QRect& box, const char* text, unsigned int flags )
  {
    QFont  font = Graphics::getNormalFont(flags&Bold);

    if ( flags & BigFont ) font.setPointSize ( 18 );

    QFontMetrics metrics = QFontMetrics(font);
    int          width   = metrics.width  ( text );
  //int          height  = metrics.height ();
    int          angle   = 0;

    if ( (width > box.width()) and (box.height() > 2*box.width()) )
      angle = -90;

    drawDisplayText ( box.center(), text, flags, angle );
  }


  void  CellWidget::drawDisplayText ( const QPoint& point, const char* text, unsigned int flags, int angle )
  {
    QPainter&    painter = _drawingPlanes.painter();
    QPen         pen     = painter.pen ();
    QBrush       brush   = painter.brush ();
    QFont        font    = Graphics::getNormalFont(flags&Bold);

    painter.save();
    if ( flags & Reverse ) painter.setPen ( Graphics::getPen("background") );
    if ( flags & Reverse ) painter.setBackgroundMode ( Qt::OpaqueMode );
    if ( flags & BigFont ) font.setPointSize ( 18 );

    QFontMetrics metrics = QFontMetrics(font);
    int          width   = metrics.width  ( text );
    int          height  = metrics.height ();

    pen.setStyle ( Qt::SolidLine );
    pen.setColor ( painter.brush().color() );

    brush.setStyle ( Qt::NoBrush );

    painter.setPen    ( pen );
    painter.setBrush  ( brush );
    painter.setFont   ( font );
    painter.translate ( point );
    painter.rotate    ( angle );

    QPoint bottomLeft ( 0, 0);
    if ( flags &  Center ) {
      bottomLeft.rx() -= width /2;
      bottomLeft.ry() += height/2;
    } else if ( flags & Top ) {
      bottomLeft.ry() += height;
    } else if ( flags & Left ) {
    }

    if ( flags & Frame ) {
      if ( flags & Rounded )
        painter.drawRoundedRect ( bottomLeft.x()-1, bottomLeft.y()-height, width+2, height, 8, 8 );
      else
        painter.drawRect ( bottomLeft.x()-1, bottomLeft.y()-height, width+2, height );
    }

    painter.drawText  ( bottomLeft.x(), bottomLeft.y()-metrics.descent(), text );
    painter.restore   ();
  }


  void  CellWidget::drawLine ( DbU::Unit x1, DbU::Unit y1, DbU::Unit x2, DbU::Unit y2, bool mode )
  {
    _redrawRectCount++;
    _drawingPlanes.setLineMode ( mode );
    _drawingPlanes.painter().drawLine ( dbuToDisplayPoint(x1,y1), dbuToDisplayPoint(x2,y2) );
  }


  void  CellWidget::drawLine ( const Point& p1, const Point& p2, bool mode )
  {
    _redrawRectCount++;
    _drawingPlanes.setLineMode ( mode );
    _drawingPlanes.painter().drawLine ( dbuToDisplayPoint(p1), dbuToDisplayPoint(p2) );
  }


  void  CellWidget::drawScreenLine ( const QPoint& p1, const QPoint& p2, size_t plane, bool mode )
  {
    _redrawRectCount++;
    _drawingPlanes.setLineMode ( mode );
    _drawingPlanes.painter(plane).drawLine ( p1, p2 );
  }


  void  CellWidget::drawScreenPolygon ( const QPoint* points, int count, size_t plane )
  {
    _drawingPlanes.painter(plane).drawPolygon ( points, count );
  }


  void  CellWidget::drawScreenPolyline ( const QPoint* points, int count, int width, size_t plane )
  {
    _drawingPlanes.painter(plane).save ();

    QPen pen = _drawingPlanes.painter(plane).pen ();
    pen.setWidth ( width );

    _drawingPlanes.painter(plane).setPen ( pen );
    _drawingPlanes.painter(plane).drawPolyline ( points, count );

    _drawingPlanes.painter(plane).restore ();
  }


  void  CellWidget::drawScreenRect ( const QPoint& p1, const QPoint& p2, size_t plane )
  {
    _drawingPlanes.setLineMode ( false );
    _drawingPlanes.painter(plane).drawRect ( QRect(p1,p2) );
  }


  void  CellWidget::drawScreenRect ( const QRect& r, size_t plane )
  {
    _redrawRectCount++;
    _drawingPlanes.setLineMode ( false );
    _drawingPlanes.painter(plane).drawRect ( r );
  }


  bool  CellWidget::_underDetailedGridThreshold () const
  {
    if ( symbolicMode() )
      return Graphics::getThreshold("grid")/DbU::lambda(1.0) < getScale()/5;
    return Graphics::getThreshold("grid")/DbU::grid(10.0) < getScale()/5;
  }


  void  CellWidget::drawGrid ( QRect redrawArea )
  {
    _drawingPlanes.select  ( PlaneId::Normal );
    _drawingPlanes.begin   ();
    _drawingPlanes.painter ().setClipRect   ( redrawArea );
    _drawingPlanes.painter ().setPen ( Graphics::getPen("grid") );

    Box redrawBox = displayToDbuBox ( redrawArea ).inflate ( DbU::lambda(1.0) );

    bool detailedGrid = _underDetailedGridThreshold();

    DbU::Unit  gridStep      = _snapGridStep();
    DbU::Unit  superGridStep = gridStep*5;
    DbU::Unit  xGrid;
    DbU::Unit  yGrid;
    QPoint     center;

    for ( yGrid = _onSnapGrid(redrawBox.getYMin())
        ; yGrid < redrawBox.getYMax()
        ; yGrid += gridStep
        ) {
      for ( xGrid = _onSnapGrid(redrawBox.getXMin())
          ; xGrid < redrawBox.getXMax()
          ; xGrid += gridStep
          ) {
        center = dbuToDisplayPoint(xGrid,yGrid);
        if ( (xGrid % superGridStep) || (yGrid % superGridStep) ) {
          if ( detailedGrid ) {
            _drawingPlanes.painter().drawPoint ( center );
          }
        } else {
          if ( detailedGrid ) {
            _drawingPlanes.painter().drawLine ( center.x()-3, center.y()  , center.x()+3, center.y()   );
            _drawingPlanes.painter().drawLine ( center.x()  , center.y()-3, center.x()  , center.y()+3 );
          } else {
            _drawingPlanes.painter().drawPoint ( center );
          }
        }
      }
    }

    _drawingPlanes.copyToSelect ( redrawArea );
    _drawingPlanes.end          ();
  }


  void  CellWidget::drawRulers ( QRect redrawArea )
  {
    _drawingPlanes.select  ( PlaneId::Normal );
    _drawingPlanes.begin   ();
    _drawingPlanes.painter ().setClipRect ( redrawArea );

    redrawArea.adjust ( -50, -50, 50, 50 );
    Box redrawBox = displayToDbuBox ( redrawArea );
    redrawArea.adjust ( 50, 50, -50, -50 );

    RulerSet::iterator iruler = _state->getRulers().begin();
    RulerSet::iterator end    = _state->getRulers().end();
    for ( ; iruler != end ; iruler++ ) {
      if ( !(*iruler)->intersect(redrawBox) ) continue;
      drawRuler ( *iruler );
    }

    _drawingPlanes.copyToSelect ( redrawArea );
    _drawingPlanes.end          ();
  }


  void  CellWidget::drawRuler ( shared_ptr<Ruler> ruler )
  {
    QFont        font          = Graphics::getNormalFont();
    QFontMetrics metrics       = QFontMetrics(font);
    int          tickLength    = metrics.width ( "+00000u" );
    Point        origin        = ruler->getOrigin    ();
    Point        extremity     = ruler->getExtremity ();
    Point        angle         = ruler->getAngle     ();
    DbU::Unit    graduation;
    DbU::Unit    gradStep;
    QPoint       pxOrigin;
    QPoint       pxExtremity;
    QPoint       pxAngle;
    bool         onScreen = ( _drawingPlanes.getWorkingPlane() > PlaneId::Selection );

  // Never less than 5 pixels between two graduations ticks.
    if ( symbolicMode() )
      gradStep = DbU::lambda(pow(10.0,ceil(log10(DbU::getLambda(displayToDbuLength(50))))))/10;
    else
      gradStep = DbU::grid(pow(10.0,ceil(log10(DbU::getGrid(displayToDbuLength(50))))))/10;

    if ( onScreen ) {
      pxOrigin    = dbuToScreenPoint ( origin );
      pxExtremity = dbuToScreenPoint ( extremity );
      pxAngle     = dbuToScreenPoint ( angle );
    } else {
      pxOrigin    = dbuToDisplayPoint ( origin );
      pxExtremity = dbuToDisplayPoint ( extremity );
      pxAngle     = dbuToDisplayPoint ( angle );
    }

    bool   hRuler = ( abs(pxAngle.x() - pxOrigin.x()) >= abs(pxAngle.y() - pxOrigin.y()) );
    int    pxGrad;
    int    pyGrad;
    string textGrad;
    int    tick;

    _drawingPlanes.painter().setPen   ( Graphics::getPen  ("text.ruler") );
    _drawingPlanes.painter().setBrush ( Graphics::getBrush("text.ruler") );

  // The horizontal ruler.
    bool increase = ( origin.getX() < extremity.getX() );
    if ( !increase )
      gradStep = -gradStep;

    if ( hRuler and (abs(pxAngle.x() - pxOrigin.x()) > 20) ) {
    // The horizontal ruler axis.
      _drawingPlanes.painter().drawLine ( pxOrigin, pxAngle );

    // The horizontal ruler ticks.
      for ( graduation=origin.getX(), tick=0 ; true ; graduation+=gradStep, tick++ ) {
        if ( increase ) {
          if ( graduation >= angle.getX() ) break;
        } else
          if ( graduation <= angle.getX() ) break;

        if ( onScreen ) pxGrad = dbuToScreenX  ( graduation );
        else            pxGrad = dbuToDisplayX ( graduation );


        if ( tick % 10 ) {
          _drawingPlanes.painter().drawLine ( pxGrad, pxOrigin.y()
                                            , pxGrad, pxOrigin.y()+((tick%2)?5:10) );
        } else {
          // if ( tick == 0 ) {
          //   int delta = (increase) ? 2 : -2;
          //   _drawingPlanes.painter().drawLine ( pxGrad-delta, pxOrigin.y()
          //                                     , pxGrad-delta, pxOrigin.y()+tickLength );
          // }

          _drawingPlanes.painter().drawLine ( pxGrad, pxOrigin.y()
                                            , pxGrad, pxOrigin.y()+tickLength );

          // if ( !tick ) continue;

          textGrad = DbU::getValueString( abs(gradStep*tick)
                                        , DbU::SmartTruncate|((symbolicMode())?DbU::Symbolic:DbU::Grid) );
          textGrad.resize ( textGrad.size()-((*textGrad.rbegin()=='m')?2:1) );

          drawDisplayText ( QPoint ( pxGrad - 1, pxOrigin.y() + tickLength )
                          , textGrad.c_str()
                          , Bold
                          , -90
                          );
        }
      }

    // The last horizontal tick.
      _drawingPlanes.painter().drawLine ( pxAngle.x(), pxAngle.y()
                                        , pxAngle.x(), pxAngle.y()+tickLength );

      textGrad = DbU::getValueString ( abs(angle.getX() - origin.getX())
                                     , DbU::SmartTruncate|((symbolicMode())?DbU::Symbolic:DbU::Grid) );
    //textGrad.resize ( textGrad.size()-1 );

      drawDisplayText ( QPoint ( pxAngle.x() - 1,pxAngle.y() + tickLength )
                      , textGrad.c_str()
                      , Bold
                      , -90
                      );
    }

    if ( not hRuler and (abs(pxAngle.y() - pxOrigin.y()) > 20) ) {
    // The vertical ruler.
      increase = ( origin.getY() < angle.getY() );
      if ( increase xor ( gradStep > 0 ) )
        gradStep = -gradStep;

    // The vertical ruler axis.
      _drawingPlanes.painter().drawLine ( pxOrigin, pxAngle );

    // The vertical ruler ticks.
      for ( graduation=origin.getY(), tick=0 ; true ; graduation+=gradStep, tick++ ) {
        if ( increase ) {
          if ( graduation >= angle.getY() ) break;
        } else
          if ( graduation <= angle.getY() ) break;

        if ( onScreen ) pyGrad = dbuToScreenY  ( graduation );
        else            pyGrad = dbuToDisplayY ( graduation );

        if ( tick % 10 ) {
          _drawingPlanes.painter().drawLine ( pxOrigin.x()                , pyGrad
                                            , pxOrigin.x()-((tick%2)?5:10), pyGrad );
        } else {
          // if ( tick == 0 ) {
          //   _drawingPlanes.painter().drawLine ( pxOrigin.x()           , pyGrad-2
          //                                     , pxOrigin.x()-tickLength, pyGrad-2);
          // }

          _drawingPlanes.painter().drawLine ( pxOrigin.x()           , pyGrad
                                            , pxOrigin.x()-tickLength, pyGrad );

          // if ( !tick ) continue;

          textGrad  = DbU::getValueString( abs(gradStep*tick)
                                         , DbU::SmartTruncate|((symbolicMode())?DbU::Symbolic:DbU::Grid) );
          textGrad.resize ( textGrad.size()-((*textGrad.rbegin()=='m')?2:1) );

          drawDisplayText ( QPoint(pxOrigin.x() - tickLength,pyGrad + 1)
                          , textGrad.c_str()
                          , Bold
                          , 0
                          );
        }
      }

    // The last vertical tick.
      _drawingPlanes.painter().drawLine ( pxOrigin.x()           , pxAngle.y()
                                        , pxOrigin.x()-tickLength, pxAngle.y() );

      textGrad = DbU::getValueString( abs(angle.getY() - origin.getY())
                                    , DbU::SmartTruncate|((symbolicMode())?DbU::Symbolic:DbU::Grid) );
    //textGrad.resize ( textGrad.size()-1 );

      drawDisplayText ( QPoint(pxOrigin.x() - tickLength,pxAngle.y() + 1)
                      , textGrad.c_str()
                      , Bold
                      , 0
                      );
    }
  }


  void  CellWidget::goLeft ( int dx )
  {
    if ( !dx ) dx = geometry().size().width() / 4;

    _redrawManager.goLeft ( dx );
  }


  void  CellWidget::goRight ( int dx )
  {
    if ( !dx ) dx = geometry().size().width() / 4;

    _redrawManager.goRight ( dx );
  }


  void  CellWidget::goUp ( int dy )
  {
    if ( !dy ) dy = geometry().size().height() / 4;

    _redrawManager.goUp ( dy );
  }


  void  CellWidget::goDown ( int dy )
  {
    if ( !dy ) dy = geometry().size().height() / 4;

    _redrawManager.goDown ( dy );
  }


  void  CellWidget::displayReframe ()
  {
    _offsetVA.rx() = _stripWidth;
    _offsetVA.ry() = _stripWidth;

    DbU::Unit xmin = (DbU::Unit)( _visibleArea.getXMin() - ((float)_offsetVA.x()/getScale()) );
    DbU::Unit xmax = (DbU::Unit)( xmin + ((float)_drawingPlanes.width()/getScale()) ) ;
    DbU::Unit ymax = (DbU::Unit)( _visibleArea.getYMax() + ((float)_offsetVA.y()/getScale()) );
    DbU::Unit ymin = (DbU::Unit)( ymax - ((float)_drawingPlanes.height()/getScale()) ) ;

    _displayArea = Box ( xmin, ymin, xmax, ymax );

    _redrawManager.refresh ();
  }


  Box  CellWidget::computeVisibleArea ( float scale ) const
  {
    Point center = _visibleArea.getCenter();

    return Box ( (DbU::Unit)( center.getX() - width () / (scale*2) ) 
               , (DbU::Unit)( center.getY() - height() / (scale*2) ) 
               , (DbU::Unit)( center.getX() + width () / (scale*2) )
               , (DbU::Unit)( center.getY() + height() / (scale*2) )
               );
  }


  Box  CellWidget::computeVisibleArea ( float scale, const Point& topLeft ) const
  {
    return Box ( topLeft.getX()
               , (DbU::Unit)( topLeft.getY() - height() / scale )
               , (DbU::Unit)( topLeft.getX() + width () / scale )
               , topLeft.getY() 
               );
  }


  Box  CellWidget::computeVisibleArea ( const Box& area, float& scale ) const
  {
    int  widgetWidth  = width  ();
    int  widgetHeight = height ();

    float scaleX = widgetWidth  / (float)area.getWidth ();
    float scaleY = widgetHeight / (float)area.getHeight();
    scale = min ( scaleX, scaleY );

    Point center = area.getCenter();

    widgetWidth  /= 2;
    widgetHeight /= 2;

    return Box ( (DbU::Unit)( center.getX() - widgetWidth  / scale ) 
               , (DbU::Unit)( center.getY() - widgetHeight / scale ) 
               , (DbU::Unit)( center.getX() + widgetWidth  / scale )
               , (DbU::Unit)( center.getY() + widgetHeight / scale )
               );
  }


  void  CellWidget::setScale ( float scale )
  {
  //cerr << "CellWidget::setScale() - " << scale << endl;

    _state->setTopLeft ( getTopLeft() );
    _visibleArea = computeVisibleArea ( scale );
    _state->setScale ( scale );

  //cerr << "_visibleArea: " << _visibleArea << " (offset: " << _offsetVA.x() << ")" << endl;
  //cerr << "              " << center << endl;

    displayReframe ();
  }


  void  CellWidget::scaleHistoryUp ()
  {
    _state->setTopLeft ( getTopLeft() );
    if ( _state->scaleHistoryUp () ) {
      _visibleArea = computeVisibleArea ( _state->getScale(), _state->getTopLeft() );
      displayReframe ();
    }
  }


  void  CellWidget::scaleHistoryDown ()
  {
    _state->setTopLeft ( getTopLeft() );
    if ( _state->scaleHistoryDown () ) {
      _visibleArea = computeVisibleArea ( _state->getScale(), _state->getTopLeft() );
      displayReframe ();
    }
  }


  void  CellWidget::reframe ()
  {
  //cerr << "CellWidget::reframe() - scale:" << _state->getScale()
  //     << " topLeft:" << _state->getTopLeft() << endl;

    _visibleArea = computeVisibleArea ( _state->getScale(), _state->getTopLeft() );
    displayReframe ();
  }


  void  CellWidget::reframe ( const Box& box, bool historyEnable )
  {
  //cerr << "CellWidget::reframe() - " << box << endl;
  //cerr << "  widget size := " << _drawingPlanes.width() << "x" << _drawingPlanes.height() << endl;

  //cerr << "  CellWidget::reframe() - widget size := " << width() << "x" << height() << endl;

    bool backupHistoryEnable = _state->getHistoryEnable ();
    _state->setHistoryEnable ( historyEnable );
    _state->setTopLeft       ( getTopLeft() );

    float scale;
    _visibleArea = computeVisibleArea ( box, scale );
    _state->setScale ( scale );
    displayReframe ();

    _state->setHistoryEnable ( backupHistoryEnable );

  //cerr << "  _displayArea: " << _displayArea << " (offset: " << _offsetVA.x() << ")" << endl;
  }


  void  CellWidget::fitToContents ( bool historyEnable )
  {
  //cerr << "CellWidget::fitToContents()" << endl;

    Box boundingBox = Box ( DbU::lambda(0)
                          , DbU::lambda(0)
                          , DbU::lambda(10)
                          , DbU::lambda(50)
                          );

    if ( getCell() ) boundingBox = getCell()->getBoundingBox();

    DbU::Unit expand;
    if ( boundingBox.getWidth() < boundingBox.getHeight() ) {
      expand = DbU::grid( DbU::getGrid(boundingBox.getWidth()) * 0.05 );
    } else {
      expand = DbU::grid( DbU::getGrid(boundingBox.getHeight()) * 0.05 );
    }

    reframe ( boundingBox.inflate(expand), historyEnable );
  }


  void  CellWidget::fitToNet ( const Net* net, bool historyEnable )
  {
    if ( !net )
      throw Error ( "<b>CellWidget::fitToNet()</b>: NULL net argument." );

    if ( net->getCell() != getCell() )
      throw Error ( "<b>CellWidget::fitToNet()</b>:<br>"
                    "Net %s (cell: %s) do not belong to Cell %s"
                  , Graphics::toHtml(getString(net->getName())).c_str()
                  , Graphics::toHtml(getString(net->getCell()->getName())).c_str()
                  , Graphics::toHtml(getString(getCell()->getName())).c_str()
                  );

    Box boundingBox = net->getBoundingBox ();
    if ( !boundingBox.isEmpty() )
      reframe ( boundingBox, historyEnable );
  }


  void  CellWidget::_goLeft ( int dx )
  {
    _visibleArea.translate ( - (DbU::Unit)( dx / getScale() ) , 0 );

    if ( _offsetVA.rx() - dx >= 0 ) {
      _offsetVA.rx() -= dx;
      repaint ();
      return;
    }

    int shift = ( 1 + ( dx - _offsetVA.rx() ) / _stripWidth ) * _stripWidth;

    _displayArea.translate ( - (DbU::Unit)( shift / getScale() ) , 0 );
    _offsetVA.rx() -= dx - shift;

    if ( shift >= _drawingPlanes.width() )
      _redrawManager.refresh ();
    else {
      _drawingPlanes.shiftLeft ( shift );
      _redraw ( QRect ( QPoint(0,0), QSize(shift,_drawingPlanes.height()) ) );
    }

    assert ( _offsetVA.rx() >= 0 );
  }


  void  CellWidget::_goRight ( int dx )
  {
  //cerr << "CellWidget::goRight() - dx: " << dx << " (offset: " << _offsetVA.rx() << ")" << endl;

    _visibleArea.translate ( (DbU::Unit)( dx / getScale() ) , 0 );

    if ( _offsetVA.rx() + dx < 2*_stripWidth ) {
      _offsetVA.rx() += dx;
      repaint ();
      return;
    }

    int shift = ( ( _offsetVA.rx() + dx ) / _stripWidth ) * _stripWidth;

    _displayArea.translate ( (DbU::Unit)( shift / getScale() ) , 0 );
    _offsetVA.rx() += dx - shift;

    if ( shift >= _drawingPlanes.width() )
      _redrawManager.refresh ();
    else {
      _drawingPlanes.shiftRight ( shift );
      _redraw ( QRect ( QPoint(_drawingPlanes.width()-shift,0)
                      , QSize (shift,_drawingPlanes.height()) ) );
    }

    assert ( _offsetVA.rx() >= 0 );
  }


  void  CellWidget::_goUp ( int dy )
  {
  //cerr << "CellWidget::shiftUp() - " << dy << " (offset: " << _offsetVA.ry() << ")" << endl;

    _visibleArea.translate ( 0, (DbU::Unit)( dy / getScale() ) );

    if ( _offsetVA.ry() - dy >= 0 ) {
      _offsetVA.ry() -= dy;
      repaint ();
      return;
    }

    int shift = ( 1 + ( dy - _offsetVA.ry() ) / _stripWidth ) * _stripWidth;

    _displayArea.translate ( 0, (DbU::Unit)( shift / getScale() ) );
    _offsetVA.ry() -= dy - shift;

    if ( shift >= _drawingPlanes.height() )
      _redrawManager.refresh ();
    else {
      _drawingPlanes.shiftUp ( shift );
      _redraw ( QRect ( QPoint(0,0), QSize(_drawingPlanes.width(),shift) ) );
    }

    assert ( _offsetVA.ry() >= 0 );
  }


  void  CellWidget::_goDown ( int dy )
  {
  //cerr << "CellWidget::shiftDown() - " << dy << " (offset: " << _offsetVA.ry() << ")" << endl;

    _visibleArea.translate ( 0, - (DbU::Unit)( dy / getScale() ) );

    if ( _offsetVA.ry() + dy < 2*_stripWidth ) {
      _offsetVA.ry() += dy;
      repaint ();
      return;
    }

    int shift = ( ( _offsetVA.ry() + dy ) / _stripWidth ) * _stripWidth;

    _displayArea.translate ( 0, - (DbU::Unit)( shift / getScale() ) );
    _offsetVA.ry() += dy - shift;

    if ( shift >= _drawingPlanes.height() )
      _redrawManager.refresh ();
    else {
      _drawingPlanes.shiftDown ( shift );
      _redraw ( QRect ( QPoint (0,_drawingPlanes.height()-shift)
                      , QSize  (_drawingPlanes.width(), shift) ) );
    }

    assert ( _offsetVA.ry() >= 0 );
  }


  void  CellWidget::_refresh ()
  {
    _redraw ( QRect(QPoint(0,0),_drawingPlanes.size()) );
  }


  void  CellWidget::showEvent ( QShowEvent* )
  {
  //cerr << "CellWidget::showEvent() - size: " << geometry().width() << "x" << geometry().height() << endl;
    if ( _cellChanged )
      fitToContents ( false );
  }


  void  CellWidget::paintEvent ( QPaintEvent* event )
  {
//  static Timer   timer;
//  static time_t  prevTime = 0;
//  static time_t  currTime = 0;

//  timer.start ();
    _drawingPlanes.pushWorkingPlane ();
    _drawingPlanes.select           ( PlaneId::Widget );
    _drawingPlanes.begin            ();
    _drawingPlanes.copyToScreen     ();
    for ( size_t i=0 ; i<_commands.size() ; i++ )
      _commands[i]->draw ();

    if ( isDrawable("spot") ) _spot.moveTo ( _mousePosition );
    _drawingPlanes.end             ();
    _drawingPlanes.popWorkingPlane ();
//  timer.stop ();

//  time ( &currTime );
//  double delta = difftime ( currTime, prevTime );

//  if ( delta )
//    cerr << "CellWidget::paintEvent() - lagging: " << delta << endl;

//  prevTime = currTime;
  }


  void  CellWidget::resizeEvent ( QResizeEvent* )
  {
//  cerr << "CellWidget::resizeEvent() - size: " << geometry().width() << "x" << geometry().height() << endl;

    QSize uaDelta ( 0, 0 );
    QSize uaSize  = geometry().size();

    uaSize.rwidth () += 2*_stripWidth;
    uaSize.rheight() += 2*_stripWidth;

    if ( uaSize.width () > _drawingPlanes.width () )
      uaDelta.rwidth () = uaSize.width () - _drawingPlanes.width ();

    if ( uaSize.height() > _drawingPlanes.height() )
      uaDelta.rheight() = uaSize.height() - _drawingPlanes.height();

    if ( uaDelta.width() || uaDelta.height() ) {
      _displayArea.inflate ( 0, 0, (DbU::Unit)(uaDelta.width()/getScale()), (DbU::Unit)(uaDelta.height()/getScale()) );
      _visibleArea.inflate ( 0, 0, (DbU::Unit)(uaDelta.width()/getScale()), (DbU::Unit)(uaDelta.height()/getScale()) );

      QSize bufferSize ( ( ( uaSize.width () / _stripWidth ) + 1 ) * _stripWidth
                       , ( ( uaSize.height() / _stripWidth ) + 1 ) * _stripWidth );
      _drawingPlanes.resize ( bufferSize );
    }

    _redrawManager.refresh ();
  }


  void  CellWidget::wheelEvent ( QWheelEvent* event )
  {
    event->ignore ();
    for ( size_t i=0 ; (i<_commands.size()) and not event->isAccepted(); i++ ) {
      if ( _activeCommand
         and (_commands[i]            != _activeCommand       )
         and (_commands[i]->getType() != Command::AlwaysActive) )
        continue;
      _commands[i]->wheelEvent ( event );
    }
  }


  void  CellWidget::keyPressEvent ( QKeyEvent* event )
  {
    event->ignore ();
  //cerr << "CellWidget::keyPressEvent " << event->isAccepted() << endl;
    for ( size_t i=0 ; (i<_commands.size()) and not event->isAccepted(); i++ ) {
      if ( _activeCommand
         and (_commands[i]            != _activeCommand       )
         and (_commands[i]->getType() != Command::AlwaysActive) )
        continue;
    //cerr << "  Calling [" << i << "] " << _commands[i]->getName() << endl;
      _commands[i]->keyPressEvent ( event );
    }
  }


  void  CellWidget::keyReleaseEvent ( QKeyEvent* event )
  {
    event->ignore ();
  //cerr << "CellWidget::keyReleaseEvent " << event->isAccepted() << endl;
    for ( size_t i=0 ; (i<_commands.size()) and not event->isAccepted(); i++ ) {
      if ( _activeCommand
         and (_commands[i]            != _activeCommand       )
         and (_commands[i]->getType() != Command::AlwaysActive) )
        continue;
    //cerr << "  Calling [" << i << "] " << _commands[i]->getName() << endl;
      _commands[i]->keyReleaseEvent ( event );
    }
  }


  void  CellWidget::mouseMoveEvent ( QMouseEvent* event )
  {
    event->ignore ();
    for ( size_t i=0 ; (i<_commands.size()) and not event->isAccepted(); i++ ) {
      if ( _activeCommand
         and (_commands[i]            != _activeCommand       )
         and (_commands[i]->getType() != Command::AlwaysActive) )
        continue;
      _commands[i]->mouseMoveEvent ( event );
    }

    _mousePosition = event->pos();
    updateMousePosition ();
    repaint ();
  }


  void  CellWidget::mousePressEvent ( QMouseEvent* event )
  {
    event->ignore ();
  //cerr << "CellWidget::mousePressEvent " << event->isAccepted() << endl;
    for ( size_t i=0 ; (i<_commands.size()) and not event->isAccepted(); i++ ) {
      if ( _activeCommand
         and (_commands[i]            != _activeCommand       )
         and (_commands[i]->getType() != Command::AlwaysActive) )
        continue;
    //cerr << "  Calling [" << i << "] " << _commands[i]->getName() << endl;
      _commands[i]->mousePressEvent ( event );
    }

    _spot.setShowSpot ( !_activeCommand );
  }


  void  CellWidget::mouseReleaseEvent ( QMouseEvent* event )
  {
    event->ignore ();
  //cerr << "CellWidget::mouseReleaseEvent " << event->isAccepted() << endl;
    for ( size_t i=0 ; (i<_commands.size()) and not event->isAccepted(); i++ ) {
      if ( _activeCommand
         and (_commands[i]            != _activeCommand       )
         and (_commands[i]->getType() != Command::AlwaysActive) )
        continue;
    //cerr << "  Calling [" << i << "] " << _commands[i]->getName() << endl;
      _commands[i]->mouseReleaseEvent ( event );
    }
  //if ( not _activeCommand ) QWidget::mouseReleaseEvent ( event );

    _spot.setShowSpot ( true );
  }


  QPoint  CellWidget::dbuToDisplayPoint ( DbU::Unit x, DbU::Unit y ) const
  {
    return QPoint ( dbuToDisplayX(x), dbuToDisplayY(y) );
  }


  QPoint  CellWidget::dbuToDisplayPoint ( const Point& point ) const
  {
    return dbuToDisplayPoint ( point.getX(), point.getY() );
  }


  QRect  CellWidget::dbuToDisplayRect ( DbU::Unit x1, DbU::Unit y1, DbU::Unit x2, DbU::Unit y2, bool usePoint ) const
  {
    int width, height;

    if ( usePoint ) {
      width  = dbuToDisplayX(x2) - dbuToDisplayX(x1);
      height = dbuToDisplayY(y1) - dbuToDisplayY(y2);
    } else {
      width  = dbuToDisplayLength ( x2 - x1 );
      height = dbuToDisplayLength ( y2 - y1 );
    }

    return QRect ( dbuToDisplayX(x1)
                 , dbuToDisplayY(y2)
                 , width  ? width  : 1
                 , height ? height : 1
                 );
  }


  QRect  CellWidget::dbuToDisplayRect ( const Box& box, bool usePoint ) const
  {
    return dbuToDisplayRect ( box.getXMin()
                            , box.getYMin()
                            , box.getXMax()
                            , box.getYMax()
                            , usePoint
                            );
  }


  QPoint  CellWidget::dbuToScreenPoint ( DbU::Unit x, DbU::Unit y ) const
  {
    return QPoint ( dbuToScreenX(x), dbuToScreenY(y) );
  }


  void  CellWidget::setCell ( Cell* cell )
  {
  //cerr << "CellWidget::setCell() - " << cell << endl;

    if ( cell == getCell() ) return;

    openRefreshSession ();

    shared_ptr<State>  state ( new State(cell) );
    setState ( state );
    if ( cell and cell->isTerminal() ) setQueryFilter ( ~0 );
  //setRealMode ();

    fitToContents ( false );

    _state->setHistoryEnable ( true );

    closeRefreshSession ();
  }


  void  CellWidget::setState ( shared_ptr<State>& state )
  {
  //cerr << "CellWidget::setState() - " << state->getName() << endl;

    if ( state == _state ) return;

    openRefreshSession ();

    cellPreModificate ();
    _state->getSelection  ().clear ();
    _state->setCellWidget ( NULL );
    _state->setTopLeft    ( getTopLeft() ); 

    _cellChanged = true;
    _state       = state;

//     cerr << "  about to restore " << (void*)_state.get()
//          << " " << _state->getName()
//          << ": _state->setTopLeft("
//          << DbU::getValueString(_state->getTopLeft().getX()) << ","
//          << DbU::getValueString(_state->getTopLeft().getY()) << ")" << endl;

    _state->setHistoryEnable ( false );
    _state->setCellWidget ( this );
    _drawingQuery    .setCell       ( getCell() );
    _drawingQuery    .setStartLevel ( _state->getStartLevel() );
    _drawingQuery    .setStopLevel  ( _state->getStopLevel() );
    _textDrawingQuery.setCell       ( getCell() );

    reframe ();
    _state->setHistoryEnable ( true );

    emit cellChanged        ( getCell() );
    emit stateChanged       ( _state );
    emit queryFilterChanged ();

    cellPostModificate ();

    closeRefreshSession ();
  }


  Occurrences  CellWidget::getOccurrencesUnder ( const Box& area ) const
  {
    return getCell()->getOccurrencesUnder(area,3).getSubSet(Occurrences_IsSelectable(this));
  }


  void  CellWidget::select ( const Net* net )
  {
    ++_delaySelectionChanged;

    bool added = _state->getSelection().add ( net );

    if ( !--_delaySelectionChanged && added ) emit selectionChanged(_selectors);
  }


  bool  CellWidget::isSelected ( Occurrence occurrence )
  {
	if ( !occurrence.isValid() )
      throw Error ( "Can't select occurrence : invalid occurrence" );

	if ( occurrence.getOwnerCell() != getCell() ) {
      string s1 = Graphics::toHtml ( getString(occurrence.getOwnerCell()) );
      string s2 = Graphics::toHtml ( getString(getCell()) );
      throw Error ( "Can't select occurrence : incompatible occurrence %s vs. %s"
                  , s1.c_str(), s2.c_str() );
    }

	Property* property = occurrence.getProperty ( Selector::getPropertyName() );
	if ( !property )
      return false;

    Selector* selector = dynamic_cast<Selector*>(property);
    if ( !selector )
      throw Error ( "Abnormal property named " + getString(Selector::getPropertyName()) );

	return selector->isAttachedTo(this);
  }


  void  CellWidget::select ( Occurrence occurrence )
  {
	if ( !occurrence.isValid() )
      throw Error ( "Can't select occurrence : invalid occurrence" );

	if ( occurrence.getOwnerCell() != getCell() ) {
      string s1 = Graphics::toHtml ( getString(occurrence.getOwnerCell()) );
      string s2 = Graphics::toHtml ( getString(getCell()) );
      throw Error ( "Can't select occurrence : incompatible occurrence %s vs. %s" 
                  , s1.c_str(), s2.c_str() );
    }

	Property* property = occurrence.getProperty ( Selector::getPropertyName() );
    Selector* selector = NULL;
	if ( !property )
      selector = Selector::create ( occurrence );
	else {
      selector = dynamic_cast<Selector*>(property);
      if ( !selector )
        throw Error ( "Abnormal property named " + getString(Selector::getPropertyName()) );
    }

	selector->attachTo(this);

    setShowSelection ( true );
    _selectionHasChanged = true;
    if ( !_delaySelectionChanged ) emit selectionChanged(_selectors);
  }


  void  CellWidget::selectOccurrencesUnder ( Box selectArea )
  {
    ++_delaySelectionChanged;

    if ( !_state->cumulativeSelection() ) {
      openRefreshSession ();
      unselectAll ();
      closeRefreshSession ();
    }
    bool added = _state->getSelection().add ( selectArea );

    if ( !--_delaySelectionChanged && added ) emit selectionChanged(_selectors);
  }


  void  CellWidget::unselect ( const Net* net )
  {
    ++_delaySelectionChanged;

    bool removed = _state->getSelection().remove ( net );
    if ( !--_delaySelectionChanged && removed ) emit selectionChanged(_selectors);
  }


  void  CellWidget::unselect ( Occurrence occurrence )
  {
	if ( !occurrence.isValid() )
		throw Error ( "Can't unselect occurrence : invalid occurrence" );

	if ( occurrence.getOwnerCell() != getCell() )
		throw Error ( "Can't unselect occurrence : incompatible occurrence" );

	Property* property = occurrence.getProperty ( Selector::getPropertyName() );
	if ( property ) {
      Selector* selector = dynamic_cast<Selector*>(property);
      if ( !selector )
        throw Error ( "Abnormal property named " + getString(Selector::getPropertyName()) );

      selector->detachFrom(this);
    }

    _selectionHasChanged = true;
    if ( !_delaySelectionChanged ) emit selectionChanged(_selectors);
  }


  void  CellWidget::unselectAll ()
  {
    ++_delaySelectionChanged;

    _state->getSelection().clear ();
    _unselectAll ();

    if ( !--_delaySelectionChanged ) emit selectionChanged(_selectors);
  }


  void  CellWidget::toggleSelection ( Occurrence occurrence )
  {
	if ( !occurrence.isValid() )
      throw Error ( "Can't select occurrence : invalid occurrence" );

	if ( occurrence.getOwnerCell() != getCell() )
      throw Error ( "Can't select occurrence : incompatible occurrence" );

	Property* property = occurrence.getProperty ( Selector::getPropertyName() );
    Selector* selector = NULL;
	if ( !property ) {
    // Net special case.
      Net* net = dynamic_cast<Net*>(occurrence.getEntity());
      if ( net ) {
        if ( occurrence.getPath().isEmpty() ) {
          select ( net );
        } else {
          cerr << "[UNIMPLEMENTED] Selection of " << occurrence << endl;
        }
      } else {
        selector = Selector::create ( occurrence );
        selector->attachTo ( this );
        setShowSelection ( true );
      }
	} else {
      selector = dynamic_cast<Selector*>(property);
      if ( !selector )
        throw Error ( "Abnormal property named " + getString(Selector::getPropertyName()) );

    // Net special case.
      Net* net = dynamic_cast<Net*>(occurrence.getEntity());
      if ( net ) {
        if ( occurrence.getPath().isEmpty() ) {
          unselect ( net );
        } else {
          cerr << "[UNIMPLEMENTED] Selection of " << occurrence << endl;
        }
      } else {
        selector->detachFrom ( this );
      }
    }

    _selectionHasChanged = true;
    if ( _state->showSelection() ) _redrawManager.refresh ();

    emit selectionToggled ( occurrence );
  }


  void  CellWidget::_select ( const Net* net )
  {
    select ( Occurrence(net) );
    forEach ( Component*, component, net->getComponents() ) {
      Occurrence occurrence ( *component );
      select ( occurrence );
    }
    forEach ( Rubber*, rubber, net->getRubbers() ) {
      Occurrence occurrence ( *rubber );
      select ( occurrence );
    }
    if ( _state->showSelection() ) _redrawManager.refresh ();
  }


  void  CellWidget::_unselect ( const Net* net )
  {
    unselect ( Occurrence(net) );
    forEach ( Component*, component, net->getComponents() ) {
      Occurrence occurrence ( *component );
      unselect ( occurrence );
    }
    forEach ( Rubber*, rubber, net->getRubbers() ) {
      Occurrence occurrence ( *rubber );
      unselect ( occurrence );
    }
    if ( _state->showSelection() ) _redrawManager.refresh ();
  }


  void  CellWidget::_selectOccurrencesUnder ( Box selectArea )
  {
    forEach ( Occurrence, ioccurrence, getOccurrencesUnder(selectArea) )
      select ( *ioccurrence );
  }


  void  CellWidget::_unselectAll ()
  {
    SelectorSet::iterator iselector;
    while ( !_selectors.empty() )
      (*_selectors.begin())->detachFrom ( this );

    if ( !_selectionHasChanged ) _selectionHasChanged = true;
    if ( _state->showSelection() ) _redrawManager.refresh ();
  }


  void  CellWidget::cellPreModificate ()
  {
    openRefreshSession ();
    _unselectAll ();
    
    emit selectionChanged(_selectors);
    emit cellPreModificated ();

    closeRefreshSession ();
  }


  void  CellWidget::cellPostModificate ()
  {
    openRefreshSession ();
    _cellModificated = true;

    ++_delaySelectionChanged;
    _state->getSelection().revalidate ();

    updatePalette ();
    _redrawManager.refresh ();

    --_delaySelectionChanged;
    emit selectionChanged(_selectors);
    emit cellPostModificated ();

    closeRefreshSession ();
  }


} // End of Hurricane namespace.
