/*************************************************************************** * Copyright (C) 2009 by Martin Moracek * * xmoracek@fi.muni.cz * * * * DiVinE is free software, distributed under GNU GPL and BSD licences. * * Detailed licence texts may be found in the COPYING file in the * * distribution tarball. The tool is a product of the ParaDiSe * * Laboratory, Faculty of Informatics of Masaryk University. * * * * This distribution includes freely redistributable third-party code. * * Please refer to AUTHORS and COPYING included in the distribution for * * copyright and licensing details. * ***************************************************************************/ #include #include #include #include "traceDock.h" #include "cycleBar.h" #include "simulationProxy.h" #include "abstractSimulator.h" #include "settings.h" #include "prettyPrint.h" #include "mainForm.h" #include "signalLock.h" // defaults #include "baseToolsModule.h" namespace divine { namespace gui { // // TraceWidget // TraceWidget::TraceWidget(QWidget * parent) : QListWidget(parent) { cbar_ = new CycleBar(this, this); lbar_ = new LineNumberBar(this, this); connect(this, SIGNAL(lineCountChanged()), lbar_, SLOT(lineCountChanged())); connect(lbar_, SIGNAL(widthChanged(int)), cbar_, SLOT(setMargin(int))); connect(lbar_, SIGNAL(widthChanged(int)), this, SLOT(updateMargins(int))); } const QPair & TraceWidget::acceptingCycle() const { return cbar_->cycle(); } int TraceWidget::lineCount() { return count(); } int TraceWidget::firstVisibleLine(const QRect & rect) { QListWidgetItem * item = itemAt(0, 0); return item ? row(item) : 0; } QList TraceWidget::visibleLines(const QRect & rect) { QList lines; for(int i = firstVisibleLine(rect); i < count(); ++i) { QRect rct = visualItemRect(item(i)); if(rct.top() >= rect.bottom()) return lines; lines.append(rct.top()); } return lines; } //! Set the displayed accepting cycle. void TraceWidget::setAcceptingCycle(const QPair & cycle) { cbar_->setCycle(cycle); } void TraceWidget::updateMargins(int width) { if (isLeftToRight()) setViewportMargins(width + cbar_->width(), 0, 0, 0); else setViewportMargins(0, 0, width + cbar_->width(), 0); } //! Reimplements QListView::resizeEvent. void TraceWidget::resizeEvent(QResizeEvent * event) { QListWidget::resizeEvent(event); lbar_->updateGeometry(); cbar_->updateGeometry(); } //! Reimplements QAbstractScrollArea::scrollContentsBy. void TraceWidget::scrollContentsBy(int dx, int dy) { QListWidget::scrollContentsBy(dx, dy); lbar_->update(); cbar_->update(); } // // TraceDock // TraceDock::TraceDock(MainForm * root) : QDockWidget(tr("Stack trace"), root), sim_(root->simulation()), state_(-1) { trace_ = new TraceWidget(this); setWidget(trace_); readSettings(); connect(trace_, SIGNAL(itemActivated(QListWidgetItem*)), SLOT(onItemActivated(QListWidgetItem*))); connect(root, SIGNAL(settingsChanged()), SLOT(readSettings())); connect(sim_, SIGNAL(reset()), SLOT(reset())); connect(sim_, SIGNAL(currentIndexChanged(int)), SLOT(setCurrentState(int))); connect(sim_, SIGNAL(stateChanged(int)), SLOT(updateState(int))); connect(sim_, SIGNAL(acceptingCycleChanged(QPair)), trace_, SLOT(setAcceptingCycle(QPair))); connect(sim_, SIGNAL(stateAdded(int,int)), SLOT(addStates(int,int))); connect(sim_, SIGNAL(stateRemoved(int,int)), SLOT(removeStates(int,int))); connect(trace_, SIGNAL(itemActivated(QListWidgetItem*)), SLOT(onItemActivated(QListWidgetItem*))); // side bars connect(sim_, SIGNAL(stateAdded(int,int)), trace_, SIGNAL(lineCountChanged())); connect(sim_, SIGNAL(stateRemoved(int,int)), trace_, SIGNAL(lineCountChanged())); connect(sim_, SIGNAL(reset()), trace_, SIGNAL(lineCountChanged())); } //! Reload settings. void TraceDock::readSettings() { Settings s("common.trace"); QColor colorD = QColor(s.value("deadlock", defTraceDeadlock).value()); QColor colorE = QColor(s.value("error", defTraceError).value()); bool changed = colorD != deadlock_ || colorE != error_; deadlock_ = colorD; error_ = colorE; // update if(changed) { QPalette def; for(int i=0; i < trace_->count(); ++i) { QListWidgetItem * item = trace_->item(i); if (!sim_->isValid(i)) { item->setForeground(error_); } else if (sim_->transitionCount(i) == 0) { item->setForeground(deadlock_); } else { item->setForeground(def.color(QPalette::Text)); } } } } void TraceDock::reset() { SignalLock lock(this); trace_->clear(); for(int i=0; i < sim_->stateCount(); ++i) { QListWidgetItem * item = new QListWidgetItem(trace_); updateState(i); } state_ = -1; setCurrentState(sim_->currentIndex()); trace_->setAcceptingCycle(sim_->acceptingCycle()); } void TraceDock::setCurrentState(int index) { // nothing to do if(index == state_) return; // update previous QListWidgetItem * item = trace_->item(state_); if(item) { QFont fnt = item->font(); fnt.setWeight(QFont::Normal); item->setFont(fnt); } // update new current item = trace_->item(index); if(item) { QFont fnt = item->font(); fnt.setWeight(QFont::Bold); item->setFont(fnt); } state_ = index; trace_->setCurrentRow(index); trace_->scrollToItem(trace_->item(index)); } void TraceDock::updateState(int index) { QListWidgetItem * item = trace_->item(index); if(!item) return; item->setText(printChildren(sim_->topLevelSymbols(index), index)); // set font QFont fnt = item->font(); fnt.setItalic(sim_->isAccepting(index)); item->setFont(fnt); // and background colour QPalette def; if (!sim_->isValid(index)) { item->setForeground(error_); } else if (sim_->transitionCount(index) == 0) { item->setForeground(deadlock_); } else { item->setForeground(def.color(QPalette::Text)); } } void TraceDock::addStates(int from, int to) { // sanity check from = qMax(0, from); to = qMin(to, sim_->stateCount()); // overlap for(; from <= qMin(trace_->count() - 1, to); ++from) { updateState(from); } // new states for(; from <= to; ++from) { QListWidgetItem * item = new QListWidgetItem(trace_); updateState(from); } } void TraceDock::removeStates(int from, int to) { // sanity check from = qMax(0, from); to = qMin(to, trace_->count()); int count = to - from + 1; for(int i=0; i < count; ++i) { delete trace_->takeItem(from); } } QString TraceDock::printSymbol(const Symbol * symbol, int state) { const SymbolType * type = sim_->typeInfo(symbol->type); if(!type) return QString(); QString val1, val2, value; if(dynamic_cast(type) != NULL) { val1 = printPODItem(sim_->symbolValue(symbol->id, state), dynamic_cast(type)); } else if(dynamic_cast(type) != NULL) { val1 = printOptionItem(sim_->symbolValue(symbol->id, state), dynamic_cast(type)); } else if(dynamic_cast(type) != NULL) { val1 = printArrayItem(sim_->symbolValue(symbol->id, state), dynamic_cast(type), state); } val2 = printChildren(symbol->children, state); if(!val1.isEmpty() && val2 != "{}") { value = QString("{%1, %2}").arg(val1, val2); } else { value = !val1.isEmpty() ? val1 : val2; } return QString("%1: %2").arg(symbol->name, value); } QString TraceDock::printPODItem(const QVariant & value, const PODType * type) { return value.toString(); } QString TraceDock::printOptionItem(const QVariant & value, const OptionType * type) { int index = value.toInt(); if(index >= 0 && index < type->options.size()) return type->options[index]; return QString(); } QString TraceDock::printArrayItem(const QVariant & value, const ArrayType * type, int state) { QStringList caps; QVariantList array = value.value(); // incompatible arrays might lead to wrong indices and crashes if(array.size() != type->size) return QString(); const SymbolType * subtype = sim_->typeInfo(type->subtype); // first check existing fields for(int i=0; i < type->size; ++i) { if(dynamic_cast(subtype) != NULL) { caps << printPODItem(array[i], dynamic_cast(subtype)); } else if(dynamic_cast(subtype) != NULL) { caps << printOptionItem(array[i], dynamic_cast(subtype)); } else if(dynamic_cast(subtype) != NULL) { caps << printArrayItem(array[i], dynamic_cast(subtype), state); } else if(dynamic_cast(subtype) != NULL) { caps << printChildren(array[i].toStringList(), state); } else { caps << ""; } } return QString("{%1}").arg(caps.join(",")); } QString TraceDock::printChildren(const QStringList & children, int state) { QStringList caps; foreach(QString sym, children) { const Symbol * symbol = sim_->symbol(sym, state); if(!symbol) continue; QString cap = printSymbol(symbol, state); if(!cap.isEmpty()) caps << cap; } return QString("{%1}").arg(caps.join(", ")); } void TraceDock::onItemActivated(QListWidgetItem * item) { sim_->setCurrentIndex(trace_->row(item)); } } }