diff --git a/doc/CMakeLists.txt b/doc/CMakeLists.txt index 8ed61f8..e587589 100644 --- a/doc/CMakeLists.txt +++ b/doc/CMakeLists.txt @@ -1,4 +1,4 @@ ########### install files ############### # #kde4_create_handbook(index.docbook INSTALL_DESTINATION ${KDE_INSTALL_DOCBUNDLEDIR}/en SUBDIR step) -kdoctools_create_handbook(index.docbook INSTALL_DESTINATION ${KDE_INSTALL_DOCBUNDLEDIR}/en SUBDIR step) \ No newline at end of file +kdoctools_create_handbook(index.docbook INSTALL_DESTINATION ${KDE_INSTALL_DOCBUNDLEDIR}/en SUBDIR step) diff --git a/step/gasgraphics.cc b/step/gasgraphics.cc index 5575b57..df20374 100644 --- a/step/gasgraphics.cc +++ b/step/gasgraphics.cc @@ -1,399 +1,399 @@ /* This file is part of Step. Copyright (C) 2007 Vladimir Kuznetsov Copyright (C) 2014 Inge Wallin Step is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Step is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Step; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "gasgraphics.h" #include #include "ui_create_gas_particles.h" #include "worldmodel.h" #include "worldfactory.h" #include #include #include #include #include #include #include #include void GasCreator::start() { showMessage(MessageFrame::Information, i18n("Press left mouse button to position\ntop left corner of a region for %1", classNameTr())); } bool GasCreator::sceneEvent(QEvent* event) { QGraphicsSceneMouseEvent* mouseEvent = static_cast(event); if(event->type() == QEvent::GraphicsSceneMousePress && mouseEvent->button() == Qt::LeftButton) { QPointF pos = mouseEvent->scenePos(); QVariant vpos = QVariant::fromValue(StepGraphicsItem::pointToVector(pos)); _worldModel->simulationPause(); _worldModel->beginMacro(i18n("Create %1", _worldModel->newItemName(_className))); _item = _worldModel->newItem(_className); Q_ASSERT(_item != NULL); _worldModel->setProperty(_item, "measureRectCenter", vpos); _worldModel->setProperty(_item, "measureRectSize", QVariant::fromValue(StepCore::Vector2d::Zero().eval())); _worldModel->selectionModel()->setCurrentIndex(_worldModel->objectIndex(_item), QItemSelectionModel::ClearAndSelect); StepCore::Gas* gas = static_cast(_item); _worldModel->newItem("GasLJForce", gas); StepCore::Object* ljforce = gas->items()[0]; _worldModel->setProperty(ljforce, "depth", 0.1); _worldModel->setProperty(ljforce, "rmin", 0.1); _topLeft = StepGraphicsItem::pointToVector(pos); showMessage(MessageFrame::Information, i18n("Move mouse and release left mouse button to position\nbottom right corner of the region for %1", classNameTr())); return true; } else if(event->type() == QEvent::GraphicsSceneMouseMove && mouseEvent->buttons() & Qt::LeftButton) { _worldModel->simulationPause(); StepCore::Vector2d pos = StepGraphicsItem::pointToVector(mouseEvent->scenePos()); StepCore::Vector2d position = (_topLeft + pos) / 2.0; StepCore::Vector2d size = _topLeft - pos; _worldModel->setProperty(_item, "measureRectCenter", QVariant::fromValue(position)); _worldModel->setProperty(_item, "measureRectSize", QVariant::fromValue(size)); return true; } else if(event->type() == QEvent::GraphicsSceneMouseRelease && mouseEvent->button() == Qt::LeftButton) { _worldModel->simulationPause(); StepCore::Vector2d pos = StepGraphicsItem::pointToVector(mouseEvent->scenePos()); StepCore::Vector2d position = (_topLeft + pos) / 2.0; StepCore::Vector2d size = _topLeft - pos; if(size[0] == 0 && size[1] == 0) { size[0] = size[1] = 1; } _worldModel->setProperty(_item, "measureRectCenter", QVariant::fromValue(position)); _worldModel->setProperty(_item, "measureRectSize", QVariant::fromValue(size)); showMessage(MessageFrame::Information, i18n("Please fill in the parameters for the gas particles.")); GasMenuHandler* menuHandler = new GasMenuHandler(_item, _worldModel, NULL); menuHandler->createGasParticles(); menuHandler->deleteLater(); _worldModel->endMacro(); showMessage(MessageFrame::Information, i18n("%1 named '%2' created", classNameTr(), _item->name()), MessageFrame::CloseButton | MessageFrame::CloseTimer); setFinished(); return true; } return false; } /* class GasArrowHandlerGraphicsItem: public ArrowHandlerGraphicsItem { public: GasArrowHandlerGraphicsItem(StepCore::Item* item, WorldModel* worldModel, QGraphicsItem* parent, const StepCore::MetaProperty* property) : ArrowHandlerGraphicsItem(item, worldModel, parent, property) {} protected: StepCore::Vector2d value() { return static_cast(_item)->measureRectCenter() + static_cast(_item)->measureRectSize()/2.0; } void setValue(const StepCore::Vector2d& value) { _worldModel->simulationPause(); StepCore::Vector2d v = (value - static_cast(_item)->measureRectCenter())*2.0; _worldModel->setProperty(_item, _property, QVariant::fromValue(v)); } }; */ inline StepCore::Gas* GasVertexHandlerGraphicsItem::gas() const { return static_cast(_item); } StepCore::Vector2d GasVertexHandlerGraphicsItem::value() { return (gas()->measureRectSize().array()*(corners[_vertexNum]).array()).matrix(); } void GasVertexHandlerGraphicsItem::setValue(const StepCore::Vector2d& value) { StepCore::Vector2d oCorner = gas()->measureRectCenter() - (gas()->measureRectSize().array()*(corners[_vertexNum].array())).matrix(); StepCore::Vector2d delta = (gas()->measureRectCenter() + value - oCorner)/2.0; StepCore::Vector2d newPos = oCorner + delta; StepCore::Vector2d newSize = (newPos - oCorner)*2.0; double d = -0.1/currentViewScale(); StepCore::Vector2d sign = (delta.array()*(corners[_vertexNum].array())).matrix(); if(sign[0] < d || sign[1] < d) { if(sign[0] < d) { newPos[0] = oCorner[0]; newSize[0] = 0; _vertexNum ^= 1; } if(sign[1] < d) { newPos[1] = oCorner[1]; newSize[1] = 0; _vertexNum ^= 2; } _worldModel->setProperty(_item, "measureRectCenter", QVariant::fromValue(newPos)); _worldModel->setProperty(_item, "measureRectSize", QVariant::fromValue(newSize)); setValue(value); return; } _worldModel->setProperty(_item, "measureRectCenter", QVariant::fromValue(newPos)); _worldModel->setProperty(_item, "measureRectSize", QVariant::fromValue(newSize)); } GasGraphicsItem::GasGraphicsItem(StepCore::Item* item, WorldModel* worldModel) : StepGraphicsItem(item, worldModel) { Q_ASSERT(dynamic_cast(_item) != NULL); setFlag(QGraphicsItem::ItemIsSelectable); setFlag(QGraphicsItem::ItemIsMovable); //setExclusiveMoving(true); //setFlag(QGraphicsItem::ItemIsMovable); //setAcceptsHoverEvents(true); //_centerHandler = new ArrowHandlerGraphicsItem(item, worldModel, this, // _item->metaObject()->property("measureRectCenter")); //_centerHandler->setVisible(false); setZValue(REGION_ZVALUE); setAcceptsHoverEvents(true); setOnHoverHandlerEnabled(true); } inline StepCore::Gas* GasGraphicsItem::gas() const { return static_cast(_item); } void GasGraphicsItem::mouseSetPos(const QPointF& pos, const QPointF&, MovingState) { #ifdef __GNUC__ #warning Consider renaming measureRectCenter to position #endif const StepCore::MetaProperty* property = _item->metaObject()->property("measureRectCenter"); _worldModel->simulationPause(); _worldModel->setProperty(_item, property, QVariant::fromValue( pointToVector(pos) )); } QPainterPath GasGraphicsItem::shape() const { QPainterPath path; //path.addRect(QRectF(-radius,-radius,radius*2,radius*2)); path.addRect(_boundingRect); return path; } void GasGraphicsItem::paint(QPainter* painter, const QStyleOptionGraphicsItem* /*option*/, QWidget* /*widget*/) { //if(_isSelected) { const StepCore::Vector2d& size = gas()->measureRectSize(); painter->setPen(QPen(QColor::fromRgba(gas()->color()), 0)); painter->drawRect(QRectF(-size[0]/2, -size[1]/2, size[0], size[1])); //} } void GasGraphicsItem::viewScaleChanged() { double s = currentViewScale(); prepareGeometryChange(); const StepCore::Vector2d& size = gas()->measureRectSize(); _boundingRect = QRectF(-(size[0]+SELECTION_MARGIN/s)/2, -(size[1]+SELECTION_MARGIN/s)/2, (size[0]+SELECTION_MARGIN/s), (size[1]+SELECTION_MARGIN/s)); update(); } void GasGraphicsItem::worldDataChanged(bool dynamicOnly) { if(!dynamicOnly) { setPos(vectorToPoint(gas()->measureRectCenter())); viewScaleChanged(); /*prepareGeometryChange(); const StepCore::Vector2d& size = gas()->measureRectSize(); StepCore::Vector2d r0 = gas()->measureRectCenter() - size/2.0; _boundingRect = QRectF(r0[0], r0[1], size[0], size[1]);*/ //stateChanged(); } } void GasGraphicsItem::stateChanged() { #if 0 if(_isSelected) { /* const StepCore::Vector2d& size = gas()->measureRectSize(); StepCore::Vector2d r0 = gas()->measureRectCenter() - size/2.0; _boundingRect = QRectF(r0[0], r0[1], size[0], size[1]); */ _centerHandler->setVisible(true); } else { //_boundingRect = QRectF(); _centerHandler->setVisible(false); } #endif update(); } OnHoverHandlerGraphicsItem* GasGraphicsItem::createOnHoverHandler(const QPointF& pos) { double s = currentViewScale(); StepCore::Vector2d size = gas()->measureRectSize(); StepCore::Vector2d l = pointToVector(pos) - gas()->measureRectCenter(); int num = -1; double minDist2 = HANDLER_SNAP_SIZE*HANDLER_SNAP_SIZE/s/s; for(unsigned int i=0; i<4; ++i) { double dist2 = (l - (size.array()*(OnHoverHandlerGraphicsItem::corners[i]).array()).matrix()).squaredNorm(); if(dist2 < minDist2) { num = i; minDist2 = dist2; } } if(_onHoverHandler && _onHoverHandler->vertexNum() == num) return _onHoverHandler; if(num >= 0) return new GasVertexHandlerGraphicsItem(_item, _worldModel, this, num); return 0; } void GasMenuHandler::populateMenu(QMenu* menu, KActionCollection* actions) { _createGasParticlesUi = 0; _creationDialog = 0; //_confChanged = false; menu->addAction(QIcon::fromTheme("step_object_GasParticle"), i18n("Create particles..."), this, SLOT(createGasParticles())); //menu->addAction(QIcon::fromTheme("edit-clear"), i18n("Clear gas"), this, SLOT(clearGas())); menu->addSeparator(); ItemMenuHandler::populateMenu(menu, actions); } inline StepCore::Gas* GasMenuHandler::gas() const { return static_cast(_object); } void GasMenuHandler::clearGas() { // _worldModel->simulationPause(); } void GasMenuHandler::createGasParticles() { if(_worldModel->isSimulationActive()) _worldModel->simulationStop(); _creationDialog = new GasCreationDialog(this, gas()); // XXX: parent? // FIXME: Remove this member variable. _createGasParticlesUi = _creationDialog->ui(); createGasParticlesCountChanged(); _createGasParticlesUi->labelVolume->setText(gas()->metaObject()->property("rectVolume")->units()); _createGasParticlesUi->labelCount->setText(gas()->metaObject()->property("rectParticleCount")->units()); _createGasParticlesUi->labelConcentration->setText(gas()->metaObject()->property("rectConcentration")->units()); _createGasParticlesUi->labelMass->setText(gas()->metaObject()->property("rectMeanParticleMass")->units()); _createGasParticlesUi->labelTemperature->setText(gas()->metaObject()->property("rectTemperature")->units()); _createGasParticlesUi->labelMeanVelocity->setText(gas()->metaObject()->property("rectMeanVelocity")->units()); - connect(_createGasParticlesUi->lineEditCount, SIGNAL(textEdited(const QString&)), + connect(_createGasParticlesUi->lineEditCount, SIGNAL(textEdited(QString)), this, SLOT(createGasParticlesCountChanged())); - connect(_createGasParticlesUi->lineEditConcentration, SIGNAL(textEdited(const QString&)), + connect(_createGasParticlesUi->lineEditConcentration, SIGNAL(textEdited(QString)), this, SLOT(createGasParticlesConcentrationChanged())); int retval = _creationDialog->exec(); if (retval == QDialog::Accepted) { createGasParticlesApply(); } delete _creationDialog; _creationDialog = 0; delete _createGasParticlesUi; _createGasParticlesUi = 0; } void GasMenuHandler::createGasParticlesCountChanged() { _createGasParticlesUi->lineEditConcentration->setText(QString::number( _createGasParticlesUi->lineEditCount->text().toDouble() / gas()->rectVolume() )); } void GasMenuHandler::createGasParticlesConcentrationChanged() { _createGasParticlesUi->lineEditCount->setText(QString::number( round(_createGasParticlesUi->lineEditConcentration->text().toDouble() * gas()->rectVolume()) )); } bool GasMenuHandler::createGasParticlesApply() { Q_ASSERT(_createGasParticlesUi && _creationDialog); int count = _createGasParticlesUi->lineEditCount->text().toInt(); if(count > MAX_PARTICLES) { int ret = KMessageBox::warningContinueCancel(NULL, i18n("You are trying to create a very large number of particles. " "This will make simulation very slow. Do you want to continue?"), i18n("Warning - Step")); if(ret != KMessageBox::Continue) return false; } double mass = _createGasParticlesUi->lineEditMass->text().toDouble(); double temperature = _createGasParticlesUi->lineEditTemperature->text().toDouble(); bool ok; StepCore::Vector2d meanVelocity = StepCore::stringToType( _createGasParticlesUi->lineEditMeanVelocity->text(), &ok); _worldModel->beginMacro(i18n("Create particles for %1", gas()->name())); std::vector particles = gas()->rectCreateParticles(count, mass, temperature, meanVelocity); const StepCore::GasParticleList::const_iterator end = particles.end(); for(StepCore::GasParticleList::const_iterator it = particles.begin(); it != end; ++it) { _worldModel->addItem(*it, gas()); } _worldModel->endMacro(); return true; } /* void GasMenuHandler::clearGas() { _worldModel->simulationPause(); //_lastPointTime = -HUGE_VAL; // XXX _worldModel->setProperty(gas(), property("points"), QVariant::fromValue(StepCore::Vector2dList()) ); } */ diff --git a/step/infobrowser.cc b/step/infobrowser.cc index b0b325b..899ef1a 100644 --- a/step/infobrowser.cc +++ b/step/infobrowser.cc @@ -1,425 +1,425 @@ /* This file is part of Step. Copyright (C) 2007 Vladimir Kuznetsov Step is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Step is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Step; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "infobrowser.h" #include "worldmodel.h" #include "settings.h" #include #include #include #include #include #include #include #include #include #include #include #include InfoBrowser::InfoBrowser(WorldModel* worldModel, QWidget* parent, Qt::WindowFlags flags) : QDockWidget(i18n("Context info"), parent, flags), _worldModel(worldModel), _wikiJob(NULL), _wikiFromHistory(false), _selectionChanged(false) { QWidget* widget = new QWidget(this); setWidget(widget); QVBoxLayout* layout = new QVBoxLayout(widget); layout->setContentsMargins(0,0,0,0); layout->setSpacing(0); _toolBar = new KToolBar(widget); layout->addWidget(_toolBar); _toolBar->setMovable(false); _toolBar->setFloatable(false); _toolBar->setIconDimensions(16); _toolBar->setContextMenuPolicy(Qt::NoContextMenu); _toolBar->setToolButtonStyle(Qt::ToolButtonIconOnly); _backAction = _toolBar->addAction(QIcon::fromTheme("go-previous"), i18n("Back"), this, SLOT(back())); _backAction->setEnabled(false); _forwardAction = _toolBar->addAction(QIcon::fromTheme("go-next"), i18n("Forward"), this, SLOT(forward())); _forwardAction->setEnabled(false); _toolBar->addSeparator(); _syncAction = _toolBar->addAction(QIcon::fromTheme("goto-page"), i18n("Sync selection"), this, SLOT(syncSelection())); // XXX: icon _syncAction->setEnabled(false); _followAction = _toolBar->addAction(QIcon::fromTheme("note2"), i18n("Follow selection")/*, this, SLOT(syncSelection(bool))*/); // XXX: icon _followAction->setCheckable(true); _followAction->setChecked(true); _toolBar->addSeparator(); _execAction = _toolBar->addAction(QIcon::fromTheme("system-run"), i18n("Open in browser"), this, SLOT(openInBrowser())); _execAction->setEnabled(false); _htmlPart = new KHTMLPart(widget); layout->addWidget(_htmlPart->widget()); _htmlPart->setJavaEnabled(false); _htmlPart->setPluginsEnabled(false); _htmlPart->setJScriptEnabled(true); _htmlPart->setMetaRefreshEnabled(true); _htmlPart->setDNDEnabled(false); connect(_htmlPart->browserExtension(), - SIGNAL(openUrlRequest(const QUrl&, const KParts::OpenUrlArguments&, const KParts::BrowserArguments&)), - this, SLOT(openUrl(const QUrl&))); + SIGNAL(openUrlRequest(QUrl,KParts::OpenUrlArguments,KParts::BrowserArguments)), + this, SLOT(openUrl(QUrl))); - connect(_worldModel->selectionModel(), SIGNAL(currentChanged(const QModelIndex&, const QModelIndex&)), - this, SLOT(worldCurrentChanged(const QModelIndex&, const QModelIndex&))); + connect(_worldModel->selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)), + this, SLOT(worldCurrentChanged(QModelIndex,QModelIndex))); syncSelection(); } void InfoBrowser::showEvent(QShowEvent* event) { QDockWidget::showEvent(event); if(_selectionChanged) { _selectionChanged = false; QModelIndex current = _worldModel->selectionModel()->currentIndex(); worldCurrentChanged(current, QModelIndex()); } } void InfoBrowser::worldCurrentChanged(const QModelIndex& /*current*/, const QModelIndex& /*previous*/) { if(isVisible()) { if(_followAction->isChecked()) syncSelection(); else updateSyncSelection(); } else { _selectionChanged = true; } } void InfoBrowser::syncSelection(bool checked) { if(checked) { const QModelIndex current = _worldModel->selectionModel()->currentIndex(); const QUrl url(QString("objinfo:").append(current.data(WorldModel::ClassNameRole).toString())); openUrl(url, true); } } void InfoBrowser::updateSyncSelection() { if(_htmlPart->url().scheme() == "objinfo") { QModelIndex current = _worldModel->selectionModel()->currentIndex(); if(_htmlPart->url().path() == current.data(WorldModel::ClassNameRole).toString()) { _syncAction->setEnabled(false); return; } } _syncAction->setEnabled(true); } void InfoBrowser::openUrl(const QUrl& url, bool clearHistory, bool fromHistory) { // Cancel the old job if(_wikiJob) _wikiJob->kill(); _wikiJob = NULL; if(clearHistory) { _forwardHistory.clear(); _forwardAction->setEnabled(false); _backHistory.clear(); _backAction->setEnabled(false); fromHistory = true; } if(url.scheme() == "objinfo") { QString className = url.path(); if(className.isEmpty()) { setHtml("" "\n" "
\n" "
\n" "\n" + i18n( "Documentation" ) + "\n" "
\n" "
\n" "

\n" + i18n("No current object.") + "

\n" "
\n" "
\n" "", fromHistory, url ); return; } QString fileName = QStandardPaths::locate(QStandardPaths::DataLocation, QString("objinfo/%1.html").arg(className)); if(!fileName.isEmpty()) { QFile file(fileName); if(file.open(QIODevice::ReadOnly | QIODevice::Text)) { setHtml(QString::fromUtf8(file.readAll()), fromHistory, url /*QUrl::fromLocalFile(fileName)*/); return; } } setHtml("" "\n" "
\n" "
\n" "\n" + i18n( "Documentation error" ) + "\n" "
\n" "
\n" "

\n" + i18n("Documentation for %1 not available. ", QCoreApplication::translate("ObjectClass", className.toUtf8().constData())) + i18n("You can help Step by writing it!") + "

\n" "
\n" "
\n" "", fromHistory, url ); return; } else if(url.scheme() == "http") { if(!Settings::wikiExternal() && QRegExp("[a-zA-Z-]+\\.wikipedia\\.org").exactMatch(url.host())) { setHtml( "" "\n" "
\n" "
\n" "\n" + i18n( "Wikipedia" ) + "\n" "
\n" "
\n" "

\n" + i18n( "Fetching Wikipedia Information..." ) + "

\n" "
\n" "
\n" "\n", fromHistory); _wikiUrl = url; _wikiFromHistory = fromHistory; _wikiJob = KIO::storedGet(url, KIO::NoReload, KIO::HideProgressInfo); - connect(_wikiJob, SIGNAL(result(KJob*)), this, SLOT( wikiResult(KJob*))); + connect(_wikiJob, SIGNAL(result(KJob*)), this, SLOT(wikiResult(KJob*))); } else { QDesktopServices::openUrl(url); } } show(); } void InfoBrowser::setHtml(const QString& data, bool fromHistory, const QUrl& url) { if(!fromHistory) { _forwardAction->setEnabled(false); _forwardHistory.clear(); QString oldUrl = _htmlPart->url().url(); if(!oldUrl.isEmpty()) { _backHistory << oldUrl; _backAction->setEnabled(true); } } if(url.scheme() == "http") _execAction->setEnabled(true); else _execAction->setEnabled(false); _htmlPart->begin(url); _htmlPart->write( data ); _htmlPart->end(); updateSyncSelection(); } void InfoBrowser::back() { Q_ASSERT(!_backHistory.isEmpty()); QString url(_backHistory.takeLast()); if(_backHistory.isEmpty()) _backAction->setEnabled(false); QString curUrl = _htmlPart->url().url(); if(!curUrl.isEmpty()) { _forwardHistory << curUrl; _forwardAction->setEnabled(true); } openUrl(QUrl(url), false, true); } void InfoBrowser::forward() { Q_ASSERT(!_forwardHistory.isEmpty()); QString url(_forwardHistory.takeLast()); if(_forwardHistory.isEmpty()) _forwardAction->setEnabled(false); QString curUrl = _htmlPart->url().url(); if(!curUrl.isEmpty()) { _backHistory << curUrl; _backAction->setEnabled(true); } openUrl(QUrl(url), false, true); } void InfoBrowser::openInBrowser() { if(_htmlPart->url().scheme() == "http") { QDesktopServices::openUrl(_htmlPart->url()); } } void InfoBrowser::wikiResult(KJob* job) { // inspired by amarok if(job != _wikiJob) return; if(job->error() != 0) { setHtml("" "\n" "
\n" "
\n" "\n" + i18n( "Wikipedia error" ) + "\n" "
\n" "
\n

\n" + i18n( "Information could not be retrieved because the server was not reachable." ) + "

\n
\n" "
\n" "\n", _wikiFromHistory); return; } KIO::StoredTransferJob* const storedJob = static_cast( job ); QByteArray rawData = storedJob->data(); QString data; // TODO: better regexp if(rawData.contains("charset=utf-8")) data = QString::fromUtf8(rawData.data()); else data = QString(rawData); //if(data.find( "var wgArticleId = 0" ) != -1) // - article not found // remove the new-lines and tabs data.replace( '\n', ' ' ); data.replace( '\t', ' ' ); QString wikiLanguages; // Get the available language list if ( data.indexOf("
") != -1 ) { wikiLanguages = data.mid( data.indexOf("
") ); wikiLanguages = wikiLanguages.mid( wikiLanguages.indexOf("
    ") ); wikiLanguages = wikiLanguages.mid( 0, wikiLanguages.indexOf( "
" ) ); } QString copyright; QString copyrightMark = "
  • "; if ( data.indexOf( copyrightMark ) != -1 ) { copyright = data.mid( data.indexOf(copyrightMark) + copyrightMark.length() ); copyright = copyright.mid( 0, copyright.indexOf( "
  • " ) ); copyright.remove( "
    " ); //only one br at the beginning copyright.prepend( "
    " ); } // Ok lets remove the top and bottom parts of the page data = data.mid( data.indexOf( "

    " ) ); data = data.mid( 0, data.indexOf( "
    " ) ); // Adding back license information data += copyright; data.append( "
    " ); // Remove unnessesary sections (do it with style?) data.remove( QRegExp("

    [^<]*

    ") ); data.remove( QRegExp( "]*>[^<]*<[^>]*>[^<]*<[^>]*>[^<]*" ) ); data.replace( QRegExp( "]*>([^<]*)" ), "\\1" ); // Remove anything inside of a class called urlexpansion, as it's pointless for us data.remove( QRegExp( "[^(]*[(][^)]*[)]ttp inthttp" ) ); // Remove hidden table rows as well QRegExp hidden( ".*", Qt::CaseInsensitive ); hidden.setMinimal( true ); //greedy behaviour wouldn't be any good! data.remove( hidden ); // Remove jump-to-nav QRegExp jumpToNav( "
    .*
    ", Qt::CaseInsensitive ); jumpToNav.setMinimal( true ); data.remove( jumpToNav ); // we want to keep our own style (we need to modify the stylesheet a bit to handle things nicely) //data.remove( QRegExp( "style= *\"[^\"]*\"" ) ); //data.remove( QRegExp( "class= *\"[^\"]*\"" ) ); // let's remove the form elements, we don't want them. data.remove( QRegExp( "]*>" ) ); data.remove( QRegExp( "]*>" ) ); data.remove( "\n" ); data.remove( QRegExp( "]*>" ) ); data.remove( "\n" ); data.remove( QRegExp( "]*>" ) ); data.remove( "" ); //first we convert all the links with protocol to external, as they should all be External Links. //data.replace( QRegExp( "href= *\"http:" ), "href=\"externalurl:" ); //QString url = _wikiUrl.url(); //QString baseUrl = url.mid(0, url.indexOf("wiki/")); //data.replace( QRegExp( "href= *\"/" ), "href=\"" + baseUrl ); //data.replace( QRegExp( "href= *\"#" ), "href=\"" + baseUrl + '#' ); data.prepend("\n" "
    \n" "
    \n" "\n" + i18n( "Wikipedia Information" ) + "\n" "
    \n" "
    \n"); data.append( "
    \n" "
    \n"); if (!wikiLanguages.isEmpty()) { data.append( "
    " "
    \n" "
    \n" "\n" + i18n( "Wikipedia Other Languages" ) + "\n" "
    \n" "
    \n" + wikiLanguages + "
    \n" "
    \n" ); } data.append( "\n" ); setHtml( data, _wikiFromHistory, _wikiUrl ); _wikiJob = NULL; } diff --git a/step/itempalette.cc b/step/itempalette.cc index 413a115..4fc0890 100644 --- a/step/itempalette.cc +++ b/step/itempalette.cc @@ -1,304 +1,304 @@ /* This file is part of Step. Copyright (C) 2007 Vladimir Kuznetsov Step is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Step is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Step; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "itempalette.h" #include "worldmodel.h" #include "worldfactory.h" #include #include "settings.h" #include #include #include #include #include #include #include #include #include #include #include #include class QPaintEvent; // Inspired by QToolBarSeparator class Separator: public QWidget { public: explicit Separator(QWidget* parent): QWidget(parent) { setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); setProperty("isSeparator", true); } QSize sizeHint() const Q_DECL_OVERRIDE { QStyleOption opt; opt.initFrom(this); const int extent = style()->pixelMetric(QStyle::PM_ToolBarSeparatorExtent, &opt, parentWidget()); return QSize(extent, extent); } void paintEvent(QPaintEvent *) Q_DECL_OVERRIDE { QPainter p(this); QStyleOption opt; opt.initFrom(this); style()->drawPrimitive(QStyle::PE_IndicatorToolBarSeparator, &opt, &p, parentWidget()); } }; class PaletteLayout: public QLayout { public: PaletteLayout(QWidget *parent, int margin = 0, int spacing = -1) : QLayout(parent) { setMargin(margin); setSpacing(spacing); resetCache(); } PaletteLayout(int spacing = -1) { setSpacing(spacing); resetCache(); } ~PaletteLayout() { QLayoutItem *item; while ((item = takeAt(0))) delete item; } void addItem(QLayoutItem *item) Q_DECL_OVERRIDE { itemList.append(item); resetCache(); } int count() const Q_DECL_OVERRIDE { return itemList.size(); } QLayoutItem* itemAt(int index) const Q_DECL_OVERRIDE { return itemList.value(index); } QLayoutItem* takeAt(int index) Q_DECL_OVERRIDE { resetCache(); if (index >= 0 && index < itemList.size()) return itemList.takeAt(index); else return 0; } Qt::Orientations expandingDirections() const Q_DECL_OVERRIDE { return Qt::Vertical; } bool hasHeightForWidth() const Q_DECL_OVERRIDE { return true; } int heightForWidth(int width) const Q_DECL_OVERRIDE { if(isCachedHeightForWidth && cachedHeightForWidth.width() == width) { return cachedHeightForWidth.height(); } else { cachedHeightForWidth.setWidth(width); cachedHeightForWidth.setHeight(doLayout(QRect(0, 0, width, 0), true)); isCachedHeightForWidth = true; return cachedHeightForWidth.height(); } } void setGeometry(const QRect &rect) Q_DECL_OVERRIDE { resetCache(); QLayout::setGeometry(rect); doLayout(rect, false); } QSize sizeHint() const Q_DECL_OVERRIDE { return minimumSize(); } QSize minimumSize() const Q_DECL_OVERRIDE { if(isCachedMinimumSize) return cachedMinimumSize; cachedMinimumSize = QSize(); QLayoutItem *item; foreach (item, itemList) cachedMinimumSize = cachedMinimumSize.expandedTo(item->minimumSize()); isCachedMinimumSize = true; return cachedMinimumSize; } void setOneLine(bool b) { oneLine = b; invalidate(); } bool isOneLine() const { return oneLine; } void invalidate() Q_DECL_OVERRIDE { resetCache(); QLayout::invalidate(); } protected: void resetCache() { isCachedMinimumSize = false; isCachedHeightForWidth = false; } int doLayout(const QRect &rect, bool testOnly) const { int x = rect.x(); int y = rect.y(); int lineHeight = 0; if(oneLine) { foreach(QLayoutItem* item, itemList) { y = y + lineHeight + spacing(); lineHeight = item->sizeHint().height(); if(!testOnly) item->setGeometry(QRect(rect.x(), y, rect.width(), lineHeight)); } } else { foreach(QLayoutItem* item, itemList) { int w = item->sizeHint().width(); int h = item->sizeHint().height(); int nextX = x + item->sizeHint().width() + spacing(); if(item->widget() && item->widget()->property("isSeparator").toBool()) { x = rect.x(); y = y + lineHeight + spacing(); nextX = x + rect.width(); w = rect.width(); lineHeight = 0; } else if(nextX - spacing() > rect.right() && lineHeight > 0) { x = rect.x(); y = y + lineHeight + spacing(); nextX = x + w + spacing(); lineHeight = 0; } if(!testOnly) item->setGeometry(QRect(x, y, w, h)); x = nextX; lineHeight = qMax(lineHeight, h); } } return y + lineHeight - rect.y(); } QList itemList; bool oneLine; mutable bool isCachedMinimumSize; mutable bool isCachedHeightForWidth; mutable QSize cachedMinimumSize; mutable QSize cachedHeightForWidth; }; class PaletteScrollArea: public QScrollArea { public: PaletteScrollArea(QWidget* parent): QScrollArea(parent) {} protected: void resizeEvent(QResizeEvent* event) Q_DECL_OVERRIDE { if(widget() && widget()->layout()) { QSize size(maximumViewportSize().width(), widget()->layout()->heightForWidth(maximumViewportSize().width())); if(size.height() > maximumViewportSize().height()) { int ext = style()->pixelMetric(QStyle::PM_LayoutHorizontalSpacing); size.setWidth(maximumViewportSize().width() - verticalScrollBar()->sizeHint().width() - ext); size.setHeight(widget()->layout()->heightForWidth(size.width())); } widget()->resize(size); } QScrollArea::resizeEvent(event); } }; ItemPalette::ItemPalette(WorldModel* worldModel, QWidget* parent, Qt::WindowFlags flags) : QDockWidget(i18n("Palette"), parent, flags), _worldModel(worldModel), _widget(0) { setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea); //setFeatures(QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetFloatable); QWidget* topWidget = new QWidget(this); _scrollArea = new PaletteScrollArea(topWidget); _scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); _scrollArea->setFrameShape(QFrame::NoFrame); _widget = new QWidget(_scrollArea); _layout = new PaletteLayout(_widget); _layout->setSpacing(0); _layout->setOneLine(Settings::showButtonText()); _actionGroup = new QActionGroup(this); _actionGroup->setExclusive(true); _pointerAction = new QAction(i18n("Pointer"), this); _pointerAction->setToolTip(i18n("Selection pointer")); _pointerAction->setIcon(QIcon::fromTheme("pointer")); _pointerAction->setCheckable(true); _pointerAction->setChecked(true); _pointerAction->setProperty("step_object", "Pointer"); _actionGroup->addAction(_pointerAction); createToolButton(_pointerAction); createSeparator(); foreach(const QString &name, _worldModel->worldFactory()->paletteMetaObjects()) { if(!name.isEmpty()) createObjectAction(_worldModel->worldFactory()->metaObject(name)); else createSeparator(); } _scrollArea->setWidget(_widget); _scrollArea->setMinimumWidth(_widget->minimumSizeHint().width()); QVBoxLayout* topLayout = new QVBoxLayout(topWidget); topLayout->addWidget(_scrollArea); setWidget(topWidget); QObject::connect(_actionGroup, SIGNAL(triggered(QAction*)), this, SLOT(actionTriggered(QAction*))); QAction* showText = new QAction(i18n("Show text"), this); showText->setCheckable(true); showText->setChecked(Settings::showButtonText()); QObject::connect(showText, SIGNAL(toggled(bool)), this, SLOT(showButtonTextToggled(bool))); _widget->addAction(showText); _widget->setContextMenuPolicy(Qt::ActionsContextMenu); } void ItemPalette::createToolButton(QAction* action) { QToolButton* button = new QToolButton(_widget); button->setToolButtonStyle(Settings::showButtonText() ? Qt::ToolButtonTextBesideIcon : Qt::ToolButtonIconOnly); button->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); button->setAutoRaise(true); button->setIconSize(QSize(22,22)); button->setDefaultAction(action); _toolButtons.append(button); _layout->addWidget(button); } void ItemPalette::createSeparator() { QAction* action = new QAction(this); action->setSeparator(true); _actionGroup->addAction(action); _layout->addWidget(new Separator(_widget)); } void ItemPalette::createObjectAction(const StepCore::MetaObject* metaObject) { Q_ASSERT(metaObject && !metaObject->isAbstract()); QAction* action = new QAction(metaObject->classNameTr(), this); action->setToolTip(metaObject->descriptionTr()); action->setIcon(_worldModel->worldFactory()->objectIcon(metaObject)); action->setCheckable(true); action->setProperty("step_object", metaObject->className()); _actionGroup->addAction(action); createToolButton(action); } void ItemPalette::showButtonTextToggled(bool b) { Settings::setShowButtonText(b); - Settings::self()->writeConfig(); + Settings::self()->save(); foreach(QToolButton* button, _toolButtons) { button->setToolButtonStyle(b ? Qt::ToolButtonTextBesideIcon : Qt::ToolButtonIconOnly); } _layout->setOneLine(b); _scrollArea->setMinimumWidth(_widget->minimumSizeHint().width()); } void ItemPalette::actionTriggered(QAction* action) { emit beginAddItem(action->property("step_object").toString()); } void ItemPalette::endAddItem(const QString& name, bool /*success*/) { if(name == _actionGroup->checkedAction()->property("step_object").toString()) _pointerAction->setChecked(true); } bool ItemPalette::event(QEvent* event) { return QDockWidget::event(event); } diff --git a/step/mainwindow.cc b/step/mainwindow.cc index 83ef7fc..bbb0a58 100644 --- a/step/mainwindow.cc +++ b/step/mainwindow.cc @@ -1,665 +1,665 @@ /* This file is part of Step. Copyright (C) 2007 Vladimir Kuznetsov Step is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Step is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Step; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "mainwindow.h" #include "ui_configure_step_general.h" #include "clipboard.h" #include "worldmodel.h" #include "worldscene.h" #include "worldbrowser.h" #include "propertiesbrowser.h" #include "infobrowser.h" #include "undobrowser.h" #include "itempalette.h" #include "settings.h" #include "unitscalc.h" #include #include #include #include #include #include #include #include -#include #include #include #include #include #include #include #include +#include #include #include #include #include #include #include #include #include #include #include MainWindow::MainWindow() { qsrand(time(NULL)); std::srand(time(NULL)); // Load UnitCalc at startup UnitsCalc::self(); setObjectName("MainWindow"); setCorner(Qt::TopLeftCorner, Qt::LeftDockWidgetArea); setCorner(Qt::BottomLeftCorner, Qt::LeftDockWidgetArea); setCorner(Qt::TopRightCorner, Qt::RightDockWidgetArea); setCorner(Qt::BottomRightCorner, Qt::RightDockWidgetArea); worldModel = new WorldModel(this); worldModel->setActions(actionCollection()); itemPalette = new ItemPalette(worldModel, this); itemPalette->setObjectName("itemPalette"); addDockWidget(Qt::LeftDockWidgetArea, itemPalette); worldBrowser = new WorldBrowser(worldModel, this); worldBrowser->setObjectName("worldBrowser"); addDockWidget(Qt::RightDockWidgetArea, worldBrowser); propertiesBrowser = new PropertiesBrowser(worldModel, this); propertiesBrowser->setObjectName("propertiesBrowser"); addDockWidget(Qt::RightDockWidgetArea, propertiesBrowser); infoBrowser = new InfoBrowser(worldModel, this); infoBrowser->setObjectName("infoBrowser"); addDockWidget(Qt::RightDockWidgetArea, infoBrowser); undoBrowser = new UndoBrowser(worldModel, this); undoBrowser->setObjectName("undoBrowser"); addDockWidget(Qt::RightDockWidgetArea, undoBrowser); worldScene = new WorldScene(worldModel); worldGraphicsView = new WorldGraphicsView(worldScene, this); setCentralWidget(worldGraphicsView); connect(worldModel, SIGNAL(simulationStopped(int)), this, SLOT(simulationStopped(int))); - connect(worldModel->selectionModel(), SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)), + connect(worldModel->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), this, SLOT(worldSelectionChanged())); - connect(itemPalette, SIGNAL(beginAddItem(const QString&)), - worldScene, SLOT(beginAddItem(const QString&))); - connect(worldScene, SIGNAL(endAddItem(const QString&, bool)), - itemPalette, SLOT(endAddItem(const QString&, bool))); - connect(worldScene, SIGNAL(linkActivated(const QUrl&)), - infoBrowser, SLOT(openUrl(const QUrl&))); - connect(worldScene, SIGNAL(endAddItem(QString, bool)), + connect(itemPalette, SIGNAL(beginAddItem(QString)), + worldScene, SLOT(beginAddItem(QString))); + connect(worldScene, SIGNAL(endAddItem(QString,bool)), + itemPalette, SLOT(endAddItem(QString,bool))); + connect(worldScene, SIGNAL(linkActivated(QUrl)), + infoBrowser, SLOT(openUrl(QUrl))); + connect(worldScene, SIGNAL(endAddItem(QString,bool)), this, SLOT(worldSelectionChanged())); setupActions(); setupGUI(); statusBar()->show(); newFile(); } MainWindow::~MainWindow() { KConfig* config = new KConfig("steprc"); actionRecentFiles->saveEntries(config->group("RecentFiles")); delete config; } void MainWindow::setupActions() { /* File menu */ KStandardAction::openNew(this, SLOT(newFile()), actionCollection()); KStandardAction::open(this, SLOT(openFile()), actionCollection()); KStandardAction::save(this, SLOT(saveFile()), actionCollection()); KStandardAction::saveAs(this, SLOT(saveFileAs()), actionCollection()); KStandardAction::quit(this, SLOT(close()), actionCollection()); - actionRecentFiles = KStandardAction::openRecent(this, SLOT(openFile(const QUrl&)), + actionRecentFiles = KStandardAction::openRecent(this, SLOT(openFile(QUrl)), actionCollection()); KConfig* config = new KConfig("steprc"); actionRecentFiles->loadEntries(config->group("RecentFiles")); delete config; QAction * actionOpenTutorial = actionCollection()->add( "file_tutorial_open", this, SLOT(openTutorial())); actionOpenTutorial->setText(i18n("&Open Tutorial...")); actionOpenTutorial->setIcon(QIcon::fromTheme("document-open")); QAction * actionOpenExample = actionCollection()->add( "file_example_open", this, SLOT(openExample())); actionOpenExample->setText(i18n("&Open Example...")); actionOpenExample->setIcon(QIcon::fromTheme("document-open")); QAction * actionOpenLocalExample = actionCollection()->add( "file_example_openlocal", this, SLOT(openLocalExample())); actionOpenLocalExample->setText(i18n("Open Down&loaded Example...")); actionOpenLocalExample->setIcon(QIcon::fromTheme("document-open")); QAction * actionUploadExample = actionCollection()->add( "file_example_upload", this, SLOT(uploadExample())); actionUploadExample->setText(i18n("Share C&urrent Experiment...")); actionUploadExample->setIcon(QIcon::fromTheme("get-hot-new-stuff")); QAction * actionDownloadExamples = actionCollection()->add( "file_example_download", this, SLOT(downloadExamples())); actionDownloadExamples->setText(i18n("&Download New Experiments...")); actionDownloadExamples->setIcon(QIcon::fromTheme("get-hot-new-stuff")); /* Edit menu */ actionRedo = KStandardAction::redo(worldModel->undoStack(), SLOT(redo()), actionCollection()); actionUndo = KStandardAction::undo(worldModel->undoStack(), SLOT(undo()), actionCollection()); actionRedo->setEnabled(false); actionUndo->setEnabled(false); actionRedo->setIconText(i18n("Redo")); actionUndo->setIconText(i18n("Undo")); connect(worldModel->undoStack(), SIGNAL(canRedoChanged(bool)), actionRedo, SLOT(setEnabled(bool))); connect(worldModel->undoStack(), SIGNAL(canUndoChanged(bool)), actionUndo, SLOT(setEnabled(bool))); connect(worldModel->undoStack(), SIGNAL(cleanChanged(bool)), this, SLOT(updateCaption())); - connect(worldModel->undoStack(), SIGNAL(undoTextChanged(const QString&)), - this, SLOT(undoTextChanged(const QString&))); - connect(worldModel->undoStack(), SIGNAL(redoTextChanged(const QString&)), - this, SLOT(redoTextChanged(const QString&))); + connect(worldModel->undoStack(), SIGNAL(undoTextChanged(QString)), + this, SLOT(undoTextChanged(QString))); + connect(worldModel->undoStack(), SIGNAL(redoTextChanged(QString)), + this, SLOT(redoTextChanged(QString))); actionCut = KStandardAction::cut(worldModel, SLOT(cutSelectedItems()), actionCollection()); actionCopy = KStandardAction::copy(worldModel, SLOT(copySelectedItems()), actionCollection()); actionPaste = KStandardAction::paste(worldModel, SLOT(pasteItems()), actionCollection()); actionCut->setEnabled(false); actionCopy->setEnabled(false); actionPaste->setEnabled(worldModel->clipboard()->canPaste()); connect(worldModel->clipboard(), SIGNAL(canPasteChanged(bool)), actionPaste, SLOT(setEnabled(bool))); actionDelete = actionCollection()->add("edit_delete", worldModel, SLOT(deleteSelectedItems())); actionDelete->setText(i18n("&Delete")); actionDelete->setIcon(QIcon::fromTheme("edit-delete")); actionDelete->setEnabled(false); actionCollection()->setDefaultShortcut(actionDelete, QKeySequence(Qt::Key_Delete)); /* Simulation menu */ // The run speed action group QActionGroup* runSpeedGroup = new QActionGroup(this); // The run action collection, this is used in the toolbar to create a dropdown menu on the run button runSpeedAction = new KToolBarPopupAction(QIcon::fromTheme("media-playback-start"), i18n("&Run"), this); connect(runSpeedAction, SIGNAL(triggered()), this, SLOT(simulationStartStop())); QMenu* runSpeedActionMenu = runSpeedAction->menu(); actionCollection()->addAction("run_speed", runSpeedAction); runSpeedActionMenu->setStatusTip(i18n("Execute the program")); runSpeedActionMenu->setWhatsThis(i18n("Run: Execute the program")); fullSpeedAct = new QAction(i18nc("@option:radio", "1x Speed"), this); actionCollection()->addAction("full_speed", fullSpeedAct ); fullSpeedAct->setCheckable(true); fullSpeedAct->setChecked(true); connect(fullSpeedAct, SIGNAL(triggered()), this, SLOT(setFullSpeed())); runSpeedGroup->addAction(fullSpeedAct); runSpeedActionMenu->addAction(fullSpeedAct); slowSpeedAct = new QAction(i18nc("@option:radio choose the slow speed", "2x Speed"), this); actionCollection()->addAction("slow_speed", slowSpeedAct ); slowSpeedAct->setCheckable(true); connect(slowSpeedAct, SIGNAL(triggered()), this, SLOT(setSlowSpeed())); runSpeedGroup->addAction(slowSpeedAct); runSpeedActionMenu->addAction(slowSpeedAct); slowerSpeedAct = new QAction(i18nc("@option:radio", "4x Speed"), this); actionCollection()->addAction("slower_speed", slowerSpeedAct ); slowerSpeedAct->setCheckable(true); connect(slowerSpeedAct, SIGNAL(triggered()), this, SLOT(setSlowerSpeed())); runSpeedGroup->addAction(slowerSpeedAct); runSpeedActionMenu->addAction(slowerSpeedAct); slowestSpeedAct = new QAction(i18nc("@option:radio", "8x Speed"), this); actionCollection()->addAction("slowest_speed", slowestSpeedAct ); slowestSpeedAct->setCheckable(true); connect(slowestSpeedAct, SIGNAL(triggered()), this, SLOT(setSlowestSpeed())); runSpeedGroup->addAction(slowestSpeedAct); runSpeedActionMenu->addAction(slowestSpeedAct); stepSpeedAct = new QAction(i18nc("@option:radio", "16x Speed"), this); actionCollection()->addAction("step_speed", stepSpeedAct ); stepSpeedAct->setCheckable(true); connect(stepSpeedAct, SIGNAL(triggered()), this, SLOT(setStepSpeed())); runSpeedGroup->addAction(stepSpeedAct); runSpeedActionMenu->addAction(stepSpeedAct); simulationStopped(0); /* View menu */ KStandardAction::actualSize(worldGraphicsView, SLOT(actualSize()), actionCollection()); KStandardAction::fitToPage(worldGraphicsView, SLOT(fitToPage()), actionCollection()); KStandardAction::zoomIn(worldGraphicsView, SLOT(zoomIn()), actionCollection()); KStandardAction::zoomOut(worldGraphicsView, SLOT(zoomOut()), actionCollection()); /* Settings menu */ KStandardAction::preferences(this, SLOT(configureStep()), actionCollection()); /* Dock widgets */ actionCollection()->addAction("toggle_palette_dock", itemPalette->toggleViewAction()); actionCollection()->addAction("toggle_world_dock", worldBrowser->toggleViewAction()); actionCollection()->addAction("toggle_properties_dock", propertiesBrowser->toggleViewAction()); actionCollection()->addAction("toggle_info_dock", infoBrowser->toggleViewAction()); actionCollection()->addAction("toggle_undo_dock", undoBrowser->toggleViewAction()); } void MainWindow::updateCaption() { QString shownName; if (currentFileUrl.isEmpty()) shownName = i18nc("filename", "untitled.step"); //Fixme if needed else shownName = currentFileUrl.url(QUrl::PreferLocalFile); //QFileInfo(currentFileName).fileName(); setCaption(shownName, !worldModel->undoStack()->isClean()); } bool MainWindow::queryClose() { if(worldModel->isSimulationActive()) simulationStop(); if(maybeSave()) { return true; } else { return false; } } bool MainWindow::newFile() { if(worldModel->isSimulationActive()) simulationStop(); if(!maybeSave()) return false; worldModel->clearWorld(); worldGraphicsView->actualSize(); worldGraphicsView->centerOn(0,0); currentFileUrl = QUrl(); updateCaption(); undoBrowser->setEmptyLabel(i18n("")); undoBrowser->setCurrentFileUrl(currentFileUrl); setFullSpeed(); // resetting the speed to the default speed of 1x return true; } bool MainWindow::openFile(const QUrl& url, const QUrl& startUrl) { if(worldModel->isSimulationActive()) simulationStop(); if(!maybeSave()) return false; QUrl fileUrl = url; if(fileUrl.isEmpty()) { fileUrl = KFileDialog::getOpenUrl(startUrl, i18n("*.step|Step files (*.step)"), this); //is this i18n legal? if(fileUrl.isEmpty()) return false; } worldModel->clearWorld(); newFile(); QString tmpFileName; if(! KIO::NetAccess::download(fileUrl, tmpFileName, this) ) { KMessageBox::error(this, KIO::NetAccess::lastErrorString()); return false; } QFile file(tmpFileName); if(!file.open(QIODevice::ReadOnly | QIODevice::Text)) { KMessageBox::sorry(this, i18n("Cannot open file '%1'", tmpFileName)); KIO::NetAccess::removeTempFile(tmpFileName); return false; } if(!worldModel->loadXml(&file)) { KMessageBox::sorry(this, i18n("Cannot parse file '%1': %2", fileUrl.url(QUrl::PreferLocalFile), worldModel->errorString())); KIO::NetAccess::removeTempFile(tmpFileName); return false; } KIO::NetAccess::removeTempFile(tmpFileName); worldGraphicsView->fitToPage(); currentFileUrl = fileUrl; updateCaption(); actionRecentFiles->addUrl(fileUrl); undoBrowser->setEmptyLabel(i18n("", fileUrl.fileName())); undoBrowser->setCurrentFileUrl(currentFileUrl); return true; } bool MainWindow::saveFileAs(const QUrl& url, const QUrl& startUrl) { if(worldModel->isSimulationActive()) simulationStop(); QUrl fileUrl = url; if(fileUrl.isEmpty()) { fileUrl = KFileDialog::getSaveUrl(startUrl.isEmpty() ? currentFileUrl : startUrl, i18n("*.step|Step files (*.step)"), this); if(fileUrl.isEmpty()) return false; else if(KIO::NetAccess::exists(fileUrl, KIO::NetAccess::DestinationSide, this)) { int ret = KMessageBox::warningContinueCancel(this, i18n("The file \"%1\" already exists. Do you wish to overwrite it?", fileUrl.url(QUrl::PreferLocalFile)), i18n("Warning - Step"), KStandardGuiItem::overwrite()); if(ret != KMessageBox::Continue) return false; } } bool local = fileUrl.isLocalFile(); QFile* file; if(!local) { QTemporaryFile *tempFile = new QTemporaryFile(); tempFile->setAutoRemove(true); file = tempFile; } else { file = new QFile(fileUrl.path()); } if(!file->open(QIODevice::WriteOnly | QIODevice::Text)) { KMessageBox::sorry(this, i18n("Cannot open file '%1'", file->fileName())); delete file; return false; } if(!worldModel->saveXml(file)) { KMessageBox::sorry(this, i18n("Cannot save file '%1': %2", fileUrl.url(QUrl::PreferLocalFile), worldModel->errorString())); delete file; return false; } if(!local) { if(!KIO::NetAccess::upload(file->fileName(), fileUrl, this)) { KMessageBox::error(this, KIO::NetAccess::lastErrorString()); delete file; return false; } } delete file; worldModel->undoStack()->setClean(); currentFileUrl = fileUrl; updateCaption(); undoBrowser->setCurrentFileUrl(currentFileUrl); return true; } bool MainWindow::saveFile() { if(worldModel->isSimulationActive()) simulationStop(); return saveFileAs(currentFileUrl); } bool MainWindow::maybeSave() { if(!worldModel->undoStack()->isClean()) { int ret = KMessageBox::warningYesNoCancel(this, i18n("The experiment has been modified.\nDo you want to save your changes?"), i18n("Warning - Step"), KStandardGuiItem::save(), KStandardGuiItem::discard()); if (ret == KMessageBox::Yes) return saveFile(); else if(ret == KMessageBox::Cancel) return false; } return true; } void MainWindow::openTutorial() { // XXX: need to be redone //qDebug() << "inside MainWindow::openTutorial()"; QStringList dirs = QStandardPaths::locateAll(QStandardPaths::DataLocation, "tutorials", QStandardPaths::LocateDirectory); QString localDir = QStandardPaths::writableLocation(QStandardPaths::DataLocation) + QLatin1Char('/'); foreach(const QString &dirName, dirs) { //qDebug() << "dirName: " << dirName; if(!dirName.startsWith(localDir)) { openFile(QUrl(), QUrl::fromLocalFile(dirName)); return; } } } void MainWindow::openExample() { //qDebug() << "inside MainWindow::openExample()"; // XXX: need to be redone QStringList dirs = QStandardPaths::locateAll(QStandardPaths::DataLocation, "examples", QStandardPaths::LocateDirectory); QString localDir = QStandardPaths::writableLocation(QStandardPaths::DataLocation) + QLatin1Char('/'); foreach(const QString &dirName, dirs) { if(!dirName.startsWith(localDir)) { openFile(QUrl(), QUrl::fromLocalFile(dirName)); return; } } } void MainWindow::openLocalExample() { // XXX: need to be redone QString dir = QStandardPaths::writableLocation(QStandardPaths::DataLocation) + "/examples"; if(dir.isEmpty()) return; - KStandardDirs::makeDir(dir); + QDir::root().mkpath(dir); openFile(QUrl(), QUrl::fromLocalFile(dir)); } void MainWindow::uploadExample() { KMessageBox::sorry(this, i18n("Uploading is still not implemented in kdelibs."), i18n("Sorry - Step")); /* int ret = KMessageBox::questionYesNo(this, i18n("Do you want to upload current experiment to public web server ?"), i18n("Question - Step")); if(ret != KMessageBox::Yes) return; if(currentFileUrl.isEmpty() || !worldModel->undoStack()->isClean()) { ret = KMessageBox::warningContinueCancel(this, i18n("The experiment is not saved. You should it before uploading."), i18n("Warning - Step"), KStandardGuiItem::save(), KStandardGuiItem::cancel()); if(ret != KMessageBox::Continue) return; if(!saveFile()) return; } KNS::Engine::upload( currentFileUrl.url() ); */ } void MainWindow::downloadExamples() { //qDebug() << "inside MainWindow::downloadExamples()"; KNS3::DownloadDialog dialog("step.knsrc", this); dialog.exec(); } void MainWindow::simulationStartStop() { if(worldModel->isSimulationActive()) simulationStop(); else simulationStart(); } void MainWindow::simulationStart() { runSpeedAction->setIconText(i18n("&Stop")); runSpeedAction->setIcon(QIcon::fromTheme("media-playback-stop")); undoBrowser->setUndoEnabled(false); actionUndo->setEnabled(false); worldModel->simulationStart(); } void MainWindow::simulationStopped(int result) { runSpeedAction->setIconText(i18n("&Simulate")); runSpeedAction->setIcon(QIcon::fromTheme("media-playback-start")); undoBrowser->setUndoEnabled(true); if(result == StepCore::Solver::ToleranceError) { KMessageBox::sorry(this, i18n("Cannot finish this step because local error " "is greater than local tolerance.\n" "Please check solver settings and try again.")); } else if(result == StepCore::Solver::IntersectionDetected || result == StepCore::Solver::CollisionDetected) { KMessageBox::sorry(this, i18n("Cannot finish this step because there are collisions " "which cannot be resolved automatically.\n" "Please move colliding objects apart and try again.")); } else if(result != StepCore::Solver::OK) { KMessageBox::sorry(this, i18n("Cannot finish this step because of an unknown error.")); } } void MainWindow::simulationStop() { worldModel->simulationStop(); } void MainWindow::setRunSpeed(int speed) { switch (speed) { case 0: fullSpeedAct->setChecked(true); worldModel->world()->setTimeScale( 1.0); break; case 1: slowSpeedAct->setChecked(true); worldModel->world()->setTimeScale( 2.0); break; case 2: slowerSpeedAct->setChecked(true); worldModel->world()->setTimeScale( 4.0); break; case 3: slowestSpeedAct->setChecked(true); worldModel->world()->setTimeScale( 8.0); break; case 4: stepSpeedAct->setChecked(true); worldModel->world()->setTimeScale( 16.0); break; } runSpeed = speed; } void MainWindow::undoTextChanged(const QString& undoText) { if(undoText.isEmpty()) actionUndo->setText(i18n("&Undo")); else actionUndo->setText(i18n("&Undo: %1", undoText)); } void MainWindow::redoTextChanged(const QString& redoText) { if(redoText.isEmpty()) actionRedo->setText(i18n("Re&do")); else actionRedo->setText(i18n("Re&do: %1", redoText)); } void MainWindow::worldSelectionChanged() { if (!worldScene->hasItemCreator()) { foreach (const QModelIndex &index, worldModel->selectionModel()->selection().indexes()) { if (index != worldModel->worldIndex() && worldModel->item(index)) { actionDelete->setEnabled(true); actionCut->setEnabled(true); actionCopy->setEnabled(true); return; } } } actionDelete->setEnabled(false); actionCut->setEnabled(false); actionCopy->setEnabled(false); } void MainWindow::configureStep() { if(KConfigDialog::showDialog( "settings" )) return; KConfigDialog* dialog = new KConfigDialog(this, "settings", Settings::self()); Ui::ConfigureStepGeneralWidget generalUi; QWidget* generalWidget = new QWidget(0); generalWidget->setObjectName("general"); generalUi.setupUi(generalWidget); dialog->addPage(generalWidget, i18n("General"), "step"); //shows the "step" icon, the "general" icon doesn't exist - connect(dialog, SIGNAL(settingsChanged(const QString&)), + connect(dialog, SIGNAL(settingsChanged(QString)), worldGraphicsView, SLOT(settingsChanged())); - connect(dialog, SIGNAL(settingsChanged(const QString&)), + connect(dialog, SIGNAL(settingsChanged(QString)), propertiesBrowser, SLOT(settingsChanged())); dialog->show(); } /* void MainWindow::on_actionNew_triggered(bool checked) { if(maybeSave()) newFile(); } void MainWindow::on_actionOpen_triggered(bool checked) { if(maybeSave()) openFile(QString()); } void MainWindow::on_actionSave_triggered(bool checked) { saveFileAs(currentFileName); } void MainWindow::on_actionSaveAs_triggered(bool checked) { saveFile(); } void MainWindow::on_actionStep_triggered(bool checked) { if(!worldModel->doWorldEvolve(0.1)) QMessageBox::warning(this, i18n("Step"), // XXX: retrieve error message from solver ! i18n("Cannot finish this step becouse local error is bigger than local tolerance.
    " "Please check solver settings and try again.")); } void MainWindow::on_actionSimulation_triggered(bool checked) { if(!simulationTimer->isActive()) { actionSimulation->setText(i18n("&Stop")); simulationTimer->start(); } else { simulationTimer->stop(); actionSimulation->setText(i18n("&Simulation")); } } void MainWindow::on_simulationTimer_timeout() { worldModel->doWorldEvolve(1.0/FPS); } void MainWindow::on_actionAboutStep_triggered(bool checked) { QMessageBox::about(this, i18n("About Step"), i18n("
    The Step is an interactive physical simulator.

    " "Distributed under terms of the GNU GPL license.
    " "(C) 2006-2007 Kuznetsov Vladimir.
    ")); } */ diff --git a/step/messageframe.cc b/step/messageframe.cc index f5c83e6..22c71a1 100644 --- a/step/messageframe.cc +++ b/step/messageframe.cc @@ -1,153 +1,153 @@ /* This file is part of Step. Copyright (C) 2007 Vladimir Kuznetsov Step is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Step is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Step; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "messageframe.h" #include #include #include #include #include #include #include MessageFrame::MessageFrame(QWidget* parent, Qt::WindowFlags f) : QFrame(parent, f), _lastId(0) { hide(); int br, bg, bb; setFrameShape(QFrame::StyledPanel); palette().color(QPalette::Window).getRgb(&br, &bg, &bb); setStyleSheet(QString(".MessageFrame {border: 2px solid rgba(133,133,133,85%);" "border-radius: 6px; background-color: rgba(%1,%2,%3,85%);}").arg(br).arg(bg).arg(bb)); _layout = new QVBoxLayout(this); _layout->setContentsMargins(9,0,9,0); _layout->setSpacing(0); _layout->setSizeConstraint(QLayout::SetFixedSize); _signalMapper = new QSignalMapper(this); connect(_signalMapper, SIGNAL(mapped(QWidget*)), this, SLOT(messageCloseClicked(QWidget*))); } int MessageFrame::showMessage(Type type, const QString& text, Flags flags) { if(_layout->count() != 0) { QFrame* line = new QFrame(this); line->setFrameShape(QFrame::HLine); line->setFrameShadow(QFrame::Sunken); _layout->addWidget(line); } QString widgetName("message"); widgetName.append(QString::number(_lastId)); QWidget* widget = new QWidget(this); widget->setObjectName(widgetName); widget->setMinimumHeight(32); QHBoxLayout* layout = new QHBoxLayout(widget); layout->setContentsMargins(0,2,0,2); QLabel* iconLabel = new QLabel(widget); iconLabel->setObjectName("iconLabel"); if(type == Error) iconLabel->setPixmap(QIcon::fromTheme("dialog-error").pixmap(16,16)); else if(type == Warning) iconLabel->setPixmap(QIcon::fromTheme("dialog-warning").pixmap(16,16)); else iconLabel->setPixmap(QIcon::fromTheme("dialog-information").pixmap(16,16)); layout->addWidget(iconLabel); QLabel* textLabel = new QLabel(widget); textLabel->setObjectName("textLabel"); //textLabel->setWordWrap(true); textLabel->setText(text); layout->addWidget(textLabel, 1); - connect(textLabel, SIGNAL(linkActivated(const QString&)), - this, SLOT(messageLinkActivated(const QString&))); + connect(textLabel, SIGNAL(linkActivated(QString)), + this, SLOT(messageLinkActivated(QString))); if(flags.testFlag(CloseButton)) { QToolButton* button = new QToolButton(widget); button->setObjectName("closeButton"); button->setIcon(QIcon::fromTheme("window-close")); button->setIconSize(QSize(16,16)); button->setAutoRaise(true); layout->addWidget(button); _signalMapper->setMapping(button, widget); connect(button, SIGNAL(clicked()), _signalMapper, SLOT(map())); } if(flags.testFlag(CloseTimer)) { QTimer* timer = new QTimer(widget); timer->setObjectName("closeTimer"); timer->setSingleShot(true); timer->setInterval(2000); _signalMapper->setMapping(timer, widget); connect(timer, SIGNAL(timeout()), _signalMapper, SLOT(map())); timer->start(); } _layout->addWidget(widget); if(!isVisible()) show(); return _lastId++; } int MessageFrame::changeMessage(int id, Type type, const QString& text, Flags flags) { QString widgetName("message"); widgetName.append(QString::number(id)); QWidget* widget = findChild(widgetName); if(widget) messageCloseClicked(widget); return showMessage(type, text, flags); } void MessageFrame::closeMessage(int id) { QString widgetName("message"); widgetName.append(QString::number(id)); QWidget* widget = findChild(widgetName); if(widget) messageCloseClicked(widget); } void MessageFrame::messageLinkActivated(const QString& link) { emit linkActivated(link); } void MessageFrame::messageCloseClicked(QWidget* widget) { int index = _layout->indexOf(widget); if(index < 0) return; _layout->itemAt(index)->widget()->hide(); _layout->takeAt(index)->widget()->deleteLater(); if(index > 0) { _layout->itemAt(index-1)->widget()->hide(); _layout->takeAt(index-1)->widget()->deleteLater(); } else if(_layout->count() > 0) { _layout->itemAt(0)->widget()->hide(); _layout->takeAt(0)->widget()->deleteLater(); } if(_layout->count() == 0) hide(); } diff --git a/step/propertiesbrowser.cc b/step/propertiesbrowser.cc index 235f6eb..dee2fee 100644 --- a/step/propertiesbrowser.cc +++ b/step/propertiesbrowser.cc @@ -1,791 +1,791 @@ /* This file is part of Step. Copyright (C) 2007 Vladimir Kuznetsov Step is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Step is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Step; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "propertiesbrowser.h" #include "settings.h" #include "worldfactory.h" #include "unitscalc.h" #include "worldmodel.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "choicesmodel.h" class PropertiesBrowserModel: public QAbstractItemModel { public: PropertiesBrowserModel(WorldModel* worldModel, QObject* parent = 0); QVariant data(const QModelIndex &index, int role) const Q_DECL_OVERRIDE; QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE; QModelIndex parent(const QModelIndex &index) const Q_DECL_OVERRIDE; int rowCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE; int columnCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const Q_DECL_OVERRIDE; Qt::ItemFlags flags(const QModelIndex &index) const Q_DECL_OVERRIDE; bool setData(const QModelIndex &index, const QVariant &value, int role) Q_DECL_OVERRIDE; void setObject(StepCore::Object* object); StepCore::Object* object() { return _object; } void emitDataChanged(bool dynamicOnly); protected: WorldModel* _worldModel; StepCore::Object* _object; StepCore::Item* _item; StepCore::ObjectErrors* _objectErrors; ChoicesModel* _solverChoices; QList _subRows; }; PropertiesBrowserModel::PropertiesBrowserModel(WorldModel* worldModel, QObject* parent) : QAbstractItemModel(parent), _worldModel(worldModel), _object(NULL) { _solverChoices = new ChoicesModel(this); // Prepare solver list foreach(const QString &name, _worldModel->worldFactory()->orderedMetaObjects()) { const StepCore::MetaObject* metaObject = _worldModel->worldFactory()->metaObject(name); if(metaObject->isAbstract()) continue; if(!metaObject->inherits(StepCore::Solver::staticMetaObject())) continue; QString solverName = QString(metaObject->className()).remove("Solver"); QStandardItem* item = new QStandardItem(solverName); item->setToolTip(QString(metaObject->descriptionTr())); _solverChoices->appendRow(item); } } void PropertiesBrowserModel::setObject(StepCore::Object* object) { _object = object; _subRows.clear(); if(_object != NULL) { _worldModel->simulationPause(); _item = dynamic_cast(_object); if(_item) { if(_item->world()->errorsCalculation()) _objectErrors = _item->objectErrors(); else _objectErrors = _item->tryGetObjectErrors(); } else { _objectErrors = NULL; } for(int i=0; i<_object->metaObject()->propertyCount(); ++i) { const StepCore::MetaProperty* p = _object->metaObject()->property(i); if(p->userTypeId() == qMetaTypeId()) _subRows << p->readVariant(_object).value().size(); else _subRows << 0; } } reset(); } void PropertiesBrowserModel::emitDataChanged(bool dynamicOnly) { if(_object == NULL) return; _worldModel->simulationPause(); _item = dynamic_cast(_object); if(_item) { if(_item->world()->errorsCalculation()) _objectErrors = _item->objectErrors(); else _objectErrors = _item->tryGetObjectErrors(); } else { _objectErrors = NULL; } for(int i=0; i<_object->metaObject()->propertyCount(); i++) { const StepCore::MetaProperty* p = _object->metaObject()->property(i); if(dynamicOnly && !p->isDynamic()) continue; if(p->userTypeId() == qMetaTypeId()) { int r = p->readVariant(_object).value().size(); if(r > _subRows[i]) { beginInsertRows(index(i, 0), _subRows[i], r-1); _subRows[i] = r; endInsertRows(); } else if(r < _subRows[i]) { beginRemoveRows(index(i, 0), r, _subRows[i]-1); _subRows[i] = r; endRemoveRows(); } if(r != 0) emit dataChanged(index(0,0,index(i,0)), index(r-1,1,index(i,0))); // XXX? } emit dataChanged(index(i,1), index(i,1)); } //emit dataChanged(index(0,1), index(rowCount()-1,1)); } QVariant PropertiesBrowserModel::data(const QModelIndex &index, int role) const { if(_object == NULL) return QVariant(); if(!index.isValid()) return QVariant(); if(index.internalId() == 0) { const StepCore::MetaProperty* p = _object->metaObject()->property(index.row()); if(role == Qt::DisplayRole || role == Qt::EditRole) { if(index.column() == 0) return p->nameTr(); else if(index.column() == 1) { _worldModel->simulationPause(); // Solver type combobox ? if(index.row() == 1 && dynamic_cast(_object)) { if(role == Qt::DisplayRole) return p->readString(_object).remove("Solver"); else return QVariant::fromValue(_solverChoices); } if(p->userTypeId() == QMetaType::Double || p->userTypeId() == qMetaTypeId() || p->userTypeId() == qMetaTypeId()) { return _worldModel->formatProperty(_object, _objectErrors, p, role == Qt::EditRole ? WorldModel::FormatEditable : WorldModel::FormatFlags(0)); } else if(p->userTypeId() == qMetaTypeId()) { return _worldModel->formatName(p->readVariant(_object).value()); } else if(p->userTypeId() == qMetaTypeId()) { Q_ASSERT( !_objectErrors || !_objectErrors->metaObject()->property(p->name() + "Variance") ); Q_ASSERT( p->units().isEmpty() ); if(role == Qt::EditRole) return QColor::fromRgba(p->readVariant(_object).value()); else return p->readString(_object); } else if(p->userTypeId() == QMetaType::Bool) { Q_ASSERT( !_objectErrors || !_objectErrors->metaObject()->property(p->name() + "Variance") ); Q_ASSERT( p->units().isEmpty() ); return p->readVariant(_object); } else { // default type // XXX: add error information //if(pe) error = QString::fromUtf8(" ± ").append(pe->readString(_objectErrors)).append(units); //if(pv) qDebug() << "Unhandled property variance type" << endl; Q_ASSERT( !_objectErrors || !_objectErrors->metaObject()->property(p->name() + "Variance") ); Q_ASSERT( p->units().isEmpty() ); if(role == Qt::EditRole) return _worldModel->formatProperty(_object, _objectErrors, p, WorldModel::FormatHideUnits | WorldModel::FormatEditable); else return _worldModel->formatProperty(_object, _objectErrors, p, WorldModel::FormatHideUnits); } ///*if(p->userTypeId() < (int) QVariant::UserType) return p->readVariant(_object); //else*/ return p->readString(_object); // XXX: default delegate for double looks ugly! } } else if(index.column() == 1 && role == Qt::ForegroundRole) { if(!p->isWritable()) { if(index.row() != 1 || !dynamic_cast(_object)) return QBrush(Qt::darkGray); // XXX: how to get scheme color ? } } else if(role == Qt::ToolTipRole) { if(index.row() == 1 && index.column() == 1 && dynamic_cast(_object)) { return _object->metaObject()->descriptionTr(); } return p->descriptionTr(); // XXX: translation } else if(index.column() == 1 && role == Qt::DecorationRole && p->userTypeId() == qMetaTypeId()) { QPixmap pix(8, 8); pix.fill(QColor::fromRgba(p->readVariant(_object).value())); return pix; } else if(index.column() == 0 && role == Qt::DecorationRole && p->userTypeId() == qMetaTypeId() && rowCount(index) > 0) { // XXX: A hack to have nested properties shifted static QPixmap empySmallPix; if(empySmallPix.isNull()) { empySmallPix = QPixmap(8,8); //XXX empySmallPix.fill(QColor(0,0,0,0)); } return empySmallPix; } } else { // index.internalId() != 0 const StepCore::MetaProperty* p = _object->metaObject()->property(index.internalId()-1); if(role == Qt::DisplayRole || role == Qt::EditRole) { if(index.column() == 0) return QString("%1[%2]").arg(p->nameTr()).arg(index.row()); else if(index.column() == 1) { #ifdef __GNUC__ #warning XXX: add error information for lists #endif QString units; if(role == Qt::DisplayRole && !p->units().isEmpty()) units.append(" [").append(p->units()).append("]"); #ifdef STEP_WITH_UNITSCALC // else if(role == Qt::EditRole && !p->units().isEmpty()) // units.append(" ").append(p->units()); #endif int pr = Settings::floatDisplayPrecision(); //int pr = role == Qt::DisplayRole ? Settings::floatDisplayPrecision() : 16; _worldModel->simulationPause(); StepCore::Vector2d v = p->readVariant(_object).value()[index.row()]; return QString("(%1,%2)%3").arg(v[0], 0, 'g', pr).arg(v[1], 0, 'g', pr).arg(units); } } else if(role == Qt::ForegroundRole && index.column() == 1) { if(!p->isWritable()) { return QBrush(Qt::darkGray); // XXX: how to get scheme color ? } } else if(role == Qt::ToolTipRole) { return p->descriptionTr(); // XXX: translation } } return QVariant(); } bool PropertiesBrowserModel::setData(const QModelIndex &index, const QVariant &value, int role) { if(_object == NULL) return false; if(index.isValid() && index.column() == 1 && role == Qt::EditRole) { _worldModel->simulationPause(); if(index.internalId() == 0) { if(index.row() == 0) { // name // XXX: do it more generally if(!_worldModel->checkUniqueName(value.toString())) return false; // XXX: error message } if(index.row() == 1 && dynamic_cast(_object)) { if(value.toString() != _object->metaObject()->className()) { _worldModel->beginMacro(i18n("Change solver type")); _object = _worldModel->newSolver(value.toString() + "Solver"); Q_ASSERT(_object != NULL); _worldModel->endMacro(); reset(); } } else { const StepCore::MetaProperty* p = _object->metaObject()->property(index.row()); const StepCore::MetaProperty* pv = _objectErrors ? _objectErrors->metaObject()->property(p->name() + "Variance") : NULL; if(p->userTypeId() == qMetaTypeId()) { Q_ASSERT(!pv); StepCore::Object* obj = _worldModel->world()->object(value.toString()); if(!obj) return false; _worldModel->beginMacro(i18n("Change %1.%2", _object->name(), p->nameTr())); _worldModel->setProperty(_object, p, QVariant::fromValue(obj)); _worldModel->endMacro(); return true; } else if(p->userTypeId() == qMetaTypeId()) { Q_ASSERT(!pv); _worldModel->beginMacro(i18n("Change %1.%2", _object->name(), p->nameTr())); _worldModel->setProperty(_object, p, value.type() == QVariant::String ? value : QVariant::fromValue(StepCore::Color(value.value().rgba()))); _worldModel->endMacro(); return true; } else if(p->userTypeId() == qMetaTypeId()) { Q_ASSERT(!pv); _worldModel->beginMacro(i18n("Change %1.%2", _object->name(), p->nameTr())); _worldModel->setProperty(_object, p, value); _worldModel->endMacro(); return true; } else if(p->userTypeId() == qMetaTypeId()) { Q_ASSERT(!pv); if(index.row() == 0) _worldModel->beginMacro(i18n("Rename %1 to %2", _object->name(), value.toString())); else _worldModel->beginMacro(i18n("Change %1.%2", _object->name(), p->nameTr())); _worldModel->setProperty(_object, p, value); _worldModel->endMacro(); return true; } QVariant v = value; QVariant vv; // Try to find ± sign if(v.canConvert(QVariant::String)) { QString str = v.toString(); int idx = str.indexOf(QString::fromUtf8("±")); if(idx >= 0) { v = str.left(idx); vv = str.mid(idx+1); } } #ifdef STEP_WITH_UNITSCALC // Convert units if(p->userTypeId() == QMetaType::Double) { double number = 0; if(UnitsCalc::self()->parseNumber(v.toString(), p->units(), number)) { v = number; } else { return false; } if(vv.isValid()) { if(UnitsCalc::self()->parseNumber(vv.toString(), p->units(), number)) { vv = number; } else { return false; } } } #endif if(vv.isValid()) { // We have got variance value if(!pv) { // check if _objectErrors can be created // and current property variance could be set const StepCore::MetaObject* me = _worldModel->worldFactory()->metaObject( _object->metaObject()->className() + "Errors"); if(!_item || !me || !me->property(p->name() + "Variance")) return false; } bool ok = true; // Calc variance = square(error) if(p->userTypeId() == QMetaType::Double) { vv = StepCore::square(vv.toDouble(&ok)); } else if(p->userTypeId() == qMetaTypeId()) { StepCore::Vector2d svv; svv = StepCore::stringToType(vv.toString(), &ok); svv[0] *= svv[0]; svv[1] *= svv[1]; vv = QVariant::fromValue(svv); /* XXX * {} else if(p->userTypeId() == qMetaTypeId()) ve = QVariant::fromValue(StepCore::Vector2dList());*/ } else { // qDebug() << "Unhandled property variance type" << endl; return false; } if(!ok) return false; } else { // vv.isValid() if(pv) { // We have to zero variance since we got exact value if(p->userTypeId() == QMetaType::Double) { vv = 0; } else if(p->userTypeId() == qMetaTypeId()) { StepCore::Vector2d svv = StepCore::Vector2d::Zero(); vv = QVariant::fromValue(svv); /* XXX * } else if(p->userTypeId() == qMetaTypeId()) ve = QVariant::fromValue(StepCore::Vector2dList());*/ } else { qWarning("Unhandled property variance type"); return false; } } } _worldModel->beginMacro(i18n("Change %1.%2", _object->name(), p->nameTr())); _worldModel->setProperty(_object, p, v); if(vv.isValid() && !pv) { // XXX: Make this undo-able _objectErrors = _item->objectErrors(); pv = _objectErrors->metaObject()->property(p->name() + "Variance"); } if(pv) _worldModel->setProperty(_objectErrors, pv, vv); _worldModel->endMacro(); } } else { const StepCore::MetaProperty* p = _object->metaObject()->property(index.internalId()-1); StepCore::Vector2dList v = p->readVariant(_object).value(); bool ok; v[index.row()] = StepCore::stringToType(value.toString(), &ok); if(!ok) return true; // dataChanged should be emitted anyway _worldModel->beginMacro(i18n("Change %1.%2", _object->name(), p->nameTr())); _worldModel->setProperty(_object, p, QVariant::fromValue(v)); _worldModel->endMacro(); } return true; } return false; } QModelIndex PropertiesBrowserModel::index(int row, int column, const QModelIndex &parent) const { if(_object == NULL) return QModelIndex(); if(!parent.isValid()) return createIndex(row, column); if(parent.internalId() == 0 && _subRows[parent.row()] != 0) return createIndex(row, column, parent.row()+1); return QModelIndex(); } QModelIndex PropertiesBrowserModel::parent(const QModelIndex& index) const { if(index.isValid() && index.internalId() != 0) return createIndex(index.internalId()-1, 0, nullptr); return QModelIndex(); } int PropertiesBrowserModel::rowCount(const QModelIndex &parent) const { if(_object == NULL) return 0; else if(parent.isValid()) { if(parent.column() == 0 && parent.internalId() == 0) return _subRows[parent.row()]; return 0; } else return _object->metaObject()->propertyCount(); } int PropertiesBrowserModel::columnCount(const QModelIndex& /*parent*/) const { return 2; } QVariant PropertiesBrowserModel::headerData(int section, Qt::Orientation /*orientation*/, int role) const { if (role != Qt::DisplayRole) return QVariant(); switch(section) { case 0: return i18n("Property"); case 1: return i18n("Value"); default: return QVariant(); } } Qt::ItemFlags PropertiesBrowserModel::flags(const QModelIndex &index) const { Qt::ItemFlags flags = QAbstractItemModel::flags(index); if(_object && index.isValid() && index.column() == 1) { if(index.internalId() == 0) { if(_object->metaObject()->property(index.row())->isWritable() || (index.row()==1 && dynamic_cast(_object))) flags |= Qt::ItemIsEditable; } else { if(_object->metaObject()->property(index.internalId()-1)->isWritable()) flags |= Qt::ItemIsEditable; } } return flags; } QWidget* PropertiesBrowserDelegate::createEditor(QWidget* parent, const QStyleOptionViewItem& /*option*/, const QModelIndex& index) const { QVariant data = index.data(Qt::EditRole); int userType = data.userType(); if(userType == qMetaTypeId()) { KComboBox* editor = new KComboBox(parent); editor->setModel(data.value()); connect(editor, SIGNAL(activated(int)), this, SLOT(editorActivated())); editor->installEventFilter(const_cast(this)); const_cast(this)->_editor = editor; const_cast(this)->_comboBox = editor; const_cast(this)->_editorType = SolverChoiser; return editor; } else if(userType == QMetaType::QColor) { QWidget* editor = new QWidget(parent); KLineEdit* lineEdit = new KLineEdit(editor); lineEdit->setFrame(false); KColorButton* colorButton = new KColorButton(editor); // XXX: do not use hard-coded pixel sizes colorButton->setMinimumWidth(15); colorButton->setMaximumWidth(15); - connect(colorButton, SIGNAL(changed(const QColor&)), this, SLOT(editorActivated())); + connect(colorButton, SIGNAL(changed(QColor)), this, SLOT(editorActivated())); QHBoxLayout* layout = new QHBoxLayout(editor); layout->setContentsMargins(0,0,0,0); layout->setSpacing(0); layout->addWidget(lineEdit); layout->addWidget(colorButton); editor->setFocusProxy(lineEdit); editor->installEventFilter(const_cast(this)); const_cast(this)->_editor = editor; const_cast(this)->_colorButton = colorButton; const_cast(this)->_lineEdit = lineEdit; const_cast(this)->_editorType = ColorChoiser; return editor; } else if(userType == QMetaType::Bool) { KComboBox* editor = new KComboBox(parent); editor->addItem(i18n("false")); editor->addItem(i18n("true")); connect(editor, SIGNAL(activated(int)), this, SLOT(editorActivated())); editor->installEventFilter(const_cast(this)); const_cast(this)->_editor = editor; const_cast(this)->_comboBox = editor; const_cast(this)->_editorType = BoolChoiser; return editor; } else { const_cast(this)->_editorType = Standard; const QItemEditorFactory *factory = itemEditorFactory(); if(!factory) factory = QItemEditorFactory::defaultFactory(); return factory->createEditor(static_cast(userType), parent); } } void PropertiesBrowserDelegate::setEditorData(QWidget* editor, const QModelIndex& index) const { if(_editorType == SolverChoiser) { QVariant data = index.data(Qt::DisplayRole); ChoicesModel* cm = static_cast(_comboBox->model()); QList items = cm->findItems(data.toString()); Q_ASSERT(items.count() == 1); _comboBox->setCurrentIndex( cm->indexFromItem(items[0]).row() ); } else if(_editorType == ColorChoiser) { QVariant data = index.data(Qt::EditRole); QVariant data1 = index.data(Qt::DisplayRole); _updating = true; _colorButton->setColor(data.value()); _lineEdit->setText(data1.toString()); _updating = false; } else if(_editorType == BoolChoiser) { bool value = index.data(Qt::EditRole).toBool(); _comboBox->setCurrentIndex(value ? 1 : 0); } else QItemDelegate::setEditorData(editor, index); } void PropertiesBrowserDelegate::setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const { if(_editorType == SolverChoiser) { model->setData(index, _comboBox->currentText()); } else if(_editorType == ColorChoiser) { model->setData(index, _lineEdit->text()); } else if(_editorType == BoolChoiser) { model->setData(index, _comboBox->currentIndex()); } else QItemDelegate::setModelData(editor, model, index); } void PropertiesBrowserDelegate::editorActivated() { if(!_updating) { if(_editorType == ColorChoiser) { QRgb v = _colorButton->color().rgba(); _lineEdit->setText(StepCore::typeToString(v)); } emit commitData(_editor); emit closeEditor(_editor); } } class PropertiesBrowserView: public QTreeView { public: PropertiesBrowserView(QWidget* parent = 0); protected: void changeEvent(QEvent* event) Q_DECL_OVERRIDE; void mousePressEvent(QMouseEvent* event) Q_DECL_OVERRIDE; void drawBranches(QPainter *painter, const QRect &rect, const QModelIndex &index) const Q_DECL_OVERRIDE; QStyleOptionViewItem viewOptions() const Q_DECL_OVERRIDE; const int _windowsDecoSize; bool _macStyle; }; PropertiesBrowserView::PropertiesBrowserView(QWidget* parent) : QTreeView(parent), _windowsDecoSize(9) { _macStyle = QApplication::style()->inherits("QMacStyle"); } void PropertiesBrowserView::changeEvent(QEvent* event) { if(event->type() == QEvent::StyleChange) _macStyle = QApplication::style()->inherits("QMacStyle"); } void PropertiesBrowserView::mousePressEvent(QMouseEvent* event) { if(columnAt(event->x()) == 0) { QModelIndex idx = indexAt(event->pos()); if(idx.isValid() && !idx.parent().isValid() && idx.model()->rowCount(idx) > 0) { QRect primitive = visualRect(idx); primitive.setWidth(indentation()); if (!_macStyle) { primitive.moveLeft(primitive.left() + (primitive.width() - _windowsDecoSize)/2); primitive.moveTop(primitive.top() + (primitive.height() - _windowsDecoSize)/2); primitive.setWidth(_windowsDecoSize); primitive.setHeight(_windowsDecoSize); } if(primitive.contains(event->pos())) { setExpanded(idx, !isExpanded(idx)); return; } } } QTreeView::mousePressEvent(event); } void PropertiesBrowserView::drawBranches(QPainter *painter, const QRect &rect, const QModelIndex &index) const { // Inspired by qt-designer code in src/components/propertyeditor/qpropertyeditor.cpp QStyleOptionViewItem opt = viewOptions(); if(model()->hasChildren(index)) { opt.state |= QStyle::State_Children; QRect primitive(rect.left() + rect.width() - indentation(), rect.top(), indentation(), rect.height()); if(!index.parent().isValid()) { primitive.moveLeft(0); } if (!_macStyle) { primitive.moveLeft(primitive.left() + (primitive.width() - _windowsDecoSize)/2); primitive.moveTop(primitive.top() + (primitive.height() - _windowsDecoSize)/2); primitive.setWidth(_windowsDecoSize); primitive.setHeight(_windowsDecoSize); } opt.rect = primitive; if(isExpanded(index)) opt.state |= QStyle::State_Open; style()->drawPrimitive(QStyle::PE_IndicatorBranch, &opt, painter, this); } } QStyleOptionViewItem PropertiesBrowserView::viewOptions() const { QStyleOptionViewItem option = QTreeView::viewOptions(); option.showDecorationSelected = true; return option; } PropertiesBrowser::PropertiesBrowser(WorldModel* worldModel, QWidget* parent, Qt::WindowFlags flags) : QDockWidget(i18n("Properties"), parent, flags) { _worldModel = worldModel; _propertiesBrowserModel = new PropertiesBrowserModel(worldModel, this); _treeView = new PropertiesBrowserView(this); _treeView->setAllColumnsShowFocus(true); _treeView->setRootIsDecorated(false); //_treeView->setAlternatingRowColors(true); _treeView->setSelectionMode(QAbstractItemView::NoSelection); _treeView->setSelectionBehavior(QTreeView::SelectRows); _treeView->setEditTriggers(QAbstractItemView::AllEditTriggers); //_treeView->setEditTriggers(/*QAbstractItemView::CurrentChanged | */QAbstractItemView::SelectedClicked | // QAbstractItemView::EditKeyPressed | QAbstractItemView::AnyKeyPressed); _treeView->setItemDelegate(new PropertiesBrowserDelegate(_treeView)); _treeView->setModel(_propertiesBrowserModel); worldCurrentChanged(_worldModel->worldIndex(), QModelIndex()); connect(_worldModel, SIGNAL(modelReset()), this, SLOT(worldModelReset())); connect(_worldModel, SIGNAL(worldDataChanged(bool)), this, SLOT(worldDataChanged(bool))); - connect(_worldModel, SIGNAL(rowsRemoved(const QModelIndex&, int, int)), - this, SLOT(worldRowsRemoved(const QModelIndex&, int, int))); + connect(_worldModel, SIGNAL(rowsRemoved(QModelIndex,int,int)), + this, SLOT(worldRowsRemoved(QModelIndex,int,int))); - connect(_worldModel->selectionModel(), SIGNAL(currentChanged(const QModelIndex&, const QModelIndex&)), - this, SLOT(worldCurrentChanged(const QModelIndex&, const QModelIndex&))); + connect(_worldModel->selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)), + this, SLOT(worldCurrentChanged(QModelIndex,QModelIndex))); - connect(_treeView->selectionModel(), SIGNAL(currentChanged(const QModelIndex&, const QModelIndex&)), - this, SLOT(currentChanged(const QModelIndex&, const QModelIndex&))); + connect(_treeView->selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)), + this, SLOT(currentChanged(QModelIndex,QModelIndex))); - //connect(_treeView, SIGNAL(doubleClicked(const QModelIndex&)), - // this, SLOT(doubleClicked(const QModelIndex&))); + //connect(_treeView, SIGNAL(doubleClicked(QModelIndex)), + // this, SLOT(doubleClicked(QModelIndex))); - connect(_propertiesBrowserModel, SIGNAL(rowsInserted(const QModelIndex&, int, int)), - this, SLOT(rowsInserted(const QModelIndex&, int, int))); - connect(_propertiesBrowserModel, SIGNAL(rowsRemoved(const QModelIndex&, int, int)), - this, SLOT(rowsRemoved(const QModelIndex&, int, int))); + connect(_propertiesBrowserModel, SIGNAL(rowsInserted(QModelIndex,int,int)), + this, SLOT(rowsInserted(QModelIndex,int,int))); + connect(_propertiesBrowserModel, SIGNAL(rowsRemoved(QModelIndex,int,int)), + this, SLOT(rowsRemoved(QModelIndex,int,int))); _treeView->viewport()->installEventFilter(this); //_treeView->setMouseTracking(true); setWidget(_treeView); } void PropertiesBrowser::worldModelReset() { _propertiesBrowserModel->setObject(NULL); } void PropertiesBrowser::worldCurrentChanged(const QModelIndex& current, const QModelIndex& /*previous*/) { _propertiesBrowserModel->setObject(_worldModel->object(current)); //_treeView->expandAll(); for(int i=0; i<_propertiesBrowserModel->rowCount(); ++i) { QModelIndex index = _propertiesBrowserModel->index(i, 0); if(_propertiesBrowserModel->rowCount(index) <= 10) // XXX: make it configurable _treeView->setExpanded(index, true); } } void PropertiesBrowser::worldDataChanged(bool dynamicOnly) { _propertiesBrowserModel->emitDataChanged(dynamicOnly); } void PropertiesBrowser::worldRowsRemoved(const QModelIndex& parent, int start, int end) { if(!_worldModel->objectIndex(_propertiesBrowserModel->object()).isValid()) _propertiesBrowserModel->setObject(NULL); } void PropertiesBrowser::currentChanged(const QModelIndex& current, const QModelIndex& /*previous*/) { if(current.isValid() && current.column() == 0) _treeView->selectionModel()->setCurrentIndex(current.sibling(current.row(), 1), QItemSelectionModel::Current); } void PropertiesBrowser::rowsInserted(const QModelIndex& parent, int start, int end) { int rowCount = _propertiesBrowserModel->rowCount(parent); if(rowCount > 10 && (rowCount - (start-end+1)) <= 10) { _treeView->setExpanded(parent, false); } } void PropertiesBrowser::rowsRemoved(const QModelIndex& parent, int start, int end) { int rowCount = _propertiesBrowserModel->rowCount(parent); if(rowCount <= 10 && rowCount + (start-end+1) > 10) { _treeView->setExpanded(parent, true); } } /* void PropertiesBrowser::doubleClicked(const QModelIndex& index) { qDebug() << "doubleClicked" << endl; if(_propertiesBrowserModel->rowCount(index) > 0) { qDebug() << " doubleClicked!!!" << endl; _treeView->setExpanded(index, !_treeView->isExpanded(index)); } } */ bool PropertiesBrowser::eventFilter(QObject* object, QEvent* event) { if(object == _treeView->viewport() && event->type() == QEvent::MouseButtonDblClick) { QMouseEvent* mouseEvent = static_cast(event); QModelIndex index = _treeView->indexAt(mouseEvent->pos()); if(_propertiesBrowserModel->rowCount(index) > 0) _treeView->setExpanded(index, !_treeView->isExpanded(index)); } return false; } void PropertiesBrowser::settingsChanged() { _propertiesBrowserModel->emitDataChanged(false); } diff --git a/step/toolgraphics.cc b/step/toolgraphics.cc index 1aa90af..18c6ae9 100644 --- a/step/toolgraphics.cc +++ b/step/toolgraphics.cc @@ -1,1926 +1,1926 @@ /* This file is part of Step. Copyright (C) 2007 Vladimir Kuznetsov Copyright (C) 2014 Inge Wallin Step is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Step is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Step; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "toolgraphics.h" #include "ui_configure_graph.h" #include "ui_configure_meter.h" #include "ui_configure_controller.h" #include #include #include #include #include #include "worldmodel.h" #include "worldscene.h" #include "worldfactory.h" #include "latexformula.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include +#include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include #include #include StepCore::Vector2d WidgetVertexHandlerGraphicsItem::value() { double s = currentViewScale(); StepCore::Vector2d size = _item->metaObject()->property("size")-> readVariant(_item).value()/s; return (size.array()* corners[_vertexNum].array()).matrix(); } void WidgetVertexHandlerGraphicsItem::setValue(const StepCore::Vector2d& value) { double s = currentViewScale(); QGraphicsView* activeView = scene()->views().first(); QTransform viewportTransform = activeView->viewportTransform(); StepCore::Vector2d size = _item->metaObject()->property("size")-> readVariant(_item).value()/s; StepCore::Vector2d position = _item->metaObject()->property("position")-> readVariant(_item).value(); StepCore::Vector2d oCorner = position - (size.array()*((corners[_vertexNum]).array())).matrix(); oCorner = pointToVector( viewportTransform.inverted().map( QPointF(viewportTransform.map(vectorToPoint(oCorner)).toPoint()) )); StepCore::Vector2d delta = (value + position - oCorner)/2.0; StepCore::Vector2d newPos = oCorner + delta; newPos = pointToVector( viewportTransform.inverted().map( QPointF(viewportTransform.map(vectorToPoint(newPos)).toPoint()) )); StepCore::Vector2d newSize = (newPos - oCorner)*2.0; StepCore::Vector2d sign = (delta.array()*(corners[_vertexNum].array())).matrix(); double d = -0.1/s; if(sign[0] < d || sign[1] < d) { if(sign[0] < d) { newPos[0] = oCorner[0]; newSize[0] = 0; _vertexNum ^= 1; } if(sign[1] < d) { newPos[1] = oCorner[1]; newSize[1] = 0; _vertexNum ^= 2; } _worldModel->setProperty(_item, "position", QVariant::fromValue(newPos)); _worldModel->setProperty(_item, "size", QVariant::fromValue((newSize*s).eval())); setValue(value); return; } _worldModel->setProperty(_item, "position", QVariant::fromValue(newPos)); _worldModel->setProperty(_item, "size", QVariant::fromValue((newSize*s).eval())); } WidgetGraphicsItem::WidgetGraphicsItem(StepCore::Item* item, WorldModel* worldModel) : StepGraphicsItem(item, worldModel), _centralWidget(0) { setFlag(QGraphicsItem::ItemIsSelectable); setFlag(QGraphicsItem::ItemIsMovable); setAcceptsHoverEvents(true); _backgroundBrush = Qt::NoBrush; _boundingRect = QRectF(0, 0, 0, 0); } WidgetGraphicsItem::~WidgetGraphicsItem() { if(_centralWidget) { _centralWidget->hide(); _centralWidget->deleteLater(); } } OnHoverHandlerGraphicsItem* WidgetGraphicsItem::createOnHoverHandler(const QPointF& pos) { double s = currentViewScale(); StepCore::Vector2d size = _item->metaObject()->property("size")-> readVariant(_item).value()/s; StepCore::Vector2d position = _item->metaObject()->property("position")-> readVariant(_item).value(); StepCore::Vector2d l = pointToVector(pos) - position; int num = -1; double minDist2 = HANDLER_SNAP_SIZE*HANDLER_SNAP_SIZE/s/s; for(unsigned int i=0; i<4; ++i) { double dist2 = (l - (size.array()*(WidgetVertexHandlerGraphicsItem::corners[i]).array()).matrix()).squaredNorm(); if(dist2 < minDist2) { num = i; minDist2 = dist2; } } if(_onHoverHandler && _onHoverHandler->vertexNum() == num) return _onHoverHandler; if(num >= 0) return new WidgetVertexHandlerGraphicsItem(_item, _worldModel, this, num); return 0; } // XXX: ??? void WidgetGraphicsItem::mouseSetPos(const QPointF& pos, const QPointF&, MovingState) { QGraphicsView* activeView = scene()->views().first(); QTransform itemTransform = activeView->transform() * deviceTransform(activeView->viewportTransform()); StepCore::Vector2d newPos = pointToVector( itemTransform.inverted().map( QPointF(itemTransform.map(pos/*/50.0*/).toPoint()) ))/**50.0*/; _worldModel->setProperty(_item, "position", QVariant::fromValue(newPos)); } void WidgetGraphicsItem::paint(QPainter* painter, const QStyleOptionGraphicsItem* /*option*/, QWidget* /*widget*/) { painter->setRenderHint(QPainter::Antialiasing, true); if(_isSelected) painter->setPen(QPen(SELECTION_COLOR, 0, Qt::DashLine)); else painter->setPen(QPen(Qt::NoPen)); painter->setBrush(_backgroundBrush); painter->drawRect(_boundingRect); } void WidgetGraphicsItem::setCenteralWidget(QWidget* widget) { if(_centralWidget) { _centralWidget->hide(); _centralWidget->deleteLater(); } _centralWidget = widget; viewScaleChanged(); } void WidgetGraphicsItem::viewScaleChanged() { if(!scene() || scene()->views().isEmpty()) return; QGraphicsView* activeView = scene()->views().first(); QPointF position = vectorToPoint(_item->metaObject()->property("position")-> readVariant(_item).value()); // Move item to the closest pixel position QPoint viewPosition = activeView->mapFromScene(position); setPos(activeView->mapToScene(viewPosition)); StepCore::Vector2d size = _item->metaObject()->property("size")-> readVariant(_item).value(); QSize viewSize(qRound(size[0]), qRound(size[1])); QPoint viewTopLeft = viewPosition - QPoint(viewSize.width() / 2, viewSize.height() / 2); QRect viewRect(viewTopLeft, viewSize); QRectF sceneRect = activeView->mapToScene(viewRect.adjusted(0, 0, 1, 1)).boundingRect(); QRectF boundingRect = mapRectFromScene(sceneRect); double s = currentViewScale(); boundingRect.adjust(-SELECTION_MARGIN/s, -SELECTION_MARGIN/s, SELECTION_MARGIN/s, SELECTION_MARGIN/s); if(boundingRect != _boundingRect) { prepareGeometryChange(); _boundingRect = boundingRect; update(); } // Reparent the widget if necessary. if(_centralWidget->parentWidget() != activeView->viewport()) { _centralWidget->setParent(activeView->viewport()); _centralWidget->show(); } _centralWidget->setGeometry(viewRect.adjusted(0, 0, 1, 1)); } void WidgetGraphicsItem::stateChanged() { update(); } void WidgetGraphicsItem::worldDataChanged(bool dynamicOnly) { if(!dynamicOnly) { /*QPointF position = vectorToPoint(_item->metaObject()->property("position")-> readVariant(_item).value()); setPos(position);*/ viewScaleChanged(); update(); } } ///////////////////////////////////////////////////////////////////////////////////////// QString NoteTextEdit::emptyNotice() const { return i18n("Click to enter text"); } /* void NoteTextEdit::focusInEvent(QFocusEvent *event) { if(_noteItem->note()->text().isEmpty()) { ++_noteItem->_updating; setPlainText(""); _noteItem->worldDataChanged(false); --_noteItem->_updating; } _noteItem->_hasFocus = true; _noteItem->_toolBar->show(); _noteItem->setSelected(true); _noteItem->viewScaleChanged(); KTextEdit::focusInEvent(event); } void NoteTextEdit::focusOutEvent(QFocusEvent *event) { qDebug() << event->reason() << endl; qDebug() << QApplication::focusWidget()->metaObject()->className() << endl; QObject* f = QApplication::focusWidget(); if(f == this) f = NULL; while(f && f != _noteItem->_widget) f = f->parent(); if(!f && event->reason() != Qt::PopupFocusReason) { if(_noteItem->note()->text().isEmpty()) { ++_noteItem->_updating; setPlainText(emptyNotice()); _noteItem->worldDataChanged(false); --_noteItem->_updating; } _noteItem->_hasFocus = false; _noteItem->_toolBar->hide(); _noteItem->viewScaleChanged(); } KTextEdit::focusOutEvent(event); } */ StepCore::NoteFormula* NoteTextEdit::formulaAt(const QPoint& pos) { int p = document()->documentLayout()->hitTest(pos, Qt::ExactHit); if(p < 0) return NULL; QTextCursor cursor(document()); cursor.setPosition(p); if(cursor.atEnd()) return NULL; cursor.setPosition(p+1); QTextFormat format = cursor.charFormat(); if(!format.isImageFormat()) return NULL; QString image = format.toImageFormat().name(); StepCore::Item* item = _noteItem->note()->childItem(image); if(!item) { foreach(StepCore::Item* it, _noteItem->_newItems) if(it->name() == image) { item = it; break; } } if(!item || !item->metaObject()->inherits()) return NULL; return static_cast(item); } void NoteTextEdit::mousePressEvent(QMouseEvent *e) { if(e->button() == Qt::LeftButton) _mousePressPoint = e->pos(); KTextEdit::mousePressEvent(e); } void NoteTextEdit::mouseMoveEvent(QMouseEvent *e) { if(formulaAt(e->pos()) != NULL) { viewport()->setCursor(Qt::PointingHandCursor); } else { viewport()->setCursor(Qt::IBeamCursor); } _mousePressPoint.setX(-1); KTextEdit::mouseMoveEvent(e); } void NoteTextEdit::mouseReleaseEvent(QMouseEvent *e) { if(e->button() == Qt::LeftButton && e->pos() == _mousePressPoint) { StepCore::NoteFormula* formula = formulaAt(e->pos()); if(formula) { e->accept(); _noteItem->editFormula(formula); setDocument(document()); return; } } KTextEdit::mouseReleaseEvent(e); } NoteGraphicsItem::NoteGraphicsItem(StepCore::Item* item, WorldModel* worldModel) : WidgetGraphicsItem(item, worldModel) { Q_ASSERT(dynamic_cast(_item) != NULL); setFlag(QGraphicsItem::ItemIsSelectable); setFlag(QGraphicsItem::ItemIsMovable); setAcceptsHoverEvents(true); _widget = new QWidget(); _widget->setPalette(QPalette(Qt::lightGray)); _textEdit = new NoteTextEdit(this, _widget); _textEdit->setFrameShape(QFrame::NoFrame); QPalette p = _textEdit->palette(); p.setColor(QPalette::Base, Qt::transparent); _textEdit->setPalette(p); //_textEdit->setStyleSheet(".NoteTextEdit {background-color: rgba(0,0,0,0%);}"); _toolBar = new KToolBar(_widget); _toolBar->setIconDimensions(16); _toolBar->setToolButtonStyle(Qt::ToolButtonIconOnly); _toolBar->setContentsMargins(0,0,0,0); if(_toolBar->layout()) _toolBar->layout()->setSpacing(0); //_toolBar->setStyleSheet(".KToolBar {margin: 0px; border-width: 0px; padding: 0px; }"); _actionColor = new QAction(QIcon(), i18n("&Color"), _toolBar); _actionBold = new KToggleAction(QIcon::fromTheme("format-text-bold"), i18n("&Bold"), _toolBar); _actionBold->setShortcut(Qt::CTRL + Qt::Key_B); _actionItalic = new KToggleAction(QIcon::fromTheme("format-text-italic"), i18n("&Italic"), _toolBar); _actionItalic->setShortcut(Qt::CTRL + Qt::Key_I); _actionUnderline = new KToggleAction(QIcon::fromTheme("format-text-underline"), i18n("&Underline"), _toolBar); _actionUnderline->setShortcut(Qt::CTRL + Qt::Key_U); _actionAlignLeft = new KToggleAction(QIcon::fromTheme("format-justify-left"), i18n("Align &Left"), _toolBar); _actionAlignLeft->setShortcut(Qt::CTRL + Qt::Key_L); _actionAlignCenter = new KToggleAction(QIcon::fromTheme("format-justify-center"), i18n("Align C&enter"), _toolBar); _actionAlignCenter->setShortcut(Qt::CTRL + Qt::Key_E); _actionAlignRight = new KToggleAction(QIcon::fromTheme("format-justify-right"), i18n("Align &Right"), _toolBar); _actionAlignRight->setShortcut(Qt::CTRL + Qt::Key_R); _actionAlignJustify = new KToggleAction(QIcon::fromTheme("format-justify-fill"), i18n("Align &Justify"), _toolBar); _actionAlignJustify->setShortcut(Qt::CTRL + Qt::Key_J); _actionAlign = new KSelectAction(i18n("&Align"), _toolBar); _actionAlign->setToolBarMode(KSelectAction::MenuMode); _actionAlign->setToolButtonPopupMode(QToolButton::InstantPopup); _actionAlign->addAction(_actionAlignLeft); _actionAlign->addAction(_actionAlignCenter); _actionAlign->addAction(_actionAlignRight); _actionAlign->addAction(_actionAlignJustify); _actionAlignLeft->setChecked(true); _actionFont = new KFontAction(i18n("&Font"), _toolBar); _actionFontSize = new KFontSizeAction(i18n("Font &Size"), _toolBar); _actionInsertImage = new QAction(QIcon::fromTheme("insert-image"), i18n("Insert &Image"), _toolBar); #ifdef __GNUC__ #warning Select right icon here #endif _actionInsertFormula = new QAction(QIcon::fromTheme("application-vnd.oasis.opendocument.formula"), i18n("Insert &Formula"), _toolBar); connect(_actionColor, SIGNAL(triggered(bool)), this, SLOT(formatColor())); connect(_actionBold, SIGNAL(triggered(bool)), this, SLOT(formatBold(bool))); connect(_actionItalic, SIGNAL(triggered(bool)), _textEdit, SLOT(setFontItalic(bool))); connect(_actionUnderline, SIGNAL(triggered(bool)), _textEdit, SLOT(setFontUnderline(bool))); connect(_actionAlign, SIGNAL(triggered(QAction*)), this, SLOT(formatAlign(QAction*))); - connect(_actionFont, SIGNAL(triggered(const QString&)), this, SLOT(formatFontFamily(const QString&))); + connect(_actionFont, SIGNAL(triggered(QString)), this, SLOT(formatFontFamily(QString))); connect(_actionFontSize, SIGNAL(fontSizeChanged(int)), this, SLOT(formatFontSize(int))); connect(_actionInsertImage, SIGNAL(triggered(bool)), this, SLOT(insertImage())); connect(_actionInsertFormula, SIGNAL(triggered(bool)), this, SLOT(insertFormula())); - connect(_textEdit, SIGNAL(currentCharFormatChanged(const QTextCharFormat&)), - this, SLOT(currentCharFormatChanged(const QTextCharFormat&))); + connect(_textEdit, SIGNAL(currentCharFormatChanged(QTextCharFormat)), + this, SLOT(currentCharFormatChanged(QTextCharFormat))); connect(_textEdit, SIGNAL(cursorPositionChanged()), this, SLOT(cursorPositionChanged())); connect(_toolBar, SIGNAL(actionTriggered(QAction*)), _textEdit, SLOT(setFocus())); _toolBar->addAction(_actionAlign); _toolBar->addAction(_actionBold); _toolBar->addAction(_actionItalic); _toolBar->addAction(_actionUnderline); _toolBar->addAction(_actionColor); //_toolBar->addSeparator(); //_toolBar->addSeparator(); _toolBar->addAction(_actionInsertImage); _toolBar->addAction(_actionInsertFormula); _toolBar->addAction(_actionFontSize); _toolBar->addAction(_actionFont); QVBoxLayout* layout = new QVBoxLayout(_widget); layout->setContentsMargins(0,0,0,0); layout->setSpacing(0); layout->addWidget(_textEdit); layout->addWidget(_toolBar); // without it focus is passed to QGrahicsView _toolBar->setFocusPolicy(Qt::ClickFocus); _toolBar->setFocusProxy(_textEdit); _widget->setFocusProxy(_textEdit); _hasFocus = false; _toolBar->hide(); _textEdit->installEventFilter(this); QComboBox* font = qobject_cast(_toolBar->widgetForAction(_actionFont)); if(font) { font->setMinimumContentsLength(5); font->setSizeAdjustPolicy(QComboBox::AdjustToMinimumContentsLength); font->installEventFilter(this); font->setToolTip(_actionFont->toolTip()); } QComboBox* fontSize = qobject_cast(_toolBar->widgetForAction(_actionFontSize)); if(fontSize) { fontSize->setMinimumContentsLength(2); fontSize->setSizeAdjustPolicy(QComboBox::AdjustToMinimumContentsLength); fontSize->installEventFilter(this); fontSize->setToolTip(_actionFontSize->toolTip()); } setCenteralWidget(_widget); setOnHoverHandlerEnabled(true); _widget->setMouseTracking(true); _textEdit->setMouseTracking(true); _toolBar->setMouseTracking(true); } inline StepCore::Note* NoteGraphicsItem::note() const { return static_cast(_item); } bool NoteGraphicsItem::eventFilter(QObject* obj, QEvent* event) { if(event->type() == QEvent::FocusIn) { if(!_hasFocus) { _hasFocus = true; if(note()->text().isEmpty()) { //++_updating; _textEdit->setPlainText(""); worldDataChanged(false); //--_updating; } _toolBar->show(); bool multiSelect = (QApplication::keyboardModifiers() & Qt::ControlModifier) != 0; if(!multiSelect/* && !isSelected()*/) { if(scene()) scene()->clearSelection(); _worldModel->selectionModel()->clearSelection(); } setSelected(true); viewScaleChanged(); } } else if(event->type() == QEvent::FocusOut && static_cast(event)->reason() != Qt::PopupFocusReason) { QObject* f = QApplication::focusWidget(); if(f == obj) f = NULL; while(f && f != _widget) f = f->parent(); if(!f) { _worldModel->simulationPause(); QString newText = _textEdit->toHtml(); if(newText != note()->text()) { //++_updating; _worldModel->beginMacro(i18n("Edit %1", _item->name())); foreach(StepCore::Item* item, note()->items()) if(!newText.contains(item->name())) _worldModel->deleteItem(item); foreach(StepCore::Item* item, _newItems) if(!newText.contains(item->name())) _newItems.removeAll(item); foreach(StepCore::Item* item, _newItems) _worldModel->addItem(item, note()); _newItems.clear(); _worldModel->setProperty(_item, "text", newText); _worldModel->endMacro(); //--_updating; } _hasFocus = false; _toolBar->hide(); _textEdit->clear(); viewScaleChanged(); worldDataChanged(false); } } return QObject::eventFilter(obj, event); } void NoteGraphicsItem::formatColor() { QColor color = _textEdit->textColor(); int result = KColorDialog::getColor(color, _widget); if(result == KColorDialog::Accepted) { _textEdit->setTextColor(color); } } void NoteGraphicsItem::formatBold(bool checked) { _textEdit->setFontWeight(checked ? QFont::Bold : QFont::Normal); } void NoteGraphicsItem::formatAlign(QAction* action) { if(action == _actionAlignLeft) _textEdit->setAlignment(Qt::AlignLeft); else if(action == _actionAlignCenter) _textEdit->setAlignment(Qt::AlignHCenter); else if(action == _actionAlignRight) _textEdit->setAlignment(Qt::AlignRight); else if(action == _actionAlignJustify) _textEdit->setAlignment(Qt::AlignJustify); _actionAlign->setIcon(action->icon()); } void NoteGraphicsItem::formatFontFamily(const QString& family) { _textEdit->setFontFamily(family); _textEdit->setFocus(); } void NoteGraphicsItem::formatFontSize(int size) { if(size > 0) _textEdit->setFontPointSize(size); else currentCharFormatChanged(_textEdit->currentCharFormat()); _textEdit->setFocus(); } void NoteGraphicsItem::currentCharFormatChanged(const QTextCharFormat& f) { _actionBold->setChecked(f.fontWeight() >= QFont::Bold); _actionItalic->setChecked(f.fontItalic()); _actionUnderline->setChecked(f.fontUnderline()); QPixmap pix(16,16); pix.fill(_textEdit->textColor()); _actionColor->setIcon(pix); QFontInfo ff(f.font()); #ifdef __GNUC__ #warning Strange, the following line does nothing ! #endif _actionFont->setFont(ff.family()); _actionFontSize->setFontSize(ff.pointSize()); } void NoteGraphicsItem::cursorPositionChanged() { if(_textEdit->alignment() & Qt::AlignLeft) _actionAlignLeft->setChecked(true); else if(_textEdit->alignment() & Qt::AlignHCenter) _actionAlignCenter->setChecked(true); else if(_textEdit->alignment() & Qt::AlignRight) _actionAlignRight->setChecked(true); else if(_textEdit->alignment() & Qt::AlignJustify) _actionAlignJustify->setChecked(true); _actionAlign->setIcon(_actionAlign->currentAction()->icon()); } void NoteGraphicsItem::insertImage() { QUrl url = KFileDialog::getOpenUrl(QUrl(), "image/png image/jpeg", _widget); if(url.isEmpty()) return; QString tmpFileName; if(! KIO::NetAccess::download(url, tmpFileName, _widget) ) { KMessageBox::error(_widget, KIO::NetAccess::lastErrorString()); return; } QFile file(tmpFileName); if(!file.open(QIODevice::ReadOnly)) { KMessageBox::error(_widget, i18n("Cannot open file '%1'", tmpFileName)); KIO::NetAccess::removeTempFile(tmpFileName); return; } QByteArray data = file.readAll(); file.close(); KIO::NetAccess::removeTempFile(tmpFileName); QPixmap pixmap; if(!pixmap.loadFromData(data)) { KMessageBox::error(_widget, i18n("Cannot parse file '%1'", tmpFileName)); return; } QString imgName; for(int n=0;; ++n) { imgName = QString("img:%1").arg(n); if(note()->childItem(imgName) != NULL) continue; bool found = false; foreach(StepCore::Item* item, _newItems) if(item->name() == imgName) { found = true; break; } if(!found) break; } _newItems << new StepCore::NoteImage(imgName, data); //_textEdit->document()->addResource(QTextDocument::ImageResource, imgName, pixmap); _textEdit->insertHtml(QString("").arg(imgName)); } void NoteGraphicsItem::insertFormula() { QString imgName; for(int n=0;; ++n) { imgName = QString("fml:%1").arg(n); if(note()->childItem(imgName) != NULL) continue; bool found = false; foreach(StepCore::Item* item, _newItems) if(item->name() == imgName) { found = true; break; } if(!found) break; } StepCore::NoteFormula* formula = new StepCore::NoteFormula(imgName); if(!editFormula(formula)) { delete formula; return; } _newItems << formula; _textEdit->insertHtml(QString("").arg(imgName)); } bool NoteGraphicsItem::editFormula(StepCore::NoteFormula* formula) { if(!LatexFormula::isLatexInstalled()) { KMessageBox::sorry(_widget, i18n("Cannot find latex installation. " "You need 'latex', 'dvips' and 'gs' executables installed and accessible from $PATH")); return false; } bool ok; - QString code = KInputDialog::getMultiLineText(i18n("LaTex Formula - Step"), - i18n("Enter LaTeX formula string"), QString(formula->code()), &ok, _widget); + QString code = QInputDialog::getMultiLineText(_widget, i18n("LaTex Formula - Step"), + i18n("Enter LaTeX formula string"), QString(formula->code()), &ok); if(!ok) return false; QByteArray image; QString error; bool result = LatexFormula::compileFormula(code, &image, &error); if(!result) { KMessageBox::error(_widget, i18n("Cannot compile LaTeX formula: %1", error)); return false; } QPixmap pixmap; if(!pixmap.loadFromData(image)) { KMessageBox::error(_widget, i18n("Cannot parse result image")); return false; } formula->setCode(code); formula->setImage(image); _textEdit->document()->addResource(QTextDocument::ImageResource, QUrl(formula->name()), pixmap); return true; } void NoteGraphicsItem::worldDataChanged(bool dynamicOnly) { if(!dynamicOnly) { if(!_hasFocus && _textEdit->toHtml() != note()->text()) { //++_updating; const StepCore::ItemList::const_iterator end = note()->items().end(); for(StepCore::ItemList::const_iterator it = note()->items().begin(); it != end; ++it) { if((*it)->metaObject()->inherits()) { QPixmap pix; pix.loadFromData(static_cast(*it)->image()); _textEdit->document()->addResource(QTextDocument::ImageResource, QUrl((*it)->name()), pix); } } /* const StepCore::NoteDataMap::const_iterator end = note()->dataMap().constEnd(); for(StepCore::NoteDataMap::const_iterator it = note()->dataMap().constBegin(); it != end; ++it) { QPixmap pix; pix.loadFromData(it.value()); _textEdit->document()->addResource(QTextDocument::ImageResource, it.key(), pix); } */ if(!_textEdit->hasFocus() && note()->text().isEmpty()) { _textEdit->setPlainText(_textEdit->emptyNotice()); } else { _textEdit->setHtml(note()->text()); } currentCharFormatChanged(_textEdit->currentCharFormat()); cursorPositionChanged(); //--_updating; } WidgetGraphicsItem::worldDataChanged(dynamicOnly); } } //////////////////////////////////////////////////// DataSourceWidget::DataSourceWidget(QWidget* parent) : QWidget(parent), _worldModel(0) { _skipReadOnly = false; QHBoxLayout *layout = new QHBoxLayout(this); layout->setContentsMargins(0,0,0,0); _object = new KComboBox(this); _object->setToolTip(i18n("Object name")); _object->setMinimumContentsLength(10); layout->addWidget(_object, 1); _property = new KComboBox(this); _property->setToolTip(i18n("Property name")); _property->setEnabled(false); _property->setMinimumContentsLength(10); layout->addWidget(_property, 1); _index = new KComboBox(this); _index->setToolTip(i18n("Vector index")); _index->setMinimumContentsLength(1); _index->setSizeAdjustPolicy(QComboBox::AdjustToMinimumContentsLength); layout->addWidget(_index, 0); connect(_object, SIGNAL(activated(int)), this, SLOT(objectSelected(int))); connect(_property, SIGNAL(activated(int)), this, SLOT(propertySelected(int))); connect(_object, SIGNAL(activated(int)), this, SIGNAL(dataSourceChanged())); connect(_property, SIGNAL(activated(int)), this, SIGNAL(dataSourceChanged())); connect(_index, SIGNAL(activated(int)), this, SIGNAL(dataSourceChanged())); } void DataSourceWidget::addObjects(const QModelIndex& parent, const QString& indent) { for(int i=0; i<_worldModel->rowCount(parent); ++i) { QModelIndex index = _worldModel->index(i, 0, parent); QString name = index.data(WorldModel::FormattedNameRole).toString(); _object->addItem(indent + name, QVariant::fromValue(_worldModel->object(index))); addObjects(index, indent + ' '); } } StepCore::Object* DataSourceWidget::dataObject() const { if(_object->currentIndex() < 0) return NULL; return _object->itemData(_object->currentIndex()).value(); } void DataSourceWidget::setDataSource(WorldModel* worldModel, StepCore::Object* object, const QString& property, int index) { _worldModel = worldModel; if(!_worldModel) return; _object->clear(); addObjects(QModelIndex(), ""); int objIndex = _object->findData(QVariant::fromValue(object)); _object->setCurrentIndex( objIndex ); objectSelected(objIndex); int propIndex = _property->findData(property); _property->setCurrentIndex( propIndex ); propertySelected(propIndex); _index->setCurrentIndex( index ); } void DataSourceWidget::objectSelected(int index) { Q_ASSERT(_worldModel); _property->clear(); const StepCore::Object* obj = _object->itemData(index).value(); if(obj != 0) { _property->setEnabled(true); for(int i=0; imetaObject()->propertyCount(); ++i) { const StepCore::MetaProperty* pr = obj->metaObject()->property(i); if(_skipReadOnly && !pr->isWritable()) continue; if(pr->userTypeId() == qMetaTypeId() || pr->userTypeId() == qMetaTypeId()) { _property->addItem(pr->nameTr(), pr->name()); } } propertySelected(_property->currentIndex()); } else { _property->setEnabled(false); } } void DataSourceWidget::propertySelected(int index) { Q_ASSERT(_worldModel); QString text = _property->itemData(index).toString(); const StepCore::Object* obj = _object->itemData(_object->currentIndex()) .value(); const StepCore::MetaProperty* pr = obj ? obj->metaObject()->property(text) : 0; _index->clear(); if(pr != 0 && pr->userTypeId() == qMetaTypeId()) { _index->setEnabled(true); _index->addItem("0"); _index->addItem("1"); } else { _index->setEnabled(false); } } //////////////////////////////////////////////////// GraphGraphicsItem::GraphGraphicsItem(StepCore::Item* item, WorldModel* worldModel) : WidgetGraphicsItem(item, worldModel) { Q_ASSERT(dynamic_cast(_item) != NULL); setFlag(QGraphicsItem::ItemIsSelectable); setFlag(QGraphicsItem::ItemIsMovable); setAcceptsHoverEvents(true); _plotWidget = new KPlotWidget(); _plotWidget->setPalette(QPalette(Qt::lightGray)); _plotWidget->setBackgroundColor(Qt::white); _plotWidget->setForegroundColor(Qt::black); //_plotWidget->setLeftPadding(0); //_plotWidget->setTopPadding(2); //_plotWidget->setRightPadding(3); _plotObject = new KPlotObject(Qt::black); _plotObject->setShowPoints(false); _plotObject->setShowLines(true); _plotObject->setPointStyle(KPlotObject::Square); _plotObject1 = new KPlotObject(Qt::red); _plotObject1->setShowPoints(true); _plotObject1->setShowLines(false); _plotObject1->setPointStyle(KPlotObject::Square); QList plotObjects; plotObjects << _plotObject; plotObjects << _plotObject1; //_plotWidget->setAntialiasing(true); _plotWidget->addPlotObjects(plotObjects); _lastColor = 0xff000000; _lastPointTime = -HUGE_VAL; setCenteralWidget(_plotWidget); setOnHoverHandlerEnabled(true); _plotWidget->setMouseTracking(true); } inline StepCore::Graph* GraphGraphicsItem::graph() const { return static_cast(_item); } void GraphGraphicsItem::adjustLimits() { double minX = HUGE_VAL, minY = HUGE_VAL; double maxX = -HUGE_VAL, maxY = -HUGE_VAL; if(graph()->autoLimitsX() || graph()->autoLimitsY()) { for(int i=0; i<(int) graph()->points().size(); ++i) { StepCore::Vector2d p = graph()->points()[i]; if(p[0] < minX) minX = p[0]; if(p[0] > maxX) maxX = p[0]; if(p[1] < minY) minY = p[1]; if(p[1] > maxY) maxY = p[1]; } } if(!graph()->autoLimitsX() || graph()->points().empty()) { minX = graph()->limitsX()[0]; maxX = graph()->limitsX()[1]; } else { double range = maxX - minX; if(range != 0) { minX -= 0.1*range; maxX += 0.1*range; } else { minX -= 0.5; maxX += 0.5; } } if(!graph()->autoLimitsY() || graph()->points().empty()) { minY = graph()->limitsY()[0]; maxY = graph()->limitsY()[1]; } else { double range = maxY - minY; if(range != 0) { minY -= 0.1*range; maxY += 0.1*range; } else { minY -= 0.5; maxY += 0.5; } } _plotWidget->setLimits(minX, maxX, minY, maxY); } void GraphGraphicsItem::worldDataChanged(bool dynamicOnly) { if(!dynamicOnly) { viewScaleChanged(); // Labels QString labelX, labelY; if(graph()->isValidX()) { labelX = i18n("%1.%2", _worldModel->formatName(graph()->objectX()), graph()->propertyX()); if(graph()->indexX() >= 0) labelX.append(i18n("[%1]", graph()->indexX())); QString units = graph()->unitsX(); if(!units.isEmpty()) labelX.append(" [").append(units).append("]"); } else { labelX = i18n("[not configured]"); } if(graph()->isValidY()) { labelY = i18n("%1.%2", _worldModel->formatName(graph()->objectY()), graph()->propertyY()); if(graph()->indexY() >= 0) labelY.append(i18n("[%1]", graph()->indexY())); QString units = graph()->unitsY(); if(!units.isEmpty()) labelY.append(" [").append(units).append("]"); } else { labelY = i18n("[not configured]"); } _plotWidget->axis( KPlotWidget::BottomAxis )->setLabel(labelX); _plotWidget->axis( KPlotWidget::LeftAxis )->setLabel(labelY); if(!graph()->autoLimitsX() && !graph()->autoLimitsY()) adjustLimits(); _plotObject->setShowPoints(graph()->showPoints()); _plotObject->setShowLines(graph()->showLines()); if(_lastColor != graph()->color()) { _lastColor = graph()->color(); _plotObject->setLinePen(QPen(QColor::fromRgba(_lastColor),0)); } /* // Points _plotObject->clearPoints(); for(int i=0; i<(int) graph()->points().size(); ++i) { StepCore::Vector2d p = graph()->points()[i]; _plotObject->addPoint(p[0], p[1]); } adjustLimits(); _plotWidget->update(); */ WidgetGraphicsItem::worldDataChanged(dynamicOnly); } if(_worldModel->isSimulationActive()) { if(_worldModel->world()->time() > _lastPointTime + 1.0/_worldModel->simulationFps() - 1e-2/_worldModel->simulationFps()) { StepCore::Vector2d point = graph()->recordPoint(); _lastPointTime = _worldModel->world()->time(); } } int po_count, p_count; do { const QList points = _plotObject->points(); po_count = points.count(); p_count = graph()->points().size(); int count = qMin(po_count, p_count); for(int p=0; p < count; ++p) points[p]->setPosition(vectorToPoint(graph()->points()[p])); } while(0); if(po_count < p_count) { for(; po_count < p_count; ++po_count) _plotObject->addPoint(vectorToPoint(graph()->points()[po_count])); } else { for(--po_count; po_count >= p_count; --po_count) _plotObject->removePoint(po_count); } if(p_count > 0) { if(_plotObject1->points().isEmpty()) { _plotObject1->addPoint(0,0); } _plotObject1->points()[0]->setPosition(vectorToPoint(graph()->points()[p_count-1])); } else { _plotObject1->clearPoints(); } if(graph()->autoLimitsX() || graph()->autoLimitsY()) adjustLimits(); _plotWidget->update(); #if 0 //#error Do setProperty here and remove DynamicOnly from points if(ok) { _plotObject->addPoint(point[0], point[1]); if(graph()->autoLimitsX() || graph()->autoLimitsY()) adjustLimits(); _plotWidget->update(); } _lastPointTime = _worldModel->world()->time(); worldDataChanged(false); //_worldModel->setProperty(graph(), "name", QString("test")); } #endif } void GraphMenuHandler::populateMenu(QMenu* menu, KActionCollection* actions) { _confUi = 0; _confDialog = 0; _confChanged = false; menu->addAction(QIcon::fromTheme("edit-clear"), i18n("Clear graph"), this, SLOT(clearGraph())); menu->addAction(QIcon::fromTheme("configure"), i18n("Configure graph..."), this, SLOT(configureGraph())); menu->addSeparator(); ItemMenuHandler::populateMenu(menu, actions); } inline StepCore::Graph* GraphMenuHandler::graph() const { return static_cast(_object); } void GraphMenuHandler::configureGraph() { if(_worldModel->isSimulationActive()) _worldModel->simulationStop(); _confChanged = false; _confDialog = new QDialog(); // XXX: parent? _confDialog->setWindowTitle(i18n("Configure graph")); _buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel | QDialogButtonBox::Apply); QWidget *mainWidget = new QWidget(); QVBoxLayout *mainLayout = new QVBoxLayout; _confDialog->setLayout(mainLayout); mainLayout->addWidget(mainWidget); QPushButton *okButton = _buttonBox->button(QDialogButtonBox::Ok); okButton->setDefault(true); okButton->setShortcut(Qt::CTRL | Qt::Key_Return); _confDialog->connect(_buttonBox, SIGNAL(accepted()), _confDialog, SLOT(accept())); _confDialog->connect(_buttonBox, SIGNAL(rejected()), _confDialog, SLOT(reject())); mainLayout->addWidget(_buttonBox); _confUi = new Ui::WidgetConfigureGraph; _confUi->setupUi(mainWidget); _confUi->dataSourceX->setDataSource(_worldModel, graph()->objectX(), graph()->propertyX(), graph()->indexX()); _confUi->dataSourceY->setDataSource(_worldModel, graph()->objectY(), graph()->propertyY(), graph()->indexY()); _confUi->checkBoxAutoX->setChecked(graph()->autoLimitsX()); _confUi->checkBoxAutoY->setChecked(graph()->autoLimitsY()); _confUi->lineEditMinX->setValidator( new QDoubleValidator(-HUGE_VAL, HUGE_VAL, DBL_DIG, _confUi->lineEditMinX)); _confUi->lineEditMaxX->setValidator( new QDoubleValidator(-HUGE_VAL, HUGE_VAL, DBL_DIG, _confUi->lineEditMaxX)); _confUi->lineEditMinY->setValidator( new QDoubleValidator(-HUGE_VAL, HUGE_VAL, DBL_DIG, _confUi->lineEditMinY)); _confUi->lineEditMaxY->setValidator( new QDoubleValidator(-HUGE_VAL, HUGE_VAL, DBL_DIG, _confUi->lineEditMaxY)); _confUi->lineEditMinX->setText(QString::number(graph()->limitsX()[0])); _confUi->lineEditMaxX->setText(QString::number(graph()->limitsX()[1])); _confUi->lineEditMinY->setText(QString::number(graph()->limitsY()[0])); _confUi->lineEditMaxY->setText(QString::number(graph()->limitsY()[1])); _confUi->checkBoxShowLines->setChecked(graph()->showLines()); _confUi->checkBoxShowPoints->setChecked(graph()->showPoints()); _buttonBox->button(QDialogButtonBox::Apply)->setEnabled(false); connect(_buttonBox, SIGNAL(clicked(QAbstractButton*)), this, SLOT(confApply(QAbstractButton*))); connect(_confUi->dataSourceX, SIGNAL(dataSourceChanged()), this, SLOT(confChanged())); connect(_confUi->dataSourceY, SIGNAL(dataSourceChanged()), this, SLOT(confChanged())); connect(_confUi->checkBoxAutoX, SIGNAL(stateChanged(int)), this, SLOT(confChanged())); connect(_confUi->checkBoxAutoY, SIGNAL(stateChanged(int)), this, SLOT(confChanged())); - connect(_confUi->lineEditMinX, SIGNAL(textEdited(const QString&)), this, SLOT(confChanged())); - connect(_confUi->lineEditMaxX, SIGNAL(textEdited(const QString&)), this, SLOT(confChanged())); - connect(_confUi->lineEditMinY, SIGNAL(textEdited(const QString&)), this, SLOT(confChanged())); - connect(_confUi->lineEditMaxY, SIGNAL(textEdited(const QString&)), this, SLOT(confChanged())); + connect(_confUi->lineEditMinX, SIGNAL(textEdited(QString)), this, SLOT(confChanged())); + connect(_confUi->lineEditMaxX, SIGNAL(textEdited(QString)), this, SLOT(confChanged())); + connect(_confUi->lineEditMinY, SIGNAL(textEdited(QString)), this, SLOT(confChanged())); + connect(_confUi->lineEditMaxY, SIGNAL(textEdited(QString)), this, SLOT(confChanged())); connect(_confUi->checkBoxShowLines, SIGNAL(stateChanged(int)), this, SLOT(confChanged())); connect(_confUi->checkBoxShowPoints, SIGNAL(stateChanged(int)), this, SLOT(confChanged())); _confDialog->exec(); delete _confDialog; _confDialog = 0; delete _confUi; _confUi = 0; } void GraphMenuHandler::confApply(QAbstractButton *button) { if (_buttonBox->button(QDialogButtonBox::Apply) != button && _buttonBox->button(QDialogButtonBox::Ok) != button) { return; } Q_ASSERT(_confUi && _confDialog); // XXX: check for actual change ? if(!_confChanged) return; _worldModel->beginMacro(i18n("Edit properties of %1", graph()->name())); QVariant objX = QVariant::fromValue(_confUi->dataSourceX->dataObject()); QVariant objY = QVariant::fromValue(_confUi->dataSourceY->dataObject()); _worldModel->setProperty(graph(), "objectX", objX); _worldModel->setProperty(graph(), "propertyX", _confUi->dataSourceX->dataProperty()); _worldModel->setProperty(graph(), "indexX", _confUi->dataSourceX->dataIndex()); _worldModel->setProperty(graph(), "objectY", objY); _worldModel->setProperty(graph(), "propertyY", _confUi->dataSourceY->dataProperty()); _worldModel->setProperty(graph(), "indexY", _confUi->dataSourceY->dataIndex()); _worldModel->setProperty(graph(), "autoLimitsX", _confUi->checkBoxAutoX->isChecked()); _worldModel->setProperty(graph(), "autoLimitsY", _confUi->checkBoxAutoY->isChecked()); StepCore::Vector2d limitsX(_confUi->lineEditMinX->text().toDouble(), _confUi->lineEditMaxX->text().toDouble()); StepCore::Vector2d limitsY(_confUi->lineEditMinY->text().toDouble(), _confUi->lineEditMaxY->text().toDouble()); _worldModel->setProperty(graph(), "limitsX", QVariant::fromValue(limitsX)); _worldModel->setProperty(graph(), "limitsY", QVariant::fromValue(limitsY)); _worldModel->setProperty(graph(), "showLines", _confUi->checkBoxShowLines->isChecked()); _worldModel->setProperty(graph(), "showPoints", _confUi->checkBoxShowPoints->isChecked()); _worldModel->endMacro(); } void GraphMenuHandler::confChanged() { Q_ASSERT(_confUi && _confDialog); _confChanged = true; _buttonBox->button(QDialogButtonBox::Apply)->setEnabled(true); } void GraphMenuHandler::clearGraph() { _worldModel->simulationPause(); //_lastPointTime = -HUGE_VAL; // XXX _worldModel->beginMacro(i18n("Clear graph %1", _object->name())); _worldModel->setProperty(graph(), "points", QVariant::fromValue(StepCore::Vector2dList()) ); _worldModel->endMacro(); } //////////////////////////////////////////////////// MeterGraphicsItem::MeterGraphicsItem(StepCore::Item* item, WorldModel* worldModel) : WidgetGraphicsItem(item, worldModel) { Q_ASSERT(dynamic_cast(_item) != NULL); setFlag(QGraphicsItem::ItemIsSelectable); setFlag(QGraphicsItem::ItemIsMovable); setAcceptsHoverEvents(true); setBackgroundBrush(QBrush(Qt::white)); _widget = new QFrame(); _widget->setFrameShape(QFrame::Box); _widget->setPalette(QPalette(Qt::lightGray)); QGridLayout* layout = new QGridLayout(_widget); layout->setContentsMargins(0,0,2,0); layout->setSpacing(0); _lcdNumber = new QLCDNumber(_widget); _lcdNumber->setFrameShape(QFrame::NoFrame); _lcdNumber->setSegmentStyle(QLCDNumber::Flat); _lcdNumber->display(0); _labelUnits = new QLabel(_widget); _labelUnits->setAlignment(Qt::AlignRight | Qt::AlignVCenter); layout->addWidget(_lcdNumber, 0, 0, 1, 1); layout->addWidget(_labelUnits, 0, 1, 1, 1); setCenteralWidget(_widget); setOnHoverHandlerEnabled(true); _widget->setMouseTracking(true); _lcdNumber->setMouseTracking(true); _labelUnits->setMouseTracking(true); } inline StepCore::Meter* MeterGraphicsItem::meter() const { return static_cast(_item); } void MeterGraphicsItem::worldDataChanged(bool dynamicOnly) { if(!dynamicOnly) { viewScaleChanged(); if(meter()->digits() != _lcdNumber->digitCount()) _lcdNumber->setDigitCount(meter()->digits()); QString units = meter()->units(); if(units != _labelUnits->text()) { QFont font(_labelUnits->font()); int pixelSize = int(meter()->size()[1]/2); for(; pixelSize > 0; --pixelSize) { font.setPixelSize(pixelSize); QFontMetrics fm(font); if(fm.width(units) < int(meter()->size()[0]/3)) break; } _labelUnits->setFont(font); _labelUnits->setText(units); } WidgetGraphicsItem::worldDataChanged(dynamicOnly); } double value = meter()->value(); _lcdNumber->display(value); } void MeterMenuHandler::populateMenu(QMenu* menu, KActionCollection* actions) { _confUi = 0; _confDialog = 0; _confChanged = false; menu->addAction(QIcon::fromTheme("configure"), i18n("Configure meter..."), this, SLOT(configureMeter())); menu->addSeparator(); ItemMenuHandler::populateMenu(menu, actions); } inline StepCore::Meter* MeterMenuHandler::meter() const { return static_cast(_object); } void MeterMenuHandler::configureMeter() { if(_worldModel->isSimulationActive()) _worldModel->simulationStop(); _confChanged = false; _confDialog = new QDialog(); // XXX _confDialog->setWindowTitle(i18n("Configure meter")); _buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel | QDialogButtonBox::Apply); QWidget *mainWidget = new QWidget(); QVBoxLayout *mainLayout = new QVBoxLayout; _confDialog->setLayout(mainLayout); mainLayout->addWidget(mainWidget); QPushButton *okButton = _buttonBox->button(QDialogButtonBox::Ok); okButton->setDefault(true); okButton->setShortcut(Qt::CTRL | Qt::Key_Return); _confDialog->connect(_buttonBox, SIGNAL(accepted()), _confDialog, SLOT(accept())); _confDialog->connect(_buttonBox, SIGNAL(rejected()), _confDialog, SLOT(reject())); mainLayout->addWidget(_buttonBox); _confUi = new Ui::WidgetConfigureMeter; _confUi->setupUi(mainWidget); _confUi->dataSource->setDataSource(_worldModel, meter()->object(), meter()->property(), meter()->index()); _confUi->lineEditDigits->setValidator( new QIntValidator(0, 100, _confUi->lineEditDigits)); _confUi->lineEditDigits->setText(QString::number(meter()->digits())); connect(_buttonBox, SIGNAL(clicked(QAbstractButton*)), this, SLOT(confApply(QAbstractButton*))); connect(_confUi->dataSource, SIGNAL(dataSourceChanged()), this, SLOT(confChanged())); - connect(_confUi->lineEditDigits, SIGNAL(textEdited(const QString&)), this, SLOT(confChanged())); + connect(_confUi->lineEditDigits, SIGNAL(textEdited(QString)), this, SLOT(confChanged())); _confDialog->exec(); delete _confDialog; _confDialog = 0; delete _confUi; _confUi = 0; } void MeterMenuHandler::confApply(QAbstractButton *button) { if (_buttonBox->button(QDialogButtonBox::Apply) != button && _buttonBox->button(QDialogButtonBox::Ok) != button) { return; } Q_ASSERT(_confUi && _confDialog); // XXX: check for actual change ? if(!_confChanged) return; _worldModel->beginMacro(i18n("Edit properties of %1", meter()->name())); _worldModel->setProperty(meter(), "object", QVariant::fromValue(_confUi->dataSource->dataObject())); _worldModel->setProperty(meter(), "property", _confUi->dataSource->dataProperty()); _worldModel->setProperty(meter(), "index", _confUi->dataSource->dataIndex()); _worldModel->setProperty(meter(), "digits", _confUi->lineEditDigits->text().toInt()); _worldModel->endMacro(); } void MeterMenuHandler::confChanged() { Q_ASSERT(_confUi && _confDialog); _confChanged = true; _buttonBox->button(QDialogButtonBox::Apply)->setEnabled(true); } //////////////////////////////////////////////////// ControllerGraphicsItem::ControllerGraphicsItem(StepCore::Item* item, WorldModel* worldModel) : WidgetGraphicsItem(item, worldModel) { Q_ASSERT(dynamic_cast(_item) != NULL); setFlag(QGraphicsItem::ItemIsSelectable); setFlag(QGraphicsItem::ItemIsMovable); setAcceptsHoverEvents(true); setBackgroundBrush(QBrush(Qt::white)); _widget = new QWidget(); _widget->setPalette(QPalette(Qt::lightGray)); QGridLayout* layout = new QGridLayout(_widget); _labelMin = new QLabel(_widget); _labelMin->setAlignment(Qt::AlignRight); _labelMax = new QLabel(_widget); _labelMax->setAlignment(Qt::AlignLeft); _labelSource = new QLabel(_widget); _labelSource->setAlignment(Qt::AlignHCenter | Qt::AlignTop); _slider = new QSlider(Qt::Horizontal, _widget); _slider->setRange(SLIDER_MIN, SLIDER_MAX); connect(_slider, SIGNAL(sliderMoved(int)), this, SLOT(sliderChanged(int))); connect(_slider, SIGNAL(sliderReleased()), this, SLOT(sliderReleased())); layout->addWidget(_labelMin, 0, 0, 1, 1); layout->addWidget(_slider, 0, 1, 1, 1); layout->addWidget(_labelMax, 0, 2, 1, 1); layout->addWidget(_labelSource, 1, 0, 1, 3); _incAction = new QAction(i18n("Increase value"), _widget); _decAction = new QAction(i18n("Decrease value"), _widget); connect(_incAction, SIGNAL(triggered(bool)), this, SLOT(incTriggered())); connect(_decAction, SIGNAL(triggered(bool)), this, SLOT(decTriggered())); _widget->addAction(_incAction); _widget->addAction(_decAction); //_widget->addAction(_configureAction); //_widget->setContextMenuPolicy(Qt::ActionsContextMenu); _lastValue = 1; _changed = false; setCenteralWidget(_widget); setOnHoverHandlerEnabled(true); _widget->setMouseTracking(true); _labelMin->setMouseTracking(true); _labelMax->setMouseTracking(true); _labelSource->setMouseTracking(true); _slider->setMouseTracking(true); } inline StepCore::Controller* ControllerGraphicsItem::controller() const { return static_cast(_item); } void ControllerGraphicsItem::decTriggered() { _worldModel->simulationPause(); _worldModel->beginMacro(i18n("Decrease controller %1", _item->name())); _worldModel->setProperty(controller(), "value", controller()->value() - controller()->increment()); _worldModel->endMacro(); } void ControllerGraphicsItem::incTriggered() { _worldModel->simulationPause(); _worldModel->beginMacro(i18n("Increase controller %1", _item->name())); _worldModel->setProperty(controller(), "value", controller()->value() + controller()->increment()); _worldModel->endMacro(); } void ControllerGraphicsItem::worldDataChanged(bool dynamicOnly) { if(!dynamicOnly) { viewScaleChanged(); // Labels _labelMin->setText(QString::number(controller()->limits()[0])); _labelMax->setText(QString::number(controller()->limits()[1])); QString source; if(controller()->isValid()) { source = i18n("%1.%2", _worldModel->formatName(controller()->object()), controller()->property()); if(controller()->index() >= 0) source.append(i18n("[%1]", controller()->index())); QString units = controller()->units(); if(!units.isEmpty()) source.append(" [").append(units).append("]"); } else { source = i18n("[not configured]"); } _labelSource->setText(source); if(_incAction->isEnabled() != controller()->isValid()) { _incAction->setEnabled(controller()->isValid()); _decAction->setEnabled(controller()->isValid()); } if(_incShortcut != controller()->increaseShortcut()) { _incShortcut = controller()->increaseShortcut(); _incAction->setShortcut(QKeySequence(_incShortcut)); } if(_decShortcut != controller()->decreaseShortcut()) { _decShortcut = controller()->decreaseShortcut(); _decAction->setShortcut(QKeySequence(_decShortcut)); } //if(!graph()->autoLimitsX() && !graph()->autoLimitsY()) adjustLimits(); /* // Points _plotObject->clearPoints(); for(int i=0; i<(int) graph()->points().size(); ++i) { StepCore::Vector2d p = graph()->points()[i]; _plotObject->addPoint(p[0], p[1]); } adjustLimits(); _plotWidget->update(); */ WidgetGraphicsItem::worldDataChanged(dynamicOnly); } double value = round((controller()->value() - controller()->limits()[0]) * (SLIDER_MAX - SLIDER_MIN) / (controller()->limits()[1] - controller()->limits()[0]) + SLIDER_MIN); if(value <= SLIDER_MIN && _lastValue > SLIDER_MIN) { QPalette palette; palette.setColor(_labelMin->foregroundRole(), Qt::red); _labelMin->setPalette(palette); } else if(value > SLIDER_MIN && _lastValue <= SLIDER_MIN) { QPalette palette; _labelMin->setPalette(palette); } if(value >= SLIDER_MAX-1 && _lastValue < SLIDER_MAX-1) { QPalette palette; palette.setColor(_labelMax->foregroundRole(), Qt::red); _labelMax->setPalette(palette); } else if(value < SLIDER_MAX-1 && _lastValue >= SLIDER_MAX-1) { QPalette palette; _labelMax->setPalette(palette); } _lastValue = value; if(value < SLIDER_MIN) value = SLIDER_MIN; else if(value > SLIDER_MAX-1) value = SLIDER_MAX-1; _slider->setValue(int(value)); #if 0 if(_worldModel->isSimulationActive()) { if(_worldModel->world()->time() > _lastPointTime + 1.0/_worldModel->simulationFps() - 1e-2/_worldModel->simulationFps()) { StepCore::Vector2d point = graph()->recordPoint(); _lastPointTime = _worldModel->world()->time(); } } int po_count, p_count; do { const QList points = _plotObject->points(); po_count = points.count(); p_count = graph()->points().size(); int count = qMin(po_count, p_count); for(int p=0; p < count; ++p) points[p]->setPosition(vectorToPoint(graph()->points()[p])); } while(0); if(po_count < p_count) { for(; po_count < p_count; ++po_count) _plotObject->addPoint(vectorToPoint(graph()->points()[po_count])); } else { for(--po_count; po_count >= p_count; --po_count) _plotObject->removePoint(po_count); } if(graph()->autoLimitsX() || graph()->autoLimitsY()) adjustLimits(); _plotWidget->update(); #endif #if 0 //#error Do setProperty here and remove DynamicOnly from points if(ok) { _plotObject->addPoint(point[0], point[1]); if(graph()->autoLimitsX() || graph()->autoLimitsY()) adjustLimits(); _plotWidget->update(); } _lastPointTime = _worldModel->world()->time(); worldDataChanged(false); //_worldModel->setProperty(graph(), "name", QString("test")); } #endif } void ControllerGraphicsItem::sliderChanged(int value) { Q_ASSERT(value == _slider->sliderPosition()); if(!controller()->isValid()) return; //if(!_worldModel->isSimulationActive()) { _worldModel->simulationPause(); if(!_changed) { _worldModel->beginMacro(i18n("Change controller %1", controller()->name())); _changed = true; } double v = controller()->limits()[0] + (value - SLIDER_MIN) * (controller()->limits()[1] - controller()->limits()[0]) / (SLIDER_MAX - SLIDER_MIN); _worldModel->setProperty(controller(), "value", v); //} } void ControllerGraphicsItem::sliderReleased() { if(_changed) { _worldModel->endMacro(); _changed = false; } } void ControllerMenuHandler::populateMenu(QMenu* menu, KActionCollection* actions) { _confUi = 0; _confDialog = 0; _confChanged = false; menu->addAction(QIcon::fromTheme("arrow-up"), i18n("Increase value"), this, SLOT(incTriggered())); menu->addAction(QIcon::fromTheme("arrow-down"), i18n("Decrease value"), this, SLOT(decTriggered())); menu->addSeparator(); menu->addAction(QIcon::fromTheme("configure"), i18n("Configure controller..."), this, SLOT(configureController())); menu->addSeparator(); ItemMenuHandler::populateMenu(menu, actions); } inline StepCore::Controller* ControllerMenuHandler::controller() const { return static_cast(_object); } void ControllerMenuHandler::configureController() { if(_worldModel->isSimulationActive()) _worldModel->simulationStop(); _confChanged = false; _confDialog = new QDialog(); // XXX _confDialog->setWindowTitle(i18n("Configure controller")); _buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel | QDialogButtonBox::Apply); QWidget *mainWidget = new QWidget(); QVBoxLayout *mainLayout = new QVBoxLayout; _confDialog->setLayout(mainLayout); mainLayout->addWidget(mainWidget); QPushButton *okButton = _buttonBox->button(QDialogButtonBox::Ok); okButton->setDefault(true); okButton->setShortcut(Qt::CTRL | Qt::Key_Return); _confDialog->connect(_buttonBox, SIGNAL(accepted()), _confDialog, SLOT(accept())); _confDialog->connect(_buttonBox, SIGNAL(rejected()), _confDialog, SLOT(reject())); mainLayout->addWidget(_buttonBox); _confUi = new Ui::WidgetConfigureController; _confUi->setupUi(mainWidget); _confUi->dataSource->setSkipReadOnly(true); _confUi->dataSource->setDataSource(_worldModel, controller()->object(), controller()->property(), controller()->index()); _confUi->lineEditMin->setValidator( new QDoubleValidator(-HUGE_VAL, HUGE_VAL, DBL_DIG, _confUi->lineEditMin)); _confUi->lineEditMax->setValidator( new QDoubleValidator(-HUGE_VAL, HUGE_VAL, DBL_DIG, _confUi->lineEditMax)); _confUi->lineEditMin->setText(QString::number(controller()->limits()[0])); _confUi->lineEditMax->setText(QString::number(controller()->limits()[1])); _confUi->keyIncrease->setModifierlessAllowed(true); _confUi->keyDecrease->setModifierlessAllowed(true); //_confUi->keyIncrease->setKeySequence(_incAction->shortcut().primary()); //_confUi->keyDecrease->setKeySequence(_decAction->shortcut().primary()); _confUi->keyIncrease->setKeySequence(QKeySequence(controller()->increaseShortcut())); _confUi->keyDecrease->setKeySequence(QKeySequence(controller()->decreaseShortcut())); _confUi->lineEditIncrement->setValidator( new QDoubleValidator(-HUGE_VAL, HUGE_VAL, DBL_DIG, _confUi->lineEditIncrement)); _confUi->lineEditIncrement->setText(QString::number(controller()->increment())); _buttonBox->button(QDialogButtonBox::Apply)->setEnabled(false); connect(_buttonBox, SIGNAL(clicked(QAbstractButton*)), this, SLOT(confApply(QAbstractButton*))); connect(_confUi->dataSource, SIGNAL(dataSourceChanged()), this, SLOT(confChanged())); - connect(_confUi->lineEditMin, SIGNAL(textEdited(const QString&)), this, SLOT(confChanged())); - connect(_confUi->lineEditMax, SIGNAL(textEdited(const QString&)), this, SLOT(confChanged())); - connect(_confUi->keyIncrease, SIGNAL(keySequenceChanged(const QKeySequence&)), this, SLOT(confChanged())); - connect(_confUi->keyDecrease, SIGNAL(keySequenceChanged(const QKeySequence&)), this, SLOT(confChanged())); - connect(_confUi->lineEditIncrement, SIGNAL(textEdited(const QString&)), this, SLOT(confChanged())); + connect(_confUi->lineEditMin, SIGNAL(textEdited(QString)), this, SLOT(confChanged())); + connect(_confUi->lineEditMax, SIGNAL(textEdited(QString)), this, SLOT(confChanged())); + connect(_confUi->keyIncrease, SIGNAL(keySequenceChanged(QKeySequence)), this, SLOT(confChanged())); + connect(_confUi->keyDecrease, SIGNAL(keySequenceChanged(QKeySequence)), this, SLOT(confChanged())); + connect(_confUi->lineEditIncrement, SIGNAL(textEdited(QString)), this, SLOT(confChanged())); _confDialog->exec(); delete _confDialog; _confDialog = 0; delete _confUi; _confUi = 0; } void ControllerMenuHandler::confApply(QAbstractButton *button) { if (_buttonBox->button(QDialogButtonBox::Apply) != button && _buttonBox->button(QDialogButtonBox::Ok) != button) { return; } Q_ASSERT(_confUi && _confDialog); // XXX: check for actual change ? if(!_confChanged) return; _worldModel->beginMacro(i18n("Edit properties of %1", controller()->name())); _worldModel->setProperty(controller(), "object", QVariant::fromValue(_confUi->dataSource->dataObject())); _worldModel->setProperty(controller(), "property", _confUi->dataSource->dataProperty()); _worldModel->setProperty(controller(), "index", _confUi->dataSource->dataIndex()); StepCore::Vector2d limits(_confUi->lineEditMin->text().toDouble(), _confUi->lineEditMax->text().toDouble()); _worldModel->setProperty(controller(), "limits", QVariant::fromValue(limits)); _worldModel->setProperty(controller(), "increaseShortcut", QVariant::fromValue(_confUi->keyIncrease->keySequence().toString())); _worldModel->setProperty(controller(), "decreaseShortcut", QVariant::fromValue(_confUi->keyDecrease->keySequence().toString())); _worldModel->setProperty(controller(), "increment", QVariant::fromValue(_confUi->lineEditIncrement->text().toDouble())); _worldModel->endMacro(); } void ControllerMenuHandler::confChanged() { Q_ASSERT(_confUi && _confDialog); _confChanged = true; _buttonBox->button(QDialogButtonBox::Apply)->setEnabled(true); } void ControllerMenuHandler::decTriggered() { _worldModel->simulationPause(); _worldModel->beginMacro(i18n("Decrease controller %1", _object->name())); _worldModel->setProperty(controller(), "value", controller()->value() - controller()->increment()); _worldModel->endMacro(); } void ControllerMenuHandler::incTriggered() { _worldModel->simulationPause(); _worldModel->beginMacro(i18n("Increase controller %1", _object->name())); _worldModel->setProperty(controller(), "value", controller()->value() + controller()->increment()); _worldModel->endMacro(); } //////////////////////////////////////////////////// TracerGraphicsItem::TracerGraphicsItem(StepCore::Item* item, WorldModel* worldModel) : StepGraphicsItem(item, worldModel), _moving(false), _movingDelta(0,0) { Q_ASSERT(dynamic_cast(_item) != NULL); setFlag(QGraphicsItem::ItemIsSelectable); setFlag(QGraphicsItem::ItemIsMovable); setZValue(HANDLER_ZVALUE); /* _lastArrowRadius = -1; _velocityHandler = new ArrowHandlerGraphicsItem(item, worldModel, this, _item->metaObject()->property("velocity")); _velocityHandler->setVisible(false);*/ //scene()->addItem(_velocityHandler); _lastPos = QPointF(0,0); _lastPointTime = -HUGE_VAL; } inline StepCore::Tracer* TracerGraphicsItem::tracer() const { return static_cast(_item); } QPainterPath TracerGraphicsItem::shape() const { QPainterPath path; double w = (HANDLER_SIZE+1)/currentViewScale(); // XXX: add _points here! path.addEllipse(QRectF(_lastPos.x()-w, _lastPos.y()-w,w*2,w*2)); return path; } void TracerGraphicsItem::paint(QPainter* painter, const QStyleOptionGraphicsItem* /*option*/, QWidget* /*widget*/) { double s = currentViewScale(); double w = HANDLER_SIZE/s; painter->setRenderHint(QPainter::Antialiasing, true); painter->setPen(QPen(QColor::fromRgba(tracer()->color()), 0)); //painter->setBrush(QBrush(Qt::black)); painter->drawPolyline(_points); painter->drawEllipse(QRectF(_lastPos.x()-w, _lastPos.y()-w, w*2,w*2)); painter->drawPoint(_lastPos); if(_isSelected) { painter->setPen(QPen(SELECTION_COLOR, 0, Qt::DashLine)); //painter->setBrush(QBrush()); //painter->setBrush(QBrush(QColor(0, 0x99, 0xff))); w = (HANDLER_SIZE + SELECTION_MARGIN)/s; painter->drawEllipse(QRectF(_lastPos.x()-w, _lastPos.y()-w, w*2, w*2)); } } void TracerGraphicsItem::viewScaleChanged() { prepareGeometryChange(); double s = currentViewScale(); double w = (HANDLER_SIZE+SELECTION_MARGIN)/s; QPointF p = vectorToPoint(tracer()->position()); _boundingRect = _points.boundingRect() | QRectF(p.x()-w, p.y()-w,2*w,2*w); update(); } void TracerGraphicsItem::worldDataChanged(bool) { /* if(_isMouseOverItem || _isSelected) { double vnorm = particle()->velocity().norm(); double anorm = particle()->force().norm() / particle()->mass(); double arrowRadius = qMax(vnorm, anorm) + ARROW_STROKE/currentViewScale(); if(arrowRadius > _lastArrowRadius || arrowRadius < _lastArrowRadius/2) { _lastArrowRadius = arrowRadius; viewScaleChanged(); } update(); } */ //setPos(vectorToPoint(tracer()->position())); if(_worldModel->isSimulationActive()) { if(_worldModel->world()->time() > _lastPointTime + 1.0/_worldModel->simulationFps() - 1e-2/_worldModel->simulationFps()) { tracer()->recordPoint(); _lastPointTime = _worldModel->world()->time(); } } bool geometryChange = false; int po_count, p_count; do { po_count = _points.size(); p_count = tracer()->points().size(); int count = qMin(po_count, p_count); for(int p=0; p < count; ++p) { QPointF point = vectorToPoint(tracer()->points()[p]); if(point != _points[p]) { geometryChange = true; _points[p] = point; } } } while(0); if(po_count < p_count) { geometryChange = true; for(; po_count < p_count; ++po_count) _points << vectorToPoint(tracer()->points()[po_count]); } else { geometryChange = true; _points.resize(p_count); } QPointF point = vectorToPoint(tracer()->position()); if(point != _lastPos) { geometryChange = true; _lastPos = point; } if(geometryChange) { viewScaleChanged(); } } void TracerGraphicsItem::mouseSetPos(const QPointF&, const QPointF& diff, MovingState movingState) { static_cast(scene())->snapItem(vectorToPoint(tracer()->position()) + diff, WorldScene::SnapRigidBody | WorldScene::SnapParticle | WorldScene::SnapSetLocalPosition, 0, movingState, _item); } void TracerMenuHandler::populateMenu(QMenu* menu, KActionCollection* actions) { menu->addAction(QIcon::fromTheme("edit-clear"), i18n("Clear trace"), this, SLOT(clearTracer())); menu->addSeparator(); ItemMenuHandler::populateMenu(menu, actions); } void TracerMenuHandler::clearTracer() { _worldModel->simulationPause(); //_lastPointTime = -HUGE_VAL; // XX _worldModel->beginMacro(i18n("Clear tracer %1", _object->name())); _worldModel->setProperty(_object, "points", QVariant::fromValue(StepCore::Vector2dList()) ); _worldModel->endMacro(); } diff --git a/step/worldscene.cc b/step/worldscene.cc index d2cc94d..bc69a9e 100644 --- a/step/worldscene.cc +++ b/step/worldscene.cc @@ -1,736 +1,736 @@ /* This file is part of Step. Copyright (C) 2007 Vladimir Kuznetsov Step is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Step is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Step; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ +#include "worldscene.h" + #ifdef _WIN32 #include #endif -#include "worldscene.h" - #include "settings.h" #include "arrow.h" #include "worldmodel.h" #include "worldfactory.h" #include "stepgraphicsitem.h" #include "worldgraphics.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include class WorldSceneAxes: public QGraphicsItem { public: WorldSceneAxes(QGraphicsItem* parent = 0, QGraphicsScene* scene = 0); QRectF boundingRect() const Q_DECL_OVERRIDE; QPainterPath shape() const Q_DECL_OVERRIDE; void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) Q_DECL_OVERRIDE; void viewScaleChanged(); protected: QRectF _boundingRect; double _viewScale; static const int LENGTH = 100; static const int LENGTHT = 100; static const int LENGTH1 = 10; static const int ARROW_STROKE = 6; }; WorldSceneAxes::WorldSceneAxes(QGraphicsItem* parent, QGraphicsScene* scene) : QGraphicsItem(parent), _boundingRect(-LENGTH, -LENGTH, LENGTH+LENGTH, LENGTH+LENGTH) { _viewScale = 1; setZValue(-100); if (scene) scene->addItem(this); } QRectF WorldSceneAxes::boundingRect() const { return _boundingRect; } QPainterPath WorldSceneAxes::shape() const { return QPainterPath(); } void WorldSceneAxes::paint(QPainter* painter, const QStyleOptionGraphicsItem* /*option*/, QWidget* /*widget*/) { QPen pen(Qt::gray, 2); pen.setCosmetic(true); painter->setPen(pen);//, Qt::DotLine, Qt::SquareCap, Qt::RoundJoin)); //painter->drawLine(QLineF(0, -LENGTH, 0, LENGTH)); //painter->drawLine(QLineF(-LENGTH, 0, LENGTH, 0)); Arrow arrow(0, LENGTH, 0, -LENGTH, 8); arrow.draw(painter); Arrow arrow2(-LENGTH, 0, LENGTH, 0, 8); arrow2.draw(painter); //painter->drawLine(QLineF(0, -LENGTH, 0, LENGTH)); //painter->drawLine(QLineF(-LENGTH, 0, LENGTH, 0)); //painter->drawLine(QLineF(-2, -LENGTHT, 2, -LENGTHT)); //painter->drawLine(QLineF(LENGTHT, -2, LENGTHT, 2)); //painter->drawLine(QLineF(0, -LENGTH, -0.5*ARROW_STROKE, -LENGTH+0.866*ARROW_STROKE )); //painter->drawLine(QLineF(0, -LENGTH, +0.5*ARROW_STROKE, -LENGTH+0.866*ARROW_STROKE )); //painter->drawLine(QLineF(LENGTH, 0, LENGTH-0.866*ARROW_STROKE, -0.5*ARROW_STROKE )); //painter->drawLine(QLineF(LENGTH, 0, LENGTH-0.866*ARROW_STROKE, +0.5*ARROW_STROKE )); painter->drawText(QRectF(-LENGTH - 6, 6, LENGTH, LENGTH), Qt::AlignRight | Qt::AlignTop, QString("%1,%2").arg( pos().x(), 0, 'g', 3 ).arg( pos().y(), 0, 'g', 3 )); painter->drawText(QRectF(6, -LENGTH, LENGTH - 6, LENGTH - 6), Qt::AlignLeft | Qt::AlignTop, QString::number( pos().y() + LENGTH/_viewScale, 'g', 3 )); painter->drawText(QRectF(6, -LENGTH, LENGTH - 6, LENGTH - 6), Qt::AlignRight | Qt::AlignBottom, QString::number( pos().x() + LENGTH/_viewScale, 'g', 3 )); //painter->drawText(QRectF(ARROW_STROKE, -LENGTHT-50, LENGTHT, 100), Qt::AlignLeft | Qt::AlignVCenter, // QString::number( pos().y() + LENGTHT/_viewScale, 'g', 3 )); //painter->drawText(QRectF(LENGTHT-50, -LENGTHT, 100, LENGTHT), Qt::AlignHCenter | Qt::AlignBottom, // QString::number( pos().x() + LENGTHT/_viewScale, 'g', 3 )); } void WorldSceneAxes::viewScaleChanged() { prepareGeometryChange(); _viewScale = static_cast(scene())->currentViewScale(); resetMatrix(); scale(1/_viewScale, -1/_viewScale); } WorldScene::WorldScene(WorldModel* worldModel, QObject* parent) : QGraphicsScene(parent), _worldModel(worldModel), _worldView(0), _currentViewScale(1), _itemCreator(0), _bgColor(0), _sceneAxes(0), _snapItem(0) { #ifdef __GNUC__ #warning TODO: measure what index method is faster #endif setItemIndexMethod(NoIndex); //XXX //setct(-200,-200,400,400); setSceneRect(-20, -20, 40, 40); _messageFrame = new MessageFrame(); _snapTimer = new QTimer(this); _snapTimer->setInterval(0); _snapTimer->setSingleShot(true); worldModelReset(); connect(_worldModel, SIGNAL(modelReset()), this, SLOT(worldModelReset())); connect(_worldModel, SIGNAL(worldDataChanged(bool)), this, SLOT(worldDataChanged(bool))); - connect(_worldModel->selectionModel(), SIGNAL(currentChanged(const QModelIndex&, const QModelIndex&)), - this, SLOT(worldCurrentChanged(const QModelIndex&, const QModelIndex&))); - connect(_worldModel->selectionModel(), SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)), - this, SLOT(worldSelectionChanged(const QItemSelection&, const QItemSelection&))); - connect(_worldModel, SIGNAL(rowsInserted(const QModelIndex&, int, int)), - this, SLOT(worldRowsInserted(const QModelIndex&, int, int))); - connect(_worldModel, SIGNAL(rowsAboutToBeRemoved(const QModelIndex&, int, int)), - this, SLOT(worldRowsAboutToBeRemoved(const QModelIndex&, int, int))); - - connect(_messageFrame, SIGNAL(linkActivated(const QString&)), - this, SLOT(messageLinkActivated(const QString&))); + connect(_worldModel->selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)), + this, SLOT(worldCurrentChanged(QModelIndex,QModelIndex))); + connect(_worldModel->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), + this, SLOT(worldSelectionChanged(QItemSelection,QItemSelection))); + connect(_worldModel, SIGNAL(rowsInserted(QModelIndex,int,int)), + this, SLOT(worldRowsInserted(QModelIndex,int,int))); + connect(_worldModel, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)), + this, SLOT(worldRowsAboutToBeRemoved(QModelIndex,int,int))); + + connect(_messageFrame, SIGNAL(linkActivated(QString)), + this, SLOT(messageLinkActivated(QString))); connect(_snapTimer, SIGNAL(timeout()), this, SLOT(snapUpdateToolTip())); } WorldScene::~WorldScene() { } StepCore::Item* WorldScene::itemFromGraphics(const QGraphicsItem* graphicsItem) const { const StepGraphicsItem* worldGraphicsItem = dynamic_cast(graphicsItem); if(worldGraphicsItem != NULL) return worldGraphicsItem->item(); else return NULL; } StepGraphicsItem* WorldScene::graphicsFromItem(const StepCore::Item* item) const { return _itemsHash.value(item, NULL); } void WorldScene::beginAddItem(const QString& name) { //_currentCreator = name; if(_itemCreator) { _itemCreator->abort(); emit endAddItem(_itemCreator->className(), _itemCreator->item() != NULL); delete _itemCreator; } if(name == "Pointer") { _itemCreator = NULL; } else { _itemCreator = _worldModel->worldFactory()->newItemCreator(name, _worldModel, this); Q_ASSERT(_itemCreator != NULL); _itemCreator->start(); } } bool WorldScene::event(QEvent* event) { //qDebug("event, _currentCreator = %s", _currentCreator.toAscii().constData()); /*if(!_currentCreator.isEmpty()) { if(_worldModel->worldFactory()->graphicsCreateItem(_currentCreator, _worldModel, this, event)) { emit endAddItem(_currentCreator, true); _currentCreator.clear(); } if(event->isAccepted()) return true; }*/ if(_itemCreator) { bool handled = _itemCreator->sceneEvent(event); if(_itemCreator->finished()) { emit endAddItem(_itemCreator->className(), _itemCreator->item() != NULL); // ~ItemCreator() will indirectly call this method, thus we must set // the pointer to NULL before deleting the ItemCreator. ItemCreator* itemCreator = _itemCreator; _itemCreator = NULL; delete itemCreator; } if(handled) { event->accept(); return true; } } return QGraphicsScene::event(event); } void WorldScene::mousePressEvent(QGraphicsSceneMouseEvent* mouseEvent) { if(itemAt(mouseEvent->scenePos()) == NULL) { // XXX: how to easily select World ? //_worldModel->selectionModel()->clearSelection(); _worldModel->selectionModel()->setCurrentIndex(_worldModel->worldIndex(), QItemSelectionModel::Clear); } QGraphicsScene::mousePressEvent(mouseEvent); } void WorldScene::helpEvent(QGraphicsSceneHelpEvent *helpEvent) { helpEvent->accept(); if(_snapItem || _snapTimer->isActive()) return; QString text; QList activeItems; foreach(QGraphicsItem* it, items(helpEvent->scenePos())) { if(it->parentItem()) continue; StepCore::Item* item = itemFromGraphics(it); if(item) activeItems << item; } int count = activeItems.count(); if(count > 1) { //XXX text = QString("

    %1

    ").arg(i18n("Objects under mouse:")); for(int i=0; i(count,10); ++i) text += QString("
    %1") .arg(_worldModel->objectIndex(activeItems[i]).data(Qt::DisplayRole).toString()); if(count > 10) text += QString("
    %1").arg(i18np("... (1 more item)", "... (%1 more items)", count - 10)); } else { for(int i=0; iobjectIndex(activeItems[i]).data(Qt::ToolTipRole).toString(); } Q_ASSERT(helpEvent->widget()); QToolTip::showText(helpEvent->screenPos(), text, helpEvent->widget(), QRect()); } void WorldScene::worldModelReset() { /* Clear */ while(!items().isEmpty()) { QGraphicsItem* item = items()[0]; removeItem(item); delete item; } _itemsHash.clear(); _sceneAxes = 0; /* Background */ if(_bgColor != _worldModel->world()->color()) { _bgColor = _worldModel->world()->color(); if(_bgColor == 0) setBackgroundBrush(Qt::NoBrush); else setBackgroundBrush(QBrush(QColor::fromRgba(_bgColor))); } /* Axes */ if(Settings::showAxes()) { _sceneAxes = new WorldSceneAxes(); addItem(_sceneAxes); _sceneAxes->viewScaleChanged(); } /* Check for new items */ worldGetItemsRecursive(_worldModel->worldIndex()); update(); } void WorldScene::worldGetItemsRecursive(const QModelIndex& parent) { for(int i=0; i<_worldModel->rowCount(parent); ++i) { worldRowsInserted(parent, i, i); worldGetItemsRecursive(_worldModel->index(i, 0, parent)); } } void WorldScene::worldRowsInserted(const QModelIndex& parent, int start, int end) { for(int i=start; i<=end; ++i) { QModelIndex index = _worldModel->index(i, 0, parent); StepCore::Item* item = _worldModel->item(index); if(!item) continue; StepGraphicsItem* graphicsItem = _worldModel->worldFactory()->newGraphicsItem(item, _worldModel); if(!graphicsItem) continue; _itemsHash.insert(item, graphicsItem); addItem(graphicsItem); graphicsItem->viewScaleChanged(); graphicsItem->worldDataChanged(false); foreach(QGraphicsItem *item, items()) { if(graphicsItem->isAncestorOf(item)) { StepGraphicsItem* gItem = dynamic_cast(item); if(gItem) gItem->viewScaleChanged(); } } worldGetItemsRecursive(index); } } void WorldScene::worldRowsAboutToBeRemoved(const QModelIndex& parent, int start, int end) { for(int i=start; i<=end; ++i) { QModelIndex index = _worldModel->index(i, 0, parent); int childCount = _worldModel->rowCount(index); if(childCount > 0) worldRowsAboutToBeRemoved(index, 0, childCount-1); QGraphicsItem* graphicsItem = graphicsFromItem(_worldModel->item(index)); if(graphicsItem) { removeItem(graphicsItem); delete graphicsItem; } } } void WorldScene::worldCurrentChanged(const QModelIndex& current, const QModelIndex& /*previous*/) { if(!_worldView || _worldView->viewport()->hasFocus()) return; QGraphicsItem* graphicsItem = graphicsFromItem(_worldModel->item(current)); if(graphicsItem) graphicsItem->ensureVisible(QRectF(), 5, 5); } void WorldScene::worldSelectionChanged(const QItemSelection& selected, const QItemSelection& deselected) { foreach(const QModelIndex &index, selected.indexes()) { QGraphicsItem* item = _itemsHash.value(_worldModel->item(index)); if(item) item->setSelected(true); } foreach(const QModelIndex &index, deselected.indexes()) { QGraphicsItem* item = _itemsHash.value(_worldModel->item(index)); if(item) item->setSelected(false); } } void WorldScene::worldDataChanged(bool dynamicOnly) { //if(dynamicOnly) return; _worldModel->simulationPause(); if(!dynamicOnly) { /* Background */ if(_bgColor != _worldModel->world()->color()) { _bgColor = _worldModel->world()->color(); if(_bgColor == 0) setBackgroundBrush(Qt::NoBrush); else setBackgroundBrush(QBrush(QColor::fromRgba(_bgColor))); } } foreach (QGraphicsItem *item, items()) { StepGraphicsItem* gItem = dynamic_cast(item); if(gItem) { gItem->worldDataChanged(dynamicOnly); } } QRectF boundingRect = itemsBoundingRect(); setSceneRect(boundingRect.united(QRectF(-20, -20, 40, 40))); } void WorldScene::updateViewScale() { if(_worldView) { _currentViewScale = _worldView->matrix().m11(); _worldModel->simulationPause(); foreach (QGraphicsItem *item, items()) { StepGraphicsItem* gItem = dynamic_cast(item); if(gItem) gItem->viewScaleChanged(); } if(_sceneAxes) _sceneAxes->viewScaleChanged(); } } QRectF WorldScene::calcItemsBoundingRect() { QRectF boundingRect; foreach(QGraphicsItem* item, items()) { StepGraphicsItem* wItem = dynamic_cast(item); if(wItem) { boundingRect |= wItem->sceneBoundingRect(); //qDebug() << itemFromGraphics(wItem)->name() << ": " << wItem->sceneBoundingRect() << endl; } } return boundingRect; } void WorldScene::messageLinkActivated(const QString& link) { emit linkActivated(QUrl::fromUserInput(link)); } void WorldScene::settingsChanged() { worldModelReset(); _messageFrame->raise(); } void WorldScene::snapClear() { if(_snapItem) { _snapItem->setItemHighlighted(false); _snapItem = 0; _snapToolTip.clear(); _snapTimer->start(); } } StepCore::Item* WorldScene::snapHighlight(QPointF pos, SnapFlags flags, const SnapList* moreTypes) { SnapList types; if(flags.testFlag(SnapParticle)) types << StepCore::Particle::staticMetaObject(); if(flags.testFlag(SnapRigidBody)) types << StepCore::RigidBody::staticMetaObject(); if(moreTypes) types << *moreTypes; StepCore::Item* item = 0; QGraphicsItem* gItem = 0; foreach(gItem, items(pos)) { item = itemFromGraphics(gItem); if(!item) continue; if(flags.testFlag(SnapParticle) && item->metaObject()->inherits()) break; if(flags.testFlag(SnapRigidBody) && item->metaObject()->inherits()) break; if(moreTypes) { bool found = false; foreach(const StepCore::MetaObject* type, *moreTypes) if(item->metaObject()->inherits(type)) { found = true; break; } if(found) break; } item = NULL; } if(item) { if(_snapItem != gItem) { snapClear(); _snapItem = static_cast(gItem); _snapItem->setItemHighlighted(true); } _snapPos = pos; _snapToolTip = _worldModel->formatNameFull(item); _snapTimer->start(); return item; } else { snapClear(); return 0; } } StepCore::Item* WorldScene::snapItem(QPointF pos, SnapFlags flags, const SnapList* moreTypes, int movingState, StepCore::Item* item, int num) { QString n; if(num >= 0) n = QString::number(num); _worldModel->simulationPause(); StepCore::Item* sItem = snapHighlight(pos, flags, moreTypes); if(movingState == StepGraphicsItem::Started || movingState == StepGraphicsItem::Moving) { if(movingState == StepGraphicsItem::Started) _worldModel->setProperty(item, "body"+n, QVariant::fromValue(NULL), WorldModel::UndoNoMerge); if(flags.testFlag(SnapSetPosition)) _worldModel->setProperty(item, "position"+n, QVariant::fromValue(StepGraphicsItem::pointToVector(pos))); if(flags.testFlag(SnapSetLocalPosition)) _worldModel->setProperty(item, "localPosition"+n, QVariant::fromValue(StepGraphicsItem::pointToVector(pos))); if(flags.testFlag(SnapSetAngle) && movingState == StepGraphicsItem::Started) _worldModel->setProperty(item, "angle"+n, QVariant::fromValue(0.0)); } else if(movingState == StepGraphicsItem::Finished) { StepCore::Vector2d wPos(StepGraphicsItem::pointToVector(pos)); StepCore::Vector2d lPos(0,0); double angle = 0.0; if(sItem) { if(sItem->metaObject()->inherits()) { wPos = static_cast(sItem)->position(); } else if(sItem->metaObject()->inherits()) { if(flags.testFlag(SnapOnCenter)) wPos = static_cast(sItem)->position(); else lPos = static_cast(sItem)->pointWorldToLocal(wPos); angle = static_cast(sItem)->angle(); } } else { lPos = wPos; } _worldModel->setProperty(item, "body"+n, QVariant::fromValue(sItem), WorldModel::UndoNoMerge); if(flags.testFlag(SnapSetPosition)) _worldModel->setProperty(item, "position"+n, QVariant::fromValue(wPos)); if(flags.testFlag(SnapSetLocalPosition)) _worldModel->setProperty(item, "localPosition"+n, QVariant::fromValue(lPos)); if(flags.testFlag(SnapSetAngle)) _worldModel->setProperty(item, "angle"+n, angle); snapClear(); } return sItem; } void WorldScene::snapUpdateToolTip() { if(!_snapToolTip.isEmpty()) { QGraphicsView* view = views()[0]; QPoint pos = view->viewport()->mapToGlobal(view->mapFromScene(_snapPos)); QPoint size(1,1); QToolTip::showText(pos, _snapToolTip, view->viewport(), QRect(pos-size,pos+size)); } else { QToolTip::hideText(); // Hack to hide tooltip immediately QWheelEvent fakeEvent(QPoint(0,0), 0, Qt::NoButton, Qt::NoModifier); QCoreApplication::sendEvent(_messageFrame, &fakeEvent); } } bool WorldScene::hasItemCreator() const { return _itemCreator && !_itemCreator->finished(); } WorldGraphicsView::WorldGraphicsView(WorldScene* worldScene, QWidget* parent) : QGraphicsView(worldScene, parent) { worldScene->_worldView = this; worldScene->_messageFrame->setParent(this); worldScene->_messageFrame->move(15,15); _sceneRect = worldScene->sceneRect(); updateSceneRect(); - connect(worldScene, SIGNAL(sceneRectChanged(const QRectF&)), - this, SLOT(sceneRectChanged(const QRectF&))); + connect(worldScene, SIGNAL(sceneRectChanged(QRectF)), + this, SLOT(sceneRectChanged(QRectF))); //worldGraphicsView->setRenderHints(QPainter::Antialiasing); setDragMode(QGraphicsView::RubberBandDrag); //setDragMode(QGraphicsView::ScrollHandDrag); setResizeAnchor(QGraphicsView::AnchorViewCenter); setOptimizationFlags(QGraphicsView::DontClipPainter/* | QGraphicsView::DontSavePainterState*/); #ifdef __GNUC__ #warning Check paint() for all items to preserve painter state #warning Use NoViewportUpdate and manual updating here ! #endif setViewportUpdateMode(QGraphicsView::SmartViewportUpdate); actualSize(); settingsChanged(); } void WorldGraphicsView::zoomIn() { scale(1.25, 1.25); updateSceneRect(); static_cast(scene())->updateViewScale(); } void WorldGraphicsView::zoomOut() { scale(1 / 1.25, 1 / 1.25); updateSceneRect(); static_cast(scene())->updateViewScale(); } void WorldGraphicsView::fitToPage() { QRectF br = static_cast(scene())->calcItemsBoundingRect(); if(br.isNull()) return; //qDebug() << br << " " << (br | QRectF(0,0,0,0)) << endl; QRect ws = viewport()->rect(); double currentViewScale = matrix().m11(); double s = qMin( ws.width()/br.width(), ws.height()/br.height() ); // XXX: use QSize::scale ! if(s < currentViewScale || s*0.8 > currentViewScale) { s *= 0.9; resetMatrix(); scale(s, -s); updateSceneRect(); } else { s = currentViewScale; } centerOn(br.center()); if(s != currentViewScale) static_cast(scene())->updateViewScale(); } void WorldGraphicsView::actualSize() { resetMatrix(); scale(100, -100); updateSceneRect(); centerOn(0, 0); static_cast(scene())->updateViewScale(); } void WorldGraphicsView::settingsChanged() { if(qobject_cast(viewport())) { if(!Settings::enableOpenGL()) setViewport(new QWidget(this)); } else { if(Settings::enableOpenGL() && QGLFormat::hasOpenGL()) { //qDebug() << "enable OpenGL" << endl; setViewport(new QGLWidget(QGLFormat(QGL::SampleBuffers), this)); if(!qobject_cast(viewport())) { qDebug() << "can't create QGLWidget!" << endl; } } } if(scene()) static_cast(scene())->settingsChanged(); } void WorldGraphicsView::mousePressEvent(QMouseEvent* e) { if(e->button() == Qt::MidButton) { setDragMode(QGraphicsView::ScrollHandDrag); QMouseEvent e1(e->type(), e->pos(), e->globalPos(), Qt::LeftButton, e->buttons(), e->modifiers()); QGraphicsView::mousePressEvent(&e1); } else { QGraphicsView::mousePressEvent(e); } } void WorldGraphicsView::mouseReleaseEvent(QMouseEvent* e) { QGraphicsView::mouseReleaseEvent(e); if(e->button() == Qt::MidButton) { setDragMode(QGraphicsView::RubberBandDrag); } } void WorldGraphicsView::wheelEvent(QWheelEvent* e) { if (e->modifiers() == Qt::ControlModifier) { if (e->orientation() != Qt::Vertical) { e->ignore(); return; } QGraphicsView::ViewportAnchor anchor = transformationAnchor(); setTransformationAnchor(QGraphicsView::AnchorUnderMouse); if (e->delta() > 0) { zoomIn(); } else { zoomOut(); } setTransformationAnchor(anchor); e->accept(); return; } QGraphicsView::wheelEvent(e); } void WorldGraphicsView::scrollContentsBy(int dx, int dy) { QGraphicsView::scrollContentsBy(dx, dy); WorldSceneAxes* axes = static_cast(scene())->_sceneAxes; if(axes) axes->setPos(mapToScene(viewport()->width()/2, viewport()->height()/2)); //axes->setPos(mapToScene(20, maximumViewportSize().height()-horizontalScrollBar()->height()-23)); } void WorldGraphicsView::sceneRectChanged(const QRectF& rect) { _sceneRect = rect; updateSceneRect(); } void WorldGraphicsView::updateSceneRect() { QPointF topleft = mapToScene(0, 0); QPointF bottomright = mapToScene(viewport()->width(), viewport()->height()); QRectF viewportRect(topleft, bottomright); QRectF sceneRect = _sceneRect.adjusted(-viewportRect.width() / 4, viewportRect.height() / 4, viewportRect.width() / 4, -viewportRect.height() / 4); setSceneRect(sceneRect.united(viewportRect)); }