coriolis/hurricane/src/viewer/CellViewer.cpp

804 lines
27 KiB
C++

// -*- C++ -*-
//
// This file is part of the Coriolis Software.
// Copyright (c) UPMC 2008-2014, All Rights Reserved
//
// +-----------------------------------------------------------------+
// | H U R R I C A N E |
// | 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 : "./CellViewer.cpp" |
// +-----------------------------------------------------------------+
#include <Python.h>
#include <unistd.h>
#include <algorithm>
#include <sstream>
#include <exception>
#include <QAction>
#include <QMenu>
#include <QMenuBar>
#include <QStatusBar>
#include <QDockWidget>
#include <QApplication>
#include <QPrinter>
#include <QPrintDialog>
#include <QFileDialog>
#include "vlsisapd/utilities/Path.h"
#include "vlsisapd/configuration/Configuration.h"
#include "hurricane/DataBase.h"
#include "hurricane/Cell.h"
//#include "MapView.h"
#include "hurricane/isobar/PyCell.h"
#include "hurricane/viewer/Script.h"
#include "hurricane/viewer/Graphics.h"
#include "hurricane/viewer/CellViewer.h"
#include "hurricane/viewer/CellPrinter.h"
#include "hurricane/viewer/CellImage.h"
#include "hurricane/viewer/MousePositionWidget.h"
#include "hurricane/viewer/ControllerWidget.h"
#include "hurricane/viewer/ScriptWidget.h"
#include "hurricane/viewer/ExceptionWidget.h"
#include "hurricane/viewer/GotoWidget.h"
#include "hurricane/viewer/SelectCommand.h"
#include "hurricane/viewer/PyCellViewer.h"
namespace Hurricane {
using Isobar::PyCell_Link;
// -------------------------------------------------------------------
// Class : "CellObserver".
void CellObserver::notify ( unsigned int flags )
{
CellViewer* viewer = getOwner();
switch ( flags & (Cell::CellAboutToChange|Cell::CellChanged) ) {
case Cell::CellAboutToChange:
viewer->emitCellAboutToChange();
break;
case Cell::CellChanged:
viewer->emitCellChanged();
break;
}
}
// -------------------------------------------------------------------
// Class : "CellViewer".
QString CellViewer::_prefixWPath ( "viewer.menuBar." );
CellViewer::CellViewer ( QWidget* parent ) : QMainWindow (parent)
, _cellObserver (this)
, _applicationName (tr("Viewer"))
, _showSelectionAction (NULL)
, _debugMenu (NULL)
, _actionCallbacks ()
//, _mapView (NULL)
, _mousePosition (NULL)
, _controller (NULL)
, _script (NULL)
, _goto (NULL)
, _cellWidget (NULL)
, _moveCommand ()
, _zoomCommand ()
, _rulerCommand ()
, _selectCommand ()
, _hierarchyCommand ()
, _cellHistory ()
, _firstShow (false)
, _toolInterrupt (false)
, _flags (0)
, _updateState (ExternalEmit)
{
setObjectName( "viewer" );
menuBar()->setObjectName ( _getAbsWidgetPath("") );
_cellWidget = new CellWidget ();
_controller = new ControllerWidget();
_goto = new GotoWidget ();
_goto->changeDbuMode( _cellWidget->getDbuMode(), _cellWidget->getUnitPower() );
_cellWidget->bindCommand( &_moveCommand );
_cellWidget->bindCommand( &_zoomCommand );
_cellWidget->bindCommand( &_rulerCommand );
_cellWidget->bindCommand( &_selectCommand );
_cellWidget->bindCommand( &_hierarchyCommand );
_controller->setCellWidget( _cellWidget );
_mousePosition = new MousePositionWidget();
statusBar()->addPermanentWidget( _mousePosition );
setCorner( Qt::TopLeftCorner , Qt::LeftDockWidgetArea );
setCorner( Qt::BottomLeftCorner , Qt::LeftDockWidgetArea );
setCorner( Qt::TopRightCorner , Qt::RightDockWidgetArea );
setCorner( Qt::BottomRightCorner, Qt::RightDockWidgetArea );
//_mapView = _cellWidget->getMapView ();
//QDockWidget* mapViewDock = new QDockWidget ( tr("Map") );
//mapViewDock->setFeatures ( QDockWidget::DockWidgetVerticalTitleBar
// | QDockWidget::DockWidgetMovable
// | QDockWidget::DockWidgetFloatable
// );
//mapViewDock->setObjectName ( "viewer.menuBar.dock.mapView" );
//mapViewDock->setWidget ( _mapView );
//mapViewDock->setAllowedAreas ( Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea );
//addDockWidget( Qt::RightDockWidgetArea, mapViewDock );
setCentralWidget( _cellWidget );
createMenus();
connect( this , SIGNAL(cellPreModificated()) , _cellWidget, SLOT(cellPreModificate()) );
connect( this , SIGNAL(cellPostModificated()), _cellWidget, SLOT(cellPostModificate()) );
connect( this , SIGNAL(redrawCellWidget()) , _cellWidget, SLOT(refresh()) );
connect( _cellWidget , SIGNAL(dbuModeChanged(unsigned int,DbU::UnitPower))
, _goto , SLOT (changeDbuMode (unsigned int,DbU::UnitPower)) );
connect( _cellWidget , SIGNAL(mousePositionChanged(const Point&))
, _mousePosition , SLOT (setPosition(const Point&)) );
connect( _cellWidget , SIGNAL(selectionModeChanged())
, this , SLOT (changeSelectionMode ()) );
connect( &_selectCommand, SIGNAL(selectionToggled (Occurrence))
, _cellWidget , SLOT (select (Occurrence)) );
connect( _cellWidget , SIGNAL(stateChanged(shared_ptr<CellWidget::State>&))
, this , SLOT (setState (shared_ptr<CellWidget::State>&)) );
connect( this , SIGNAL(stateChanged(shared_ptr<CellWidget::State>&))
, _cellWidget , SLOT (setState (shared_ptr<CellWidget::State>&)) );
_cellWidget->refresh();
}
CellViewer::~CellViewer ()
{
_controller->deleteLater ();
//_script->deleteLater ();
_goto->deleteLater ();
}
QString CellViewer::_getAbsWidgetPath ( const QString& relPath ) const
{
if (relPath.startsWith("viewer.")) return relPath;
return QString(_prefixWPath).append( relPath );
}
bool CellViewer::hasMenu( const QString& relativePath ) const
{ return findChild<QObject*>(_getAbsWidgetPath(relativePath)) != NULL; }
bool CellViewer::hasMenuAction( const QString& relativePath ) const
{ return findChild<QAction*>(_getAbsWidgetPath(relativePath)) != NULL; }
QMenu* CellViewer::_getParentMenu( const QString& absolutePath ) const
{
QString parentPath = absolutePath.section('.',0,-2);
QMenu* parentMenu = findChild<QMenu*>(parentPath);
if (parentMenu == NULL) {
if (parentPath != "viewer") {
cerr << Warning( "CellViewer::_getParentMenu() - Missing parent menu for %s."
, absolutePath.toStdString().c_str() ) << endl;
}
return NULL;
}
return parentMenu;
}
QMenu* CellViewer::addMenu ( const QString& path, string text, unsigned int flags )
{
QString absolutePath = _getAbsWidgetPath( path );
QMenu* menu = findChild<QMenu*>(absolutePath);
if (menu != NULL) return menu;
if (flags & TopMenu) {
menu = menuBar()->addMenu( tr(text.c_str()) );
menu->setObjectName( absolutePath );
} else {
QMenu* parentMenu = _getParentMenu( absolutePath );
if (parentMenu == NULL) return NULL;
menu = parentMenu->addMenu( tr(text.c_str()) );
menu->setObjectName( absolutePath );
}
return menu;
}
bool CellViewer::addToMenu ( const QString& path )
{
if (not path.endsWith("====")) return false;
QMenu* menu = _getParentMenu( _getAbsWidgetPath(path) );
if (menu == NULL) return false;
menu->addSeparator();
return true;
}
QAction* CellViewer::addToMenu ( const QString& path
, string text
, string textTip
, std::function< void() > callback
, QIcon icon )
{
QString absolutePath = _getAbsWidgetPath( path );
QAction* action = findChild<QAction*>(absolutePath);
if (action == NULL) {
QMenu* parentMenu = _getParentMenu( absolutePath );
if (parentMenu == NULL) return NULL;
action = new QAction( tr(text.c_str()), this );
action->setObjectName( absolutePath );
action->setStatusTip ( tr(textTip.c_str()) );
action->setVisible ( true );
if (not icon.isNull()) action->setIcon( icon );
parentMenu->addAction( action );
_actionCallbacks.insert( make_pair(absolutePath,boost::any(callback)) );
connect( action, SIGNAL(triggered()), this, SLOT(doAction()) );
}
return action;
}
QAction* CellViewer::addToMenu ( const QString& path
, string text
, string textTip
, string scriptPath )
{
QString absolutePath = _getAbsWidgetPath( path );
QAction* action = findChild<QAction*>(absolutePath);
if (action == NULL) {
action = new QAction( tr(text.c_str()), this );
action->setObjectName( absolutePath );
action->setStatusTip ( tr(textTip.c_str()) );
action->setVisible ( true );
QMenu* parentMenu = _getParentMenu( absolutePath );
if (parentMenu != NULL) {
parentMenu->addAction( action );
} else if (absolutePath == "viewer") {
addAction( action );
}
_actionCallbacks.insert( make_pair(absolutePath,boost::any(QString(scriptPath.c_str()))) );
connect( action, SIGNAL(triggered()), this, SLOT(doAction()) );
}
return action;
}
QAction* CellViewer::addToMenu ( QString path
, QString text
, QString textTip
, const QKeySequence& shortCut
, QIcon icon
//, QWidget* receiver
//, SlotMethod slotMethod
)
{
QString absolutePath = _getAbsWidgetPath( path );
QAction* action = findChild<QAction*>(absolutePath);
if (action == NULL) {
action = new QAction( text, this );
action->setObjectName( absolutePath );
action->setStatusTip ( textTip );
action->setShortcut ( shortCut );
action->setVisible ( true );
if (not icon.isNull()) action->setIcon( icon );
QMenu* parentMenu = _getParentMenu( absolutePath );
if (parentMenu != NULL) {
parentMenu->addAction( action );
} else if (absolutePath == "viewer") {
addAction( action );
}
//if ((receiver != NULL) and (slotMethod != NULL))
// connect( action, &QAction::triggered, receiver, slotMethod );
}
return action;
}
void CellViewer::doAction ()
{
QString path = sender()->objectName();
ActionLut::const_iterator iaction = _actionCallbacks.find( path );
if (iaction == _actionCallbacks.end()) {
cerr << Error( "CellViewer::doAction() - Path \"%s\" in not registered."
, path.toStdString().c_str() ) << endl;
return;
}
const boost::any& callback = iaction->second;
if (callback.type() == typeid( std::function<void()> )) {
ExceptionWidget::catchAllWrapper( boost::any_cast< std::function<void()> >(callback) );
} else if (callback.type() == typeid( QString )) {
runScript( boost::any_cast<QString>(callback) );
} else {
cerr << Error("CellViewer::doAction(): For action \"%s\",\n"
" cannot cast the callback into QString or std::function<void()>."
, path.toStdString().c_str() ) << endl;
}
}
void CellViewer::createMenus ()
{
addMenu ( "file" , "File" , TopMenu );
addMenu ( "view" , "View" , TopMenu );
addMenu ( "tools", "Tools", TopMenu );
// Building the "File" menu.
QAction* action = addToMenu( "viewer.interrupt"
, tr("Interrupt")
, tr("Interrupt the running tool")
, QKeySequence(tr("CTRL+C"))
);
action->setVisible( false );
addAction( action );
connect( action, SIGNAL(triggered()), this, SLOT(raiseToolInterrupt()) );
action = addToMenu( "file.openCell"
, tr("&Open Cell")
, tr("Open (load) a new Cell")
, QKeySequence(tr("CTRL+O"))
, QIcon(":/images/stock_open.png")
);
addToMenu( "file.========" );
for ( int i=0 ; i<CellHistorySize ; i++ ) {
_cellHistoryAction[i] = addToMenu( QString("file.cellHistory[%1]").arg(i)
, QString("Slot[%1]").arg(i)
, QString("History empty Slot[%1]").arg(i)
, QKeySequence()
);
_cellHistoryAction[i]->setVisible( false );
_cellHistoryAction[i]->setData ( i );
_cellHistoryAction[i]->setFont ( Graphics::getFixedFont(QFont::Bold,false,false) );
connect( _cellHistoryAction[i], SIGNAL(triggered()), this, SLOT(openHistoryCell()) );
}
addToMenu( "file.========" );
action = addToMenu( "file.saveCell"
, tr("&Save Cell")
, tr("Save (write) the current Cell")
, QKeySequence()
, QIcon(":/images/stock_save.png")
);
action->setVisible( false );
addToMenu( "file.========" );
action = addToMenu( "file.importCell"
, tr("&Import Cell")
, tr("Import (convert) a new Cell")
, QKeySequence()
);
action = addToMenu( "file.exportCell"
, tr("&Export Cell")
, tr("Export (convert) Cell")
, QKeySequence()
);
addToMenu( "file.========" );
action = addToMenu( "file.print"
, tr("&Print")
, tr("Print the displayed area")
, QKeySequence(tr("CTRL+P"))
);
connect( action, SIGNAL(triggered()), this, SLOT(printDisplay()) );
action = addToMenu( "file.image"
, tr("Save to &Image")
, tr("Save the displayed area to image")
, QKeySequence()
);
connect( action, SIGNAL(triggered()), this, SLOT(imageDisplay()) );
action = addToMenu( "file.nextBreakpoint"
, tr("&Next Breakpoint")
, tr("Proceed to the next breakpoint")
, QKeySequence()
);
addToMenu( "file.========" );
action = addToMenu( "file.close"
, tr("&Close")
, tr("Close This Coriolis CellViewer")
, QKeySequence(tr("CTRL+W"))
);
connect( action, SIGNAL(triggered()), this, SLOT(close()) );
action = addToMenu( "file.close"
, tr("&Close")
, tr("Close This Coriolis CellViewer")
, QKeySequence(tr("CTRL+W"))
);
action = addToMenu( "file.exit"
, tr("&Exit")
, tr("Exit All Coriolis CellViewer")
, QKeySequence(tr("CTRL+Q"))
);
connect( action, SIGNAL(triggered()), qApp, SLOT(closeAllWindows()) );
// Building the "View" menu.
action = addToMenu( "view.refresh"
, tr("&Refresh")
, tr("Force full redrawing of the display")
, QKeySequence(tr("CTRL+L"))
);
connect( action, SIGNAL(triggered()), _cellWidget, SLOT(refresh()) );
action = addToMenu( "view.fit"
, tr("&Fit to Contents")
, tr("Adjust zoom to fit the whole cell's contents")
, Qt::Key_F
);
connect( action, SIGNAL(triggered()), _cellWidget, SLOT(fitToContents()) );
action = addToMenu( "view.goto"
, tr("&Goto")
, tr("Center view on that point, with zoom adjustment")
, Qt::Key_G
);
connect( action, SIGNAL(triggered()), this, SLOT(doGoto()) );
_showSelectionAction = addToMenu( "view.showSelection"
, tr("&Show Selection")
, tr("Highlight the selected items (darken others)")
, Qt::Key_S
);
_showSelectionAction->setCheckable( true );
connect( _showSelectionAction, SIGNAL(toggled(bool)), this, SLOT(setShowSelection(bool)) );
action = addToMenu( "view.changeRubber"
, tr("Change Rubber Style")
, tr("Cycle through all avalaibles rubber drawing styles")
, Qt::Key_Asterisk
);
connect( action, SIGNAL(triggered()), _cellWidget, SLOT(rubberChange()) );
action = addToMenu( "view.clearRulers"
, tr("Clear Rulers")
, tr("Remove all rulers")
, QKeySequence()
);
connect( action, SIGNAL(triggered()), _cellWidget, SLOT(clearRulers()) );
// Building the "Tools" menu.
action = addToMenu( "tools.controller"
, tr("Controller")
, tr("Fine Tune && Inspect DataBase")
, QKeySequence(tr("CTRL+I"))
, QIcon(":/images/swiss-knife.png")
);
connect( action, SIGNAL(triggered()), _controller, SLOT(toggleShow()) );
action = addToMenu( "tools.script"
, tr("Python Script")
, tr("Run Python Script. Must provide a ScripMain(cell) function")
, QKeySequence(tr("SHIFT+P,SHIFT+S"))
, QIcon(":/images/python-logo-v3.png")
);
connect( action, SIGNAL(triggered()), this, SLOT(runScriptWidget()) );
}
QMenu* CellViewer::createDebugMenu ()
{
if (not _debugMenu)
addMenu ( "debug" , "Debug" , TopMenu );
return _debugMenu;
}
void CellViewer::refreshTitle ()
{
QString cellName = "None";
if ( getCell() )
cellName = getString(getCell()->getName()).c_str();
QString title = QString("%1:<%2>").arg(_applicationName).arg(cellName);
setWindowTitle ( title );
}
void CellViewer::refreshHistory ()
{
if ( getCell() == NULL ) return;
shared_ptr<CellWidget::State> activeState = _cellWidget->getState();
_cellHistory.remove ( activeState );
if ( _cellHistory.size() > CellHistorySize-1 )
_cellHistory.pop_front ();
_cellHistory.push_back ( activeState );
list< shared_ptr<CellWidget::State> >::iterator istate = _cellHistory.begin();
for ( size_t i=0 ; i<CellHistorySize ; i++ ) {
if ( istate != _cellHistory.end() ) {
QString entry = tr("&%1: %2").arg(i+1).arg( getString((*istate)->getName()).c_str() );
_cellHistoryAction[i]->setText ( entry );
_cellHistoryAction[i]->setVisible ( true );
istate++;
} else {
_cellHistoryAction[i]->setVisible ( false );
}
}
}
void CellViewer::setState ( shared_ptr<CellWidget::State>& state )
{
static bool isEmitter = false;
if ( sender() == this ) {
isEmitter = true;
emit stateChanged ( state );
} else {
if ( !isEmitter ) {
blockSignals ( true );
refreshTitle ();
refreshHistory ();
blockSignals ( false );
} else
isEmitter = false;
}
}
void CellViewer::setCell ( Cell* cell )
{
if (cell == getCell()) return;
if (getCell()) getCell()->removeObserver( getCellObserver() );
Name cellName = (cell) ? cell->getName() : "empty";
list< shared_ptr<CellWidget::State> >::iterator istate
= find_if( _cellHistory.begin(), _cellHistory.end(), CellWidget::FindStateName(cellName) );
if (istate != _cellHistory.end()) {
(*istate)->getCell()->addObserver( getCellObserver() );
emit stateChanged ( *istate );
return;
}
cell->addObserver( getCellObserver() );
_cellWidget->setCell( cell );
}
Cell* CellViewer::getCell () const
{ return getCellWidget()->getCell(); }
Cell* CellViewer::getCellFromDb ( const char* name )
{
cerr << "[ERROR] virtual function CellViewer::getCellFromDb() has not been overloaded.\n"
<< " (this will prevent \"Open Cell\" to work)"
<< endl;
return NULL;
}
void CellViewer::renameCell ( const char* name )
{
Cell* cell = getCell();
if ( cell == NULL ) return;
cell->setName ( name );
refreshTitle ();
refreshHistory ();
}
void CellViewer::doGoto ()
{
if ( _goto->exec() == QDialog::Accepted ) {
if ( not _goto->hasXy() ) return;
Box gotoArea ( _goto->getX(), _goto->getY() );
DbU::Unit width;
DbU::Unit height;
if ( _goto->hasAperture() ) {
width = height = _goto->getAperture() / 2;
} else {
Box visibleArea = _cellWidget->getVisibleArea ();
width = visibleArea.getWidth ()/2;
height = visibleArea.getHeight()/2;
}
gotoArea.inflate ( width, height );
_cellWidget->reframe ( gotoArea );
}
}
void CellViewer::setAnonNetSelectable ( bool state )
{
SelectCommand* command = static_cast<SelectCommand*>( getCellWidget()->getCommand(SelectCommand::getStaticName()) );
unsigned int mode = (state) ? SelectCommand::NetMode : SelectCommand::NoAnonNetMode;
if (command) command->setSelectMode( mode );
}
void CellViewer::changeSelectionMode ()
{
if ( _updateState != InternalEmit ) {
_showSelectionAction->blockSignals ( true );
_showSelectionAction->setChecked ( _cellWidget->getState()->cumulativeSelection() );
_showSelectionAction->blockSignals ( false );
}
_updateState = ExternalEmit;
}
void CellViewer::setShowSelection ( bool state )
{
_updateState = InternalEmit;
_cellWidget->setShowSelection ( state );
}
void CellViewer::raiseToolInterrupt ()
{ _toolInterrupt = true; }
void CellViewer::clearToolInterrupt ()
{ _toolInterrupt = false; }
void CellViewer::openHistoryCell ()
{
QAction* historyAction = qobject_cast<QAction*> ( sender() );
if ( historyAction ) {
list< shared_ptr<CellWidget::State> >::iterator istate = _cellHistory.begin();
size_t index = historyAction->data().toUInt();
for ( ; index>0 ; index--, istate++ );
//cerr << "History: " << (*istate)->getName() << endl;
emit stateChanged ( *istate );
}
}
void CellViewer::select ( Occurrence& occurrence )
{ if ( _cellWidget ) _cellWidget->select ( occurrence ); }
void CellViewer::unselect ( Occurrence& occurrence )
{ if ( _cellWidget ) _cellWidget->unselect ( occurrence ); }
void CellViewer::unselectAll ()
{ if ( _cellWidget ) _cellWidget->unselectAll(); }
void CellViewer::printDisplay ()
{
if ( !_cellWidget ) return;
if ( !_cellWidget->getCell() ) {
cerr << Warning("Unable to print, no cell loaded yet.") << endl;
return;
}
QPrinter printer ( QPrinter::ScreenResolution );
printer.setPaperSize ( (QPrinter::PaperSize)Cfg::getParamEnumerate("viewer.printer.paper",0)->asInt() );
printer.setOutputFileName ( "unicorn-snapshot.pdf" );
QPrintDialog dialog ( &printer );
if ( dialog.exec() == QDialog::Accepted )
print ( &printer );
}
void CellViewer::print ( QPrinter* printer )
{
CellPrinter* cellPrinter = new CellPrinter();
cellPrinter->setScreenCellWidget( _cellWidget );
cellPrinter->toPdf ( printer, false );
delete cellPrinter;
}
void CellViewer::imageDisplay ()
{
if ( !_cellWidget ) return;
if ( !_cellWidget->getCell() ) {
cerr << Warning("Unable to save to image, no cell loaded yet.") << endl;
return;
}
CellImage* cellImage = new CellImage();
cellImage->setScreenCellWidget( _cellWidget );
QImage* image = cellImage->toImage(0);
delete cellImage;
char workingDirectory [1024];
getcwd ( workingDirectory, 1024 );
QString filePath = QFileDialog::getSaveFileName ( this
, tr("Save Image as ...")
, workingDirectory
, tr("Image (*.png)")
);
image->save ( filePath, "png" );
delete image;
}
void CellViewer::_runScript ( QString scriptPath )
{
if (scriptPath.endsWith(".py",Qt::CaseInsensitive))
scriptPath.truncate( scriptPath.size()-3 );
Utilities::Path userScript ( scriptPath.toStdString() );
Utilities::Path userDirectory ( userScript.dirname() );
if (not userDirectory.absolute())
userDirectory = Utilities::Path::cwd() / userDirectory;
Isobar::Script::addPath( userDirectory.string() );
dbo_ptr<Isobar::Script> script = Isobar::Script::create(userScript.basename().string());
script->addKwArgument( "cell" , (PyObject*)PyCell_Link(getCell()) );
script->addKwArgument( "editor" , (PyObject*)PyCellViewer_Link(this) );
script->runFunction ( "ScriptMain", getCell() );
Isobar::Script::removePath( userDirectory.string() );
}
void CellViewer::runScript ( QString scriptPath )
{ ExceptionWidget::catchAllWrapper( std::bind( &CellViewer::_runScript, this, scriptPath ) ); }
void CellViewer::runScriptWidget ()
{ ScriptWidget::runScript( this, getCell() ); }
string CellViewer::_getString () const
{
ostringstream s;
s << "<CellViewer ";
Cell* cell = getCell();
if (cell) s << getString(cell->getName());
else s << "No_Cell_Loaded";
s << ">";
return s.str();
}
} // End of Hurricane namespace.