diff options
author | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2010-02-24 01:49:02 +0000 |
---|---|---|
committer | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2010-02-24 01:49:02 +0000 |
commit | 5de3dd4762ca33a0f92e79ffa4fe2ff67069d531 (patch) | |
tree | bad482b7afa4cdf47422d60a5dd2c61c7e333b09 /src | |
download | ktechlab-5de3dd4762ca33a0f92e79ffa4fe2ff67069d531.tar.gz ktechlab-5de3dd4762ca33a0f92e79ffa4fe2ff67069d531.zip |
Added KDE3 version of ktechlab
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/applications/ktechlab@1095338 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'src')
456 files changed, 82759 insertions, 0 deletions
diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 0000000..a41144e --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,88 @@ +## Makefile.am for ktechlab + +# this is the program that gets installed. it's name is used for all +# of the other Makefile.am variables +bin_PROGRAMS = ktechlab + +# set the include path for X, qt and KDE +INCLUDES = -I$(top_srcdir)/src -I$(top_srcdir)/src/drawparts \ + -I$(top_srcdir)/src/electronics -I$(top_srcdir)/src/electronics/components \ + -I$(top_srcdir)/src/electronics/simulation -I$(top_srcdir)/src/flowparts -I$(top_srcdir)/src/gui \ + -I$(top_srcdir)/src/languages -I$(top_srcdir)/src/mechanics -I$(top_srcdir)/src/micro $(all_includes) + +# the library search path. +ktechlab_LDFLAGS = -module $(all_libraries) $(KDE_RPATH) + +# the libraries to link against. +ktechlab_LDADD = $(top_builddir)/src/gui/libgui.la \ + $(top_builddir)/src/micro/libmicro.la $(top_builddir)/src/flowparts/libflowparts.la \ + $(top_builddir)/src/mechanics/libmechanics.la $(top_builddir)/src/electronics/libelectronics.la \ + $(top_builddir)/src/electronics/simulation/libelements.la $(top_builddir)/src/electronics/components/libcomponents.la \ + $(top_builddir)/src/languages/liblanguages.la $(top_builddir)/src/drawparts/libdrawparts.la \ + $(top_builddir)/src/core/libcore.la -lkutils $(LIB_GPSIM) -lktexteditor $(LIB_KFILE) $(LIB_KDEPRINT) + +# which sources should be compiled for ktechlab +ktechlab_SOURCES = ktechlab.cpp node.cpp connector.cpp itemlibrary.cpp \ + libraryitem.cpp projectmanager.cpp picitem.cpp variant.cpp canvasitemparts.cpp \ + flowcontainer.cpp microsettings.cpp fpnode.cpp cells.cpp asmformatter.cpp conrouter.cpp \ + nodegroup.cpp canvasmanipulator.cpp iteminterface.cpp itemgroup.cpp ciwidgetmgr.cpp \ + filemetainfo.cpp resizeoverlay.cpp document.cpp view.cpp docmanager.cpp cnitem.cpp \ + item.cpp cnitemgroup.cpp itemview.cpp itemdocument.cpp textview.cpp \ + textdocument.cpp circuitdocument.cpp flowcodedocument.cpp icnview.cpp icndocument.cpp \ + viewcontainer.cpp circuitview.cpp flowcodeview.cpp eventinfo.cpp oscilloscopedata.cpp \ + itemdocumentdata.cpp docmanageriface.cpp documentiface.cpp viewiface.cpp \ + docmanageriface.skel viewiface.skel documentiface.skel simulator.cpp katemdi.cpp \ + debugmanager.cpp recentfilesaction.cpp variablelabel.cpp + +ktechlab_PCH = AUTO + + +# these are the headers for your project +noinst_HEADERS = ktechlab.h node.h connector.h itemlibrary.h libraryitem.h \ + projectmanager.h picitem.h canvasitemparts.h microsettings.h fpnode.h cells.h \ + asmformatter.h conrouter.h nodegroup.h canvasmanipulator.h iteminterface.h itemgroup.h \ + ciwidgetmgr.h filemetainfo.h resizeoverlay.h document.h view.h docmanager.h cnitem.h \ + item.h cnitemgroup.h itemview.h itemdocument.h textview.h textdocument.h \ + circuitdocument.h flowcodedocument.h icnview.h icndocument.h viewcontainer.h \ + circuitview.h flowcodeview.h eventinfo.h oscilloscopedata.h itemdocumentdata.h \ + docmanageriface.h documentiface.h viewiface.h simulator.h katemdi.h debugmanager.h \ + recentfilesaction.h variablelabel.h + + +# client stuff + + +# let automoc handle all of the meta source files (moc) +METASOURCES = AUTO + + +messages: rc.cpp + $(EXTRACTRC) `find . -name "*.rc" -o -name "*.ui" -o -name "*.kcfg"` > rc.cpp + LIST=`find . -name \*.h -o -name \*.hh -o -name \*.H -o -name \*.hxx -o -name \*.hpp -o -name \*.cpp -o -name \*.cc -o -name \*.cxx -o -name \*.ecpp -o -name \*.C`; \ + if test -n "$$LIST"; then \ + $(XGETTEXT) $$LIST -o $(podir)/ktechlab.pot; \ + fi + +#KDE_ICON = ktechlab +#KDE_ICON = AUTO + +# this is where the kdelnk file will go +kdelnkdir = $(kde_appsdir)/Development +kdelnk_DATA = ktechlab.desktop + +# this is where the XML-GUI resource file goes +rcdir = $(kde_datadir)/ktechlab +rc_DATA = ktechlabui.rc error_messages_en_gb ktechlabcircuitui.rc \ + ktechlabflowcodeui.rc ktechlabitemviewui.rc ktechlabmechanicsui.rc ktechlabtextui.rc ktechlabkateui.rc + +SUBDIRS = core gui flowparts micro mechanics electronics languages drawparts +#iconsdir = $(kde_datadir)/ktechlab/icons + + +mimedir = $(kde_mimedir)/application +mime_DATA = x-circuit.desktop x-flowcode.desktop x-ktechlab.desktop x-microbe.desktop +EXTRA_DIST = $(mime_DATA) + +katesyntaxdir = $(kde_datadir)/katepart/syntax +katesyntax_DATA = microbe.xml + diff --git a/src/asmformatter.cpp b/src/asmformatter.cpp new file mode 100644 index 0000000..fb77789 --- /dev/null +++ b/src/asmformatter.cpp @@ -0,0 +1,209 @@ +/*************************************************************************** + * Copyright (C) 2003-2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "asmformatter.h" +#include "core/ktlconfig.h" +#include "picinfo12bit.h" +#include "picinfo14bit.h" +#include "picinfo16bit.h" + +static QString extractComment( const QString & line ) +{ + int pos = line.find( ';' ); + + if ( pos == -1 ) + return ""; + + return line.right( line.length() - pos ); +} + + +//BEGIN class AsmFormatter +AsmFormatter::AsmFormatter() +{ +} + + +AsmFormatter::~AsmFormatter() +{ +} + + +QString AsmFormatter::tidyAsm( QStringList lines ) +{ + // Update our indentation values from config + m_indentAsmName = KTLConfig::indentAsmName(); + m_indentAsmData = KTLConfig::indentAsmData(); + m_indentEqu = KTLConfig::indentEqu(); + m_indentEquValue = KTLConfig::indentEquValue(); + m_indentComment = m_indentEquComment = KTLConfig::indentComment(); + + QStringList::iterator end = lines.end(); + for ( QStringList::iterator slit = lines.begin(); slit != end; ++slit ) + { + switch ( lineType(*slit) ) + { + case Other: + break; + + case Equ: + *slit = tidyEqu( *slit ); + break; + + case Instruction: + *slit = tidyInstruction( *slit ); + break; + } + } + + QString code; + + for ( QStringList::iterator slit = lines.begin(); slit != end; ++slit ) + code.append( *slit + '\n' ); + + return code; +} + + +void AsmFormatter::pad( QString & text, int length ) +{ + int padLength = length - text.length(); + if ( padLength <= 0 ) + return; + + QString pad; + pad.fill( ' ', padLength ); + text += pad; +} + + +QString AsmFormatter::tidyInstruction( const QString & oldLine ) +{ + InstructionParts parts( oldLine ); + QString line; + + if ( !parts.label().isEmpty() ) + line = parts.label() + ' '; + pad( line, m_indentAsmName ); + + if ( !parts.operand().isEmpty() ) + line += parts.operand() + ' '; + pad( line, m_indentAsmData ); + + if ( !parts.operandData().isEmpty() ) + line += parts.operandData(); + pad( line, m_indentComment ); + + if ( parts.comment().isEmpty() ) + { + // Remove any whitespace at the end if we're not padding out a comment + while ( !line.isEmpty() && line[ line.length() - 1 ].isSpace() ) + line.remove( line.length() -1, 1 ); + } + else + line += parts.comment(); + + return line; +} + + +QString AsmFormatter::tidyEqu( const QString & oldLine ) +{ + QString comment = extractComment( oldLine ); + QString code = oldLine; + code.remove( comment ); + code = code.simplifyWhiteSpace(); + + QStringList parts = QStringList::split( ' ', code ); + + QString pad0, pad1, pad2; + pad0.fill( ' ', m_indentEqu - (*parts.at(0)).length() ); + pad1.fill( ' ', m_indentEquValue - m_indentEqu - (*parts.at(1)).length() ); + pad2.fill( ' ', m_indentEquComment - m_indentEquValue - m_indentEqu - (*parts.at(2)).length() ); + + code = *parts.at(0) + pad0; + code += *parts.at(1) + pad1; + code += *parts.at(2); + if ( !comment.isEmpty() ) + { + code += pad2; + code += comment; + } + + return code; +} + + +AsmFormatter::LineType AsmFormatter::lineType( QString line ) +{ + line = line.simplifyWhiteSpace(); + + line.remove( extractComment( line ) ); + + QStringList parts = QStringList::split( ' ', line ); + QStringList::iterator end = parts.end(); + for ( QStringList::iterator it = parts.begin(); it != end; ++it ) + { + if ( (*it).lower() == "equ" ) + return Equ; + } + + InstructionParts instructionParts( line ); + if ( !instructionParts.operand().isEmpty() ) + return Instruction; + + return Other; +} +//END class AsmFormatter + + + +//BEGIN class InstructionParts +InstructionParts::InstructionParts( QString line ) +{ + m_comment = extractComment( line ); + line.remove( m_comment ); + + line = line.simplifyWhiteSpace(); + QStringList parts = QStringList::split( ' ', line ); + + bool foundOperand = false; + QStringList::iterator end = parts.end(); + for ( QStringList::iterator it = parts.begin(); it != end; ++it ) + { + if ( foundOperand ) + { + // Already found the operand, so anything else must be the operand + // data. + + if ( m_operandData.isEmpty() ) + m_operandData = *it; + else + m_operandData += ' ' + *it; + + continue; + } + + if ( PicAsm12bit::self()->operandList().contains( (*it).upper() ) + || PicAsm14bit::self()->operandList().contains( (*it).upper() ) + || PicAsm16bit::self()->operandList().contains( (*it).upper() ) ) + { + m_operand = *it; + foundOperand = true; + } + else + { + // Must be a label + m_label = *it; + } + } +} +//END class InstructionParts + diff --git a/src/asmformatter.h b/src/asmformatter.h new file mode 100644 index 0000000..a6fa361 --- /dev/null +++ b/src/asmformatter.h @@ -0,0 +1,76 @@ +/*************************************************************************** + * Copyright (C) 2003-2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef ASMFORMATTER_H +#define ASMFORMATTER_H + +#include <qstringlist.h> + +/** +@author David Saxton + */ +class InstructionParts +{ + public: + /** + * Breaks up the line into parts. + */ + InstructionParts( QString line ); + + QString label() const { return m_label; } + QString operand() const { return m_operand; } + QString operandData() const { return m_operandData; } + QString comment() const { return m_comment; } + + protected: + QString m_label; + QString m_operand; + QString m_operandData; + QString m_comment; ///< includes the ";" part +}; + +/** +@author David Saxton +*/ +class AsmFormatter +{ + public: + AsmFormatter(); + ~AsmFormatter(); + + enum LineType + { + Equ, + Instruction, // could include label + Other, // eg comments, __config + }; + + QString tidyAsm( QStringList lines ); + + static LineType lineType( QString line ); + + protected: + QString tidyInstruction( const QString & line ); + QString tidyEqu( const QString & line ); + /** + * Appends spaces to the end of text until it is greater or equakl to + * length. + */ + static void pad( QString & text, int length ); + + int m_indentAsmName; + int m_indentAsmData; + int m_indentEqu; + int m_indentEquValue; + int m_indentEquComment; + int m_indentComment; +}; + +#endif diff --git a/src/canvasitemparts.cpp b/src/canvasitemparts.cpp new file mode 100644 index 0000000..7f4b03d --- /dev/null +++ b/src/canvasitemparts.cpp @@ -0,0 +1,553 @@ +/*************************************************************************** + * Copyright (C) 2003-2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "canvasitemparts.h" +#include "cells.h" +#include "cnitem.h" +#include "icndocument.h" + +#include <qpainter.h> + + +//BEGIN Class GuiPart +GuiPart::GuiPart( CNItem *parent, const QRect & r, QCanvas * canvas ) + : QObject(parent), + QCanvasRectangle( r, canvas ), + m_angleDegrees(0), + p_parent(parent), + b_pointsAdded(false), + m_originalRect(r) +{ + connect( parent, SIGNAL(movedBy(double, double )), this, SLOT(slotMoveBy(double, double )) ); + setZ( parent->z() + 0.5 ); +} + + +GuiPart::~GuiPart() +{ + hide(); +} + + +void GuiPart::setAngleDegrees( int angleDegrees ) +{ + m_angleDegrees = angleDegrees; + posChanged(); + if (canvas()) + canvas()->setChanged( boundingRect() ); +} + + +void GuiPart::setGuiPartSize( int width, int height ) +{ + updateConnectorPoints(false); + setSize( width, height ); + posChanged(); +} + + +void GuiPart::initPainter( QPainter &p ) +{ + if ( (m_angleDegrees%180) == 0 ) + return; + + p.translate( int(x()+(width()/2)), int(y()+(height()/2)) ); + p.rotate(m_angleDegrees); + p.translate( -int(x()+(width()/2)), -int(y()+(height()/2)) ); +} + + +void GuiPart::deinitPainter( QPainter &p ) +{ + if ( (m_angleDegrees%180) == 0 ) + return; + + p.translate( int(x()+(width()/2)), int(y()+(height()/2)) ); + p.rotate(-m_angleDegrees); + p.translate( -int(x()+(width()/2)), -int(y()+(height()/2)) ); +} + + +void GuiPart::slotMoveBy( double dx, double dy ) +{ + if ( dx==0 && dy==0 ) + return; + + moveBy( dx, dy ); + posChanged(); +} + + +void GuiPart::updateConnectorPoints( bool add ) +{ + ICNDocument *icnd = dynamic_cast<ICNDocument*>(p_parent->itemDocument()); + if ( !icnd) + return; + + Cells * cells = icnd->cells(); + if (!cells) + return; + + if ( add == b_pointsAdded ) + return; + + b_pointsAdded = add; + + int mult = add ? 1 : -1; + int sx = int(x()/8); + int sy = int(y()/8); + int ex = int((x()+width())/8); + int ey = int((y()+height())/8); + + for ( int x=sx; x<=ex; ++x ) + { + for ( int y=sy; y<=ey; ++y ) + { + if ( icnd->isValidCellReference( x, y ) ) + (*cells)[x][y].CIpenalty += mult*ICNDocument::hs_item/2; + } + } +} + + +QRect GuiPart::drawRect() +{ + QRect dr = rect(); + if ( m_angleDegrees%180 != 0 ) + { + QWMatrix m; + m.translate( int(x()+(width()/2)), int(y()+(height()/2)) ); + + if ( (m_angleDegrees%180) != 0 ) + m.rotate(-m_angleDegrees); + + m.translate( -int(x()+(width()/2)), -int(y()+(height()/2)) ); + + dr = m.mapRect(dr); + } + return dr; +} +//END Class GuiPart + + + +//BEGIN Class Text +Text::Text( const QString &text, CNItem *parent, const QRect & r, QCanvas * canvas, int flags ) + : GuiPart( parent, r, canvas ) +{ + m_flags = flags; + setText(text); +} + + +Text::~Text() +{ +} + + +bool Text::setText( const QString & text ) +{ + if ( m_text == text ) + return false; + + updateConnectorPoints(false); + + m_text = text; + return true; +} + + +void Text::setFlags( int flags ) +{ + updateConnectorPoints( false ); + m_flags = flags; +} + + +void Text::drawShape( QPainter & p ) +{ + initPainter(p); + p.setFont( p_parent->font() ); + p.drawText( drawRect(), m_flags, m_text ); + deinitPainter(p); +} + + +QRect Text::recommendedRect() const +{ + return QFontMetrics( p_parent->font() ).boundingRect( m_originalRect.x(), m_originalRect.y(), m_originalRect.width(), m_originalRect.height(), m_flags, m_text ); +} +//END Class Text + + + +//BEGIN Class Widget +Widget::Widget( const QString & id, CNItem * parent, const QRect & r, QCanvas * canvas ) + : GuiPart( parent, r, canvas ) +{ + m_id = id; + show(); +} + +Widget::~Widget() +{ +} + + +int Widget::rtti() const +{ + return ItemDocument::RTTI::Widget; +} + + +void Widget::setEnabled( bool enabled ) +{ + widget()->setEnabled(enabled); +} + + +void Widget::posChanged() +{ + // Swap around the width / height if we are rotated at a non-half way around + if ( m_angleDegrees%90 != 0 ) + widget()->setFixedSize( QSize( height(), width() ) ); + else + widget()->setFixedSize( size() ); + + widget()->move( int(x()), int(y()) ); +} + + +void Widget::drawShape( QPainter &p ) +{ +// initPainter(p); + p.drawPixmap( int(x()), int(y()), QPixmap::grabWidget( widget() ) ); +// deinitPainter(p); +} +//END Class Widget + + +//BEGIN Class ToolButton +ToolButton::ToolButton( QWidget *parent ) + : QToolButton(parent) +{ + m_angleDegrees = 0; + if ( QFontInfo(m_font).pixelSize() > 11 ) // It has to be > 11, not > 12, as (I think) pixelSize() rounds off the actual size + m_font.setPixelSize(12); +} + + +void ToolButton::drawButtonLabel( QPainter * p ) +{ + if ( m_angleDegrees % 180 == 0 || text().isEmpty() ) + { + QToolButton::drawButtonLabel(p); + return; + } + + double dx = size().width()/2; + double dy = size().height()/2; + + p->translate( dx, dy ); + p->rotate( m_angleDegrees ); + p->translate( -dx, -dy ); + + p->translate( -dy+dx, 0 ); + + int m = width() > height() ? width() : height(); + + p->setPen( Qt::black ); + p->drawText( isDown()?1:0, isDown()?1:0, m, m, Qt::AlignVCenter | Qt::AlignHCenter, text() ); + + p->translate( dy-dx, 0 ); + + p->translate( dx, dy ); + p->rotate( -m_angleDegrees ); + p->translate( -dx, -dy ); +} +//END Class ToolButton + + +//BEGIN Class Button +Button::Button( const QString & id, CNItem * parent, bool isToggle, const QRect & r, QCanvas * canvas ) + : Widget( id, parent, r, canvas ) +{ + b_isToggle = isToggle; + m_button = new ToolButton(0l); + m_button->setUsesTextLabel(false); + m_button->setToggleButton(b_isToggle); + connect( m_button, SIGNAL(pressed()), this, SLOT(slotStateChanged()) ); + connect( m_button, SIGNAL(released()), this, SLOT(slotStateChanged()) ); + posChanged(); +} + + +Button::~Button() +{ + delete m_button; +} + + +void Button::setToggle( bool toggle ) +{ + if ( b_isToggle == toggle ) + return; + + if (b_isToggle) + { + // We must first untoggle it, else it'll be forever stuck... + setState(false); + } + + b_isToggle = toggle; + m_button->setToggleButton(b_isToggle); +} + + +void Button::posChanged() +{ + Widget::posChanged(); + m_button->setAngleDegrees(m_angleDegrees); +} + +void Button::slotStateChanged() +{ + parent()->buttonStateChanged( id(), m_button->isDown() || m_button->isOn() ); +} +QWidget* Button::widget() const +{ + return m_button; +} +void Button::setPixmap( const QPixmap &p ) +{ + m_button->setPixmap(p); +} +void Button::setState( bool state ) +{ + if ( this->state() == state ) + return; + + if ( isToggle() ) + m_button->setOn(state); + else + m_button->setDown(state); + + slotStateChanged(); +} +bool Button::state() const +{ + if ( isToggle() ) + return m_button->state(); + else + return m_button->isDown(); +} + + +QRect Button::recommendedRect() const +{ + QSize sizeHint = m_button->sizeHint(); + if ( sizeHint.width() < m_originalRect.width() ) + sizeHint.setWidth( m_originalRect.width() ); + + // Hmm...for now, lets just keep the recomended rect the same height as the original rect + sizeHint.setHeight( m_originalRect.height() ); + + int hdw = (sizeHint.width() - m_originalRect.width())/2; + int hdh = (sizeHint.height() - m_originalRect.height())/2; + + return QRect( m_originalRect.x()-hdw, m_originalRect.y()-hdh, sizeHint.width(), sizeHint.height() ); +} + + +void Button::setText( const QString &text ) +{ + if ( m_button->text() == text ) + return; + + updateConnectorPoints(false); + + m_button->setUsesTextLabel(true); + m_button->setText(text); + m_button->setTextLabel(text); + canvas()->setChanged( rect() ); + p_parent->updateAttachedPositioning(); +} + + +void Button::mousePressEvent( QMouseEvent *e ) +{ + if ( !m_button->isEnabled() ) + return; + + QMouseEvent event( QEvent::MouseButtonPress, e->pos()-QPoint(int(x()),int(y())), e->button(), e->state() ); + m_button->mousePressEvent(&event); + if (event.isAccepted()) + e->accept(); + canvas()->setChanged( rect() ); +} + + +void Button::mouseReleaseEvent( QMouseEvent *e ) +{ + QMouseEvent event( QEvent::MouseButtonRelease, e->pos()-QPoint(int(x()),int(y())), e->button(), e->state() ); + m_button->mouseReleaseEvent(&event); + if (event.isAccepted()) + e->accept(); + canvas()->setChanged( rect() ); +} + + +void Button::enterEvent() +{ + m_button->enterEvent(); +// m_button->setFocus(); +// bool hasFocus = m_button->hasFocus(); +// m_button->setAutoRaise(true); +// m_button->setOn(true); +} + +void Button::leaveEvent() +{ + m_button->leaveEvent(); +// m_button->clearFocus(); +// bool hasFocus = m_button->hasFocus(); +// m_button->setAutoRaise(false); +// m_button->setOn(false); +} +//END Class Button + + +//BEGIN Class SliderWidget +SliderWidget::SliderWidget( QWidget *parent ) + : QSlider(parent) +{ + setWFlags(WNoAutoErase|WRepaintNoErase); +} +//END Class SliderWidget + + +//BEGIN Class Slider +Slider::Slider( const QString & id, CNItem * parent, const QRect & r, QCanvas * canvas ) + : Widget( id, parent, r, canvas ) +{ + m_orientation = Qt::Vertical; + m_slider = new SliderWidget(0l); + m_slider->setPaletteBackgroundColor(Qt::white); + m_slider->setPaletteForegroundColor(Qt::white); + m_slider->setEraseColor(Qt::white); + m_slider->setBackgroundMode( Qt::NoBackground ); + connect( m_slider, SIGNAL(valueChanged(int)), this, SLOT(slotValueChanged(int)) ); + posChanged(); +} + + +Slider::~Slider() +{ + delete m_slider; +} + + +QWidget* Slider::widget() const +{ + return m_slider; +} + + +int Slider::value() const +{ + return m_slider->value(); +} + +void Slider::setValue( int value ) +{ + m_slider->setValue(value); +} + + +void Slider::mousePressEvent( QMouseEvent *e ) +{ + QMouseEvent event( QEvent::MouseButtonPress, e->pos()-QPoint(int(x()),int(y())), e->button(), e->state() ); + m_slider->mousePressEvent(&event); + if (event.isAccepted()) + e->accept(); + canvas()->setChanged( rect() ); +} + +void Slider::mouseReleaseEvent( QMouseEvent *e ) +{ + QMouseEvent event( QEvent::MouseButtonRelease, e->pos()-QPoint(int(x()),int(y())), e->button(), e->state() ); + m_slider->mouseReleaseEvent(&event); + if (event.isAccepted()) + e->accept(); + canvas()->setChanged( rect() ); +} + +void Slider::mouseDoubleClickEvent ( QMouseEvent *e ) +{ + QMouseEvent event( QEvent::MouseButtonDblClick, e->pos()-QPoint(int(x()),int(y())), e->button(), e->state() ); + m_slider->mouseDoubleClickEvent(&event); + if (event.isAccepted()) + e->accept(); + canvas()->setChanged( rect() ); +} + +void Slider::mouseMoveEvent( QMouseEvent *e ) +{ + QMouseEvent event( QEvent::MouseMove, e->pos()-QPoint(int(x()),int(y())), e->button(), e->state() ); + m_slider->mouseMoveEvent(&event); + if (event.isAccepted()) + e->accept(); +} + +void Slider::wheelEvent( QWheelEvent *e ) +{ + QWheelEvent event( e->pos()-QPoint(int(x()),int(y())), e->delta(), e->state(), e->orientation() ); + m_slider->wheelEvent(&event); + if (event.isAccepted()) + e->accept(); + canvas()->setChanged( rect() ); +} + +void Slider::enterEvent() +{ + m_slider->enterEvent(); +} + +void Slider::leaveEvent() +{ + m_slider->leaveEvent(); +} + +void Slider::slotValueChanged( int value ) +{ + parent()->itemDocument()->setModified(true); + parent()->sliderValueChanged(id(),value); +} + +void Slider::setOrientation( Qt::Orientation o ) +{ + m_orientation = o; + posChanged(); +} + +void Slider::posChanged() +{ + Widget::posChanged(); + + if ( m_orientation == Qt::Vertical ) + m_slider->setOrientation( (m_angleDegrees%180 == 0) ? Qt::Vertical : Qt::Horizontal ); + + else + m_slider->setOrientation( (m_angleDegrees%180 == 0) ? Qt::Horizontal : Qt::Vertical ); +} +//END Class Slider + + +#include "canvasitemparts.moc" diff --git a/src/canvasitemparts.h b/src/canvasitemparts.h new file mode 100644 index 0000000..44bd6be --- /dev/null +++ b/src/canvasitemparts.h @@ -0,0 +1,291 @@ +/*************************************************************************** + * Copyright (C) 2003-2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef CANVASITEMPARTS_H +#define CANVASITEMPARTS_H + +#include <qcanvas.h> +#include <qguardedptr.h> +#include <qslider.h> +#include <qtoolbutton.h> + +class Cells; +class CIWidgetMgr; +class CNItem; +class SliderWidget; +class ToolButton; +class QString; + +class GuiPart : public QObject, public QCanvasRectangle +{ + Q_OBJECT + public: + /** + * Create a GuiPart. Control the position using setGuiPartSize, instead + * of calling QCanvasRectangle::setSize. This allows GuiPart to know + * when its size has been changed + */ + GuiPart( CNItem *parent, const QRect & r, QCanvas * canvas ); + virtual ~GuiPart(); + + virtual QRect recommendedRect() const { return m_originalRect; } + void setOriginalRect( const QRect & r ) { m_originalRect = r; } + + virtual void updateConnectorPoints( bool add ); + + /** + * Set the angle that the GuiPart draws itself (if the GuiPart chooses + * to use it by calling initPainter and deinitPainter from drawShape). + * Note that this doesn't affect the rectangle position that the + * GuiPart is in. The rotation is taken to be about the center of the + * rectangle. + */ + void setAngleDegrees( int angleDegrees ); + /** + * Control the size. Call this instead of QCanvasRectangle::setSize. In + * turn, this function will notify subclasses via posChanged(); + */ + void setGuiPartSize( int width, int height ); + /** + * Returns the rectangle to draw in to compensate for rotation of + * the QPainter + */ + QRect drawRect(); + + int angleDegrees() const { return m_angleDegrees; } + CNItem *parent() const { return p_parent; } + + protected: + /** + * Called when the size or angle changes + */ + virtual void posChanged() {;} + /** + * Rotate / etc the painter. You must call deinitPainter after + * calling this function. + */ + void initPainter( QPainter & p ); + /** + * Complement function to initPainter - restores painter to normal + * transform + */ + void deinitPainter( QPainter & p ); + int m_angleDegrees; + CNItem *p_parent; + bool b_pointsAdded; + QRect m_originalRect; + + private slots: + void slotMoveBy( double dx, double dy ); +}; + + +/** +@short Stores internal information about text associated with CNItem +@author David Saxton +*/ +class Text : public GuiPart +{ + Q_OBJECT + public: + Text( const QString &text, CNItem *parent, const QRect & r, QCanvas * canvas, int flags = Qt::AlignHCenter | Qt::AlignVCenter ); + ~Text(); + + /** + * Set the text, returning true if the size of this Text on the canvas + * has changed. + */ + bool setText( const QString & text ); + virtual QRect recommendedRect() const; + virtual void drawShape ( QPainter & p ); + /** + * The text flags (see QPainter::drawText) - Qt::AlignmentFlags and + * Qt::TextFlags OR'd together. + */ + int flags() const { return m_flags; } + /** + * @see flags + */ + void setFlags( int flags ); + + protected: + QString m_text; + int m_flags; +}; +typedef QMap<QString, QGuardedPtr<Text> > TextMap; + + +/** +@short Base class for embedding Qt Widgets into the canvas +@author David Saxton +*/ +class Widget : public GuiPart +{ + public: + Widget( const QString & id, CNItem *parent, const QRect & r, QCanvas * canvas ); + ~Widget(); + + virtual int rtti() const; + + virtual QWidget *widget() const = 0; + QString id() const { return m_id; } + + /** + * Set the widget enabled/disabled + */ + void setEnabled( bool enabled ); + + virtual void enterEvent() {}; + virtual void leaveEvent() {}; + + /** + * Mouse was pressed. pos is given relative to CNItem position. + */ + virtual void mousePressEvent( QMouseEvent *e ) { Q_UNUSED(e); } + /** + * Mouse was released. pos is given relative to CNItem position. + */ + virtual void mouseReleaseEvent( QMouseEvent *e ) { Q_UNUSED(e); } + /** + * Mouse was double clicked. pos is given relative to CNItem position. + */ + virtual void mouseDoubleClickEvent( QMouseEvent *e ) { Q_UNUSED(e); } + /** + * Mouse was moved. pos is given relative to CNItem position. + */ + virtual void mouseMoveEvent( QMouseEvent *e ) { Q_UNUSED(e); } + /** + * Mouse was scrolled. pos is given relative to CNItem position. + */ + virtual void wheelEvent( QWheelEvent *e ) { Q_UNUSED(e); } + + virtual void drawShape( QPainter &p ); + + protected: + virtual void posChanged(); + QString m_id; +}; + + +class ToolButton : public QToolButton +{ + public: + ToolButton( QWidget* parent ); + + virtual void mousePressEvent( QMouseEvent *e ) { QToolButton::mousePressEvent(e); } + virtual void mouseReleaseEvent( QMouseEvent *e ) { QToolButton::mouseReleaseEvent(e); } + virtual void mouseDoubleClickEvent ( QMouseEvent *e ) { QToolButton::mouseDoubleClickEvent(e); } + virtual void mouseMoveEvent( QMouseEvent *e ) { QToolButton::mouseMoveEvent(e); } + virtual void wheelEvent( QWheelEvent *e ) { QToolButton::wheelEvent(e); } + virtual void enterEvent() { QToolButton::enterEvent(0l); } + virtual void leaveEvent() { QToolButton::leaveEvent(0l); } + + void setAngleDegrees( int angleDegrees ) { m_angleDegrees = angleDegrees; } + + protected: + virtual void drawButtonLabel( QPainter * p ); + + int m_angleDegrees; + QFont m_font; +}; + + +/** +@short Stores internal information about button associated with CNItem +@author David Saxton +*/ +class Button : public Widget +{ + Q_OBJECT + public: + Button( const QString & id, CNItem *parent, bool isToggle, const QRect & r, QCanvas * canvas ); + ~Button(); + + virtual void mousePressEvent( QMouseEvent *e ); + virtual void mouseReleaseEvent( QMouseEvent *e ); + virtual void enterEvent(); + virtual void leaveEvent(); + + /** + * Set the text displayed inside the button + */ + void setText( const QString &text ); + void setToggle( bool toggle ); + bool isToggle() const { return b_isToggle; } + virtual QWidget *widget() const; + bool state() const; + void setPixmap( const QPixmap & ); + void setState( bool state ); + virtual QRect recommendedRect() const; + + protected: + virtual void posChanged(); + + private slots: + void slotStateChanged(); + + private: + bool b_isToggle; // i.e. whether it should be depressed when the mouse is released + ToolButton *m_button; +}; + + +class SliderWidget : public QSlider +{ + public: + SliderWidget( QWidget* parent ); + + virtual void mousePressEvent( QMouseEvent *e ) { QSlider::mousePressEvent(e); } + virtual void mouseReleaseEvent( QMouseEvent *e ) { QSlider::mouseReleaseEvent(e); } + virtual void mouseDoubleClickEvent ( QMouseEvent *e ) { QSlider::mouseDoubleClickEvent(e); } + virtual void mouseMoveEvent( QMouseEvent *e ) { QSlider::mouseMoveEvent(e); } + virtual void wheelEvent( QWheelEvent *e ) { QSlider::wheelEvent(e); } + virtual void enterEvent() { QSlider::enterEvent(0l); } + virtual void leaveEvent() { QSlider::leaveEvent(0l); } +}; + + +/** +@short Stores internal information about a QSlider associated with CNItem +@author David Saxton +*/ +class Slider : public Widget +{ + Q_OBJECT + public: + Slider( const QString & id, CNItem *parent, const QRect & r, QCanvas * canvas ); + ~Slider(); + + virtual void mousePressEvent( QMouseEvent *e ); + virtual void mouseReleaseEvent( QMouseEvent *e ); + virtual void mouseDoubleClickEvent ( QMouseEvent *e ); + virtual void mouseMoveEvent( QMouseEvent *e ); + virtual void wheelEvent( QWheelEvent *e ); + virtual void enterEvent(); + virtual void leaveEvent(); + + virtual QWidget *widget() const; + int value() const; + void setValue( int value ); + void setOrientation( Qt::Orientation o ); + + protected: + virtual void posChanged(); + + private slots: + void slotValueChanged( int value ); + + private: + SliderWidget *m_slider; + Orientation m_orientation; +}; + +#endif + diff --git a/src/canvasmanipulator.cpp b/src/canvasmanipulator.cpp new file mode 100644 index 0000000..7c3ed17 --- /dev/null +++ b/src/canvasmanipulator.cpp @@ -0,0 +1,1904 @@ +/*************************************************************************** + * Copyright (C) 2004-2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "cnitemgroup.h" +#include "canvasitemparts.h" +#include "dptext.h" +#include "canvasmanipulator.h" +#include "connector.h" +#include "flowcontainer.h" +#include "icndocument.h" +#include "itemview.h" +#include "mechanicsdocument.h" +#include "mechanicsgroup.h" +#include "mechanicsitem.h" +#include "node.h" +#include "nodegroup.h" +#include "picitem.h" +#include "resizeoverlay.h" + +#include <cmath> + +#include <kconfig.h> +#include <kdebug.h> +#include <kglobal.h> + +#include <qpainter.h> +#include <qtimer.h> + + +CMManager::CMManager( ItemDocument *itemDocument ) + : QObject() +{ + b_allowItemScroll = true; + p_lastMouseOverResizeHandle = 0l; + m_canvasManipulator = 0l; + p_itemDocument = itemDocument; + m_cmState = 0; + p_lastMouseOverItem = 0l; + p_lastItemClicked = 0l; + m_drawAction = -1; + m_allowItemScrollTmr = new QTimer(this); + connect( m_allowItemScrollTmr, SIGNAL(timeout()), this, SLOT(slotAllowItemScroll()) ); + + KGlobal::config()->setGroup("General"); + slotSetManualRoute( KGlobal::config()->readBoolEntry( "ManualRouting", false ) ); +} + + +CMManager::~CMManager() +{ + delete m_allowItemScrollTmr; + m_allowItemScrollTmr = 0l; + delete m_canvasManipulator; + m_canvasManipulator = 0l; + + const ManipulatorInfoList::iterator end = m_manipulatorInfoList.end(); + for ( ManipulatorInfoList::iterator it = m_manipulatorInfoList.begin(); it != end; ++it ) + { + delete *it; + } + m_manipulatorInfoList.clear(); +} + + +void CMManager::addManipulatorInfo( ManipulatorInfo *eventInfo ) +{ + if ( eventInfo && !m_manipulatorInfoList.contains(eventInfo) ) { + m_manipulatorInfoList.prepend(eventInfo); + } +} + + +void CMManager::cancelCurrentManipulation() +{ + delete m_canvasManipulator; + m_canvasManipulator = 0l; + setRepeatedAddId(); +} + + +void CMManager::mousePressEvent( EventInfo eventInfo ) +{ + if (m_canvasManipulator) + { + if (m_canvasManipulator->mousePressedRepeat(eventInfo)) + { + delete m_canvasManipulator; + m_canvasManipulator = 0l; + } + return; + } + + uint eventState=0; + if (eventInfo.isRightClick) + eventState |= CMManager::es_right_click; + + if (eventInfo.ctrlPressed) + eventState |= CMManager::es_ctrl_pressed; + + uint itemType=0; + uint cnItemType=0; + + + switch ( eventInfo.itemRtti ) + { + case ItemDocument::RTTI::None: itemType = CMManager::it_none; break; + case ItemDocument::RTTI::Node: itemType = CMManager::it_node; break; + case ItemDocument::RTTI::Connector: itemType = CMManager::it_connector; break; + case ItemDocument::RTTI::Pin: itemType = CMManager::it_pin; break; + case ItemDocument::RTTI::ResizeHandle: itemType = CMManager::it_resize_handle; break; + case ItemDocument::RTTI::DrawPart: + { + itemType = CMManager::it_drawpart; + DrawPart *drawPartClickedOn = dynamic_cast<DrawPart*>(eventInfo.qcanvasItemClickedOn); + + if ( drawPartClickedOn->mousePressEvent(eventInfo) ) + { + p_lastItemClicked = drawPartClickedOn; + return; + } + + if ( drawPartClickedOn->isMovable() ) + cnItemType |= CMManager::isi_isMovable; + break; + } + case ItemDocument::RTTI::Widget: + { + Widget *widget = dynamic_cast<Widget*>(eventInfo.qcanvasItemClickedOn); + if (widget) + eventInfo.qcanvasItemClickedOn = widget->parent(); + } + case ItemDocument::RTTI::CNItem: + { + itemType = CMManager::it_canvas_item; + CNItem *cnItemClickedOn = dynamic_cast<CNItem*>(eventInfo.qcanvasItemClickedOn); + + if ( cnItemClickedOn->mousePressEvent(eventInfo) ) + { + p_lastItemClicked = cnItemClickedOn; + return; + } + + if ( cnItemClickedOn->isMovable() ) + cnItemType |= CMManager::isi_isMovable; + break; + } + case ItemDocument::RTTI::MechanicsItem: + { + itemType = CMManager::it_mechanics_item; + MechanicsItem *p_mechanicsItemClickedOn = dynamic_cast<MechanicsItem*>(eventInfo.qcanvasItemClickedOn); + + if ( p_mechanicsItemClickedOn->mousePressEvent(eventInfo) ) + { + p_lastItemClicked = p_mechanicsItemClickedOn; + return; + } + break; + } + } + +// uint highestScore=0; +// ManipulatorInfo *best=0l; + const ManipulatorInfoList::iterator end = m_manipulatorInfoList.end(); + for ( ManipulatorInfoList::iterator it = m_manipulatorInfoList.begin(); it != end && !m_canvasManipulator; ++it ) + { + if ( (*it)->m_acceptManipulationPtr( eventState, m_cmState, itemType, cnItemType ) ) + { + m_canvasManipulator = (*it)->m_createManipulatorPtr( p_itemDocument, this ); + } + } + if (m_canvasManipulator) + { + if (m_canvasManipulator->mousePressedInitial(eventInfo)) + { + delete m_canvasManipulator; + m_canvasManipulator = 0l; + } + } +} + + +void CMManager::mouseDoubleClickEvent( const EventInfo &eventInfo ) +{ + Item *item = dynamic_cast<Item*>(eventInfo.qcanvasItemClickedOn); + if (item) + { + item->mouseDoubleClickEvent(eventInfo); + return; + } + Widget *widget = dynamic_cast<Widget*>(eventInfo.qcanvasItemClickedOn); + if (widget) + { + widget->parent()->mouseDoubleClickEvent(eventInfo); + return; + } +} + + +void CMManager::mouseMoveEvent( const EventInfo &eventInfo ) +{ + if (m_canvasManipulator) + { + if (m_canvasManipulator->mouseMoved(eventInfo)) + { + kdDebug() << k_funcinfo << "About to delete" << endl; + delete m_canvasManipulator; + kdDebug() << k_funcinfo << "Deleted" << endl; + m_canvasManipulator = 0l; + kdDebug() << k_funcinfo << "Nullified" << endl; + } + ItemView *itemView = dynamic_cast<ItemView*>(p_itemDocument->activeView()); + if (itemView) + itemView->scrollToMouse(eventInfo.pos); + return; + } + + //BEGIN + QCanvasItem *qcnItem = p_itemDocument->itemAtTop(eventInfo.pos); + Item *item; + Widget *widget = dynamic_cast<Widget*>(qcnItem); + if (widget) + item = widget->parent(); + else + item = dynamic_cast<Item*>(qcnItem); + + if ( p_lastMouseOverItem != (QGuardedPtr<Item>)item ) + { + QEvent event(QEvent::Leave); + + if (p_lastMouseOverItem) + p_lastMouseOverItem->leaveEvent(); + + if (item) + item->enterEvent(); + + p_lastMouseOverItem = item; + } + + // If we clicked on an item, then continue to pass mouse events to that item until we release the mouse... + if (p_lastItemClicked) + { + p_lastItemClicked->mouseMoveEvent(eventInfo); + } + else if (item) + { + item->mouseMoveEvent(eventInfo); + } + //END + + updateCurrentResizeHandle( dynamic_cast<ResizeHandle*>(qcnItem) ); +} + + +void CMManager::updateCurrentResizeHandle( ResizeHandle * resizeHandle ) +{ + if ( p_lastMouseOverResizeHandle != (QGuardedPtr<ResizeHandle>)resizeHandle ) + { + if (p_lastMouseOverResizeHandle) + p_lastMouseOverResizeHandle->setHover(false); + p_lastMouseOverResizeHandle = resizeHandle; + if (resizeHandle) + resizeHandle->setHover(true); + } +} + + +void CMManager::mouseReleaseEvent( const EventInfo &eventInfo ) +{ + // If it returns true, then it has finished its editing operation + if ( m_canvasManipulator && m_canvasManipulator->mouseReleased(eventInfo) ) + { + delete m_canvasManipulator; + m_canvasManipulator = 0l; + } + + if (p_lastItemClicked) + { + p_lastItemClicked->mouseReleaseEvent(eventInfo); + p_lastItemClicked=0l; + } + + updateCurrentResizeHandle( dynamic_cast<ResizeHandle*>( p_itemDocument->itemAtTop(eventInfo.pos) ) ); +} + + +void CMManager::wheelEvent( const EventInfo &eventInfo ) +{ + bool accepted = false; + if (b_allowItemScroll) + { + QCanvasItem *qcnItem = p_itemDocument->itemAtTop(eventInfo.pos); + Item *item; + Widget *widget = dynamic_cast<Widget*>(qcnItem); + if (widget) + item = widget->parent(); + else + item = dynamic_cast<Item*>(qcnItem); + + if (item) + accepted = item->wheelEvent(eventInfo); + } + if (!accepted) + { + // Only allow scrolling of items if we have not just been scrolling the canvas + b_allowItemScroll = false; + m_allowItemScrollTmr->stop(); + m_allowItemScrollTmr->start(500,true); + + ItemView *itemView = dynamic_cast<ItemView*>(p_itemDocument->activeView()); + if (itemView) + { + itemView->cvbEditor()->setPassEventsToView(false); + itemView->cvbEditor()->contentsWheelEvent( eventInfo.wheelEvent( 0, 0 ) ); + itemView->cvbEditor()->setPassEventsToView(true); + } + } +} + + +void CMManager::setDrawAction( int drawAction ) +{ + if ( m_drawAction == drawAction ) + return; + + m_drawAction = drawAction; + setCMState( cms_draw, (m_drawAction != -1) ); +} + + +void CMManager::slotSetManualRoute( bool manualRoute ) +{ + KGlobal::config()->setGroup("General"); + KGlobal::config()->writeEntry( "ManualRouting", manualRoute ); + + setCMState( cms_manual_route, manualRoute ); +} + + +void CMManager::setCMState( CMState type, bool state ) +{ + // Set or clear the correct bit + state ? (m_cmState|=type) : (m_cmState&=(~type)); + + if ( type == CMManager::cms_manual_route ) + emit manualRoutingChanged(state); +} + + +void CMManager::setRepeatedAddId( const QString & repeatedId ) +{ + m_repeatedItemId = repeatedId; +} + + + + +CanvasManipulator::CanvasManipulator( ItemDocument *itemDocument, CMManager *cmManager ) +{ + p_itemDocument = itemDocument; + p_icnDocument = dynamic_cast<ICNDocument*>(itemDocument); + p_mechanicsDocument = dynamic_cast<MechanicsDocument*>(itemDocument); + p_canvas = p_itemDocument->canvas(); +// b_connectorsAllowedRouting = true; + p_selectList = p_itemDocument->selectList(); + p_cnItemSelectList = dynamic_cast<CNItemGroup*>(p_selectList); + p_mechItemSelectList = dynamic_cast<MechanicsGroup*>(p_selectList); + p_cnItemClickedOn = 0l; + p_cmManager = cmManager; +} + + +CanvasManipulator::~CanvasManipulator() +{ +} + + + + +CMRepeatedItemAdd::CMRepeatedItemAdd( ItemDocument *itemDocument, CMManager *cmManager ) + : CanvasManipulator( itemDocument, cmManager ) +{ +} +CMRepeatedItemAdd::~CMRepeatedItemAdd() +{ +} +CanvasManipulator* CMRepeatedItemAdd::construct( ItemDocument *itemDocument, CMManager *cmManager ) +{ + return new CMRepeatedItemAdd(itemDocument,cmManager); +} +ManipulatorInfo *CMRepeatedItemAdd::manipulatorInfo() +{ + ManipulatorInfo *eventInfo = new ManipulatorInfo(); + eventInfo->m_acceptManipulationPtr = CMRepeatedItemAdd::acceptManipulation; + eventInfo->m_createManipulatorPtr = CMRepeatedItemAdd::construct; + return eventInfo; +} +bool CMRepeatedItemAdd::acceptManipulation( uint /*eventState*/, uint cmState, uint /*itemType*/, uint /*cnItemType*/ ) +{ + return (cmState & CMManager::cms_repeated_add); +} + +bool CMRepeatedItemAdd::mousePressedRepeat( const EventInfo &eventInfo ) +{ + return mousePressedInitial(eventInfo); +} +bool CMRepeatedItemAdd::mousePressedInitial( const EventInfo &eventInfo ) +{ + m_eventInfo = eventInfo; + if (eventInfo.isRightClick) + { + p_cmManager->setCMState( CMManager::cms_repeated_add, false ); + return true; + } + + p_icnDocument->addItem( p_cmManager->repeatedItemId(), eventInfo.pos, true ); + p_itemDocument->requestStateSave(); + return false; +} + + +bool CMRepeatedItemAdd::mouseMoved( const EventInfo &/*eventInfo*/ ) +{ + return false; +} + + +bool CMRepeatedItemAdd::mouseReleased( const EventInfo &/*eventInfo*/ ) +{ + return false; +} + + + + +CMRightClick::CMRightClick( ItemDocument *itemDocument, CMManager *cmManager ) + : CanvasManipulator( itemDocument, cmManager ) +{ +} +CMRightClick::~CMRightClick() +{ +} +CanvasManipulator* CMRightClick::construct( ItemDocument *itemDocument, CMManager *cmManager ) +{ + return new CMRightClick(itemDocument,cmManager); +} +ManipulatorInfo *CMRightClick::manipulatorInfo() +{ + ManipulatorInfo *eventInfo = new ManipulatorInfo(); +// eventInfo->m_eventState.m_activate = CMManager::es_right_click; + eventInfo->m_acceptManipulationPtr = CMRightClick::acceptManipulation; + eventInfo->m_createManipulatorPtr = CMRightClick::construct; + return eventInfo; +} +bool CMRightClick::acceptManipulation( uint eventState, uint /*cmState*/, uint /*itemType*/, uint /*cnItemType*/ ) +{ + return eventState & CMManager::es_right_click; +} + + +bool CMRightClick::mousePressedInitial( const EventInfo &eventInfo ) +{ + m_eventInfo = eventInfo; + p_itemDocument->canvasRightClick( eventInfo.globalPos, eventInfo.qcanvasItemClickedOn ); + return true; +} + + +bool CMRightClick::mouseMoved( const EventInfo &/*eventInfo*/ ) +{ + return true; +} + + +bool CMRightClick::mouseReleased( const EventInfo &/*eventInfo*/ ) +{ + return true; +} + + + +//BEGIN class ConnectorDraw +ConnectorDraw::ConnectorDraw( ItemDocument *itemDocument, CMManager *cmManager ) + : CanvasManipulator( itemDocument, cmManager ) +{ + p_startNode = 0l; + p_startConnector = 0l; + p_endNode = 0l; + p_endConnector = 0l; +} + + +ConnectorDraw::~ConnectorDraw() +{ +} + + +QColor ConnectorDraw::validConnectionColor() +{ + return QColor( 255, 166, 0 ); +} + + +static double qpoint_distance( const QPoint & p1, const QPoint & p2 ) +{ + double dx = p1.x() - p2.x(); + double dy = p1.y() - p2.y(); + + return std::sqrt( dx*dx + dy*dy ); +} + + +QPoint ConnectorDraw::toValidPos( const QPoint & clickPos, Connector * clickedConnector ) const +{ + if ( !clickedConnector ) + return clickPos; + + const QPointList pointList = clickedConnector->connectorPoints(); + + QPointList::const_iterator end = pointList.end(); + + double dl[] = { 0.5, 8.5, 11.5, 18.0, 23.0 }; // various distances rounded up of (0,0) cells, (0,1), etc + for ( unsigned i = 0; i < 5; ++i ) + { + for ( QPointList::const_iterator it = pointList.begin(); it != end; ++it ) + { + if ( qpoint_distance( *it, clickPos ) <= dl[i] ) + return *it; + } + } + + return clickPos; +} + + +Connector * ConnectorDraw::toConnector( Node * node ) +{ + if ( !node || node->numCon( true, false ) < 3 ) + return 0l; + + const ConnectorList inList = node->inputConnectorList(); + if ( !inList.isEmpty() ) + return *inList.begin(); + + const ConnectorList outList = node->outputConnectorList(); + if ( !outList.isEmpty() ) + return *outList.begin(); + + return 0l; +} + + +void ConnectorDraw::grabEndStuff( QCanvasItem * endItem, const QPoint & pos, bool posIsExact ) +{ + if (!endItem) + return; + + CNItem * cnItem = dynamic_cast<CNItem*>(endItem); + if ( cnItem && !posIsExact ) + p_endNode = cnItem->getClosestNode(pos); + else + p_endNode = dynamic_cast<Node*>(endItem); + + if ( p_endNode && p_endNode->numCon( true, false ) > 2 ) + { + p_endConnector = toConnector(p_endNode); + p_endNode = 0l; + } + + // If the endItem is a node, we have to finish exactly on the end when posIsExact is true + if ( posIsExact && p_endNode && (p_endNode->x() != pos.x() || p_endNode->y() != pos.y()) ) + p_endNode = 0l; + + if (!p_endConnector) + p_endConnector = dynamic_cast<Connector*>(endItem); +} +//END class ConnectorDraw + + + +//BEGIN class CMAutoConnector +CMAutoConnector::CMAutoConnector( ItemDocument *itemDocument, CMManager *cmManager ) + : ConnectorDraw( itemDocument, cmManager ) +{ + m_connectorLine = 0l; + p_startNode = 0l; + p_startConnector = 0l; +} +CMAutoConnector::~CMAutoConnector() +{ + delete m_connectorLine; + m_connectorLine = 0l; +} +CanvasManipulator* CMAutoConnector::construct( ItemDocument *itemDocument, CMManager *cmManager ) +{ + return new CMAutoConnector(itemDocument,cmManager); +} +ManipulatorInfo *CMAutoConnector::manipulatorInfo() +{ + ManipulatorInfo *eventInfo = new ManipulatorInfo(); + eventInfo->m_acceptManipulationPtr = CMAutoConnector::acceptManipulation; + eventInfo->m_createManipulatorPtr = CMAutoConnector::construct; + return eventInfo; +} +bool CMAutoConnector::acceptManipulation( uint /*eventState*/, uint cmState, uint itemType, uint /*cnItemType*/ ) +{ + return (itemType & (CMManager::it_node | CMManager::it_connector)) && !(cmState & CMManager::cms_manual_route); +} + +bool CMAutoConnector::mousePressedInitial( const EventInfo &eventInfo ) +{ + m_eventInfo = eventInfo; + + p_startNode = dynamic_cast<Node*>(eventInfo.qcanvasItemClickedOn); + + if (p_startNode) + { + m_eventInfo.pos = m_prevPos = p_icnDocument->gridSnap( QPoint( (int)p_startNode->x(), (int)p_startNode->y() ) ); + if (p_startNode->numCon( true, false ) > 2) + { + p_startConnector = toConnector(p_startNode); + p_startNode = 0l; + } + } + else if (p_startConnector = dynamic_cast<Connector*>(eventInfo.qcanvasItemClickedOn) ) + { +// startConnectorPoint = m_eventInfo.pos = m_prevPos = p_icnDocument->gridSnap(m_eventInfo.pos); + startConnectorPoint = m_eventInfo.pos = m_prevPos = toValidPos( m_eventInfo.pos, p_startConnector ); + } + + else + return true; + + p_icnDocument->unselectAll(); + + delete m_connectorLine; + m_connectorLine = new QCanvasLine(p_canvas); + m_connectorLine->setPen( QColor(0,0,0) ); + m_connectorLine->setZ( ItemDocument::Z::ConnectorCreateLine ); + m_connectorLine->show(); + return false; +} + + +bool CMAutoConnector::mouseMoved( const EventInfo &eventInfo ) +{ + const QPoint pos = eventInfo.pos; + + int newX = p_icnDocument->gridSnap( pos.x() ); + int newY = p_icnDocument->gridSnap( pos.y() ); + + bool movedFlag = false; + + if ( newX != m_prevPos.x() ) + { + m_prevPos.setX(newX); + movedFlag = true; + } + + if ( newY != m_prevPos.y() ) + { + m_prevPos.setY(newY); + movedFlag = true; + } + + m_connectorLine->setPoints( m_eventInfo.pos.x(), m_eventInfo.pos.y(), newX, newY ); + + if (movedFlag) + { + QCanvasItem *startItem = 0l; + if (p_startNode) + startItem = p_startNode; + else if (p_startConnector) + startItem = p_startConnector; + + QCanvasItem *endItem = p_icnDocument->itemAtTop( QPoint( newX, newY ) ); + if ( endItem && endItem->rtti() == ItemDocument::RTTI::CNItem ) + endItem = (static_cast<CNItem*>(endItem))->getClosestNode( QPoint( newX, newY ) ); + + bool validLine = p_icnDocument->canConnect( startItem, endItem ); + m_connectorLine->setPen( validLine ? validConnectionColor() : Qt::black ); + } + return false; +} + + +bool CMAutoConnector::mouseReleased( const EventInfo &eventInfo ) +{ + const QPoint pos = eventInfo.pos; + + QPoint end = m_connectorLine->endPoint(); + delete m_connectorLine; + m_connectorLine = 0l; + + QCanvasItem *qcanvasItem = p_icnDocument->itemAtTop(end); + if ( !qcanvasItem ) + return true; + + grabEndStuff( qcanvasItem, pos, false ); + + if (p_startConnector) + { + if (p_endConnector) + { + if ( !p_icnDocument->createConnector( p_endConnector, p_startConnector, p_icnDocument->gridSnap(pos), startConnectorPoint ) ) + return true; + } + else if (p_endNode) + { + if ( !p_icnDocument->createConnector( p_endNode, p_startConnector, startConnectorPoint ) ) + return true; + } + else + return true; + } + else if (p_startNode) + { + if (p_endConnector) + { + if ( !p_icnDocument->createConnector( p_startNode, p_endConnector, p_icnDocument->gridSnap(pos) ) ) + return true; + } + else if (p_endNode) + { + if ( !p_icnDocument->createConnector( p_startNode, p_endNode ) ) + return true; + } + else + return true; + } + else + return true; + + p_itemDocument->requestStateSave(); + return true; +} +//END class CMAutoConnector + + + +//BEGIN class CMManualConnector +CMManualConnector::CMManualConnector( ItemDocument *itemDocument, CMManager *cmManager ) + : ConnectorDraw( itemDocument, cmManager ) +{ + m_manualConnectorDraw = 0l; +} +CMManualConnector::~CMManualConnector() +{ + delete m_manualConnectorDraw; + m_manualConnectorDraw = 0l; +} +CanvasManipulator* CMManualConnector::construct( ItemDocument *itemDocument, CMManager *cmManager ) +{ + return new CMManualConnector(itemDocument,cmManager); +} +ManipulatorInfo *CMManualConnector::manipulatorInfo() +{ + ManipulatorInfo *eventInfo = new ManipulatorInfo(); + eventInfo->m_acceptManipulationPtr = CMManualConnector::acceptManipulation; + eventInfo->m_createManipulatorPtr = CMManualConnector::construct; + return eventInfo; +} +bool CMManualConnector::acceptManipulation( uint /*eventState*/, uint cmState, uint itemType, uint /*cnItemType*/ ) +{ + return (itemType & (CMManager::it_node | CMManager::it_connector)) && (cmState & CMManager::cms_manual_route); +} + + +bool CMManualConnector::mousePressedInitial( const EventInfo &eventInfo ) +{ + if ( eventInfo.isRightClick ) { + return true; + } + + m_eventInfo = eventInfo; + + p_icnDocument->unselectAll(); + + QPoint sp; + + if ( eventInfo.itemRtti == ItemDocument::RTTI::Node ) + { + p_startNode = static_cast<Node*>(eventInfo.qcanvasItemClickedOn); + sp.setX( (int)p_startNode->x() ); + sp.setY( (int)p_startNode->y() ); + if ( p_startNode->numCon( true, false ) > 2 ) + { + p_startConnector = toConnector(p_startNode); + p_startNode = 0l; + } + } + else + { + p_startConnector = dynamic_cast<Connector*>(eventInfo.qcanvasItemClickedOn); + sp = toValidPos( eventInfo.pos, p_startConnector ); + } + startConnectorPoint = sp; + + if (m_manualConnectorDraw) + delete m_manualConnectorDraw; + m_manualConnectorDraw = new ManualConnectorDraw( p_icnDocument, sp ); + return false; +} + + +bool CMManualConnector::mousePressedRepeat( const EventInfo &eventInfo ) +{ + m_eventInfo = eventInfo; + if ( eventInfo.isRightClick ) { + return true; + } + m_manualConnectorDraw->mouseClicked( p_icnDocument->gridSnap(m_eventInfo.pos) ); + return false; +} + + +bool CMManualConnector::mouseMoved( const EventInfo &eventInfo ) +{ + if ( !m_manualConnectorDraw ) + return true; + + const QPoint pos = eventInfo.pos; + + int newX = p_icnDocument->gridSnap( pos.x() ); + int newY = p_icnDocument->gridSnap( pos.y() ); + + bool movedFlag = false; + + if ( newX != m_prevPos.x() ) + { + m_prevPos.setX(newX); + movedFlag = true; + } + + if ( newY != m_prevPos.y() ) + { + m_prevPos.setY(newY); + movedFlag = true; + } + + if ( movedFlag ) + { + QCanvasItem *startItem = 0l; + if (p_startNode) + startItem = p_startNode; + else if (p_startConnector) + startItem = p_startConnector; + + QCanvasItem * endItem = p_icnDocument->itemAtTop( QPoint( newX, newY ) ); + + // If the endItem is a node, we have to finish exactly on the end. + if ( Node * node = dynamic_cast<Node*>(endItem) ) + { + if ( node->x() != newX || node->y() != newY ) + endItem = 0l; + } + + bool validLine = p_icnDocument->canConnect( startItem, endItem ); + + m_manualConnectorDraw->setColor( validLine ? validConnectionColor() : Qt::black ); + m_manualConnectorDraw->mouseMoved( QPoint( newX, newY ) ); + } + + return false; +} + + +bool CMManualConnector::mouseReleased( const EventInfo &eventInfo ) +{ + if (!m_manualConnectorDraw) { + return true; + } + + QPoint pos = p_icnDocument->gridSnap(eventInfo.pos); + + grabEndStuff( m_manualConnectorDraw->mouseClicked(pos), pos, true ); + + if ( !p_endNode && !p_endConnector ) + return false; + + // Create the points that define the manual route + QPointList list = m_manualConnectorDraw->pointList(); + delete m_manualConnectorDraw; + m_manualConnectorDraw = 0l; + + if (p_startConnector) + { + if (p_endConnector) + { + if ( !p_icnDocument->createConnector( p_endConnector, p_startConnector, p_icnDocument->gridSnap(pos), startConnectorPoint, &list ) ) + return true; + } + else // if (p_endNode) + { + if ( !p_icnDocument->createConnector( p_endNode, p_startConnector, startConnectorPoint, &list ) ) + return true; + } + } + else if (p_startNode) + { + if (p_endConnector) + { + if ( !p_icnDocument->createConnector( p_startNode, p_endConnector, p_icnDocument->gridSnap(pos), &list ) ) + return true; + } + else // if (p_endNode) + { + if ( !p_icnDocument->createConnector( p_startNode, p_endNode, &list ) ) + return true; + } + } + else + return true; + + p_itemDocument->requestStateSave(); + return true; +} +//END class CMManualConnector + + + + +CMItemMove::CMItemMove( ItemDocument *itemDocument, CMManager *cmManager ) + : CanvasManipulator( itemDocument, cmManager ) +{ + p_flowContainerCandidate = 0l; +} +CMItemMove::~CMItemMove() +{ +} +CanvasManipulator* CMItemMove::construct( ItemDocument *itemDocument, CMManager *cmManager ) +{ + return new CMItemMove(itemDocument,cmManager); +} +ManipulatorInfo *CMItemMove::manipulatorInfo() +{ + ManipulatorInfo *eventInfo = new ManipulatorInfo(); + eventInfo->m_acceptManipulationPtr = CMItemMove::acceptManipulation; + eventInfo->m_createManipulatorPtr = CMItemMove::construct; + return eventInfo; +} +bool CMItemMove::acceptManipulation( uint eventState, uint /*cmState*/, uint itemType, uint cnItemType ) +{ + return ((itemType & CMManager::it_canvas_item) || (itemType & CMManager::it_drawpart)) && (cnItemType & CMManager::isi_isMovable) && !(eventState & CMManager::es_right_click); +} + + +bool CMItemMove::mousePressedInitial( const EventInfo &eventInfo ) +{ + m_eventInfo = eventInfo; + m_prevPos = eventInfo.pos; + + Item *item = dynamic_cast<Item*>(eventInfo.qcanvasItemClickedOn); + if (!item) + return true; + + + if ( !p_selectList->contains(item) ) + { + if (!eventInfo.ctrlPressed) + p_itemDocument->unselectAll(); + + p_itemDocument->select(item); + } + else if (m_eventInfo.ctrlPressed) + p_itemDocument->unselect(item); + + if ( p_selectList->isEmpty() ) + return true; + + // We want to allow dragging into FlowContainers if this is a FlowView + p_flowContainerCandidate = 0l; + { + const ItemList &itemList = p_icnDocument->itemList(); + const ItemList::const_iterator ciEnd = itemList.end(); + for ( ItemList::const_iterator it = itemList.begin(); it != ciEnd; ++it ) + { + if ( FlowContainer *flowContainer = dynamic_cast<FlowContainer*>((Item*)*it) ) + flowContainer->setFullBounds(true); + } + } + + ItemList itemList = p_cnItemSelectList->items(false); + itemList.remove((Item*)0l); + + const ItemList::iterator itemListEnd = itemList.end(); + for ( ItemList::iterator it = itemList.begin(); it != itemListEnd; ++it ) + { + CNItem *cnItem = dynamic_cast<CNItem*>((Item*)*it); + if ( !cnItem || !cnItem->canvas() ) + continue; + + cnItem->setInitialPos(m_eventInfo.pos); + } + + ConnectorList fixedConnectors; + p_icnDocument->getTranslatable( itemList, &fixedConnectors, &m_translatableConnectors, &m_translatableNodeGroups ); + + const ConnectorList::iterator fixedConnectorsEnd = fixedConnectors.end(); + for ( ConnectorList::iterator it = fixedConnectors.begin(); it != fixedConnectorsEnd; ++it ) + (*it)->setSemiHidden(true); + + p_flowContainerCandidate = p_icnDocument->flowContainer(eventInfo.pos); + + return false; +} + + +bool CMItemMove::mouseMoved( const EventInfo &eventInfo ) +{ + const QPoint pos = eventInfo.pos; + + int x = pos.x(); + int y = pos.y(); + + + const ItemList itemList = p_cnItemSelectList->items(); + const ItemList::const_iterator end = itemList.end(); + int dx=0, dy=0; + for ( ItemList::const_iterator it = itemList.begin(); it != end; ++it ) + { + if ( !*it || !(*it)->isMovable() ) + continue; + + const QRect oldRect = (*it)->boundingRect(); + (*it)->setChanged(); + + if ( CNItem *cnItem = dynamic_cast<CNItem*>((Item*)*it) ) + { + dx = -int((*it)->x()); + dy = -int((*it)->y()); + cnItem->snap( x, y ); + dx += int((*it)->x()); + dy += int((*it)->y()); + } + else + (*it)->moveBy( eventInfo.pos.x()-m_prevPos.x(), eventInfo.pos.y()-m_prevPos.y() ); + + QRect newRect = (*it)->boundingRect(); + QRect merged = oldRect | newRect; + } + + if ( (dx != 0) || (dy != 0) ) + { + const ConnectorList::iterator frEnd = m_translatableConnectors.end(); + for ( ConnectorList::iterator it = m_translatableConnectors.begin(); it != frEnd; ++it ) + (*it)->translateRoute( dx, dy ); + + const NodeGroupList::iterator end = m_translatableNodeGroups.end(); + for ( NodeGroupList::iterator it = m_translatableNodeGroups.begin(); it != end; ++it ) + (*it)->translate( dx, dy ); + } + + FlowContainer *fc = p_icnDocument->flowContainer(pos); + if ( fc != p_flowContainerCandidate ) + { + if ( p_flowContainerCandidate ) + { + p_flowContainerCandidate->setSelected(false); + p_flowContainerCandidate = 0l; + } + } + if (fc) + { + p_flowContainerCandidate = fc; + p_flowContainerCandidate->setSelected(true); + } + + p_itemDocument->requestEvent( ItemDocument::ItemDocumentEvent::ResizeCanvasToItems ); + p_canvas->update(); + m_prevPos = eventInfo.pos; + return false; +} + + +bool CMItemMove::mouseReleased( const EventInfo &eventInfo ) +{ + QStringList itemIDs; + + const ItemList itemList = p_cnItemSelectList->items(); + const ItemList::const_iterator ilEnd = itemList.end(); + for ( ItemList::const_iterator it = itemList.begin(); it != ilEnd; ++it ) + { + if (*it) + itemIDs.append( (*it)->id() ); + } + + const QPoint pos = eventInfo.pos; + + // And make sure all connectors are properly shown + const ConnectorList &connectorList = p_icnDocument->connectorList(); + const ConnectorList::const_iterator conEnd = connectorList.end(); + for ( ConnectorList::const_iterator it = connectorList.begin(); it != conEnd; ++it ) + { + (*it)->setSemiHidden(false); + } + + if (p_flowContainerCandidate) + { + for ( ItemList::const_iterator it = itemList.begin(); it != ilEnd; ++it ) + p_flowContainerCandidate->addChild(*it); + + p_flowContainerCandidate->setSelected(false); + p_flowContainerCandidate = 0l; + } + else + { + for ( ItemList::const_iterator it = itemList.begin(); it != ilEnd; ++it ) + (*it)->setParentItem(0l); + } + + // And disable the FlowContainers again... + const ItemList &cnItemList = p_icnDocument->itemList(); + const ItemList::const_iterator end = cnItemList.end(); + for ( ItemList::const_iterator it = cnItemList.begin(); it != end; ++it ) + { + if ( FlowContainer *flowContainer = dynamic_cast<FlowContainer*>((Item*)*it) ) + flowContainer->setFullBounds(false); + } + + if (p_icnDocument) + p_icnDocument->requestRerouteInvalidatedConnectors(); + + if ( m_eventInfo.pos != eventInfo.pos ) + p_itemDocument->requestStateSave(); + + p_itemDocument->requestEvent( ItemDocument::ItemDocumentEvent::ResizeCanvasToItems ); + + return true; +} + + + + + +CMItemResize::CMItemResize( ItemDocument *itemDocument, CMManager *cmManager ) + : CanvasManipulator( itemDocument, cmManager ) +{ +} +CMItemResize::~CMItemResize() +{ +} +CanvasManipulator* CMItemResize::construct( ItemDocument *itemDocument, CMManager *cmManager ) +{ + return new CMItemResize(itemDocument,cmManager); +} +ManipulatorInfo *CMItemResize::manipulatorInfo() +{ + ManipulatorInfo *eventInfo = new ManipulatorInfo(); +// eventInfo->m_itemType.m_activate = CMManager::it_canvas_item; + eventInfo->m_acceptManipulationPtr = CMItemResize::acceptManipulation; + eventInfo->m_createManipulatorPtr = CMItemResize::construct; + return eventInfo; +} +bool CMItemResize::acceptManipulation( uint eventState, uint /*cmState*/, uint itemType, uint /*cnItemType*/ ) +{ + return (itemType & CMManager::it_resize_handle) && !(eventState & CMManager::es_right_click); +} + + + +bool CMItemResize::mousePressedInitial( const EventInfo &eventInfo ) +{ + m_eventInfo = eventInfo; + p_resizeHandle = dynamic_cast<ResizeHandle*>(eventInfo.qcanvasItemClickedOn); + m_rh_dx = p_resizeHandle->x()-eventInfo.pos.x(); + m_rh_dy = p_resizeHandle->y()-eventInfo.pos.y(); + return false; +} + + +bool CMItemResize::mouseMoved( const EventInfo &eventInfo ) +{ + int _x = int(m_rh_dx + eventInfo.pos.x()); + int _y = int(m_rh_dy + eventInfo.pos.y()); + + // Shift pressed == snap to grid + if ( eventInfo.shiftPressed ) + { + _x = int(_x/8)*8+4; + _y = int(_y/8)*8+4; + } + + p_resizeHandle->moveRH( _x, _y ); + return false; +} + + +bool CMItemResize::mouseReleased( const EventInfo &/*eventInfo*/ ) +{ + if (p_icnDocument) + p_icnDocument->requestRerouteInvalidatedConnectors(); + p_itemDocument->requestStateSave(); + return true; +} + + + + + + + +CMMechItemMove::CMMechItemMove( ItemDocument *itemDocument, CMManager *cmManager ) + : CanvasManipulator( itemDocument, cmManager ) +{ +} +CMMechItemMove::~CMMechItemMove() +{ +} +CanvasManipulator* CMMechItemMove::construct( ItemDocument *itemDocument, CMManager *cmManager ) +{ + return new CMMechItemMove(itemDocument,cmManager); +} +ManipulatorInfo *CMMechItemMove::manipulatorInfo() +{ + ManipulatorInfo *eventInfo = new ManipulatorInfo(); +// eventInfo->m_itemType.m_activate = CMManager::it_canvas_item; + eventInfo->m_acceptManipulationPtr = CMMechItemMove::acceptManipulation; + eventInfo->m_createManipulatorPtr = CMMechItemMove::construct; + return eventInfo; +} +bool CMMechItemMove::acceptManipulation( uint eventState, uint /*cmState*/, uint itemType, uint /*cnItemType*/ ) +{ + return ((itemType & CMManager::it_mechanics_item) || (itemType & CMManager::it_drawpart)) && !(eventState & CMManager::es_right_click); +} + + +bool CMMechItemMove::mousePressedInitial( const EventInfo &eventInfo ) +{ + m_eventInfo = eventInfo; + m_prevPos = eventInfo.pos; + + Item *item = dynamic_cast<Item*>(eventInfo.qcanvasItemClickedOn); + if (!item) + return true; + + MechanicsItem *mechItem = dynamic_cast<MechanicsItem*>(eventInfo.qcanvasItemClickedOn); + + if (mechItem) + m_prevClickedOnSM = mechItem->selectionMode(); + + if (eventInfo.shiftPressed) + { + p_mechanicsDocument->unselectAll(); + p_mechanicsDocument->select(item); + if (mechItem) + { + mechItem->setSelectionMode(MechanicsItem::sm_move); + mechItem->setParentItem(0l); + } + } + else if ( !p_selectList->contains(mechItem) ) + { + if (!eventInfo.ctrlPressed) + p_mechanicsDocument->unselectAll(); + + p_mechanicsDocument->select(item); + + if (mechItem) + mechItem->setSelectionMode(MechanicsItem::sm_move); + } + else + { + if (mechItem) + mechItem->setSelectionMode(MechanicsItem::sm_move); + + if (m_eventInfo.ctrlPressed) + p_mechanicsDocument->unselect(item); + } + + if ( p_selectList->isEmpty() ) + return true; + + p_mechItemSelectList->setSelectionMode( MechanicsItem::sm_move ); + p_mechItemSelectList->setRaised(true); + return false; +} + + +bool CMMechItemMove::mouseMoved( const EventInfo &eventInfo ) +{ + const QPoint pos = eventInfo.pos; + + int x = pos.x(); + int y = pos.y(); + + const MechItemList itemList = p_mechItemSelectList->toplevelMechItemList(); + const MechItemList::const_iterator ilEnd = itemList.end(); + for ( MechItemList::const_iterator it = itemList.begin(); it != ilEnd; ++it ) + { + if (*it) + (*it)->moveBy( x - m_prevPos.x(), y - m_prevPos.y() ); + } + + m_prevPos = QPoint( x, y ); + + p_canvas->update(); + p_itemDocument->requestEvent( ItemDocument::ItemDocumentEvent::ResizeCanvasToItems ); + return false; +} + + +bool CMMechItemMove::mouseReleased( const EventInfo &eventInfo ) +{ + const QPoint pos = eventInfo.pos; + + int dx = pos.x() - m_eventInfo.pos.x(); + int dy = pos.y() - m_eventInfo.pos.y(); + + p_mechItemSelectList->setRaised(false); + + MechanicsItem *mechItem = dynamic_cast<MechanicsItem*>(m_eventInfo.qcanvasItemClickedOn); + if ( dx == 0 && dy == 0 ) + { + if ( mechItem && mechItem->isSelected() ) + { + if ( m_prevClickedOnSM == MechanicsItem::sm_resize ) + mechItem->setSelectionMode( MechanicsItem::sm_rotate ); + else + mechItem->setSelectionMode( MechanicsItem::sm_resize ); + } + p_itemDocument->requestStateSave(); + return true; + } + + if ( mechItem && mechItem->isSelected() ) + { + if ( m_prevClickedOnSM == MechanicsItem::sm_rotate ) + mechItem->setSelectionMode(MechanicsItem::sm_rotate); + else + mechItem->setSelectionMode(MechanicsItem::sm_resize); + } + + QStringList itemIDs; + + ItemList itemList = p_mechItemSelectList->items(); + const ItemList::iterator ilEnd = itemList.end(); + for ( ItemList::iterator it = itemList.begin(); it != ilEnd; ++it ) + { + if (*it) { + itemIDs.append( (*it)->id() ); + } + } + + p_mechItemSelectList->setSelectionMode( MechanicsItem::sm_resize ); + p_itemDocument->requestStateSave(); + p_itemDocument->requestEvent( ItemDocument::ItemDocumentEvent::ResizeCanvasToItems ); + return true; +} + + + +//BEGIN class SelectRectangle +SelectRectangle::SelectRectangle( int x, int y, int w, int h, QCanvas *qcanvas ) + : m_x(x), m_y(y) +{ + m_topLine = new QCanvasLine(qcanvas); + m_rightLine = new QCanvasLine(qcanvas); + m_bottomLine = new QCanvasLine(qcanvas); + m_leftLine = new QCanvasLine(qcanvas); + setSize( w, h ); + + QCanvasLine* lines[] = { m_topLine, m_rightLine, m_bottomLine, m_leftLine }; + for ( int i=0; i<4; ++ i) + { + lines[i]->setPen( QPen( QColor(190,190,190), 1, Qt::DotLine ) ); + lines[i]->setZ( ICNDocument::Z::Select ); + lines[i]->show(); + } +} + + +SelectRectangle::~SelectRectangle() +{ + delete m_topLine; + delete m_rightLine; + delete m_bottomLine; + delete m_leftLine; +} + + +void SelectRectangle::setSize( int w, int h ) +{ + m_topLine->setPoints( m_x, m_y, m_x+w, m_y ); + m_rightLine->setPoints( m_x+w, m_y, m_x+w, m_y+h ); + m_bottomLine->setPoints( m_x+w, m_y+h, m_x, m_y+h ); + m_leftLine->setPoints( m_x, m_y+h, m_x, m_y ); + m_w = w; + m_h = h; +} + + +QCanvasItemList SelectRectangle::collisions() +{ + QCanvas *canvas = m_topLine->canvas(); + + return canvas->collisions( QRect( m_x, m_y, m_w, m_h ) ); +} +//END class SelectRectangle + + +//BEGIN class CMSelect +CMSelect::CMSelect( ItemDocument *itemDocument, CMManager *cmManager ) + : CanvasManipulator( itemDocument, cmManager ) +{ + m_selectRectangle = 0l; +} +CMSelect::~CMSelect() +{ + delete m_selectRectangle; + m_selectRectangle = 0l; +} +CanvasManipulator* CMSelect::construct( ItemDocument *itemDocument, CMManager *cmManager ) +{ + return new CMSelect(itemDocument,cmManager); +} +ManipulatorInfo *CMSelect::manipulatorInfo() +{ + ManipulatorInfo *eventInfo = new ManipulatorInfo(); +// eventInfo->m_itemType.m_activate = CMManager::it_none; + eventInfo->m_acceptManipulationPtr = CMSelect::acceptManipulation; + eventInfo->m_createManipulatorPtr = CMSelect::construct; + return eventInfo; +} +bool CMSelect::acceptManipulation( uint /*eventState*/, uint /*cmState*/, uint itemType, uint /*cnItemType*/ ) +{ + return (itemType & CMManager::it_none); +} + + +bool CMSelect::mousePressedInitial( const EventInfo &eventInfo ) +{ + m_eventInfo = eventInfo; + + if (!eventInfo.ctrlPressed) { + p_itemDocument->unselectAll(); + } + + m_selectRectangle = new SelectRectangle( eventInfo.pos.x(), eventInfo.pos.y(), 0, 0, p_canvas ); + return false; +} + + +bool CMSelect::mouseMoved( const EventInfo &eventInfo ) +{ + QPoint pos = eventInfo.pos; + + m_selectRectangle->setSize( pos.x()-m_eventInfo.pos.x(), pos.y()-m_eventInfo.pos.y() ); + + if (m_eventInfo.ctrlPressed) { + p_itemDocument->select( m_selectRectangle->collisions() ); + } else if (p_selectList) { + p_selectList->setItems( m_selectRectangle->collisions() ); + } + + if (p_selectList && !p_mechanicsDocument) { + p_selectList->setSelected(true); + } + return false; +} + + +bool CMSelect::mouseReleased( const EventInfo &/*eventInfo*/ ) +{ + delete m_selectRectangle; + m_selectRectangle = 0l; + + return true; +} +//END class CMSelect + + + + +CMItemDrag::CMItemDrag( ItemDocument *itemDocument, CMManager *cmManager ) + : CanvasManipulator( itemDocument, cmManager ) +{ + b_dragged = false; +} +CMItemDrag::~CMItemDrag() +{ +} +CanvasManipulator* CMItemDrag::construct( ItemDocument *itemDocument, CMManager *cmManager ) +{ + return new CMItemDrag(itemDocument,cmManager); +} +ManipulatorInfo *CMItemDrag::manipulatorInfo() +{ + ManipulatorInfo *eventInfo = new ManipulatorInfo(); +// eventInfo->m_itemType.m_activate = CMManager::it_canvas_item; + eventInfo->m_acceptManipulationPtr = CMItemDrag::acceptManipulation; + eventInfo->m_createManipulatorPtr = CMItemDrag::construct; + return eventInfo; +} +bool CMItemDrag::acceptManipulation( uint /*eventState*/, uint /*cmState*/, uint itemType, uint cnItemType ) +{ + return (itemType & (CMManager::it_canvas_item|CMManager::it_pin)) && !(cnItemType & CMManager::isi_isMovable); +} + + +bool CMItemDrag::mousePressedInitial( const EventInfo &eventInfo ) +{ + m_eventInfo = eventInfo; + b_dragged = false; + return false; +} + + +bool CMItemDrag::mouseMoved( const EventInfo &eventInfo ) +{ + const QPoint pos = eventInfo.pos; + + if ( b_dragged || + pos.x() > (m_eventInfo.pos.x()+4 ) || + pos.x() < (m_eventInfo.pos.x()-4) || + pos.y() > (m_eventInfo.pos.y()+4) || + pos.y() < (m_eventInfo.pos.y()-4) ) + { + + b_dragged = true; + + if ( m_eventInfo.itemRtti == ItemDocument::RTTI::Pin ) + ((PinItem*)m_eventInfo.qcanvasItemClickedOn)->dragged( pos.x() - m_eventInfo.pos.x() ); + } + return false; +} + + +bool CMItemDrag::mouseReleased( const EventInfo &/*eventInfo*/ ) +{ + if ( !b_dragged && m_eventInfo.itemRtti == ItemDocument::RTTI::Pin ) + (static_cast<PinItem*>(m_eventInfo.qcanvasItemClickedOn))->switchState(); + + p_itemDocument->requestStateSave(); + return true; +} + + +//BEGIN class CanvasEllipseDraw +CanvasEllipseDraw::CanvasEllipseDraw( int x, int y, QCanvas * canvas ) + : QCanvasEllipse( 0, 0, canvas ) +{ + move( x, y ); +} + + +void CanvasEllipseDraw::drawShape( QPainter & p ) +{ + p.drawEllipse( int(x()-width()/2), int(y()-height()/2), width(), height() ); +} +//END class CanvasEllipseDraw + + + +//BEGIN class CMDraw +CMDraw::CMDraw( ItemDocument *itemDocument, CMManager *cmManager ) + : CanvasManipulator( itemDocument, cmManager ) +{ + m_pDrawLine = 0l; + m_pDrawRectangle = 0l; + m_pDrawEllipse = 0l; +} +CMDraw::~CMDraw() +{ + p_cmManager->setDrawAction(-1); +} +CanvasManipulator* CMDraw::construct( ItemDocument *itemDocument, CMManager *cmManager ) +{ + return new CMDraw(itemDocument,cmManager); +} +ManipulatorInfo *CMDraw::manipulatorInfo() +{ + ManipulatorInfo *eventInfo = new ManipulatorInfo(); + eventInfo->m_acceptManipulationPtr = CMDraw::acceptManipulation; + eventInfo->m_createManipulatorPtr = CMDraw::construct; + return eventInfo; +} +bool CMDraw::acceptManipulation( uint /*eventState*/, uint cmState, uint /*itemType*/, uint /*cnItemType*/ ) +{ + return (cmState & CMManager::cms_draw); +} + + +bool CMDraw::mousePressedInitial( const EventInfo &eventInfo ) +{ + m_eventInfo = eventInfo; + + switch ( (DrawPart::DrawAction) p_cmManager->drawAction() ) + { + case DrawPart::da_text: + case DrawPart::da_rectangle: + { + m_pDrawRectangle = new QCanvasRectangle( eventInfo.pos.x(), eventInfo.pos.y(), 0, 0, p_canvas ); + m_pDrawRectangle->setPen( QPen( QColor(0,0,0), 1 ) ); + m_pDrawRectangle->setZ( ICNDocument::Z::Select ); + m_pDrawRectangle->show(); + break; + } + case DrawPart::da_ellipse: + { + m_pDrawEllipse = new CanvasEllipseDraw( eventInfo.pos.x(), eventInfo.pos.y(), p_canvas ); + m_pDrawEllipse->setPen( QPen( QColor(0,0,0), 1 ) ); + m_pDrawEllipse->setZ( ICNDocument::Z::Select ); + m_pDrawEllipse->show(); + break; + } + case DrawPart::da_line: + case DrawPart::da_arrow: + { + m_pDrawLine = new QCanvasLine(p_canvas); + m_pDrawLine->setPoints( eventInfo.pos.x(), eventInfo.pos.y(), eventInfo.pos.x(), eventInfo.pos.y() ); + m_pDrawLine->setPen( QPen( QColor(0,0,0), 1 ) ); + m_pDrawLine->setZ( ICNDocument::Z::Select ); + m_pDrawLine->show(); + break; + } + default: + return true; + } + + return false; +} + + +bool CMDraw::mouseMoved( const EventInfo &eventInfo ) +{ + const QPoint pos = eventInfo.pos; + + if (m_pDrawRectangle) + m_pDrawRectangle->setSize( pos.x()-m_eventInfo.pos.x(), pos.y()-m_eventInfo.pos.y() ); + + else if (m_pDrawEllipse) + { +// QRect r( m_eventInfo.pos.x(), m_eventInfo.pos.y(), pos.x()-m_eventInfo.pos.x(), pos.y()-m_eventInfo.pos.y() ); +// r = r.normalize(); +// +// m_pDrawEllipse->setSize( r.width(), r.height() ); +// m_pDrawEllipse->move( r.left()+(r.width()/2), r.top()+(r.height()/2) ); + + m_pDrawEllipse->setSize( 2*QABS(pos.x()-m_eventInfo.pos.x()), 2*QABS(pos.y()-m_eventInfo.pos.y()) ); + } + + else if (m_pDrawLine) + m_pDrawLine->setPoints( eventInfo.pos.x(), eventInfo.pos.y(), m_pDrawLine->endPoint().x(), m_pDrawLine->endPoint().y() ); + + else + return true; + + return false; +} + + +bool CMDraw::mouseReleased( const EventInfo &eventInfo ) +{ + const QPoint pos = eventInfo.pos; + + QRect sizeRect; + + if ( m_pDrawRectangle || m_pDrawEllipse ) + { + if (m_pDrawRectangle) + { + sizeRect = m_pDrawRectangle->rect(); + + // We have to manually adjust the size rect so that it matches up with what the user has drawn + + sizeRect.setWidth( sizeRect.width()+1 ); + sizeRect.setHeight( sizeRect.height()+1 ); + + sizeRect = sizeRect.normalize(); + + if ( m_pDrawRectangle->rect().width() < 0 ) + sizeRect.moveLeft( sizeRect.left() + 1); + + if ( m_pDrawRectangle->rect().height() < 0 ) + sizeRect.moveTop( sizeRect.top() + 1); + } + else + { + int w = m_pDrawEllipse->width()+1; + int h = m_pDrawEllipse->height()+1; + int x = int(m_pDrawEllipse->x()-w/2); + int y = int(m_pDrawEllipse->y()-h/2); + sizeRect = QRect( x, y, w, h ).normalize(); + } + + delete m_pDrawRectangle; + delete m_pDrawEllipse; + m_pDrawRectangle = 0l; + m_pDrawEllipse = 0l; + } + else if (m_pDrawLine) + { + int sx = m_pDrawLine->startPoint().x(); + int sy = m_pDrawLine->startPoint().y(); + int ex = m_pDrawLine->endPoint().x(); + int ey = m_pDrawLine->endPoint().y(); + + sizeRect = QRect( ex, ey, sx-ex, sy-ey ); + + delete m_pDrawLine; + m_pDrawLine = 0l; + } + else + return true; + + QString id; + switch ( (DrawPart::DrawAction) p_cmManager->drawAction() ) + { + case DrawPart::da_rectangle: + id = "dp/rectangle"; + break; + + case DrawPart::da_ellipse: + id = "dp/ellipse"; + break; + + case DrawPart::da_text: + id = "dp/canvas_text"; + + if ( sizeRect.width() < 56 ) + sizeRect.setWidth( 56 ); + + if ( sizeRect.height() < 24 ) + sizeRect.setHeight( 24 ); + + break; + + case DrawPart::da_line: + id = "dp/line"; + break; + + case DrawPart::da_arrow: + id = "dp/arrow"; + break; + } + if ( id.isEmpty() ) + return true; + + Item * item = p_itemDocument->addItem( id, sizeRect.topLeft(), true ); + if (!item) + return true; + item->move( sizeRect.x(), sizeRect.y() ); // We call this again as p_itemDocument->addItem will move the item if it is slightly off the canvas. + + item->setSize( 0, 0, sizeRect.width(), sizeRect.height() ); + + p_itemDocument->requestStateSave(); + return true; +} +//END class CMDraw + + + +//BEGIN class ManualConnectorDraw +ManualConnectorDraw::ManualConnectorDraw( ICNDocument *_icnDocument, const QPoint &initialPos ) +{ + m_color = Qt::black; + + icnDocument = _icnDocument; + m_currentPos = m_previousPos = m_initialPos = initialPos; + p_initialItem = icnDocument->itemAtTop(initialPos); + + b_currentVertical = false; + b_orientationDefined = false; + + m_connectorLines.append( m_previousCon = new QCanvasLine( icnDocument->canvas() ) ); + m_connectorLines.append( m_currentCon = new QCanvasLine( icnDocument->canvas() ) ); + + m_currentCon->setPoints( initialPos.x(), initialPos.y(), initialPos.x(), initialPos.y() ); + m_previousCon->setPoints( initialPos.x(), initialPos.y(), initialPos.x(), initialPos.y() ); + + m_currentCon->setPen( m_color ); + m_previousCon->setPen( m_color ); + + updateConnectorEnds(); + + m_currentCon->show(); + m_previousCon->show(); +} + + +ManualConnectorDraw::~ManualConnectorDraw() +{ + const QValueList<QCanvasLine*>::iterator end = m_connectorLines.end(); + for ( QValueList<QCanvasLine*>::iterator it = m_connectorLines.begin(); it != end; ++it ) + delete *it; + + m_connectorLines.clear(); +} + + +void ManualConnectorDraw::setColor( const QColor & color ) +{ + m_color = color; + + const QValueList<QCanvasLine*>::iterator end = m_connectorLines.end(); + for ( QValueList<QCanvasLine*>::iterator it = m_connectorLines.begin(); it != end; ++it ) + (*it)->setPen( m_color ); +} + + +void ManualConnectorDraw::mouseMoved( const QPoint &pos ) +{ + if ( m_currentPos == pos ) return; + + if (!b_orientationDefined) + { + QPoint previousStart = m_previousCon->startPoint(); + + double distance = std::sqrt( std::pow( (double)(m_currentPos.x()-previousStart.x()), 2. ) + + std::pow( (double)(m_currentPos.y()-previousStart.y()), 2. ) ); + + if ( distance < 24 ) + { + b_currentVertical = ( std::abs( double(m_currentPos.x()-previousStart.x()) ) >= std::abs( double(m_currentPos.y()-previousStart.y()) ) ); + } + + } + + m_previousPos = m_currentPos; + m_currentPos = pos; + updateConnectorEnds(); +} + + +QCanvasItem* ManualConnectorDraw::mouseClicked( const QPoint &pos ) +{ + if (b_orientationDefined) + b_currentVertical = !b_currentVertical; + + else + mouseMoved(pos); + + b_orientationDefined = true; + + m_currentPos = pos; + + QCanvasItem * qcanvasItem = icnDocument->itemAtTop(pos); + + if ( qcanvasItem && pos != m_initialPos && qcanvasItem != p_initialItem ) + return qcanvasItem; + + m_previousCon = m_currentCon; + + m_connectorLines.append( m_currentCon = new QCanvasLine( icnDocument->canvas() ) ); + m_currentCon->setPoints( pos.x(), pos.y(), pos.x(), pos.y() ); + m_currentCon->setPen( m_color ); + updateConnectorEnds(); + m_currentCon->show(); + + return 0L; +} + + +void ManualConnectorDraw::updateConnectorEnds() +{ + QPoint pivot = m_currentPos; + QPoint previousStart = m_previousCon->startPoint(); + + if (b_currentVertical) + { + pivot.setY( previousStart.y() ); + m_currentCon->setPoints( pivot.x(), pivot.y(), pivot.x(), m_currentPos.y() ); + } + else + { + pivot.setX( previousStart.x() ); + m_currentCon->setPoints( pivot.x(), pivot.y(), m_currentPos.x(), pivot.y() ); + } + + m_previousCon->setPoints( previousStart.x(), previousStart.y(), pivot.x(), pivot.y() ); +} + + +QPointList ManualConnectorDraw::pointList() +{ + QPointList list; + list.append(m_initialPos); + + const QValueList<QCanvasLine*>::iterator end = m_connectorLines.end(); + for ( QValueList<QCanvasLine*>::iterator it = m_connectorLines.begin(); it != end; ++it ) + { + list.append( (*it)->endPoint() ); + } + + return list; +} +//END class ManualConnectorDraw + + +//BEGIN class ManipulatorInfo +ManipulatorInfo::ManipulatorInfo() +{ +} +//END class ManipulatorInfo + + +#include "canvasmanipulator.moc" diff --git a/src/canvasmanipulator.h b/src/canvasmanipulator.h new file mode 100644 index 0000000..06d463c --- /dev/null +++ b/src/canvasmanipulator.h @@ -0,0 +1,605 @@ +/*************************************************************************** + * Copyright (C) 2004-2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef CANVAsmANIPULATOR_H +#define CANVAsmANIPULATOR_H + +#include "eventinfo.h" + +#include <qcanvas.h> +#include <qguardedptr.h> + +class CanvasManipulator; +class Connector; +class CMManager; +class CNItem; +class CNItemGroup; +class EventInfo; +class FlowContainer; +class ICNDocument; +class Item; +class ItemDocument; +class ItemGroup; +class ItemView; +class ManipulatorInfo; +class ManualConnectorDraw; +class MechanicsItem; +class MechanicsGroup; +class MechanicsDocument; +class Node; +class NodeGroup; +class ResizeHandle; + +class QCanvas; +class QCanvasItem; +class QCanvasLine; +class QCanvasRectangle; +class QMouseEvent; +class QTimer; +class QWheelEvent; + +typedef CanvasManipulator*(*CreateManipulatorPtr)( ItemDocument *, CMManager * ); +typedef bool(*AcceptManipulationPtr)( uint eventState, uint cmState, uint itemType, uint cnItemType ); +typedef QValueList<NodeGroup*> NodeGroupList; +typedef QValueList<QGuardedPtr<Connector> > ConnectorList; +typedef QValueList<QPoint> QPointList; + + +class ManipulatorInfo +{ +public: + ManipulatorInfo(); + AcceptManipulationPtr m_acceptManipulationPtr; + CreateManipulatorPtr m_createManipulatorPtr; +}; +typedef QValueList<ManipulatorInfo*> ManipulatorInfoList; + + +/** +Handles canvas manipulation, such as moving an item or resizing the canvas +@author David Saxton +*/ +class CMManager : public QObject +{ +Q_OBJECT +public: + enum EventState + { + es_right_click = 1 << 0, + es_ctrl_pressed = 1 << 1 + }; + enum CMState + { + cms_repeated_add = 1 << 0, + cms_manual_route = 1 << 1, + cms_draw = 1 << 2 + }; + enum ItemType + { + it_none = 1 << 0, + it_node = 1 << 1, + it_connector = 1 << 2, + it_pin = 1 << 3, + it_canvas_item = 1 << 4, + it_mechanics_item = 1 << 5, + it_resize_handle = 1 << 6, + it_drawpart = 1 << 7 + }; + enum ItemStateInfo + { + isi_isMovable = 0x2, + }; + CMManager( ItemDocument *itemDocument ); + ~CMManager(); + /** + * Called when the user single-clicks the mouse + */ + void mousePressEvent( EventInfo eventInfo ); + /** + * Called when the user releases the mouse + */ + void mouseReleaseEvent( const EventInfo &eventInfo ); + /** + * Called when the user double clicks the mouse + */ + void mouseDoubleClickEvent( const EventInfo &eventInfo ); + /** + * Called when the user moves the mouse + */ + void mouseMoveEvent( const EventInfo &eventInfo ); + /** + * Called when the user scrolls the mouse + */ + void wheelEvent( const EventInfo &eventInfo ); + /** + * Set a current CMState to true or false + */ + void setCMState( CMState type, bool state ); + /** + * Cancels the current manipulation (if there is one) + */ + void cancelCurrentManipulation(); + CanvasManipulator * currentManipulator() const { return m_canvasManipulator; } + void setRepeatedAddId( const QString & repeatedId = QString::null ); + uint cmState() const { return m_cmState; } + void addManipulatorInfo( ManipulatorInfo *info ); + QString repeatedItemId() const { return m_repeatedItemId; } + void setDrawAction( int drawAction ); + int drawAction() const { return m_drawAction; } + +public slots: + void slotSetManualRoute( bool manualRoute ); + +signals: + void manualRoutingChanged( bool manualRouting ); + +protected: + /** + * Called when the mouse is moved or released, with the ResizeHandle that + * the mouse is currently over (which can be null). Updates which handle is + * selected, etc. + */ + void updateCurrentResizeHandle( ResizeHandle * mouseOver ); + CanvasManipulator *m_canvasManipulator; + uint m_cmState; + QString m_repeatedItemId; + ItemDocument *p_itemDocument; + ManipulatorInfoList m_manipulatorInfoList; + QGuardedPtr<Item> p_lastMouseOverItem; // Pointer to the item where the mouse was last over - this is used to determine when mouse + QGuardedPtr<ResizeHandle> p_lastMouseOverResizeHandle; + QGuardedPtr<Item> p_lastItemClicked; + QTimer *m_allowItemScrollTmr; // When a user scrolls on the canvas, we don't want to stop scrolling when the user gets to (e.g.) a scrollable widget. So this timer prevents scrolling a widget for a few hundred milliseconds after a scroll event if it was initiated over the canvas + bool b_allowItemScroll; // See above. + int m_drawAction; + +private slots: + void slotAllowItemScroll() { b_allowItemScroll = true; } +}; + + +/** +Abstract class for a "editing operation" on the ICNDocument, such as moving an item or resizing the canvas +@author David Saxton +*/ +class CanvasManipulator +{ +public: + CanvasManipulator( ItemDocument *itemDocument, CMManager *cmManager ); + virtual ~CanvasManipulator(); + enum Type + { + RepeatedItemAdd, + RightClick, + AutoConnector, + ManualConnector, + ItemMove, + ItemResize, + MechItemMove, + Select, + CanvasResize, + ItemDrag, + Draw + }; + virtual Type type() const = 0; + /** + * Called when the user single-clicks the mouse + * @returns true if the manipulation operation has finished + */ + virtual bool mousePressedInitial( const EventInfo &/*info*/ ) { return false; } + /** + * Called when the user single-clicks the mouse after the first time (only + * applicable for those operations who are not oneClick + * @returns true if the manipulation operation has finished + */ + virtual bool mousePressedRepeat( const EventInfo &/*info*/ ) { return false; }; + /** + * Called when the user moves the mouse + * @returns true if the manipulation operation has finished + */ + virtual bool mouseMoved( const EventInfo &/*info*/ ) { return false; }; + /** + * Called when the user releases the mouse + * @returns true if the manipulation operation has finished + */ + virtual bool mouseReleased( const EventInfo &/*info*/ ) { return true; } + +protected: + Type m_type; + EventInfo m_eventInfo; + QPoint m_prevPos; + ItemDocument *p_itemDocument; + ICNDocument *p_icnDocument; + MechanicsDocument *p_mechanicsDocument; + QCanvas *p_canvas; + ItemGroup *p_selectList; + CNItemGroup *p_cnItemSelectList; + MechanicsGroup *p_mechItemSelectList; + CNItem *p_cnItemClickedOn; + MechanicsItem *p_mechanicsItemClickedOn; + CMManager *p_cmManager; +}; + + +/** +@author David Saxton +*/ +class CMRepeatedItemAdd : public CanvasManipulator +{ +public: + CMRepeatedItemAdd( ItemDocument *itemDocument, CMManager *cmManager ); + virtual ~CMRepeatedItemAdd(); + virtual Type type() const { return RepeatedItemAdd; } + + static CanvasManipulator* construct( ItemDocument *itemDocument, CMManager *cmManager ); + static ManipulatorInfo *manipulatorInfo(); + static bool acceptManipulation( uint eventState, uint cmState, uint itemType, uint cnItemType ); + + virtual bool mousePressedInitial( const EventInfo &info ); + virtual bool mousePressedRepeat( const EventInfo &info ); + virtual bool mouseMoved( const EventInfo &info ); + virtual bool mouseReleased( const EventInfo &info ); + +protected: +}; + + + +/** +@author David Saxton +*/ +class CMRightClick : public CanvasManipulator +{ +public: + CMRightClick( ItemDocument *itemDocument, CMManager *cmManager ); + virtual ~CMRightClick(); + virtual Type type() const { return RightClick; } + + static CanvasManipulator* construct( ItemDocument *itemDocument, CMManager *cmManager ); + static ManipulatorInfo *manipulatorInfo(); + static bool acceptManipulation( uint eventState, uint cmState, uint itemType, uint cnItemType ); + + virtual bool mousePressedInitial( const EventInfo &info ); + virtual bool mouseMoved( const EventInfo &info ); + virtual bool mouseReleased( const EventInfo &info ); + +protected: +}; + + +/** +@author David Saxton +*/ +class ConnectorDraw : public CanvasManipulator +{ + public: + ConnectorDraw( ItemDocument *itemDocument, CMManager *cmManager ); + virtual ~ConnectorDraw(); + + /** + * Returns the colour used to indicate that the current connection + * being drawn is valid. Invalid colour is black. + */ + static QColor validConnectionColor(); + + protected: + /** + * If the node has more than 2 connections, return one of the + * connectors + */ + Connector * toConnector( Node * node ); + /** + * Converts the given qcanvasitem to an appropriate node or connector. + * @param posIsExact if true, then only gets an appropriate node or + * connector when the to-be end-point of the new connector will coincide + * with pos (i.e. auto-connector will call this with posIsExact = false, + * and manual-connector will call this with posIsExact = true). + */ + void grabEndStuff( QCanvasItem * endItem, const QPoint & pos, bool posIsExact ); + /** + * Returns the closest point to the clickPos that is on the given + * connector. + */ + QPoint toValidPos( const QPoint & clickPos, Connector * clickedConnector ) const; + + QGuardedPtr<Node> p_startNode; + QGuardedPtr<Connector> p_startConnector; + Node * p_endNode; + Connector * p_endConnector; + QPoint startConnectorPoint; +}; + + +/** +@author David Saxton +*/ +class CMAutoConnector : public ConnectorDraw +{ +public: + CMAutoConnector( ItemDocument *itemDocument, CMManager *cmManager ); + virtual ~CMAutoConnector(); + virtual Type type() const { return AutoConnector; } + + static CanvasManipulator* construct( ItemDocument *itemDocument, CMManager *cmManager ); + static ManipulatorInfo *manipulatorInfo(); + static bool acceptManipulation( uint eventState, uint cmState, uint itemType, uint cnItemType ); + + virtual bool mousePressedInitial( const EventInfo &info ); + virtual bool mouseMoved( const EventInfo &info ); + virtual bool mouseReleased( const EventInfo &info ); + +protected: + QCanvasLine *m_connectorLine; +}; + + +/** +@author David Saxton +*/ +class CMManualConnector : public ConnectorDraw +{ +public: + CMManualConnector( ItemDocument *itemDocument, CMManager *cmManager ); + virtual ~CMManualConnector(); + virtual Type type() const { return ManualConnector; } + + static CanvasManipulator* construct( ItemDocument *itemDocument, CMManager *cmManager ); + static ManipulatorInfo *manipulatorInfo(); + static bool acceptManipulation( uint eventState, uint cmState, uint itemType, uint cnItemType ); + + virtual bool mousePressedInitial( const EventInfo &info ); + virtual bool mousePressedRepeat( const EventInfo &info ); + virtual bool mouseMoved( const EventInfo &info ); + virtual bool mouseReleased( const EventInfo &info ); + +protected: + ConnectorList m_fixedRouteConnectors; + ManualConnectorDraw *m_manualConnectorDraw; +}; + + +/** +@author David Saxton +*/ +class CMItemMove : public CanvasManipulator +{ +public: + CMItemMove( ItemDocument *itemDocument, CMManager *cmManager ); + virtual ~CMItemMove(); + virtual Type type() const { return ItemMove; } + + static CanvasManipulator* construct( ItemDocument *itemDocument, CMManager *cmManager ); + static ManipulatorInfo *manipulatorInfo(); + static bool acceptManipulation( uint eventState, uint cmState, uint itemType, uint cnItemType ); + + virtual bool mousePressedInitial( const EventInfo &info ); + virtual bool mouseMoved( const EventInfo &info ); + virtual bool mouseReleased( const EventInfo &info ); + +protected: + ConnectorList m_translatableConnectors; + NodeGroupList m_translatableNodeGroups; + FlowContainer *p_flowContainerCandidate; +}; + + +/** +@author David Saxton +*/ +class CMItemResize : public CanvasManipulator +{ +public: + CMItemResize( ItemDocument *itemDocument, CMManager *cmManager ); + virtual ~CMItemResize(); + virtual Type type() const { return ItemResize; } + + static CanvasManipulator* construct( ItemDocument *itemDocument, CMManager *cmManager ); + static ManipulatorInfo *manipulatorInfo(); + static bool acceptManipulation( uint eventState, uint cmState, uint itemType, uint cnItemType ); + + virtual bool mousePressedInitial( const EventInfo &info ); + virtual bool mouseMoved( const EventInfo &info ); + virtual bool mouseReleased( const EventInfo &info ); + +protected: + ResizeHandle *p_resizeHandle; + double m_rh_dx; + double m_rh_dy; +}; + + +/** +@author David Saxton +*/ +class CMMechItemMove : public CanvasManipulator +{ +public: + CMMechItemMove( ItemDocument *itemDocument, CMManager *cmManager ); + virtual ~CMMechItemMove(); + virtual Type type() const { return MechItemMove; } + + static CanvasManipulator* construct( ItemDocument *itemDocument, CMManager *cmManager ); + static ManipulatorInfo *manipulatorInfo(); + static bool acceptManipulation( uint eventState, uint cmState, uint itemType, uint cnItemType ); + + virtual bool mousePressedInitial( const EventInfo &info ); + virtual bool mouseMoved( const EventInfo &info ); + virtual bool mouseReleased( const EventInfo &info ); + +protected: + uint m_prevClickedOnSM; // Previous select mode of the item that was clicked on +}; + + +/** +@author David Saxton +*/ +class SelectRectangle +{ + public: + SelectRectangle( int x, int y, int w, int h, QCanvas *qcanvas ); + ~SelectRectangle(); + + void setSize( int w, int h ); + QCanvasItemList collisions(); + + protected: + QCanvasLine *m_topLine; + QCanvasLine *m_rightLine; + QCanvasLine *m_bottomLine; + QCanvasLine *m_leftLine; + const int m_x; + const int m_y; + int m_w; + int m_h; + int m_prevCollisions_w; + int m_prevCollisions_h; + QCanvasItemList m_prevCollisions; +}; + + +/** +@author David Saxton +*/ +class CMSelect : public CanvasManipulator +{ +public: + CMSelect( ItemDocument *itemDocument, CMManager *cmManager ); + virtual ~CMSelect(); + virtual Type type() const { return Select; } + + static CanvasManipulator* construct( ItemDocument *itemDocument, CMManager *cmManager ); + static ManipulatorInfo *manipulatorInfo(); + static bool acceptManipulation( uint eventState, uint cmState, uint itemType, uint cnItemType ); + + virtual bool mousePressedInitial( const EventInfo &info ); + virtual bool mouseMoved( const EventInfo &info ); + virtual bool mouseReleased( const EventInfo &info ); + +protected: + SelectRectangle *m_selectRectangle; +}; + + +/** +@author David Saxton +*/ +class CMItemDrag : public CanvasManipulator +{ +public: + CMItemDrag( ItemDocument *itemDocument, CMManager *cmManager ); + virtual ~CMItemDrag(); + virtual Type type() const { return ItemDrag; } + + static CanvasManipulator* construct( ItemDocument *itemDocument, CMManager *cmManager ); + static ManipulatorInfo *manipulatorInfo(); + static bool acceptManipulation( uint eventState, uint cmState, uint itemType, uint cnItemType ); + + virtual bool mousePressedInitial( const EventInfo &info ); + virtual bool mouseMoved( const EventInfo &info ); + virtual bool mouseReleased( const EventInfo &info ); + +protected: + bool b_dragged; +}; + + + +/** +@author David Saxton +A QCanvasEllipse that uses a pen (not a brush) to paint +*/ +class CanvasEllipseDraw : public QCanvasEllipse +{ + public: + CanvasEllipseDraw( int x, int y, QCanvas * canvas ); + + protected: + virtual void drawShape( QPainter & p ); +}; + + +/** +@author David Saxton +*/ +class CMDraw : public CanvasManipulator +{ + public: + CMDraw( ItemDocument *itemDocument, CMManager *cmManager ); + virtual ~CMDraw(); + virtual Type type() const { return Draw; } + + static CanvasManipulator* construct( ItemDocument *itemDocument, CMManager *cmManager ); + static ManipulatorInfo *manipulatorInfo(); + static bool acceptManipulation( uint eventState, uint cmState, uint itemType, uint cnItemType ); + + virtual bool mousePressedInitial( const EventInfo &info ); + virtual bool mouseMoved( const EventInfo &info ); + virtual bool mouseReleased( const EventInfo &info ); + + protected: + QCanvasRectangle * m_pDrawRectangle; + CanvasEllipseDraw * m_pDrawEllipse; + QCanvasLine * m_pDrawLine; +}; + + +/** +@author David Saxton +*/ +class ManualConnectorDraw +{ + public: + ManualConnectorDraw( ICNDocument *_icnDocument, const QPoint &initialPos ); + virtual ~ManualConnectorDraw(); + + /** + * Called when the mouse is moved. + * Normally will do something like updating the connector route + */ + void mouseMoved( const QPoint &pos ); + /** + * Called when the user clicks the mouse. If the connector finishes on a + * valid QCanvasItem (Node or Connetor), then this is returned. Otherwise, + * null is returned. + */ + QCanvasItem * mouseClicked( const QPoint &pos ); + /** + * Returns the list of points that define the manual connection route + */ + QPointList pointList(); + /** + * Sets the colour used to draw the connection lines. + */ + void setColor( const QColor & color ); + + protected: + void updateConnectorEnds(); + + QValueList<QCanvasLine*> m_connectorLines; + ICNDocument *icnDocument; + + bool b_currentVertical; + bool b_orientationDefined; + + QPoint m_initialPos; + QPoint m_previousPos; + QPoint m_currentPos; + + QCanvasLine *m_currentCon; + QCanvasLine *m_previousCon; + + // The first item that we clicked on + QCanvasItem *p_initialItem; + + QColor m_color; +}; + + +#endif diff --git a/src/cells.cpp b/src/cells.cpp new file mode 100644 index 0000000..804a5dc --- /dev/null +++ b/src/cells.cpp @@ -0,0 +1,140 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "cells.h" + + + +#if 0 +class CellSmall +{ + public: + /** + * Resets bestScore, prevX, prevY, addedToLabels, it, permanent for each cell + */ + void reset(); + +// Point *point; // Pointer to the point in the TempLabelMap + short prevX, prevY; // Which cell this came from, (-1,-1) if originating cell + unsigned short CIpenalty; // 'Penalty' of using the cell from CNItem + unsigned short Cpenalty; // 'Penalty' of using the cell from Connector + unsigned short bestScore; // Best (lowest) score so far, _the_ best if it is permanent + unsigned char numCon; // Number of connectors through that point + bool permanent:1; // Whether the score can be improved on + bool addedToLabels:1; // Whether the cell has already been added to the list of cells to check +}; + +class CellBig +{ + public: + /** + * Resets bestScore, prevX, prevY, addedToLabels, it, permanent for each cell + */ + void reset(); + + Point *point; // Pointer to the point in the TempLabelMap + short prevX, prevY; // Which cell this came from, (-1,-1) if originating cell + unsigned short CIpenalty; // 'Penalty' of using the cell from CNItem + unsigned short Cpenalty; // 'Penalty' of using the cell from Connector + unsigned short bestScore; // Best (lowest) score so far, _the_ best if it is permanent + unsigned char numCon; // Number of connectors through that point + bool permanent:1; // Whether the score can be improved on + bool addedToLabels:1; // Whether the cell has already been added to the list of cells to check +}; +#endif + + +Cells::Cells( const uint w, const uint h ) +{ +#if 0 + kdDebug() << "sizeof(CellSmall)="<<sizeof(CellSmall)<<endl; + kdDebug() << "sizeof(CellBig)="<<sizeof(Cell)<<endl; + kdDebug() << "sizeof(unsigned short)="<<sizeof(unsigned short)<<endl; + kdDebug() << "sizeof(short)="<<sizeof(short)<<endl; + kdDebug() << "sizeof(Point*)="<<sizeof(Point*)<<endl; + kdDebug() << "sizeof(bool)="<<sizeof(bool)<<endl; + kdDebug() << "sizeof(char)="<<sizeof(char)<<endl; +#endif + init( w, h ); +} + + +Cells::~Cells() +{ + for ( uint i=0; i<m_w; ++i ) + { + delete [] m_cells[i]; + } + delete [] m_cells; +} + +Cells::Cells( const Cells &c ) +{ + init( c.width(), c.height() ); + for ( uint x=0; x<m_w; x++ ) + { + for ( uint y=0; y<m_h; y++ ) + { + m_cells[x][y] = c.cell( x, y ); + } + } +} + + +void Cells::init( const uint w, const uint h ) +{ + m_w = w; + m_h = h; + + typedef Cell* cellptr; + m_cells = new cellptr[m_w]; + for ( uint i=0; i<m_w; ++i ) + { + m_cells[i] = new Cell[m_h]; + } +} + + +void Cells::reset() +{ + for ( uint x=0; x<m_w; x++ ) + { + for ( uint y=0; y<m_h; y++ ) + { + m_cells[x][y].reset(); + } + } +} + + +Point::Point() +{ + x = y = prevX = prevY = -1; +} + +Cell::Cell() +{ + addedToLabels = false; + permanent = false; + CIpenalty = 0; + numCon = 0; + Cpenalty = 0; + bestScore = (int)1e9; // Nice large value +} + +void Cell::reset() +{ + addedToLabels = false; + permanent = false; + bestScore = (int)1e9; // Nice large value +} + + + diff --git a/src/cells.h b/src/cells.h new file mode 100644 index 0000000..c8c739c --- /dev/null +++ b/src/cells.h @@ -0,0 +1,91 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef CELLS_H +#define CELLS_H + +#include <map> + +class Point +{ +public: + Point(); + + short x; + short y; + short prevX; + short prevY; +}; + +// Key = cell, data = previous cell, compare = score +typedef std::multimap< unsigned short, Point > TempLabelMap; + +// Used for mapping out connections +const int cellSize = 8; +class Cell +{ +public: + Cell(); + /** + * Resets bestScore, prevX, prevY, addedToLabels, it, permanent for each cell + */ + void reset(); + + unsigned short CIpenalty; // 'Penalty' of using the cell from CNItem + unsigned short Cpenalty; // 'Penalty' of using the cell from Connector + unsigned short bestScore; // Best (lowest) score so far, _the_ best if it is permanent + short prevX, prevY; // Which cell this came from, (-1,-1) if originating cell + bool permanent:1; // Whether the score can be improved on + bool addedToLabels:1; // Whether the cell has already been added to the list of cells to check + Point *point; // Pointer to the point in the TempLabelMap + unsigned short numCon; // Number of connectors through that point +}; + + +/** +@author David Saxton +*/ + +typedef unsigned int uint; + +class Cells +{ +public: + Cells( const uint w, const uint h ); + ~Cells(); + /** + * Resets bestScore, prevX, prevY, addedToLabels, it, permanent for each cell + */ + void reset(); + + inline Cell* operator[] ( const uint x ) const + { + if ( x<m_w ) return m_cells[x]; + return 0l; + } + + const uint width() const { return m_w; } + const uint height() const { return m_h; } + + const Cell &cell( const uint x, const uint y ) const { return m_cells[x][y]; } + +private: + Cells( const Cells & ); + Cells & operator= ( const Cells & ); + void init( const uint w, const uint h ); + + uint m_w; + uint m_h; + + Cell **m_cells; +}; + +#endif + diff --git a/src/circuitdocument.cpp b/src/circuitdocument.cpp new file mode 100644 index 0000000..43f4fca --- /dev/null +++ b/src/circuitdocument.cpp @@ -0,0 +1,821 @@ +/*************************************************************************** + * Copyright (C) 2003-2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "canvasmanipulator.h" +#include "circuitdocument.h" +#include "circuitview.h" +#include "component.h" +#include "connector.h" +#include "core/ktlconfig.h" +#include "cnitemgroup.h" +#include "documentiface.h" +#include "drawpart.h" +#include "ecnode.h" +#include "itemdocumentdata.h" +#include "ktechlab.h" +#include "pin.h" +#include "simulator.h" +#include "subcircuits.h" +#include "switch.h" + +#include <kdebug.h> +#include <kinputdialog.h> +#include <klocale.h> +#include <kmessagebox.h> +#include <qregexp.h> +#include <qtimer.h> + + +CircuitDocument::CircuitDocument( const QString & caption, KTechlab *ktechlab, const char *name ) + : ICNDocument( caption, ktechlab, name ) +{ + m_pOrientationAction = new KActionMenu( i18n("Orientation"), "rotate", this ); + + m_type = Document::dt_circuit; + m_pDocumentIface = new CircuitDocumentIface(this); + m_fileExtensionInfo = i18n("*.circuit|Circuit(*.circuit)\n*|All Files"); + + m_cmManager->addManipulatorInfo( CMSelect::manipulatorInfo() ); + m_cmManager->addManipulatorInfo( CMDraw::manipulatorInfo() ); + m_cmManager->addManipulatorInfo( CMRightClick::manipulatorInfo() ); + m_cmManager->addManipulatorInfo( CMRepeatedItemAdd::manipulatorInfo() ); + m_cmManager->addManipulatorInfo( CMItemResize::manipulatorInfo() ); + m_cmManager->addManipulatorInfo( CMItemDrag::manipulatorInfo() ); + + connect( this, SIGNAL(connectorAdded(Connector*)), this, SLOT(requestAssignCircuits()) ); + connect( this, SIGNAL(connectorAdded(Connector*)), this, SLOT(connectorAdded(Connector*)) ); + + m_updateCircuitsTmr = new QTimer(); + connect( m_updateCircuitsTmr, SIGNAL(timeout()), this, SLOT(assignCircuits()) ); + + requestStateSave(); +} + +CircuitDocument::~CircuitDocument() +{ + m_bDeleted = true; + deleteCircuits(); + + delete m_updateCircuitsTmr; + m_updateCircuitsTmr = 0l; + + delete m_pDocumentIface; + m_pDocumentIface = 0l; +} + + +void CircuitDocument::slotInitItemActions( Item *itemBase ) +{ + ICNDocument::slotInitItemActions(itemBase); + + CircuitView * activeCircuitView = dynamic_cast<CircuitView*>(activeView()); + if ( !p_ktechlab || !activeCircuitView ) + return; + + Component * item = dynamic_cast<Component*>(itemBase); + + if ( !item && m_selectList->count() > 0 || !m_selectList->itemsAreSameType() ) + return; + + KAction * orientation_actions[] = { + activeCircuitView->action("edit_orientation_0"), + activeCircuitView->action("edit_orientation_90"), + activeCircuitView->action("edit_orientation_180"), + activeCircuitView->action("edit_orientation_270") }; + + if ( !item || !item->canRotate() ) + { + for ( unsigned i = 0; i < 4; ++i ) + orientation_actions[i]->setEnabled(false); + return; + } + + for ( unsigned i = 0; i < 4; ++ i) + { + orientation_actions[i]->setEnabled(true); + m_pOrientationAction->remove( orientation_actions[i] ); + m_pOrientationAction->insert( orientation_actions[i] ); + } + + if ( item->angleDegrees() == 0 ) + (static_cast<KToggleAction*>( orientation_actions[0] ))->setChecked(true); + + else if ( item->angleDegrees() == 90 ) + (static_cast<KToggleAction*>( orientation_actions[1] ))->setChecked(true); + + else if ( item->angleDegrees() == 180 ) + (static_cast<KToggleAction*>( orientation_actions[2] ))->setChecked(true); + + else if ( item->angleDegrees() == 270 ) + (static_cast<KToggleAction*>( orientation_actions[3] ))->setChecked(true); +} + + +void CircuitDocument::rotateCounterClockwise() +{ + m_selectList->slotRotateCCW(); + requestRerouteInvalidatedConnectors(); +} +void CircuitDocument::rotateClockwise() +{ + m_selectList->slotRotateCW(); + requestRerouteInvalidatedConnectors(); +} +void CircuitDocument::itemFlip() +{ + m_selectList->slotFlip(); + requestRerouteInvalidatedConnectors(); +} +void CircuitDocument::setOrientation0() +{ + m_selectList->slotSetOrientation0(); + requestRerouteInvalidatedConnectors(); +} +void CircuitDocument::setOrientation90() +{ + m_selectList->slotSetOrientation90(); + requestRerouteInvalidatedConnectors(); +} +void CircuitDocument::setOrientation180() +{ + m_selectList->slotSetOrientation180(); + requestRerouteInvalidatedConnectors(); +} +void CircuitDocument::setOrientation270() +{ + m_selectList->slotSetOrientation270(); + requestRerouteInvalidatedConnectors(); +} + + +View *CircuitDocument::createView( ViewContainer *viewContainer, uint viewAreaId, const char *name ) +{ + View *view = new CircuitView( this, viewContainer, viewAreaId, name ); + handleNewView(view); + return view; +} + + +void CircuitDocument::slotUpdateConfiguration() +{ + ICNDocument::slotUpdateConfiguration(); + + NodeList::iterator nodeEnd = m_nodeList.end(); + for ( NodeList::iterator it = m_nodeList.begin(); it != nodeEnd; ++it ) + { + (static_cast<ECNode*>((Node*)*it))->setShowVoltageBars( KTLConfig::showVoltageBars() ); + } + + ComponentList::iterator componentsEnd = m_componentList.end(); + for ( ComponentList::iterator it = m_componentList.begin(); it != componentsEnd; ++it ) + (*it)->slotUpdateConfiguration(); +} + + +void CircuitDocument::update() +{ + ICNDocument::update(); + + if ( KTLConfig::showVoltageBars() ) + { + NodeList::iterator end = m_nodeList.end(); + for ( NodeList::iterator it = m_nodeList.begin(); it != end; ++it ) + { + (static_cast<ECNode*>((Node*)*it))->setNodeChanged(); + } + } +} + + +void CircuitDocument::fillContextMenu( const QPoint &pos ) +{ + ICNDocument::fillContextMenu(pos); + + CircuitView * activeCircuitView = dynamic_cast<CircuitView*>(activeView()); + + if ( m_selectList->count() < 1 || !activeCircuitView ) + return; + + Component * item = dynamic_cast<Component*>( selectList()->activeItem() ); + + // NOTE: I negated this whole condition because I couldn't make out quite what the + //logic was --electronerd + if (!( !item && m_selectList->count() > 0 || !m_selectList->itemsAreSameType() )) + { + KAction * orientation_actions[] = { + activeCircuitView->action("edit_orientation_0"), + activeCircuitView->action("edit_orientation_90"), + activeCircuitView->action("edit_orientation_180"), + activeCircuitView->action("edit_orientation_270") }; + + if ( !item || !item->canRotate() ) + return; + + for ( unsigned i = 0; i < 4; ++ i) + { + m_pOrientationAction->remove( orientation_actions[i] ); + m_pOrientationAction->insert( orientation_actions[i] ); + } + + QPtrList<KAction> orientation_actionlist; + // orientation_actionlist.prepend( new KActionSeparator() ); + orientation_actionlist.append( m_pOrientationAction ); + p_ktechlab->plugActionList( "orientation_actionlist", orientation_actionlist ); + } + + if(m_selectList->count() > 1 && countExtCon(m_selectList->items()) > 0) + { + QPtrList<KAction> component_actionlist; + // component_actionlist.append( new KActionSeparator() ); + component_actionlist.append( activeCircuitView->action("circuit_create_subcircuit") ); + p_ktechlab->plugActionList( "component_actionlist", component_actionlist ); + } +} + + +void CircuitDocument::deleteCircuits() +{ + const CircuitList::iterator end = m_circuitList.end(); + for ( CircuitList::iterator it = m_circuitList.begin(); it != end; ++it ) + { + Simulator::self()->detachCircuit(*it); + delete *it; + } + m_circuitList.clear(); + m_pinList.clear(); + m_wireList.clear(); +} + + +void CircuitDocument::requestAssignCircuits() +{ +// kdDebug() << k_funcinfo << endl; + deleteCircuits(); + m_updateCircuitsTmr->stop(); + m_updateCircuitsTmr->start( 0, true ); +} + + +void CircuitDocument::connectorAdded( Connector * connector ) +{ + if (connector) + { + connect( connector, SIGNAL(numWiresChanged(unsigned )), this, SLOT(requestAssignCircuits()) ); + connect( connector, SIGNAL(removed(Connector*)), this, SLOT(requestAssignCircuits()) ); + } +} + + +void CircuitDocument::itemAdded( Item * item) +{ + ICNDocument::itemAdded( item ); + componentAdded( item ); +} + + +void CircuitDocument::componentAdded( Item * item ) +{ + Component * component = dynamic_cast<Component*>(item); + if (!component) + return; + + requestAssignCircuits(); + + connect( component, SIGNAL(elementCreated(Element*)), this, SLOT(requestAssignCircuits()) ); + connect( component, SIGNAL(elementDestroyed(Element*)), this, SLOT(requestAssignCircuits()) ); + connect( component, SIGNAL(removed(Item*)), this, SLOT(componentRemoved(Item*)) ); + + // We don't attach the component to the Simulator just yet, as the + // Component's vtable is not yet fully constructed, and so Simulator can't + // tell whether or not it is a logic component + if ( !m_toSimulateList.contains(component) ) + m_toSimulateList << component; +} + + +void CircuitDocument::componentRemoved( Item * item ) +{ + Component * component = dynamic_cast<Component*>(item); + if (!component) + return; + m_componentList.remove(component); + + requestAssignCircuits(); + Simulator::self()->detachComponent(component); +} + + +void CircuitDocument::calculateConnectorCurrents() +{ + const CircuitList::iterator circuitEnd = m_circuitList.end(); + for ( CircuitList::iterator it = m_circuitList.begin(); it != circuitEnd; ++it ) + (*it)->updateCurrents(); + + PinList groundPins; + + // Tell the Pins to reset their calculated currents to zero + m_pinList.remove((Pin*)0l); + const PinList::iterator pinEnd = m_pinList.end(); + for ( PinList::iterator it = m_pinList.begin(); it != pinEnd; ++it ) + { + if ( Pin *n = dynamic_cast<Pin*>((Pin*)*it) ) + { + n->resetCurrent(); + n->setSwitchCurrentsUnknown(); + + if ( !n->parentECNode()->isChildNode() ) + { + n->setCurrentKnown( true ); + // (and it has a current of 0 amps) + } + else if ( n->groundType() == Pin::gt_always ) + { + groundPins << n; + n->setCurrentKnown( false ); + } + else + { + // Child node that is non ground + n->setCurrentKnown( n->parentECNode()->numPins() < 2 ); + } + } + } + + + // Tell the components to update their ECNode's currents' from the elements + const ComponentList::iterator componentEnd = m_componentList.end(); + for ( ComponentList::iterator it = m_componentList.begin(); it != componentEnd; ++it ) + (*it)->setNodalCurrents(); + + + // And now for the wires and switches... + m_wireList.remove((Wire*)0l); + const WireList::iterator clEnd = m_wireList.end(); + for ( WireList::iterator it = m_wireList.begin(); it != clEnd; ++it ) + (*it)->setCurrentKnown(false); + + SwitchList switches = m_switchList; + WireList wires = m_wireList; + bool found = true; + while ( (!wires.isEmpty() || !switches.isEmpty() || !groundPins.isEmpty()) && found ) + { + found = false; + + WireList::iterator wiresEnd = wires.end(); + for ( WireList::iterator it = wires.begin(); it != wiresEnd; ) + { + if ( (*it)->calculateCurrent() ) + { + found = true; + WireList::iterator oldIt = it; + ++it; + wires.remove(oldIt); + } + else + ++it; + } + + SwitchList::iterator switchesEnd = switches.end(); + for ( SwitchList::iterator it = switches.begin(); it != switchesEnd; ) + { + if ( (*it)->calculateCurrent() ) + { + found = true; + SwitchList::iterator oldIt = it; + ++it; + switches.remove(oldIt); + } + else + ++it; + } + + PinList::iterator groundPinsEnd = groundPins.end(); + for ( PinList::iterator it = groundPins.begin(); it != groundPinsEnd; ) + { + if ( (*it)->calculateCurrentFromWires() ) + { + found = true; + PinList::iterator oldIt = it; + ++it; + groundPins.remove(oldIt); + } + else + ++it; + } + } +} + + +void CircuitDocument::assignCircuits() +{ + // Now we can finally add the unadded components to the Simulator + const ComponentList::iterator toSimulateEnd = m_toSimulateList.end(); + for ( ComponentList::iterator it = m_toSimulateList.begin(); it != toSimulateEnd; ++it ) + Simulator::self()->attachComponent(*it); + m_toSimulateList.clear(); + + + + // Stage 0: Build up pin and wire lists + m_pinList.clear(); + const NodeList::const_iterator nodeListEnd = m_nodeList.end(); + for ( NodeList::const_iterator it = m_nodeList.begin(); it != nodeListEnd; ++it ) + { + if ( ECNode * ecnode = dynamic_cast<ECNode*>((Node*)*it) ) + { + for ( unsigned i = 0; i < ecnode->numPins(); i++ ) + m_pinList << ecnode->pin(i); + } + } + + m_wireList.clear(); + const ConnectorList::const_iterator connectorListEnd = m_connectorList.end(); + for ( ConnectorList::const_iterator it = m_connectorList.begin(); it != connectorListEnd; ++it ) + { + for ( unsigned i = 0; i < (*it)->numWires(); i++ ) + m_wireList << (*it)->wire(i); + } + + + + + typedef QValueList<PinList> PinListList; + + // Stage 1: Partition the circuit up into dependent areas (bar splitting + // at ground pins) + PinList unassignedPins = m_pinList; + PinListList pinListList; + while ( !unassignedPins.isEmpty() ) + { + PinList pinList; + getPartition( *unassignedPins.begin(), & pinList, & unassignedPins ); + pinListList.append(pinList); + } +// kdDebug () << "pinListList.size()="<<pinListList.size()<<endl; + + // Stage 2: Split up each partition into circuits by ground pins + const PinListList::iterator nllEnd = pinListList.end(); + for ( PinListList::iterator it = pinListList.begin(); it != nllEnd; ++it ) + splitIntoCircuits(&*it); + + // Stage 3: Initialize the circuits + m_circuitList.remove(0l); + CircuitList::iterator circuitListEnd = m_circuitList.end(); + for ( CircuitList::iterator it = m_circuitList.begin(); it != circuitListEnd; ++it ) + (*it)->init(); + + m_switchList.clear(); + m_componentList.clear(); + const ItemList::const_iterator cilEnd = m_itemList.end(); + for ( ItemList::const_iterator it = m_itemList.begin(); it != cilEnd; ++it ) + { + Component * component = dynamic_cast<Component*>((Item*)(*it)); + if ( !component ) + continue; + + m_componentList << component; + component->initElements(0); + m_switchList += component->switchList(); + } + + circuitListEnd = m_circuitList.end(); + for ( CircuitList::iterator it = m_circuitList.begin(); it != circuitListEnd; ++it ) + (*it)->createMatrixMap(); + + for ( ItemList::const_iterator it = m_itemList.begin(); it != cilEnd; ++it ) + { + Component * component = dynamic_cast<Component*>((Item*)(*it)); + if (component) + component->initElements(1); + } + + circuitListEnd = m_circuitList.end(); + for ( CircuitList::iterator it = m_circuitList.begin(); it != circuitListEnd; ++it ) + { + (*it)->initCache(); + Simulator::self()->attachCircuit(*it); + } +} + + +void CircuitDocument::getPartition( Pin *pin, PinList *pinList, PinList *unassignedPins, bool onlyGroundDependent ) +{ + if (!pin) + return; + + unassignedPins->remove(pin); + + if ( pinList->contains(pin) ) + return; + pinList->append(pin); + + const PinList localConnectedPins = pin->localConnectedPins(); + const PinList::const_iterator end = localConnectedPins.end(); + for ( PinList::const_iterator it = localConnectedPins.begin(); it != end; ++it ) + getPartition( *it, pinList, unassignedPins, onlyGroundDependent ); + + const PinList groundDependentPins = pin->groundDependentPins(); + const PinList::const_iterator dEnd = groundDependentPins.end(); + for ( PinList::const_iterator it = groundDependentPins.begin(); it != dEnd; ++it ) + getPartition( *it, pinList, unassignedPins, onlyGroundDependent ); + + if (!onlyGroundDependent) + { + PinList circuitDependentPins = pin->circuitDependentPins(); + const PinList::const_iterator dEnd = circuitDependentPins.end(); + for ( PinList::const_iterator it = circuitDependentPins.begin(); it != dEnd; ++it ) + getPartition( *it, pinList, unassignedPins, onlyGroundDependent ); + } +} + + +void CircuitDocument::splitIntoCircuits( PinList *pinList ) +{ + // First: identify ground + PinList unassignedPins = *pinList; + typedef QValueList<PinList> PinListList; + PinListList pinListList; + while ( !unassignedPins.isEmpty() ) + { + PinList tempPinList; + getPartition( *unassignedPins.begin(), & tempPinList, & unassignedPins, true ); + pinListList.append(tempPinList); + } + const PinListList::iterator nllEnd = pinListList.end(); + for ( PinListList::iterator it = pinListList.begin(); it != nllEnd; ++it ) + Circuit::identifyGround(*it); + + + + bool allGround = false; + while ( !pinList->isEmpty() && !allGround ) + { + PinList::iterator end = pinList->end(); + PinList::iterator it = pinList->begin(); + + while ( it != end && (*it)->eqId() == -1 ) + ++it; + + if ( it == end ) + allGround = true; + + else + { + Circuitoid *circuitoid = new Circuitoid; + recursivePinAdd( *it, circuitoid, pinList ); + + if ( !tryAsLogicCircuit(circuitoid) ) + m_circuitList += createCircuit(circuitoid); + + delete circuitoid; + } + } + + + // Remaining pins are ground; tell them about it + // TODO This is a bit hacky.... + const PinList::iterator end = pinList->end(); + for ( PinList::iterator it = pinList->begin(); it != end; ++it ) + { + (*it)->setVoltage(0.0); + + ElementList elements = (*it)->elements(); + const ElementList::iterator eEnd = elements.end(); + for ( ElementList::iterator it = elements.begin(); it != eEnd; ++it ) + { + if ( LogicIn * logicIn = dynamic_cast<LogicIn*>(*it) ) + { + logicIn->setLastState(false); + logicIn->callCallback(); + } + } + } +} + + +void CircuitDocument::recursivePinAdd( Pin *pin, Circuitoid *circuitoid, PinList *unassignedPins ) +{ + if (!pin) + return; + + if ( pin->eqId() != -1 ) + unassignedPins->remove(pin); + + if ( circuitoid->contains(pin) ) + return; + circuitoid->addPin(pin); + + if ( pin->eqId() == -1 ) + return; + + const PinList localConnectedPins = pin->localConnectedPins(); + const PinList::const_iterator end = localConnectedPins.end(); + for ( PinList::const_iterator it = localConnectedPins.begin(); it != end; ++it ) + recursivePinAdd( *it, circuitoid, unassignedPins ); + + const WireList inputList = pin->inputWireList(); + WireList::const_iterator conEnd = inputList.end(); + for ( WireList::const_iterator it = inputList.begin(); it != conEnd; ++it ) + circuitoid->addWire(*it); + + const WireList outputList = pin->outputWireList(); + conEnd = outputList.end(); + for ( WireList::const_iterator it = outputList.begin(); it != conEnd; ++it ) + circuitoid->addWire(*it); + + const PinList groundDependentPins = pin->groundDependentPins(); + const PinList::const_iterator gdEnd = groundDependentPins.end(); + for ( PinList::const_iterator it = groundDependentPins.begin(); it != gdEnd; ++it ) + recursivePinAdd( *it, circuitoid, unassignedPins ); + + const PinList circuitDependentPins = pin->circuitDependentPins(); + const PinList::const_iterator cdEnd = circuitDependentPins.end(); + for ( PinList::const_iterator it = circuitDependentPins.begin(); it != cdEnd; ++it ) + recursivePinAdd( *it, circuitoid, unassignedPins ); + + const ElementList elements = pin->elements(); + const ElementList::const_iterator eEnd = elements.end(); + for ( ElementList::const_iterator it = elements.begin(); it != eEnd; ++it ) + circuitoid->addElement(*it); +} + + +bool CircuitDocument::tryAsLogicCircuit( Circuitoid *circuitoid ) +{ + if (!circuitoid) + return false; + + if ( circuitoid->elementList.size() == 0 ) + { + // This doesn't quite belong here...but whatever. Initialize all + // pins to voltage zero as they won't get set to zero otherwise + const PinList::const_iterator pinListEnd = circuitoid->pinList.constEnd(); + for ( PinList::const_iterator it = circuitoid->pinList.constBegin(); it != pinListEnd; ++it ) + (*it)->setVoltage(0.0); + + // A logic circuit only requires there to be no non-logic components, + // and at most one LogicOut - so this qualifies + return true; + } + + LogicInList logicInList; + LogicOut *out = 0l; + + uint logicInCount = 0; + uint logicOutCount = 0; + ElementList::const_iterator end = circuitoid->elementList.end(); + for ( ElementList::const_iterator it = circuitoid->elementList.begin(); it != end; ++it ) + { + if ( (*it)->type() == Element::Element_LogicOut ) + { + logicOutCount++; + out = static_cast<LogicOut*>(*it); + } + else if ( (*it)->type() == Element::Element_LogicIn ) + { + logicInCount++; + logicInList += static_cast<LogicIn*>(*it); + } + else + return false; + } + if ( logicOutCount > 1 ) + return false; + + else if ( logicOutCount == 1 ) + Simulator::self()->createLogicChain( out, logicInList, circuitoid->pinList ); + + else + { + // We have ourselves stranded LogicIns...so lets set them all to low + + const PinList::const_iterator pinListEnd = circuitoid->pinList.constEnd(); + for ( PinList::const_iterator it = circuitoid->pinList.constBegin(); it != pinListEnd; ++it ) + (*it)->setVoltage(0.0); + + for ( ElementList::const_iterator it = circuitoid->elementList.begin(); it != end; ++it ) + { + LogicIn * logicIn = static_cast<LogicIn*>(*it); + logicIn->setNextLogic(0l); + logicIn->setElementSet(0l); + if ( logicIn->isHigh() ) + { + logicIn->setLastState(false); + logicIn->callCallback(); + } + } + } + + return true; +} + + +Circuit *CircuitDocument::createCircuit( Circuitoid *circuitoid ) +{ + if (!circuitoid) { + return 0l; + } + + Circuit *circuit = new Circuit(); + + const PinList::const_iterator nEnd = circuitoid->pinList.end(); + for ( PinList::const_iterator it = circuitoid->pinList.begin(); it != nEnd; ++it ) + circuit->addPin(*it); + + const ElementList::const_iterator eEnd = circuitoid->elementList.end(); + for ( ElementList::const_iterator it = circuitoid->elementList.begin(); it != eEnd; ++it ) + circuit->addElement(*it); + + return circuit; +} + + + +void CircuitDocument::createSubcircuit() +{ + ItemList itemList = m_selectList->items(); + const ItemList::iterator itemListEnd = itemList.end(); + for ( ItemList::iterator it = itemList.begin(); it != itemListEnd; ++it ) + { + if ( !dynamic_cast<Component*>((Item*)*it) ) + *it = 0l; + } + itemList.remove((Item*)0l); + + if ( itemList.isEmpty() ) + { + KMessageBox::sorry( activeView(), i18n("No components were found in the selection.") ); + return; + } + + // Number of external connections + const int extConCount = countExtCon(itemList); + if ( extConCount == 0 ) + { + KMessageBox::sorry( activeView(), i18n("No External Connection components were found in the selection.") ); + return; + } + + bool ok; + const QString name = KInputDialog::getText( "Subcircuit", "Name", QString::null, &ok, activeView() ); + if (!ok) + return; + + SubcircuitData subcircuit; + subcircuit.addItems(itemList); + subcircuit.addNodes( getCommonNodes(itemList) ); + subcircuit.addConnectors( getCommonConnectors(itemList) ); + + Subcircuits::addSubcircuit( name, subcircuit.toXML() ); +} + + +int CircuitDocument::countExtCon( const ItemList &itemList ) const +{ + int count = 0; + const ItemList::const_iterator end = itemList.end(); + for ( ItemList::const_iterator it = itemList.begin(); it != end; ++it ) + { + Item * item = *it; + if ( item && item->type() == "ec/external_connection" ) + count++; + } + return count; +} + + +bool CircuitDocument::isValidItem( const QString &itemId ) +{ + return itemId.startsWith("ec/") || itemId.startsWith("dp/") || itemId.startsWith("sc/"); +} + + +bool CircuitDocument::isValidItem( Item *item ) +{ + return (dynamic_cast<Component*>(item) || dynamic_cast<DrawPart*>(item)); +} + + +void CircuitDocument::displayEquations() +{ + kdDebug() << "######################################################" << endl; + const CircuitList::iterator end = m_circuitList.end(); + int i = 1; + for ( CircuitList::iterator it = m_circuitList.begin(); it != end; ++it ) + { + kdDebug() << "Equation set "<<i<<":\n"; + (*it)->displayEquations(); + i++; + } + kdDebug() << "######################################################" << endl; +} + + +#include "circuitdocument.moc" diff --git a/src/circuitdocument.h b/src/circuitdocument.h new file mode 100644 index 0000000..581438b --- /dev/null +++ b/src/circuitdocument.h @@ -0,0 +1,157 @@ +/*************************************************************************** + * Copyright (C) 2003-2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef CIRCUITDOCUMENT_H +#define CIRCUITDOCUMENT_H + +#include "icndocument.h" + +class Circuit; +class Component; +class Connector; +class ECNode; +class Element; +class ICNDocument; +class KTechlab; +class Pin; +class QTimer; +class Switch; +class Wire; + +class KActionMenu; + +typedef QValueList<Circuit*> CircuitList; +typedef QValueList<Component*> ComponentList; +typedef QValueList<QGuardedPtr<Connector> > ConnectorList; +typedef QValueList<ECNode*> ECNodeList; +typedef QValueList<Element*> ElementList; +typedef QValueList<QGuardedPtr<Pin> > PinList; +typedef QValueList<Switch*> SwitchList; +typedef QValueList<QGuardedPtr<Wire> > WireList; + +class Circuitoid +{ +public: + bool contains( Pin *node ) { return pinList.contains(node); } + bool contains( Wire *con ) { return wireList.contains(con); } + bool contains( Element *ele ) { return elementList.contains(ele); } + + void addPin( Pin *node ) { if (node && !contains(node)) pinList += node; } + void addWire( Wire *con ) { if (con && !contains(con)) wireList += con; } + void addElement( Element *ele ) { if (ele && !contains(ele)) elementList += ele; } + + WireList wireList; + PinList pinList; + ElementList elementList; +}; + +/** +CircuitDocument handles allocation of the components displayed in the ICNDocument +to various Circuits, where the simulation can be performed, and displays the +information from those simulations back on the ICNDocument +@short Circuit view +@author David Saxton +*/ +class CircuitDocument : public ICNDocument +{ + Q_OBJECT + public: + CircuitDocument( const QString &caption, KTechlab *ktechlab, const char *name = 0L ); + ~CircuitDocument(); + + virtual View *createView( ViewContainer *viewContainer, uint viewAreaId, const char *name = 0l ); + + void calculateConnectorCurrents(); + /** + * Count the number of ExternalConnection components in the CNItemList + */ + int countExtCon( const ItemList &cnItemList ) const; + + virtual void update(); + + public slots: + /** + * Creates a subcircuit from the currently selected components + */ + void createSubcircuit(); + void displayEquations(); + void setOrientation0(); + void setOrientation90(); + void setOrientation180(); + void setOrientation270(); + void rotateCounterClockwise(); + void rotateClockwise(); + void itemFlip(); + /** + * Enables / disables / selects various actions depending on what is + * selected or not. + * @param plugContextMenu If true, then will insert actions into contextmenu + */ + virtual void slotInitItemActions( Item *item = 0L ); + void requestAssignCircuits(); + void componentAdded( Item * item ); + void componentRemoved( Item * item ); + void connectorAdded( Connector * connector ); + virtual void slotUpdateConfiguration(); + + protected: + virtual void itemAdded( Item * item ); + virtual void fillContextMenu( const QPoint &pos ); + virtual bool isValidItem( Item *item ); + virtual bool isValidItem( const QString &itemId ); + + KActionMenu * m_pOrientationAction; + + private slots: + void assignCircuits(); + + private: + /** + * If the given circuitoid can be a LogicCircuit, then it will be added to + * m_logicCircuits, and return true. Else returns false. + */ + bool tryAsLogicCircuit( Circuitoid *circuitoid ); + /** + * Creates a circuit from the circuitoid + */ + Circuit *createCircuit( Circuitoid *circuitoid ); + /** + * @param node Current node (will be added, then tested for further + * connections). + * @param nodeList List of nodes in current partition. + * @param unassignedNodes The pool of all nodes in the CircuitDocument + * waiting for assignment. + * @param onlyGroundDependent if true, then the partition will not use + * circuit-dependent pins to include new pins while growing the + * partition. + */ + void getPartition( Pin * pin, PinList * pinList, PinList * unassignedPins, bool onlyGroundDependent = false ); + /** + * Takes the nodeList (generated by getPartition), splits it at ground nodes, + * and creates circuits from each split. + */ + void splitIntoCircuits( PinList * pinList ); + /** + * Construct a circuit from the given node, stopping at the groundnodes + */ + void recursivePinAdd( Pin * pin, Circuitoid *circuitoid, PinList * unassignedPins ); + void deleteCircuits(); + + QTimer *m_updateCircuitsTmr; + CircuitList m_circuitList; + ComponentList m_toSimulateList; + ComponentList m_componentList; // List is built up during call to assignCircuits + PinList m_pinList; + WireList m_wireList; + SwitchList m_switchList; +}; + +#endif + diff --git a/src/circuitview.cpp b/src/circuitview.cpp new file mode 100644 index 0000000..f451c26 --- /dev/null +++ b/src/circuitview.cpp @@ -0,0 +1,92 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "circuitdocument.h" +#include "circuitview.h" +#include "config.h" +#include "ktechlab.h" +#include "simulator.h" +#include "viewiface.h" + +#include <klocale.h> +#include <qwhatsthis.h> + +CircuitView::CircuitView( CircuitDocument * circuitDocument, ViewContainer *viewContainer, uint viewAreaId, const char *name ) + : ICNView( circuitDocument, viewContainer, viewAreaId, name ), + p_circuitDocument(circuitDocument) +{ + KActionCollection * ac = actionCollection(); + + new KAction( "Dump linear equations", Qt::CTRL|Qt::Key_D, circuitDocument, SLOT(displayEquations()), ac, "dump_les" ); + + //BEGIN Item Control Actions + KRadioAction * ra; + ra = new KRadioAction( i18n("0 Degrees"), "", 0, circuitDocument, SLOT(setOrientation0()), ac, "edit_orientation_0" ); + ra->setExclusiveGroup("orientation"); + ra->setChecked(true); + ra = new KRadioAction( i18n("90 Degrees"), "", 0, circuitDocument, SLOT(setOrientation90()), ac, "edit_orientation_90" ); + ra->setExclusiveGroup("orientation"); + ra = new KRadioAction( i18n("180 Degrees"), "", 0, circuitDocument, SLOT(setOrientation180()), ac, "edit_orientation_180" ); + ra->setExclusiveGroup("orientation"); + ra =new KRadioAction( i18n("270 Degrees"), "", 0, circuitDocument, SLOT(setOrientation270()), ac, "edit_orientation_270" ); + ra->setExclusiveGroup("orientation"); + + new KAction( i18n("Create Subcircuit"), "", 0, circuitDocument, SLOT(createSubcircuit()), ac, "circuit_create_subcircuit" ); + new KAction( i18n("Rotate Clockwise"), "rotate_cw", "]", circuitDocument, SLOT(rotateClockwise()), ac, "edit_rotate_cw" ); + new KAction( i18n("Rotate Counter-Clockwise"), "rotate_ccw", "[", circuitDocument, SLOT(rotateCounterClockwise()), ac, "edit_rotate_ccw" ); + new KAction( i18n("Flip"), "", 0, circuitDocument, SLOT(itemFlip()), ac, "edit_flip" ); + //END Item Control Actions + + setXMLFile( "ktechlabcircuitui.rc", true ); + + + QWhatsThis::add( this, i18n( + "Construct a circuit by dragging components from the Component selector from the left. Create the connections by dragging a wire from the component connectors.<br><br>" + + "The simulation is running by default, but can be paused and resumed from the Tools menu.<br><br>" + + "To delete a wire, select it with a select box, and hit delete.<br><br>" + + "To edit the attributes of a component, select it (making sure that no components of another type are also selected), and edit in the toolbar. More advanced properties can be edited using the item editor on the right.<br><br>" + + "Subcircuits can be created by connecting the components with an External Connection, selecting the desired components and clicking on \"Create Subcircuit\" in the right-click menu.") + ); + + m_pViewIface = new CircuitViewIface(this); + + m_statusBar->insertItem( "", ViewStatusBar::SimulationState ); + connect( Simulator::self(), SIGNAL(simulatingStateChanged(bool )), this, SLOT(slotUpdateRunningStatus(bool )) ); + slotUpdateRunningStatus( Simulator::self()->isSimulating() ); +} + + +CircuitView::~CircuitView() +{ + delete m_pViewIface; + m_pViewIface = 0l; +} + + +void CircuitView::slotUpdateRunningStatus( bool isRunning ) +{ + m_statusBar->changeItem( isRunning ? i18n("Simulation Running") : i18n("Simulation Paused"), ViewStatusBar::SimulationState ); +} + + +void CircuitView::dragEnterEvent( QDragEnterEvent * e ) +{ + ICNView::dragEnterEvent(e); + if ( e->isAccepted() ) + return; + + e->accept( e->provides("ktechlab/component") || e->provides("ktechlab/subcircuit") ); +} + +#include "circuitview.moc" diff --git a/src/circuitview.h b/src/circuitview.h new file mode 100644 index 0000000..8097685 --- /dev/null +++ b/src/circuitview.h @@ -0,0 +1,36 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef CIRCUITVIEW_H +#define CIRCUITVIEW_H + +#include <icnview.h> + +class CircuitDocument; + +/** +@author David Saxton +*/ +class CircuitView : public ICNView +{ +Q_OBJECT +public: + CircuitView( CircuitDocument *circuitDocument, ViewContainer *viewContainer, uint viewAreaId, const char *name = 0l ); + ~CircuitView(); + +public slots: + virtual void slotUpdateRunningStatus( bool isRunning ); + +protected: + virtual void dragEnterEvent( QDragEnterEvent * e ); + CircuitDocument *p_circuitDocument; +}; + +#endif diff --git a/src/ciwidgetmgr.cpp b/src/ciwidgetmgr.cpp new file mode 100644 index 0000000..6e32423 --- /dev/null +++ b/src/ciwidgetmgr.cpp @@ -0,0 +1,308 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "cnitem.h" +#include "canvasitemparts.h" +#include "eventinfo.h" + +#include <kdebug.h> + +CIWidgetMgr::CIWidgetMgr( QCanvas *canvas, CNItem *item ) +{ + p_cnItem = item; + p_canvas = canvas; +} + + +CIWidgetMgr::~CIWidgetMgr() +{ + // QCanvas deletes our items for us. Actually, it pretty much insists on deleting them, + // despite me telling it not to, so if I delete them then it gets confused and crashes. + // Naughty QCanvas! + const WidgetMap::iterator widgetMapEnd = m_widgetMap.end(); + for ( WidgetMap::iterator it = m_widgetMap.begin(); it != widgetMapEnd; ++it ) + { + delete it.data(); + } + m_widgetMap.clear(); +} + + +void CIWidgetMgr::setWidgetsPos( const QPoint &pos ) +{ + m_pos = pos; +} + + +void CIWidgetMgr::setDrawWidgets( bool draw ) +{ + const WidgetMap::iterator widgetMapEnd = m_widgetMap.end(); + for ( WidgetMap::iterator it = m_widgetMap.begin(); it != widgetMapEnd; ++it ) + { + draw ? it.data()->show() : it.data()->hide(); + } +} + + +Widget *CIWidgetMgr::widgetWithID( const QString &id ) const +{ + WidgetMap::const_iterator it = m_widgetMap.find(id); + if ( it == m_widgetMap.end() ) + return 0l; + else return it.data(); +} + + +Button *CIWidgetMgr::button( const QString &id ) const +{ + return dynamic_cast<Button*>(widgetWithID(id)); +} + +Slider *CIWidgetMgr::slider( const QString &id ) const +{ + return dynamic_cast<Slider*>(widgetWithID(id)); +} + + + +void CIWidgetMgr::setButtonState( const QString &id, int state ) +{ + Button *b = button(id); + if (!b) + return; + + // Actually, we don't want to check to see if we are already down; this way, + // we get toggle events when loading from file +// bool oldState = b->isDown(); +// if ( oldState == state ) +// return; + + b->setState(state); +} + + +void CIWidgetMgr::drawWidgets( QPainter &p ) +{ + const WidgetMap::iterator widgetMapEnd = m_widgetMap.end(); + for ( WidgetMap::iterator it = m_widgetMap.begin(); it != widgetMapEnd; ++it ) + { + it.data()->drawShape(p); + } +} + + +void CIWidgetMgr::removeWidget( const QString & id ) +{ + if ( !m_widgetMap.contains(id) ) + return; + + delete m_widgetMap[id]; + m_widgetMap.remove(id); +} + + +Button* CIWidgetMgr::addButton( const QString &id, const QRect & pos, const QString &display, bool toggle ) +{ + WidgetMap::iterator it; + + Button *button = new Button( id, p_cnItem, toggle, pos, p_canvas ); + (dynamic_cast<QButton*>(button->widget()))->setText(display); + + it = m_widgetMap.find(id); + if ( it == m_widgetMap.end() ) + { + m_widgetMap[id] = button; + } + else + { + kdWarning() << "CIWidgetMgr::addButton: Attempting to re-add button with same id as previous"<<endl; + delete it.data(); + it.data() = button; + } + + p_cnItem->updateAttachedPositioning(); + return button; +} + + +Button* CIWidgetMgr::addButton( const QString &id, const QRect & pos, QPixmap pixmap, bool toggle ) +{ + WidgetMap::iterator it; + + Button *button = new Button( id, p_cnItem, toggle, pos, p_canvas ); + button->setPixmap(pixmap); + + it = m_widgetMap.find(id); + if ( it == m_widgetMap.end() ) + { + m_widgetMap[id] = button; + } + else + { + kdWarning() << "CIWidgetMgr::addButton: Attempting to re-add button with same id as previous"<<endl; + delete it.data(); + it.data() = button; + } + + p_cnItem->updateAttachedPositioning(); + return button; +} + + +Slider* CIWidgetMgr::addSlider( const QString &id, int minValue, int maxValue, int pageStep, int value, Qt::Orientation orientation, const QRect & pos ) +{ + Slider *slider = new Slider( id, p_cnItem, pos, p_canvas ); + QSlider *qslider = dynamic_cast<QSlider*>(slider->widget()); + + qslider->setMinValue(minValue); + qslider->setMaxValue(maxValue); + qslider->setPageStep(pageStep); + qslider->setValue(value); + slider->setOrientation(orientation); + + WidgetMap::iterator it = m_widgetMap.find(id); + if ( it == m_widgetMap.end() ) + { + m_widgetMap[id] = slider; + } + else + { + kdWarning() << "CIWidgetMgr::addSlider: Attempting to re-add slider with same id as previous"<<endl; + delete slider; + return 0l; + } + + p_cnItem->updateAttachedPositioning(); + return slider; +} + + +bool CIWidgetMgr::mousePressEvent( const EventInfo &info ) +{ + QMouseEvent *e = info.mousePressEvent( 0, 0 ); + + const WidgetMap::iterator widgetMapEnd = m_widgetMap.end(); + for ( WidgetMap::iterator it = m_widgetMap.begin(); it != widgetMapEnd; ++it ) + { + if ( it.data()->rect().contains(info.pos) ) + { + it.data()->mousePressEvent(e); + if (e->isAccepted()) + { + delete e; + return true; + } + } + } + delete e; + return false; +} + + +bool CIWidgetMgr::mouseReleaseEvent( const EventInfo &info ) +{ + QMouseEvent *e = info.mouseReleaseEvent( 0, 0 ); + + const WidgetMap::iterator widgetMapEnd = m_widgetMap.end(); + for ( WidgetMap::iterator it = m_widgetMap.begin(); it != widgetMapEnd; ++it ) + { + it.data()->mouseReleaseEvent(e); + } + + bool accepted = e->isAccepted(); + delete e; + return accepted; +} + + +bool CIWidgetMgr::mouseDoubleClickEvent( const EventInfo &info ) +{ + QMouseEvent *e = info.mouseDoubleClickEvent( 0, 0 ); + + const WidgetMap::iterator widgetMapEnd = m_widgetMap.end(); + for ( WidgetMap::iterator it = m_widgetMap.begin(); it != widgetMapEnd; ++it ) + { + if ( it.data()->rect().contains(info.pos) ) + { + it.data()->mouseDoubleClickEvent(e); + if (e->isAccepted()) + { + delete e; + return true; + } + } + } + delete e; + return false; +} + + +bool CIWidgetMgr::mouseMoveEvent( const EventInfo &info ) +{ + QMouseEvent *e = info.mouseMoveEvent( 0, 0 ); + + const WidgetMap::iterator widgetMapEnd = m_widgetMap.end(); + for ( WidgetMap::iterator it = m_widgetMap.begin(); it != widgetMapEnd; ++it ) + { + it.data()->mouseMoveEvent(e); + if (e->isAccepted()) + { + delete e; + return true; + } + } + delete e; + return false; +} + + +bool CIWidgetMgr::wheelEvent( const EventInfo &info ) +{ + QWheelEvent *e = info.wheelEvent( 0, 0 ); + + const WidgetMap::iterator widgetMapEnd = m_widgetMap.end(); + for ( WidgetMap::iterator it = m_widgetMap.begin(); it != widgetMapEnd; ++it ) + { + if ( it.data()->rect().contains(info.pos) ) + { + it.data()->wheelEvent(e); + if (e->isAccepted()) + { + delete e; + return true; + } + } + } + + delete e; + return false; +} + + +void CIWidgetMgr::enterEvent() +{ + const WidgetMap::iterator widgetMapEnd = m_widgetMap.end(); + for ( WidgetMap::iterator it = m_widgetMap.begin(); it != widgetMapEnd; ++it ) + { + it.data()->enterEvent(); + } +} + + +void CIWidgetMgr::leaveEvent() +{ + const WidgetMap::iterator widgetMapEnd = m_widgetMap.end(); + for ( WidgetMap::iterator it = m_widgetMap.begin(); it != widgetMapEnd; ++it ) + { + it.data()->leaveEvent(); + } +} + + diff --git a/src/ciwidgetmgr.h b/src/ciwidgetmgr.h new file mode 100644 index 0000000..b79fc06 --- /dev/null +++ b/src/ciwidgetmgr.h @@ -0,0 +1,103 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef CIWIDGETMGR_H +#define CIWIDGETMGR_H + +#include <qmap.h> +#include <qstring.h> + +class Button; +class CNItem; +class Slider; +class QCanvas; +class Widget; + +typedef QMap<QString, Widget*> WidgetMap; + +/** +This class handles the widgets (those things associated with CNItems that use QWidgets. +This class is pretty much to maintain a tidy interface: the functions could just as well be +all shoved in CNItem, but that gets messy. +@author David Saxton +*/ +class CIWidgetMgr +{ +public: + CIWidgetMgr( QCanvas *canvas, CNItem *item ); + virtual ~CIWidgetMgr(); + + /** + * Set the top-left position from which mouse events are interpreted and the + * widgets are drawn from. + */ + void setWidgetsPos( const QPoint &pos ); + /** + * Returns a pointer to the widget with the given id, or NULL if no such + * widgets are found. + */ + Widget *widgetWithID( const QString &id ) const; + Button *button( const QString &id ) const; + Slider *slider( const QString &id ) const; + void setButtonState( const QString &id, int state ); + /** + * Adds a slider with the given id and values to the position + */ + Slider* addSlider( const QString &id, int minValue, int maxValue, int pageStep, int value, Qt::Orientation orientation, const QRect & pos ); + /** + * Essentially the same as addDisplayText, but displays a button with + * text on it. The virtual functions buttonPressed( const QString &id ) and + * buttonReleased( const QString &id ) are called as appropriate with button id + */ + Button* addButton( const QString &id, const QRect & pos, const QString &display, bool toggle = false ); + /** + * Adds a button with a QPixmap pixmap on it instead of text + * @see void addButton( const QString &id, QRect pos, const QString &display ) + */ + Button* addButton( const QString &id, const QRect & pos, QPixmap pixmap, bool toggle = false ); + /** + * Removes the widget with the given id. + */ + void removeWidget( const QString & id ); + /** + * Sets whether or not to draw the widgets (drawing widgets mucks up SVG + * export). This function just calls either hide() or show() in each widget. + */ + void setDrawWidgets( bool draw ); + + bool mousePressEvent( const EventInfo &info ); + bool mouseReleaseEvent( const EventInfo &info ); + bool mouseDoubleClickEvent ( const EventInfo &info ); + bool mouseMoveEvent( const EventInfo &info ); + bool wheelEvent( const EventInfo &info ); + void enterEvent(); + void leaveEvent(); + + virtual void buttonStateChanged( const QString &/*id*/, bool /*on*/ ) {}; + virtual void sliderValueChanged( const QString &/*id*/, int /*value*/ ) {}; + + int mgrX() const { return m_pos.x(); } + int mgrY() const { return m_pos.y(); } + /** + * Draw the widgets using the given painter. This function isn't actually + * used to draw the widgets on the canvas, as they are QCanvasItems + * themselves, but allows other classes (e.g. ItemLibrary) to draw them + * using a special painter. + */ + void drawWidgets( QPainter &p ); + +protected: + WidgetMap m_widgetMap; + QPoint m_pos; + QCanvas *p_canvas; + CNItem *p_cnItem; +}; + +#endif diff --git a/src/cnitem.cpp b/src/cnitem.cpp new file mode 100644 index 0000000..9d949f0 --- /dev/null +++ b/src/cnitem.cpp @@ -0,0 +1,647 @@ +/*************************************************************************** + * Copyright (C) 2003-2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "canvasitemparts.h" +#include "icndocument.h" +#include "cells.h" +#include "component.h" +#include "ecnode.h" +#include "fpnode.h" +#include "itemdocumentdata.h" +#include <kdebug.h> + +#include <qbitarray.h> +#include <qpainter.h> + +#include <cmath> + +// Degrees per radian +const double DPR = 57.29577951308232087665461840231273527024; + + +CNItem::CNItem( ICNDocument *icnDocument, bool newItem, const QString &id ) + : Item( icnDocument, newItem, id ), + CIWidgetMgr( icnDocument->canvas(), this ), + p_icnDocument(icnDocument), + b_pointsAdded(false) +{ + setZ( ICNDocument::Z::Item ); + setSelected(false); + + m_brushCol = QColor( 0xf7, 0xf7, 0xff ); + m_selectedCol = QColor( 101, 134, 192 ); + + setBrush(m_brushCol); + setPen( Qt::black ); +} + +CNItem::~CNItem() +{ + const TextMap::iterator textMapEnd = m_textMap.end(); + for ( TextMap::iterator it = m_textMap.begin(); it != textMapEnd; ++it ) + { + if (it.data()) + it.data()->setCanvas(0l); + delete (Text*)it.data(); + } + m_textMap.clear(); + + updateConnectorPoints(false); +} + + +int CNItem::rtti() const +{ + return ItemDocument::RTTI::CNItem; +} + + +bool CNItem::preResize( QRect sizeRect ) +{ + if ( (std::abs((double)sizeRect.width()) < minimumSize().width()) || + (std::abs((double)sizeRect.height()) < minimumSize().height()) ) + return false; + + updateConnectorPoints(false); + return true; +} + + +void CNItem::postResize() +{ + updateAttachedPositioning(); +} + + +void CNItem::setVisible( bool yes ) +{ + if (b_deleted) + { + Item::setVisible(false); + return; + } + + Item::setVisible(yes); + + const TextMap::iterator textMapEnd = m_textMap.end(); + for ( TextMap::iterator it = m_textMap.begin(); it != textMapEnd; ++it ) + { + it.data()->setVisible(yes); + } + + const NodeMap::iterator nodeMapEnd = m_nodeMap.end(); + for ( NodeMap::iterator it = m_nodeMap.begin(); it != nodeMapEnd; ++it ) + { + it.data().node->setVisible(yes); + } + + CNItem::setDrawWidgets(yes); + + if (!yes) + updateConnectorPoints(false); +} + + +void CNItem::setInitialPos( const QPoint &pos ) +{ + m_offset = pos - QPoint( (int)x(), (int)y() ); +} + + +void CNItem::reparented( Item *oldParent, Item *newParent ) +{ + Item::reparented( oldParent, newParent ); + updateNodeLevels(); +} + + +void CNItem::updateNodeLevels() +{ + int l = level(); + + // Tell our nodes about our level + const NodeMap::iterator nodeMapEnd = m_nodeMap.end(); + for ( NodeMap::iterator it = m_nodeMap.begin(); it != nodeMapEnd; ++it ) + { + it.data().node->setLevel(l); + } + + const ItemList::iterator end = m_children.end(); + for ( ItemList::iterator it = m_children.begin(); it != end; ++it ) + { + if ( CNItem *cnItem = dynamic_cast<CNItem*>((Item*)*it) ) + cnItem->updateNodeLevels(); + } +} + + +ConnectorList CNItem::connectorList() +{ + ConnectorList list; + + const NodeMap::iterator nodeMapEnd = m_nodeMap.end(); + for ( NodeMap::iterator it = m_nodeMap.begin(); it != nodeMapEnd; ++it ) + { + Node *node = p_icnDocument->nodeWithID(it.data().id); + if (node) + { + ConnectorList nodeList = node->inputConnectorList(); + ConnectorList::iterator end = nodeList.end(); + for ( ConnectorList::iterator it = nodeList.begin(); it != end; ++it ) + { + if ( *it && !list.contains(*it) ) + { + list.append(*it); + } + } + nodeList = node->outputConnectorList(); + end = nodeList.end(); + for ( ConnectorList::iterator it = nodeList.begin(); it != end; ++it ) + { + if ( *it && !list.contains(*it) ) + { + list.append(*it); + } + } + } + } + + return list; +} + + +void CNItem::removeItem() +{ + if (b_deleted) + return; + + const TextMap::iterator textMapEnd = m_textMap.end(); + for ( TextMap::iterator it = m_textMap.begin(); it != textMapEnd; ++it ) + it.data()->setCanvas(0l); + + Item::removeItem(); + updateConnectorPoints(false); +} + + +void CNItem::restoreFromItemData( const ItemData &itemData ) +{ + Item::restoreFromItemData(itemData); + + updateConnectorPoints(false); + + { + const BoolMap::const_iterator end = itemData.buttonMap.end(); + for ( BoolMap::const_iterator it = itemData.buttonMap.begin(); it != end; ++it ) + { + Button *b = button(it.key()); + if (b) + b->setState(it.data()); + } + } + { + const IntMap::const_iterator end = itemData.sliderMap.end(); + for ( IntMap::const_iterator it = itemData.sliderMap.begin(); it != end; ++it ) + { + Slider *s = slider(it.key()); + if (s) + s->setValue(it.data()); + } + } +} + + +ItemData CNItem::itemData() const +{ + ItemData itemData = Item::itemData(); + + const WidgetMap::const_iterator end = m_widgetMap.end(); + for ( WidgetMap::const_iterator it = m_widgetMap.begin(); it != end; ++it ) + { + if ( Slider *slider = dynamic_cast<Slider*>(*it) ) + itemData.sliderMap[slider->id()] = slider->value(); + + else if ( Button *button = dynamic_cast<Button*>(*it) ) + itemData.buttonMap[button->id()] = button->state(); + + } + + return itemData; +} + + +Node* CNItem::createNode( double _x, double _y, int orientation, const QString &name, uint type ) +{ + orientation %= 360; + if ( orientation < 0 ) + orientation += 360; + + Node::node_dir dir; + + if ( orientation == 0 ) dir = Node::dir_right; + else if ( orientation == 90 ) dir = Node::dir_down; + else if ( orientation == 180 ) dir = Node::dir_left; + else if ( orientation == 270 ) dir = Node::dir_up; + else + { + kdError() << k_funcinfo << "Unknown orientation: " << orientation << endl; + return 0l; + } + + Node *node; + if ( (type == Node::ec_pin) || (type == Node::ec_junction) ) + { + node = new ECNode( p_icnDocument, Node::node_type(type), dir, QPoint( 0, 0 ) ); + } + else + { + node = new FPNode( p_icnDocument, Node::node_type(type), dir, QPoint( 0, 0 ) ); + } + node->setLevel( level() ); + + node->setParentItem(this); + node->setChildId(name); + + NodeInfo info; + info.id = node->id(); + info.node = node; + info.x = _x; + info.y = _y; + info.orientation = orientation; + + m_nodeMap[name] = info; + + updateAttachedPositioning(); + + return node; +} + + +bool CNItem::removeNode( const QString &name ) +{ + NodeMap::iterator it = m_nodeMap.find(name); + if ( it == m_nodeMap.end() ) { + return false; + } + it.data().node->removeNode(); + p_icnDocument->flushDeleteList(); + m_nodeMap.erase(it); + return true; +} + + +Node *CNItem::getClosestNode( const QPoint &pos ) +{ + // Work through the nodes, finding the one closest to the (x, y) position + Node *shortestNode = 0L; + double shortestDistance = 1e10; // Nice large distance :-) + + const NodeMap::iterator end = m_nodeMap.end(); + for ( NodeMap::iterator it = m_nodeMap.begin(); it != end; ++it ) + { + Node *node = p_icnDocument->nodeWithID(it.data().id); + if (node) + { + // Calculate the distance + // Yeah, it's actually the distance squared; but it's all relative, so doesn't matter + double distance = std::pow(node->x()-pos.x(),2) + std::pow(node->y()-pos.y(),2); + + if ( distance < shortestDistance ) + { + shortestDistance = distance; + shortestNode = node; + } + } + } + + return shortestNode; +} + + +void CNItem::updateAttachedPositioning() +{ + if (b_deleted) + return; + + // Actually, we don't do anything anymore... +} + + +void CNItem::updateZ( int baseZ ) +{ + Item::updateZ(baseZ); + + double _z = z(); + + const NodeMap::iterator nodeMapEnd = m_nodeMap.end(); + for ( NodeMap::iterator it = m_nodeMap.begin(); it != nodeMapEnd; ++it ) + it.data().node->setZ( _z + 0.5 ); + + const WidgetMap::iterator widgetMapEnd = m_widgetMap.end(); + for ( WidgetMap::iterator it = m_widgetMap.begin(); it != widgetMapEnd; ++it ) + it.data()->setZ( _z + 0.5 ); + + const TextMap::iterator textMapEnd = m_textMap.end(); + for ( TextMap::iterator it = m_textMap.begin(); it != textMapEnd; ++it ) + it.data()->setZ( _z + 0.5 ); +} + + +void CNItem::snap( int newx, int newy ) +{ + // Ugly looking thing + // Basically means: Move item to the new position of newx-offsetx and then snap it to the 8-square-side grid + // This is in one move item call so that any attached connectors are only called once to update their routes. + moveBy( 4+newx-m_offset.x()-x()-(int)(newx-m_offset.x())%8, 4+newy-m_offset.y()-y()-(int)(newy-m_offset.y())%8 ); +} + +void CNItem::moveBy( double dx, double dy ) +{ + if ( dx == 0 && dy == 0 ) return; + updateConnectorPoints(false); + Item::moveBy( dx, dy ); + + setWidgetsPos( QPoint( int(x()), int(y()) ) ); +} + + +bool CNItem::mousePressEvent( const EventInfo &info ) +{ + bool accepted = Item::mousePressEvent(info); + if (!accepted) + accepted = CIWidgetMgr::mousePressEvent(info); + if (accepted) + setChanged(); + return accepted; +} + + +bool CNItem::mouseReleaseEvent( const EventInfo &info ) +{ + bool accepted = Item::mouseReleaseEvent(info); + if (!accepted) + accepted = CIWidgetMgr::mouseReleaseEvent(info); + if (accepted) + setChanged(); + return accepted; +} + + +bool CNItem::mouseDoubleClickEvent( const EventInfo &info ) +{ + bool accepted = Item::mouseDoubleClickEvent(info); + if (!accepted) + accepted = CIWidgetMgr::mouseDoubleClickEvent(info); + if (accepted) + setChanged(); + return accepted; +} + + +bool CNItem::mouseMoveEvent( const EventInfo &info ) +{ + bool accepted = Item::mouseMoveEvent(info); + if (!accepted) + accepted = CIWidgetMgr::mouseMoveEvent(info); + if (accepted) + setChanged(); + return accepted; +} + + +bool CNItem::wheelEvent( const EventInfo &info ) +{ + bool accepted = Item::wheelEvent(info); + if (!accepted) + accepted = CIWidgetMgr::wheelEvent(info); + if (accepted) + setChanged(); + return accepted; +} + + +void CNItem::enterEvent() +{ + Item::enterEvent(); + CIWidgetMgr::enterEvent(); + setChanged(); +} + + +void CNItem::leaveEvent() +{ + Item::leaveEvent(); + CIWidgetMgr::leaveEvent(); + setChanged(); +} + + +void CNItem::drawShape( QPainter &p ) +{ + if (!isVisible()) + return; + +// initPainter(p); + if ( isSelected() ) + p.setPen(m_selectedCol); + + p.drawPolygon(areaPoints()); + p.drawPolyline(areaPoints()); +// deinitPainter(p); +} + + +void CNItem::initPainter( QPainter &p ) +{ + if ( isSelected() ) + p.setPen(m_selectedCol); +} + + +void CNItem::updateConnectorPoints( bool add ) +{ + if ( b_deleted || !isVisible() ) + add = false; + + if ( b_pointsAdded == add ) + return; + + b_pointsAdded = add; + + Cells *cells = p_icnDocument->cells(); + if (!cells) return; + + const int cx = cells->width(); + const int cy = cells->height(); + + if ( cx < 1 || cy < 1 ) { + return; + } + + // Get translation matrix + // Hackish... + QWMatrix m; + if ( Component *c = dynamic_cast<Component*>(this) ) + m = c->transMatrix( c->angleDegrees(), c->flipped(), int(x()), int(y()), false ); + + // Convention used here: _UM = unmapped by both matrix and cell reference, _M = mapped + + const QPoint start_UM = QPoint( int(x()+offsetX())-cellSize, int(y()+offsetY())-cellSize ); + const QPoint end_UM = start_UM + QPoint( width()+2*cellSize, height()+2*cellSize ); + + const QPoint start_M = m.map(start_UM)/cellSize; + const QPoint end_M = m.map(end_UM)/cellSize; + + + int sx_M = start_M.x(); + int ex_M = end_M.x(); + + int sy_M = start_M.y(); + int ey_M = end_M.y(); + + + // Normalise start and end points + if ( sx_M > ex_M ) + { + const int temp = sx_M; + sx_M = ex_M; + ex_M = temp; + } + if ( sy_M > ey_M ) + { + const int temp = sy_M; + sy_M = ey_M; + ey_M = temp; + } + + ex_M++; + ey_M++; + + const int mult = add ? 1 : -1; + + for ( int x = sx_M; x < ex_M; x++ ) + { + for ( int y = sy_M; y < ey_M; y++ ) + { + if ( p_icnDocument->isValidCellReference( x, y ) ) + { + if ( x != sx_M && y != sy_M && x != (ex_M-1) && y != (ey_M-1) ) + { + (*cells)[x][y].CIpenalty += mult*ICNDocument::hs_item; + } + else + { +// (*cells)[x][y].CIpenalty += mult*ICNDocument::hs_item/2; + (*cells)[x][y].CIpenalty += mult*ICNDocument::hs_connector*5; + } + } + } + } + +#if 0 + // And subtract the positions of the node on the border + NodeMap::iterator end = m_nodeMap.end(); + for ( NodeMap::iterator it = m_nodeMap.begin(); it != end; ++it ) + { + const int x = (int)((it->second.node->x()-4)/cellSize); + const int y = (int)((it->second.node->y()-4)/cellSize); + if ( p_icnDocument->isValidCellReference(x,y) ) { + (*cells)[x][y].CIpenalty -= mult*ICNDocument::hs_connector*5; + } + } +#endif + + const TextMap::iterator textMapEnd = m_textMap.end(); + for ( TextMap::iterator it = m_textMap.begin(); it != textMapEnd; ++it ) + { + it.data()->updateConnectorPoints(add); + } + const WidgetMap::iterator widgetMapEnd = m_widgetMap.end(); + for ( WidgetMap::iterator it = m_widgetMap.begin(); it != widgetMapEnd; ++it ) + { + it.data()->updateConnectorPoints(add); + } +} + + +Text* CNItem::addDisplayText( const QString &id, const QRect & pos, const QString &display, bool internal, int flags ) +{ + Text *text = 0l; + TextMap::iterator it = m_textMap.find(id); + if ( it != m_textMap.end() ) + { +// kdWarning() << "CNItem::addDisplayText: removing old text"<<endl; + delete it.data(); + m_textMap.remove(it); + } + + text = new Text( "", this, pos, canvas(), flags ); + text->setZ( z()+(internal?0.1:-0.1) ); + + m_textMap[id] = text; + + // Calculate the correct size + setDisplayText( id, display ); + text->show(); + return text; +} + + +void CNItem::setDisplayText( const QString &id, const QString &display ) +{ + TextMap::iterator it = m_textMap.find(id); + if ( it == m_textMap.end() ) + { + kdError() << "CNItem::setDisplayText: Could not find text with id \""<<id<<"\""<<endl; + return; + } + it.data()->setText(display); + updateAttachedPositioning(); +} + + +void CNItem::removeDisplayText( const QString &id ) +{ + TextMap::iterator it = m_textMap.find(id); + if ( it == m_textMap.end() ) + { +// kdError() << "CNItem::removeDisplayText: Could not find text with id \""<<id<<"\""<<endl; + return; + } + it.data()->updateConnectorPoints(false); + delete it.data(); + m_textMap.remove(it); +} + + +QString CNItem::nodeId( const QString &internalNodeId ) +{ + NodeMap::iterator it = m_nodeMap.find(internalNodeId); + if ( it == m_nodeMap.end() ) return ""; + else return it.data().id; +} + + +Node *CNItem::childNode( const QString &childId ) +{ + return p_icnDocument->nodeWithID( nodeId(childId) ); +} + + +NodeInfo::NodeInfo() +{ + node = 0l; + x = 0.; + y = 0.; + orientation = 0; +} + + +#include "cnitem.moc" diff --git a/src/cnitem.h b/src/cnitem.h new file mode 100644 index 0000000..7a5f55a --- /dev/null +++ b/src/cnitem.h @@ -0,0 +1,191 @@ +/*************************************************************************** + * Copyright (C) 2003-2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef CNITEM_H +#define CNITEM_H + +#include "item.h" +#include "ciwidgetmgr.h" + + +class Button; +class CNItem; +class ICNDocument; +class Connector; +class DoubleSpinBox; +class LibraryItem; +class Node; +class QSlider; +class QString; +class QToolButton; +class QWMatrix; +class Slider; +class Text; + + +class NodeInfo +{ +public: + NodeInfo(); + + QString id; // External id (ICNDocument scope) + Node *node; //Pointer to the node + double x; // X position relative to item + double y; // Y position relative to item + int orientation; // Orientation relative to item +}; + +typedef QMap<QString, QString> StringMap; +typedef QMap<QString, NodeInfo> NodeMap; // Internal id, node info +typedef QValueList<QGuardedPtr<Connector> > ConnectorList; +typedef QMap<QString, QGuardedPtr<Text> > TextMap; + +/** +Essentially, all items that live on ICNDocument should inherit from this class. +This class provides much functionality (moving items, creation of associated nodes, +saving and editing of associated data, cutting / copying, etc) +@short Base class for all components/flowparts/etc +@author Daniel Clarke +@author David Saxton +*/ +class CNItem : public Item, public CIWidgetMgr +{ +Q_OBJECT +public: + CNItem( ICNDocument *_icnView, bool newItem, const QString &id ); + virtual ~CNItem(); + + /** + * Returns the run-time identifier for the CNItem - ItemDocument::RTTI::CNItem + */ + int rtti() const; + /** + * Creates a node which is attached to the item. The node will be moved + * about with the item, and destroyed along with the item. The position + * coordinates of the node are relative to the upper left corner of the item. + * @param type See Node::node_type + */ + Node* createNode( double _x, double _y, int orientation, const QString &name, uint type ); + /** + * Removes a child node. You should use this function if you want to remove + * any nodes during the lifetime of the CNItem. + */ + bool removeNode( const QString &name ); + /** + * Sets the mouse click point when moving this item + */ + void setInitialPos( const QPoint &pos ); + /** + * Snaps the component to the grid. + */ + void snap( int newx = -1, int newy = -1 ); + /** + * Returns the closest node that is associated with the CNItem + */ + Node *getClosestNode( const QPoint &pos ); + /** + * Returns a list of connectors associated with the CNItem + */ + ConnectorList connectorList(); + virtual bool preResize( QRect sizeRect ); + virtual bool mousePressEvent( const EventInfo &eventInfo ); + virtual bool mouseReleaseEvent( const EventInfo &eventInfo ); + virtual bool mouseDoubleClickEvent ( const EventInfo &eventInfo ); + virtual bool mouseMoveEvent( const EventInfo &eventInfo ); + virtual bool wheelEvent( const EventInfo &eventInfo ); + virtual void enterEvent(); + virtual void leaveEvent(); + /** + * ICNDocument needs to know what 'cells' a CNItem is present in, + * so that connection mapping can be done to avoid CNItems. + * This function will add the hit penalty to the cells pointed to + * by ICNDocument::cells() + */ + virtual void updateConnectorPoints( bool add ); + /** + * Converts the id used to internally identify a node to the global + * ICNDocument node id. eg "vss" might return "node__13". + */ + QString nodeId( const QString &internalNodeId ); + /** + * Returns a pointer to the node with the given internal (child) id + */ + Node *childNode( const QString &childId ); + /** + * Returns the node map used: + * QMap<QString, NodeInfo> NodeMap + * It's probably best to cache this data + */ + NodeMap nodeMap() const { return m_nodeMap; } + /** + * Returns the TextMap used for canvas text + */ + TextMap textMap() const { return m_textMap; } + virtual void setVisible( bool yes ); + virtual void updateZ( int baseZ ); + + virtual ItemData itemData() const; + virtual void restoreFromItemData( const ItemData &itemData ); + virtual void updateNodeLevels(); + +public slots: + /** + * Moves item - use this instead of moveBy() so that associated Nodes also get moved + */ + virtual void moveBy( double dx, double dy ); + /** + * Remove the item and associated nodes. It appends the item to the + * ICNDocument's delete list, so you must call ICNDocument::flushDeleteList() + * after calling this (and possible ICNDocument::clearDeleteList() befor + * calling it) The virtual function void handleRemove() is called to allow + * any child classes to clear up any neccessary data (which doesn't do + * anything by default), before CNItem does the rest + */ + virtual void removeItem(); + /** + * This item has been resized, so update the nodes relative positions + */ + virtual void updateAttachedPositioning(); + +protected: + virtual void reparented( Item *oldParent, Item *newParent ); + virtual void drawShape( QPainter &p ); + virtual void postResize(); + /** + * CNItem handles drawing of text associated with the CNItem. + * @param id is a unique identifier that can be used to change the text displayed. + * @param pos is the position that the text occupies relative to the top left corner of the CNItem. + * @param display is the actual text to be displayed. + * @param internal is used to determine the z-level of the text - whether it should be below or above the item + * @param flags Text alignment flags - Qt::AlignmentFlags and Qt::TextFlags OR'd together. + */ + Text* addDisplayText( const QString &id, const QRect & pos, const QString &display, bool internal = true, int flags = Qt::AlignHCenter | Qt::AlignVCenter ); + void setDisplayText( const QString &id, const QString &display ); + /** + * Remove the display text with the given id + */ + void removeDisplayText( const QString &id ); + /** + * Sets the right colour if selected, transforms the matrix of the painter + */ + virtual void initPainter( QPainter &p ); + + QPoint m_offset; + QGuardedPtr<ICNDocument> p_icnDocument; + TextMap m_textMap; + NodeMap m_nodeMap; + QColor m_selectedCol; + QColor m_brushCol; + bool b_pointsAdded; +}; +typedef QValueList<CNItem*> CNItemList; + +#endif + diff --git a/src/cnitemgroup.cpp b/src/cnitemgroup.cpp new file mode 100644 index 0000000..21a9878 --- /dev/null +++ b/src/cnitemgroup.cpp @@ -0,0 +1,598 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "cnitemgroup.h" +#include "component.h" +#include "connector.h" +#include "flowpart.h" +#include "icndocument.h" +#include "node.h" +#include "nodegroup.h" + +CNItemGroup::CNItemGroup( ICNDocument *icnDocument, const char *name) + : ItemGroup( icnDocument, name ) +{ + p_icnDocument = icnDocument; + m_connectorCount = 0; + m_nodeCount = 0; + m_currentLevel = -1; +} + + +CNItemGroup::~CNItemGroup() +{ +} + + +bool CNItemGroup::addItem( Item *item ) +{ + // Note, we must prepend the item to the list so that + // activeCNItem() can return the item at the start + // of the list as the most recently added item if some + // the previous activeCNItem is removed + + if ( !item || !item->canvas() || m_itemList.contains(item) || !item->isMovable() ) + return false; + + if ( m_currentLevel != -1 && item->level() > m_currentLevel ) + return false; + + if ( item && m_currentLevel > item->level() ) + removeAllItems(); + + registerItem(item); + m_currentLevel = item->level(); + setActiveItem(item); + item->setSelected(true); + updateInfo(); + emit itemAdded(item); + return true; +} + + +bool CNItemGroup::addNode( Node *node ) +{ + if ( !node || m_nodeList.contains(node) || node->isChildNode() ) + return false; + m_nodeList.prepend(node); + node->setSelected(true); + updateInfo(); + emit nodeAdded(node); + return true; +} + + +bool CNItemGroup::addConnector( Connector *con ) +{ + if ( !con || m_connectorList.contains(con) ) + return false; + m_connectorList.prepend(con); + con->setSelected(true); + updateInfo(); + emit connectorAdded(con); + return true; +} + + +bool CNItemGroup::addQCanvasItem( QCanvasItem *qcanvasItem ) +{ + if (!qcanvasItem) return false; + + Item *item = dynamic_cast<Item*>(qcanvasItem); + if (item) + return addItem(item); + + Node *node = dynamic_cast<Node*>(qcanvasItem); + if (node) + return addNode(node); + + Connector *connector = dynamic_cast<Connector*>(qcanvasItem); + if (!connector) + { + ConnectorLine *connectorLine = dynamic_cast<ConnectorLine*>(qcanvasItem); + if (connectorLine) + connector = connectorLine->parent(); + } + if (connector) + return addConnector(connector); + + return false; +} + +void CNItemGroup::setItems( QCanvasItemList list ) +{ + ItemList itemRemoveList = m_itemList; + ConnectorList connectorRemoveList = m_connectorList; + NodeList nodeRemoveList = m_nodeList; + + const QCanvasItemList::const_iterator end = list.end(); + for ( QCanvasItemList::const_iterator it = list.begin(); it != end; ++it ) + { + switch ( *it ? (*it)->rtti() : 0 ) + { + case ItemDocument::RTTI::CNItem: + case ItemDocument::RTTI::DrawPart: + { + itemRemoveList.remove( dynamic_cast<Item*>(*it) ); + break; + } + case ItemDocument::RTTI::Node: + { + nodeRemoveList.remove( dynamic_cast<Node*>(*it) ); + break; + } + case ItemDocument::RTTI::Connector: + { + connectorRemoveList.remove( dynamic_cast<Connector*>(*it) ); + break; + } + case ItemDocument::RTTI::ConnectorLine: + { + connectorRemoveList.remove( (dynamic_cast<ConnectorLine*>(*it))->parent() ); + break; + } + default: + break; + } + } + + { + const ItemList::const_iterator end = itemRemoveList.end(); + for ( ItemList::const_iterator it = itemRemoveList.begin(); it != end; ++it ) + { + removeItem(*it); + (*it)->setSelected(false); + } + } + + { + const NodeList::const_iterator end = nodeRemoveList.end(); + for ( NodeList::const_iterator it = nodeRemoveList.begin(); it != end; ++it ) + { + removeNode(*it); + (*it)->setSelected(false); + } + } + + { + const ConnectorList::const_iterator end = connectorRemoveList.end(); + for ( ConnectorList::const_iterator it = connectorRemoveList.begin(); it != end; ++it ) + { + removeConnector(*it); + (*it)->setSelected(false); + } + } + + { + const QCanvasItemList::const_iterator end = list.end(); + for ( QCanvasItemList::const_iterator it = list.begin(); it != end; ++it ) + { + // We don't need to check that we've already got the item as it will + // be checked in the function call + addQCanvasItem(*it); + } + } +} + + +void CNItemGroup::removeItem( Item *item ) +{ + if ( !item || !m_itemList.contains(item) ) + return; + unregisterItem(item); + if ( m_activeItem == item ) + getActiveItem(); + + item->setSelected(false); + updateInfo(); + emit itemRemoved(item); +} + + +void CNItemGroup::removeNode( Node *node ) +{ + if ( !node || !m_nodeList.contains(node) ) + return; + m_nodeList.remove(node); + node->setSelected(false); + updateInfo(); + emit nodeRemoved(node); +} + + +void CNItemGroup::removeConnector( Connector *con ) +{ + if ( !con || !m_connectorList.contains(con) ) return; + m_connectorList.remove(con); + con->setSelected(false); + updateInfo(); + emit connectorRemoved(con); +} + + +void CNItemGroup::removeQCanvasItem( QCanvasItem *qcanvasItem ) +{ + if (!qcanvasItem) return; + + Item *item = dynamic_cast<Item*>(qcanvasItem); + if (item) + return removeItem(item); + + Node *node = dynamic_cast<Node*>(qcanvasItem); + if (node) + return removeNode(node); + + Connector *connector = dynamic_cast<Connector*>(qcanvasItem); + if (!connector) + { + ConnectorLine *connectorLine = dynamic_cast<ConnectorLine*>(qcanvasItem); + if (connectorLine) + connector = connectorLine->parent(); + } + if (connector) + return removeConnector(connector); +} + + +NodeList CNItemGroup::nodes( bool excludeParented ) const +{ + NodeList nodeList = m_nodeList; + if (excludeParented) + return nodeList; + + NodeGroupList translatableNodeGroups; + p_icnDocument->getTranslatable( items(false), 0l, 0l, &translatableNodeGroups ); + + NodeGroupList::iterator end = translatableNodeGroups.end(); + for ( NodeGroupList::iterator it = translatableNodeGroups.begin(); it != end; ++it ) + { + const NodeList internal = (*it)->internalNodeList(); + NodeList::const_iterator internalEnd = internal.end(); + for ( NodeList::const_iterator intIt = internal.begin(); intIt != internalEnd; ++intIt ) + { + if ( *intIt && !nodeList.contains(*intIt) ) + nodeList << *intIt; + } + } + + return nodeList; +} + + +ConnectorList CNItemGroup::connectors( bool excludeParented ) const +{ + ConnectorList connectorList = m_connectorList; + if (excludeParented) + return connectorList; + + ConnectorList translatableConnectors; + NodeGroupList translatableNodeGroups; + p_icnDocument->getTranslatable( items(false), 0l, &translatableConnectors, &translatableNodeGroups ); + + ConnectorList::iterator tcEnd = translatableConnectors.end(); + for ( ConnectorList::iterator it = translatableConnectors.begin(); it != tcEnd; ++it ) + { + if ( *it && !connectorList.contains(*it) ) + connectorList << *it; + } + + NodeGroupList::iterator end = translatableNodeGroups.end(); + for ( NodeGroupList::iterator it = translatableNodeGroups.begin(); it != end; ++it ) + { + const NodeList internal = (*it)->internalNodeList(); + NodeList::const_iterator internalEnd = internal.end(); + for ( NodeList::const_iterator intIt = internal.begin(); intIt != internalEnd; ++intIt ) + { + const ConnectorList connected = (*intIt)->inputConnectorList() + (*intIt)->outputConnectorList(); + ConnectorList::const_iterator connectedEnd = connected.end(); + for ( ConnectorList::const_iterator conIt = connected.begin(); conIt != connectedEnd; ++conIt ) + { + if ( *conIt && !connectorList.contains(*conIt) ) + connectorList << *conIt; + } + } + } + + return connectorList; +} + + +bool CNItemGroup::contains( QCanvasItem *qcanvasItem ) const +{ + if (!qcanvasItem) + return false; + + const ItemList::const_iterator ciEnd = m_itemList.end(); + for ( ItemList::const_iterator it = m_itemList.begin(); it != ciEnd; ++it ) + { + if ( *it == qcanvasItem ) + return true; + } + const ConnectorList::const_iterator conEnd = m_connectorList.end(); + for ( ConnectorList::const_iterator it = m_connectorList.begin(); it != conEnd; ++it ) + { + if ( *it == qcanvasItem ) + return true; + } + const NodeList::const_iterator nodeEnd = m_nodeList.end(); + for ( NodeList::const_iterator it = m_nodeList.begin(); it != nodeEnd; ++it ) + { + if ( *it == qcanvasItem ) + return true; + } + + return false; +} + + +void CNItemGroup::setSelected( bool sel ) +{ + const ItemList::iterator ciEnd = m_itemList.end(); + for ( ItemList::iterator it = m_itemList.begin(); it != ciEnd; ++it ) + { + if (*it && (*it)->isSelected() != sel ) + (*it)->setSelected(sel); + } + const ConnectorList::iterator conEnd = m_connectorList.end(); + for ( ConnectorList::iterator it = m_connectorList.begin(); it != conEnd; ++it ) + { + if ( *it && (*it)->isSelected() != sel ) + (*it)->setSelected(sel); + } + const NodeList::iterator nodeEnd = m_nodeList.end(); + for ( NodeList::iterator it = m_nodeList.begin(); it != nodeEnd; ++it ) + { + if ( *it && (*it)->isSelected() != sel ) + (*it)->setSelected(sel); + } +} + + +bool CNItemGroup::canRotate() const +{ + const ItemList::const_iterator end = m_itemList.end(); + for ( ItemList::const_iterator it = m_itemList.begin(); it != end; ++it ) + { + CNItem *cnItem = dynamic_cast<CNItem*>((Item*)*it); + if ( cnItem && cnItem->canRotate() ) + return true; + } + return false; +} + + +bool CNItemGroup::canFlip() const +{ + const ItemList::const_iterator end = m_itemList.end(); + for ( ItemList::const_iterator it = m_itemList.begin(); it != end; ++it ) + { + CNItem *cnItem = dynamic_cast<CNItem*>((Item*)*it); + if ( cnItem && cnItem->canFlip() ) + return true; + } + return false; +} + + +void CNItemGroup::slotRotateCW() +{ + const ItemList::iterator end = m_itemList.end(); + for ( ItemList::iterator it = m_itemList.begin(); it != end; ++it ) + { + Component *component = dynamic_cast<Component*>((Item*)*it); + if ( component && component->isMovable() && component->canRotate() ) + { + int oldAngle = component->angleDegrees(); + component->setAngleDegrees((oldAngle+90)%360); + } + } + p_icnDocument->requestStateSave(); +} + +void CNItemGroup::slotRotateCCW() +{ + const ItemList::iterator end = m_itemList.end(); + for ( ItemList::iterator it = m_itemList.begin(); it != end; ++it ) + { + Component *component = dynamic_cast<Component*>((Item*)*it); + if ( component && component->isMovable() && component->canRotate() ) + { + int oldAngle = component->angleDegrees(); + component->setAngleDegrees((oldAngle+270)%360); + } + } + p_icnDocument->requestStateSave(); +} + +void CNItemGroup::slotFlip() +{ + const ItemList::iterator end = m_itemList.end(); + for ( ItemList::iterator it = m_itemList.begin(); it != end; ++it ) + { + Component *component = dynamic_cast<Component*>((Item*)*it); + if ( component && component->isMovable() && component->canFlip() ) + { + bool oldFlipped = component->flipped(); + component->setFlipped(!oldFlipped); + } + } + p_icnDocument->requestStateSave(); +} + + +void CNItemGroup::setOrientationAngle( int _angle ) +{ + const ItemList::iterator end = m_itemList.end(); + for ( ItemList::iterator it = m_itemList.begin(); it != end; ++it ) + { + Component *component = dynamic_cast<Component*>((Item*)*it); + if ( component && component->isMovable() && component->canRotate() ) + { + int oldAngle = component->angleDegrees(); + if ( oldAngle != _angle ) + { + component->setAngleDegrees(_angle); + } + } + } + p_icnDocument->requestStateSave(); +} + + +void CNItemGroup::setComponentOrientation( int angleDegrees, bool flipped ) +{ + bool flipping = flipped; + bool rotating = (((angleDegrees%360)+360)%360) != 0; + + const ItemList::iterator end = m_itemList.end(); + for ( ItemList::iterator it = m_itemList.begin(); it != end; ++it ) + { + Component *component = dynamic_cast<Component*>((Item*)*it); + if ( component && component->isMovable() && (!flipping || component->canFlip()) && (!rotating || component->canRotate()) ) + { + int oldAngle = component->angleDegrees(); + int oldFlipped = component->flipped(); + if ( (oldAngle != angleDegrees) || (oldFlipped != flipped) ) + { + if ( component->canFlip() ) + component->setFlipped(flipped); + if ( component->canRotate() ) + component->setAngleDegrees(angleDegrees); + } + } + } + p_icnDocument->requestStateSave(); +} + + +void CNItemGroup::setFlowPartOrientation( unsigned orientation ) +{ + const ItemList::iterator end = m_itemList.end(); + for ( ItemList::iterator it = m_itemList.begin(); it != end; ++it ) + { + FlowPart * flowPart = dynamic_cast<FlowPart*>((Item*)*it); + if ( flowPart && flowPart->isMovable() ) + flowPart->setOrientation(orientation); + } + p_icnDocument->requestStateSave(); +} + + +void CNItemGroup::mergeGroup( ItemGroup *itemGroup ) +{ + CNItemGroup *group = dynamic_cast<CNItemGroup*>(itemGroup); + if (!group) return; + + const ItemList items = group->items(); + const ConnectorList connectors = group->connectors(); + const NodeList nodes = group->nodes(); + + const ItemList::const_iterator ciEnd = items.end(); + for ( ItemList::const_iterator it = items.begin(); it != ciEnd; ++it ) + { + addItem(*it); + } + const ConnectorList::const_iterator conEnd = connectors.end(); + for ( ConnectorList::const_iterator it = connectors.begin(); it != conEnd; ++it ) + { + addConnector(*it); + } + const NodeList::const_iterator nodeEnd = nodes.end(); + for ( NodeList::const_iterator it = nodes.begin(); it != nodeEnd; ++it ) + { + addNode(*it); + } +} + + +void CNItemGroup::removeAllItems() +{ + while ( !m_itemList.isEmpty() ) + removeItem(*m_itemList.begin()); + + while ( !m_connectorList.isEmpty() ) + removeConnector(*m_connectorList.begin()); + + while ( !m_nodeList.isEmpty() ) + removeNode(*m_nodeList.begin()); +} + + +void CNItemGroup::deleteAllItems() +{ + const ItemList::iterator ciEnd = m_itemList.end(); + for ( ItemList::iterator it = m_itemList.begin(); it != ciEnd; ++it ) + { + if (*it) + (*it)->removeItem(); + } + const NodeList::iterator nodeEnd = m_nodeList.end(); + for ( NodeList::iterator it = m_nodeList.begin(); it != nodeEnd; ++it ) + { + if ( *it && !(*it)->isChildNode() ) + { + (*it)->removeNode(); + } + } + const ConnectorList::iterator conEnd = m_connectorList.end(); + for ( ConnectorList::iterator it = m_connectorList.begin(); it != conEnd; ++it ) + { + if (*it) + { + (*it)->removeConnector(); + } + } + + // Clear the lists + removeAllItems(); +} + + +void CNItemGroup::updateInfo() +{ + m_connectorCount = m_connectorList.count(); + m_nodeCount = m_nodeList.count(); + + if ( m_itemList.isEmpty() ) + m_currentLevel = -1; +} + + +void CNItemGroup::getActiveItem() +{ + if ( m_itemList.isEmpty() ) + setActiveItem(0l); + else + setActiveItem( *m_itemList.begin() ); +} + + +void CNItemGroup::setActiveItem( Item *item ) +{ + if ( item == m_activeItem ) + return; + m_activeItem = item; +} + + +QStringList CNItemGroup::itemIDs() +{ + QStringList list; + ItemList::iterator end = m_itemList.end(); + for ( ItemList::iterator it = m_itemList.begin(); it != end; ++it ) + { + if (*it) { + list += (*it)->id(); + } + } + return list; +} + +#include "cnitemgroup.moc" diff --git a/src/cnitemgroup.h b/src/cnitemgroup.h new file mode 100644 index 0000000..fe2c064 --- /dev/null +++ b/src/cnitemgroup.h @@ -0,0 +1,215 @@ +/*************************************************************************** + * Copyright (C) 2003-2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef CANVASITEMGROUP_H +#define CANVASITEMGROUP_H + +#include "itemgroup.h" + + +class CNItem; +class Item; +class ICNDocument; +class Component; +class Connector; +class FlowPart; +class Node; +class ECNode; +class FPNode; +class ICNDocument; +class QCanvasItem; +class QCanvasItemList; + +typedef QValueList<QGuardedPtr<Item> > ItemList; +typedef QValueList<QGuardedPtr<Node> > NodeList; +typedef QValueList<QGuardedPtr<Connector> > ConnectorList; + +/** +@author David Saxton +*/ +class CNItemGroup : public ItemGroup +{ +Q_OBJECT +public: + CNItemGroup( ICNDocument *icnDocument, const char *name = 0 ); + ~CNItemGroup(); + + /** + * Adds a CNItem to the group, if it is not already in it, or other items at + * a lower levels are already in the group. If there are items are a high level, + * those items are removed first. Returns false on failure to add. + */ + bool addItem( Item *item ); + /** + * Adds a Node to the group, if it is not already in it. Note: This node + * will *NOT* be added if it is a child node, and the function will return false. + * If the node is not already present, and is added, then this will return true. + */ + bool addNode( Node *node ); + /** + * Adds a Connector to the group, if it is not already in it (if it is, returns false) + */ + bool addConnector( Connector *con ); + /** + * If the item is a a CNItem, Node or Connector, returns the status + * for that particular add function, else returns false + */ + virtual bool addQCanvasItem( QCanvasItem *qcanvasItem ); + /** + * Sets the contained items to those in this list + */ + virtual void setItems( QCanvasItemList list ); + /** + * Removes the CNItem from the group + */ + void removeItem( Item *item ); + /** + * Removes the Node from the group + */ + void removeNode( Node *node ); + /** + * Removes the Connector from the group + */ + void removeConnector( Connector *con ); + /** + * If the item is a a CNItem, Node or Connector, then attempts to remove it + */ + virtual void removeQCanvasItem( QCanvasItem *qcanvasItem ); + /** + * Returns true if the QCanvasItem passed is contained in the group + */ + virtual bool contains( QCanvasItem *qcanvasItem ) const; + /** + * Returns the number of Nodes in the CanvasGroup + */ + uint nodeCount() const { return m_nodeCount; } + /** + * Returns the number of Connectors in the CanvasGroup + */ + uint connectorCount() const { return m_connectorCount; } + /** + * Returns the total number of items in the group + * (CNItems, Nodes, Connectors) + */ + uint count() const { return itemCount()+m_nodeCount+m_connectorCount; } + /** + * Sets the selected state of all items in the group + */ + virtual void setSelected( bool sel ); + /** + * Sets the orientation (degrees component) of all items in the group + */ + void setOrientationAngle( int angleDegrees ); + /** + * Sets the orientation (flipped component) of all items in the group + */ + void setOrientationFlipped( bool flipped ); + /** + * Sets the orientation of all flowparts in the group + */ + void setFlowPartOrientation( unsigned orientation ); + /** + * Sets the orientation (degrees and flipped) of all components in the group + */ + void setComponentOrientation( int angleDegrees, bool flipped ); + /** + * Merges all items in the given group with this group + */ + virtual void mergeGroup( ItemGroup *group ); + /** + * Removes all items from this group (doesn't delete them) + * @param unselect whether to unselect the items or not. This will be done after removal from group + */ + virtual void removeAllItems(); + /** + * Attempts to delete everything in the group. + * Note: You *must* call ICNDocument::flushDeleteList() after calling this function, + * as this function only tells the items to remove themselves + */ + virtual void deleteAllItems(); + /** + * Returns a list of all the Nodes in the group. + * @param excludeParented if false, then nodes that are fully contained + * within item children will also be returned. + */ + NodeList nodes( bool excludeParented = true ) const; + /** + * Returns a list of all the Connectors in the group. + * @param excludeParented if false, then connectors that are fully contained + * within item children will also be returned. + */ + ConnectorList connectors( bool excludeParented = true ) const; + /** + * Returns a list of the ids of all the CNItems in the group. + */ + QStringList itemIDs(); + /** + * Returns true if at least some of the CNItems in this group can be + * rotated. Returns false if no items present. + */ + bool canRotate() const; + /** + * Returns true if at least some of the CNItems in this group can be + * flipped. Returns false if no items present. + */ + bool canFlip() const; + + +public slots: + /** + * Sets the orientation of all selected items to 0 degrees. + */ + void slotSetOrientation0() { setOrientationAngle(0); } + /** + * Sets the orientation of all selected items to 90 degrees. + */ + void slotSetOrientation90() { setOrientationAngle(90); } + /** + * Sets the orientation of all selected items to 180 degrees. + */ + void slotSetOrientation180() { setOrientationAngle(180); } + /** + * Sets the orientation of all selected items to 270 (actually -90) degrees. + */ + void slotSetOrientation270() { setOrientationAngle(-90); } + /** + * Rotates all CNItems in the group clockwise + */ + void slotRotateCW(); + /** + * Rotates all CNItems in the group counter-clockwise + */ + void slotRotateCCW(); + /** + * Flips all CNItems in the group + */ + void slotFlip(); + +signals: + void connectorAdded( Connector *con ); + void connectorRemoved( Connector *con ); + void nodeAdded( Node *node ); + void nodeRemoved( Node *node ); + +protected: + void updateInfo(); + void getActiveItem(); + void setActiveItem( Item *item ); + +private: + ICNDocument *p_icnDocument; + ConnectorList m_connectorList; + NodeList m_nodeList; + uint m_connectorCount; + uint m_nodeCount; + int m_currentLevel; // We can only accept CNItems of one level +}; + +#endif diff --git a/src/connector.cpp b/src/connector.cpp new file mode 100644 index 0000000..03a304c --- /dev/null +++ b/src/connector.cpp @@ -0,0 +1,652 @@ +/*************************************************************************** + * Copyright (C) 2003-2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "circuitdocument.h" +#include "connector.h" +#include "conrouter.h" +#include "cnitem.h" +#include "ecnode.h" +#include "itemdocumentdata.h" +#include "wire.h" + +#include <kdebug.h> + + +#include <cmath> + +inline static int toCanvas( int pos ) +{ + return pos*8+4; +} +inline static int fromCanvas( int pos ) +{ + return (pos-4)/8; +// return (pos>0) ? int((pos-3)/8) : int((pos-5)/8); +} + +inline static QPoint toCanvas( const QPoint * const pos ) +{ + return QPoint( toCanvas(pos->x()), toCanvas(pos->y()) ); +} +inline static QPoint fromCanvas( const QPoint * const pos ) +{ + return QPoint( fromCanvas(pos->x()), fromCanvas(pos->y()) ); +} + +inline static QPoint toCanvas( const QPoint &pos ) +{ + return QPoint( toCanvas(pos.x()), toCanvas(pos.y()) ); +} +inline static QPoint fromCanvas( const QPoint &pos ) +{ + return QPoint( fromCanvas(pos.x()), fromCanvas(pos.y()) ); +} + + +//BEGIN class Connector +Connector::Connector( Node * startNode, Node * endNode, ICNDocument *icnDocument, QString *id ) + : QObject(icnDocument), + QCanvasPolygon( icnDocument->canvas() ) +{ + p_icnDocument = icnDocument; + m_conRouter = new ConRouter(p_icnDocument); + p_parentContainer = 0l; + m_startNode = startNode; + m_endNode = endNode; + p_nodeGroup = 0l; + b_semiHidden = false; + b_deleted = false; + b_pointsAdded = false; + b_manualPoints = false; + m_bIsSyncingWires = false; + + if (id) + { + m_id = *id; + if ( !p_icnDocument->registerUID(*id) ) + { +// kdDebug() << k_funcinfo << "KTechlab: Connector attempted to register given ID, but ID already in use"<<endl; + } + } + else + m_id = p_icnDocument->generateUID("connector"); + + p_icnDocument->registerItem(this); + + p_icnDocument->requestRerouteInvalidatedConnectors(); + setVisible(true); + + ECNode * startECNode = dynamic_cast<ECNode*>(startNode); + ECNode * endECNode = dynamic_cast<ECNode*>(endNode); + if ( startECNode && endECNode ) + { + connect( startECNode, SIGNAL(numPinsChanged(unsigned)), this, SLOT(syncWiresWithNodes()) ); + connect( endECNode, SIGNAL(numPinsChanged(unsigned)), this, SLOT(syncWiresWithNodes()) ); + syncWiresWithNodes(); + } +} + + +Connector::~Connector() +{ + p_icnDocument->unregisterUID( id() ); + + delete m_conRouter; + m_conRouter = 0l; + + for ( unsigned i = 0; i < m_wires.size(); i++ ) + delete m_wires[i]; + m_wires.resize(0); +} + + +int Connector::rtti() const +{ + return ItemDocument::RTTI::Connector; +} + + +void Connector::syncWiresWithNodes() +{ + ECNode * startECNode = dynamic_cast<ECNode*>((Node*)m_startNode); + ECNode * endECNode = dynamic_cast<ECNode*>((Node*)m_endNode); + + if ( !startECNode || !endECNode ) + return; + + unsigned newNumWires = 0; + + if ( startECNode->type() == Node::ec_junction || + endECNode->type() == Node::ec_junction ) + newNumWires = QMAX( startECNode->numPins(), endECNode->numPins() ); + + else + newNumWires = QMIN( startECNode->numPins(), endECNode->numPins() ); + + unsigned oldNumWires = m_wires.size(); + + if ( newNumWires == oldNumWires ) + return; + + m_bIsSyncingWires = true; + if ( startECNode->type() == Node::ec_junction ) + startECNode->setNumPins(newNumWires); + if ( endECNode->type() == Node::ec_junction ) + endECNode->setNumPins(newNumWires); + m_bIsSyncingWires = false; + + if ( newNumWires > oldNumWires ) + { + m_wires.resize(newNumWires); + for ( unsigned i = oldNumWires; i < newNumWires; i++ ) + { + if ( startECNode->pin(i) && endECNode->pin(i) ) + m_wires[i] = new Wire( startECNode->pin(i), endECNode->pin(i) ); + } + } + else + { + for ( unsigned i = newNumWires; i < oldNumWires; i++ ) + delete m_wires[i]; + m_wires.resize(newNumWires); + } + + updateConnectorLines(); + emit numWiresChanged(newNumWires); +} + + +void Connector::setParentContainer( const QString &cnItemId ) +{ +// // We only allow the node to be parented once +// if ( p_parentContainer || !ICNDocument->itemWithID(cnItemId) ) return; + p_parentContainer = p_icnDocument->cnItemWithID(cnItemId); +} + + +void Connector::removeConnector( Node* ) +{ + if (b_deleted) + return; + b_deleted = true; + + // Remove 'penalty' points for this connector from the ICNDocument + updateConnectorPoints(false); + + emit selected(false); + emit removed(this); + if ( m_startNode ) + m_startNode->removeConnector(this); + if ( m_endNode ) + m_endNode->removeConnector(this); + p_icnDocument->appendDeleteList(this); +} + + +int getSlope( float x1, float y1, float x2, float y2 ) +{ + enum slope + { + s_n = 0,// . + s_v, // | + s_h, // - + s_s, // / + s_d // \ (backwards slash) + + }; + + if ( x1 == x2 ) + { + if ( y1 == y2 ) { + return s_n; + } + return s_v; + } + else if ( y1 == y2 ) { + return s_h; + } + else if ( (y2-y1)/(x2-x1) > 0 ) { + return s_s; + } + else { + return s_d; + } +} + + +void Connector::updateDrawList() +{ + if ( !m_startNode || !m_endNode || !canvas() ) { + return; + } + + QPointList drawLineList; + + int prevX = (*m_conRouter->cellPointList()->begin()).x(); + int prevY = (*m_conRouter->cellPointList()->begin()).y(); + + int prevX_canvas = toCanvas(prevX); + int prevY_canvas = toCanvas(prevY); + + Cells *cells = p_icnDocument->cells(); + + bool bumpNow = false; + const QPointList::const_iterator cplEnd = m_conRouter->cellPointList()->end(); + for ( QPointList::const_iterator it = m_conRouter->cellPointList()->begin(); it != cplEnd; ++it ) + { + const int x = (*it).x(); + const int y = (*it).y(); + const int numCon = p_icnDocument->isValidCellReference(x,y) ? (*cells)[x][y].numCon : 0; + + const int y_canvas = toCanvas(y); + const int x_canvas = toCanvas(x); + + const bool bumpNext = ( prevX == x && + numCon > 1 && + std::abs(y_canvas-m_startNode->y())>8 && + std::abs(y_canvas-m_endNode->y())>8 ); + + int x0 = prevX_canvas; + int x2 = x_canvas; + int x1 = (x0+x2)/2; + + int y0 = prevY_canvas; + int y3 = y_canvas; + int y1 = ( y0 == y3 ) ? y0 : ((y0<y3) ? y0+3 : y0-3); + int y2 = ( y0 == y3 ) ? y3 : ((y0<y3) ? y3-3 : y3+3); + + if (bumpNow) x0 += 3; + if (bumpNext) x2 += 3; + + if ( !bumpNow && !bumpNext ) + { + drawLineList += QPoint( x0, y0 ); + drawLineList += QPoint( x2, y3 ); + } + else if (bumpNow) + { + drawLineList += QPoint( x0, y0 ); + drawLineList += QPoint( x1, y1 ); + drawLineList += QPoint( x2, y3 ); + } + else if (bumpNext) + { + drawLineList += QPoint( x0, y0 ); + drawLineList += QPoint( x1, y2 ); + drawLineList += QPoint( x2, y3 ); + } + else + { + drawLineList += QPoint( x0, y0 ); + drawLineList += QPoint( x1, y1 ); + drawLineList += QPoint( x1, y2 ); + drawLineList += QPoint( x2, y3 ); + } + + prevX = x; + prevY = y; + prevY_canvas = y_canvas; + prevX_canvas = x_canvas; + bumpNow = bumpNext; + } + + // Now, remove redundant points (i.e. those that are either repeated or are + // in the same direction as the previous points) + + if ( drawLineList.size() < 3 ) { + return; + } + + const QPointList::iterator dllEnd = drawLineList.end(); + + QPointList::iterator previous = drawLineList.begin(); + + QPointList::iterator current = previous; + current++; + + QPointList::const_iterator next = current; + next++; + + while ( previous != dllEnd && current != dllEnd && next != dllEnd ) + { + const int slope1 = getSlope( (*previous).x(), (*previous).y(), (*current).x(), (*current).y() ); + const int slope2 = getSlope( (*current).x(), (*current).y(), (*next).x(), (*next).y() ); + + if ( slope1 == slope2 || slope1 == 0 || slope2 == 0 ) + { + *current = QPoint( -1, -1 ); + } + else + { + previous = current; + } + + current++; + next++; + } + + drawLineList.remove( QPoint( -1, -1 ) ); + + // Find the bounding rect + { + int x1=-1, y1=-1, x2=-1, y2=-1; + const QPointList::iterator end = drawLineList.end(); + for ( QPointList::iterator it = drawLineList.begin(); it != end; ++it ) + { + const QPoint p = *it; + if ( p.x() < x1 || x1 == -1 ) { + x1 = p.x(); + } + if ( p.x() > x2 || x2 == -1 ) { + x2 = p.x(); + } + if ( p.y() < y1 || y1 == -1 ) { + y1 = p.y(); + } + if ( p.y() > y2 || y2 == -1 ) { + y2 = p.y(); + } + } + + QRect boundRect( x1, y1, x2-x1, y2-y1 ); + if ( boundRect != m_oldBoundRect ) + { + canvas()->setChanged( boundRect | m_oldBoundRect ); + m_oldBoundRect = boundRect; + } + } + + //BEGIN build up ConnectorLine list + const ConnectorLineList::iterator ConnectorLineEnd = m_connectorLineList.end(); + for ( ConnectorLineList::iterator it = m_connectorLineList.begin(); it != ConnectorLineEnd; ++it ) + delete *it; + m_connectorLineList.clear(); + + if ( drawLineList.size() > 1 ) + { + QPoint prev = drawLineList.first(); + const QPointList::iterator end = drawLineList.end(); + for ( QPointList::iterator it = ++drawLineList.begin(); it != end; ++it ) + { + const QPoint next = *it; + ConnectorLine *line = new ConnectorLine(this); + line->setPoints( prev.x(), prev.y(), next.x(), next.y() ); + m_connectorLineList.append(line); + prev = next; + } + } + updateConnectorLines(); + //END build up ConnectorPoint list +} + + +void Connector::setSemiHidden( bool semiHidden ) +{ + if ( !canvas() || semiHidden == b_semiHidden ) + return; + + b_semiHidden = semiHidden; + updateConnectorLines(); +} + + +void Connector::updateConnectorPoints( bool add ) +{ + if (!canvas()) { + return; + } + + if ( b_deleted || !isVisible() ) + add = false; + + // Check we haven't already added/removed the points... + if ( b_pointsAdded == add ) + return; + + b_pointsAdded = add; + + // We don't include the end points in the mapping + if ( m_conRouter->cellPointList()->size() < 3 ) { + return; + } + + const int mult = (add)?1:-1; + const QPointList::iterator end = --m_conRouter->cellPointList()->end(); + for ( QPointList::iterator it = ++m_conRouter->cellPointList()->begin(); it != end; ++it ) + { + int x = (*it).x(); + int y = (*it).y(); + + // Add the points of this connector to the cell array in the ICNDocument, + // so that other connectors still to calculate their points know to try + // and avoid this connector + + p_icnDocument->addCPenalty( x, y-1, mult*ICNDocument::hs_connector/2 ); + p_icnDocument->addCPenalty( x-1, y, mult*ICNDocument::hs_connector/2 ); + p_icnDocument->addCPenalty( x, y, mult*ICNDocument::hs_connector ); + p_icnDocument->addCPenalty( x+1, y, mult*ICNDocument::hs_connector/2 ); + p_icnDocument->addCPenalty( x, y+1, mult*ICNDocument::hs_connector/2 ); + + if ( p_icnDocument->isValidCellReference( x, y ) ) { + (*p_icnDocument->cells())[x][y].numCon += mult; + } + } + +// updateDrawList(); +} + + +void Connector::setRoutePoints( QPointList pointList, bool setManual, bool checkEndPoints ) +{ + if (!canvas()) { + return; + } + updateConnectorPoints(false); + + bool reversed = pointsAreReverse(pointList); + if (checkEndPoints) + { + if (reversed) + { + pointList.prepend( QPoint( int(m_endNode->x()), int(m_endNode->y()) ) ); + pointList.append( QPoint( int(m_startNode->x()), int(m_startNode->y()) ) ); + } + else + { + pointList.prepend( QPoint( int(m_startNode->x()), int(m_startNode->y()) ) ); + pointList.append( QPoint( int(m_endNode->x()), int(m_endNode->y()) ) ); + } + } + + m_conRouter->setPoints( pointList, reversed ); + b_manualPoints = setManual; + updateConnectorPoints(true); +} + + +bool Connector::pointsAreReverse( const QPointList &pointList ) const +{ + if ( !m_startNode || !m_endNode ) + { + kdWarning() << k_funcinfo << "Cannot determine orientation as no start and end nodes" << endl; + return false; + } + + if ( pointList.isEmpty() ) + return false; + + + int plsx = pointList.first().x(); + int plsy = pointList.first().y(); + int plex = pointList.last().x(); + int pley = pointList.last().y(); + + double nsx = m_startNode->x(); + double nsy = m_startNode->y(); + double nex = m_endNode->x(); + double ney = m_endNode->y(); + + double dist_normal = (nsx-plsx)*(nsx-plsx) + (nsy-plsy)*(nsy-plsy) + (nex-plex)*(nex-plex) + (ney-pley)*(ney-pley); + double dist_reverse = (nsx-plex)*(nsx-plex) + (nsy-pley)*(nsy-pley) + (nex-plsx)*(nex-plsx) + (ney-plsy)*(ney-plsy); + + return dist_reverse < dist_normal; +} + + +void Connector::rerouteConnector() +{ + if (!isVisible()) + return; + + if ( nodeGroup() ) + { + kdWarning() << k_funcinfo << "Connector is controlled by a NodeGroup! Use that to reroute the connector" << endl; + return; + } + + if ( !startNode() || !endNode() ) + return; + + updateConnectorPoints(false); + m_conRouter->mapRoute( int(startNode()->x()), int(startNode()->y()), int(endNode()->x()), int(endNode()->y()) ); + b_manualPoints = false; + updateConnectorPoints(true); +} + + +void Connector::translateRoute( int dx, int dy ) +{ + updateConnectorPoints(false); + m_conRouter->translateRoute( dx, dy ); + updateConnectorPoints(true); + updateDrawList(); +} + + +void Connector::restoreFromConnectorData( const ConnectorData &connectorData ) +{ + updateConnectorPoints(false); + b_manualPoints = connectorData.manualRoute; + m_conRouter->setRoutePoints( connectorData.route ); + updateConnectorPoints(true); + updateDrawList(); +} + + +ConnectorData Connector::connectorData() const +{ + ConnectorData connectorData; + if ( !m_startNode || !m_endNode ) + { + kdDebug() << k_funcinfo << " m_startNode="<<m_startNode<<" m_endNode="<<m_endNode<<endl; + return connectorData; + } + + connectorData.manualRoute = usesManualPoints(); + connectorData.route = *m_conRouter->cellPointList(); + + if ( m_startNode->isChildNode() ) + { + connectorData.startNodeIsChild = true; + connectorData.startNodeCId = m_startNode->childId(); + connectorData.startNodeParent = m_startNode->parentItem()->id(); + } + else + { + connectorData.startNodeIsChild = false; + connectorData.startNodeId = m_startNode->id(); + } + + if ( m_endNode->isChildNode() ) + { + connectorData.endNodeIsChild = true; + connectorData.endNodeCId = m_endNode->childId(); + connectorData.endNodeParent = m_endNode->parentItem()->id(); + } + else + { + connectorData.endNodeIsChild = false; + connectorData.endNodeId = m_endNode->id(); + } + + return connectorData; +} + + +void Connector::setVisible( bool yes ) +{ + if ( !canvas() || isVisible() == yes ) + return; + + QCanvasPolygon::setVisible(yes); + updateConnectorLines(); +} + + +void Connector::setSelected( bool yes ) +{ + if ( !canvas() || isSelected() == yes ) + return; + + QCanvasPolygon::setSelected(yes); + updateConnectorLines(); + emit selected(yes); +} + + +void Connector::updateConnectorLines() +{ + const QColor color = b_semiHidden ? Qt::gray : (isSelected() ? QColor( 101, 134, 192 ) : Qt::black); +// const QColor color = b_semiHidden ? Qt::gray : (isSelected() ? QColor( 0x7f, 0x7f, 0xff ) : Qt::black); + const int z = ICNDocument::Z::Connector + (isSelected() ? 5 : 0); + + QPen pen( color, (numWires() > 1) ? 2 : 1 ); + + const ConnectorLineList::iterator end = m_connectorLineList.end(); + for ( ConnectorLineList::iterator it = m_connectorLineList.begin(); it != end; ++it ) + { + QCanvasPolygonalItem *item = static_cast<QCanvasPolygonalItem*>(*it); + item->setZ(z); + item->setPen(pen); + item->setBrush(color); + item->setVisible( isVisible() ); + } +} + + +QValueList<QPointList> Connector::splitConnectorPoints( const QPoint & pos ) const +{ + return m_conRouter->splitPoints(pos); +} + + +QPointList Connector::connectorPoints( bool reverse ) const +{ + bool doReverse = (reverse != pointsAreReverse( m_conRouter->pointList(false) )); + return m_conRouter->pointList(doReverse); +} +//END class Connector + + +//BEGIN class ConnectorLine +ConnectorLine::ConnectorLine( Connector *connector ) + : QObject(connector), QCanvasLine( connector->canvas() ) +{ + p_connector = connector; +} + + +int ConnectorLine::rtti() const +{ + return ICNDocument::RTTI::ConnectorLine; +} +//END class ConnectorLine + +#include "connector.moc" diff --git a/src/connector.h b/src/connector.h new file mode 100644 index 0000000..357fde9 --- /dev/null +++ b/src/connector.h @@ -0,0 +1,201 @@ +/*************************************************************************** + * Copyright (C) 2003-2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef CONNECTOR_H +#define CONNECTOR_H + +#include <qcanvas.h> +#include <qguardedptr.h> +#include <qvaluevector.h> + +class Cell; +class ConnectorData; +class ConnectorLine; +class ConRouter; +class CNItem; +class ICNDocument; +class Node; +class NodeGroup; +class Wire; + +typedef QValueList<ConnectorLine*> ConnectorLineList; +typedef QValueList<QPoint> QPointList; +typedef QValueVector<QGuardedPtr<Wire> > WireVector; + + +/** +@short Represents a connection between two Nodes on a ICNDocument +@author David Saxton +*/ +class Connector : public QObject, public QCanvasPolygon +{ +Q_OBJECT +public: + Connector( Node * startNode, Node * endNode, ICNDocument *_ICNDocument, QString *id = 0L ); + ~Connector(); + + virtual int rtti() const; + /** + * Node at start of connector (which refers to this as the output connector) + */ + Node * startNode() const { return m_startNode; } + /** + * Node at end of connector (which refers to this as the input connector) + */ + Node * endNode() const { return m_endNode; } + /** + * @returns connector data describing this connector + */ + ConnectorData connectorData() const; + /** + * Restore the state of the connector (route, etc) from the saved data + */ + void restoreFromConnectorData( const ConnectorData &connectorData ); + /** + * If selected, will be drawn in a different colour + */ + virtual void setSelected( bool yes ); + /** + * Connected id + */ + QString id() const { return m_id; } + /** + * Update the list of lines and connetion-points that the connector uses for + * drawing. + */ + void updateDrawList(); + /** + * Tells the connector that it is under the control of a NodeGroup. When + * the connector is under the control of a NodeGroup, all requests for + * connection rerouting will be passed onto that NodeGroup + */ + void setNodeGroup( NodeGroup *nodeGroup ) { p_nodeGroup = nodeGroup; } + /** + * Returns the NodeGroup that the connector is under the control of (if any) + */ + NodeGroup *nodeGroup() const { return p_nodeGroup; } + /** + * ICNDocument needs to know what 'cells' a connector is present in, + * so that connection mapping can be done to avoid connectors. + * This function will add the hit penalty to the cells pointed to + * by ICNDocument::cells() + */ + void updateConnectorPoints( bool add ); + /** + * Sets the canvas points that the connector should route itself along. + * This is used for manual routing. The cells points are absolute positions + * (unlike the points stored internally in this class, which are the cell poisition + * @param setManual if true then the connector will change to a manual route one + * @param checkEndPoints if true then will check to see if the end points are at the nodes, and adds them if not + */ + void setRoutePoints( QPointList pointList, bool setManual, bool checkEndPoints = false ); + /** + * Call this function (e.g. when moving a CNItem connected to the connector) + * to make the connector partially hidden - probably grayed out - if semiHidden + * is true. + */ + void setSemiHidden( bool semiHidden ); + /** + * Sets the container parent (i.e. the container of the parent item) + */ + void setParentContainer( const QString &cnItemId ); + /** + * Returns a pointer to the parent item container + */ + CNItem *parentContainer() const { return p_parentContainer; } + /** + * @returns whether the points have been set by the user manually defining them + */ + bool usesManualPoints() const { return b_manualPoints; } + /** + * Returns two sets of points (in canvas-reference) that define the connector + * from start to finish, when it is split at the given point (in canvas-reference) + */ + QValueList<QPointList> splitConnectorPoints( const QPoint &pos ) const; + /** + * @returns pointer to ICNDocument that this connector is a member of + */ + ICNDocument *icnDocument() const { return p_icnDocument; } + /** + * Looks at the set of canvas points and tries to determine whether they are + * in the reverse order from start to end node + */ + bool pointsAreReverse( const QPointList &pointList ) const; + /** + * Returns the points, given in canvas-reference, in order of start node to + * end node if reverse is false + * @param reverse whether or not to reverse the points from start node to end node + */ + QPointList connectorPoints( bool reverse = false ) const; + /** + * Reroute the connector. Note that if this connector is controlled by a + * NodeGroup, it will do nothing (other than print out a warning) + */ + void rerouteConnector(); + /** + * Translates the route by the given amoumt. No checking is done to see if + * the translation is useful, etc. + */ + void translateRoute( int dx, int dy ); + virtual void setVisible( bool yes ); + WireVector wires() const { return m_wires; } + unsigned numWires() const { return m_wires.size(); } + Wire * wire( unsigned num = 0 ) const { return (num < m_wires.size()) ? m_wires[num] : 0l; } + +signals: + void removed( Connector *connector ); + void selected( bool yes ); + void numWiresChanged( unsigned newNum ); + +public slots: + void removeConnector( Node* = 0L ); + /** + * Takes the minimum pin count of the start and end nodes, and creates a + * connector for each pin up to that minimum. + */ + void syncWiresWithNodes(); + +protected: + void updateConnectorLines(); + + bool m_bIsSyncingWires; + bool b_semiHidden; + QGuardedPtr<Node> m_startNode; + QGuardedPtr<Node> m_endNode; + NodeGroup *p_nodeGroup; + CNItem *p_parentContainer; + ICNDocument *p_icnDocument; + ConRouter *m_conRouter; + QString m_id; + ConnectorLineList m_connectorLineList; + QRect m_oldBoundRect; + WireVector m_wires; + bool b_deleted; + bool b_manualPoints; + bool b_pointsAdded; +}; +typedef QValueList<QGuardedPtr<Connector> > ConnectorList; + + +//BEGIN ConnectorLine things +class ConnectorLine : public QObject, public QCanvasLine +{ + public: + ConnectorLine( Connector *connector ); + Connector *parent() const { return p_connector; } + virtual int rtti() const; + + protected: + Connector *p_connector; +}; +//END ConnectorLine things + +#endif + diff --git a/src/conrouter.cpp b/src/conrouter.cpp new file mode 100644 index 0000000..2c2d6da --- /dev/null +++ b/src/conrouter.cpp @@ -0,0 +1,537 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "conrouter.h" +#include "icndocument.h" + +#include <kdebug.h> + +#include <assert.h> +#include <cmath> + +inline static int toCanvas( int pos ) +{ + return pos*8+4; +} +inline static int fromCanvas( int pos ) +{ + return (pos-4)/8; +} + +inline static QPoint toCanvas( const QPoint * const pos ) +{ + return QPoint( toCanvas(pos->x()), toCanvas(pos->y()) ); +} +inline static QPoint fromCanvas( const QPoint * const pos ) +{ + return QPoint( fromCanvas(pos->x()), fromCanvas(pos->y()) ); +} + +inline static QPoint toCanvas( const QPoint &pos ) +{ + return QPoint( toCanvas(pos.x()), toCanvas(pos.y()) ); +} +inline static QPoint fromCanvas( const QPoint &pos ) +{ + return QPoint( fromCanvas(pos.x()), fromCanvas(pos.y()) ); +} + +static inline int roundDouble( const double x ) +{ + return int(std::floor(x+0.5)); +} + +ConRouter::ConRouter( ICNDocument *cv ) +{ + p_icnDocument = cv; + m_lcx = m_lcy = 0; +} + + +ConRouter::~ConRouter() +{ +} + + +QPointList ConRouter::pointList( bool reverse ) const +{ + QPointList pointList; + + if (reverse) + { + bool notDone = m_cellPointList.size() > 0; + for ( QPointList::const_iterator it = m_cellPointList.fromLast(); notDone; --it ) + { + pointList.append( toCanvas(&*it) ); + if ( it == m_cellPointList.begin() ) notDone = false; + } + } + else + { + const QPointList::const_iterator end = m_cellPointList.end(); + for ( QPointList::const_iterator it = m_cellPointList.begin(); it != end; ++it ) + { + pointList.append( toCanvas(&*it) ); + } + } + + return pointList; +} + + +static double qpoint_distance( const QPoint & p1, const QPoint & p2 ) +{ + double dx = p1.x() - p2.x(); + double dy = p1.y() - p2.y(); + + return std::sqrt( dx*dx + dy*dy ); +} + + +QPointListList ConRouter::splitPoints( const QPoint &pos ) const +{ + const QPoint split = fromCanvas(&pos); + + QValueList<QPointList> list; + + // Check that the point is in the connector points, and not at the start or end + bool found = false; + QPointList::const_iterator end = m_cellPointList.end(); + + double dl[] = { 0.0, 1.1, 1.5 }; // sqrt(2) < 1.5 < sqrt(5) + for ( unsigned i = 0; (i < 3) && !found; ++i ) + { + for ( QPointList::const_iterator it = m_cellPointList.begin(); it != end && !found; ++it ) + { + if ( qpoint_distance( *it, split ) <= dl[i] && it != m_cellPointList.begin() && it != m_cellPointList.fromLast() ) + found = true; + } + } + + QPointList first; + QPointList second; + + if (!found) + { + kdWarning() << "ConRouter::splitConnectorPoints: Could not find point ("<<pos.x()<<", "<<pos.y()<<") in connector points"<<endl; + kdWarning() << "ConRouter::splitConnectorPoints: Returning generic list"<<endl; + + first.append( toCanvas(m_cellPointList.first()) ); + first.append(pos); + second.append(pos); + second.append( toCanvas(m_cellPointList.last()) ); + + list.append(first); + list.append(second); + + return list; + } + + // Now add the points to the two lists + bool gotToSplit = false; + for ( QPointList::const_iterator it = m_cellPointList.begin(); it != end; ++it ) + { + QPoint canvasPoint = toCanvas(&*it); + if ( *it == split ) + { + gotToSplit = true; + first.append(canvasPoint); + second.prepend(canvasPoint); + } + else if (!gotToSplit) + { + first.append(canvasPoint); + } + else /*if (gotToSplit)*/ + { + second.append(canvasPoint); + } + } + + list.append(first); + list.append(second); + + return list; +} + + +QPointListList ConRouter::dividePoints( uint n ) const +{ + // Divide the points up into n pieces... + + QPointList points = m_cellPointList; + assert( n != 0 ); + if ( points.size() == 0 ) { + points += QPoint( toCanvas(m_lcx), toCanvas(m_lcy) ); + } + + const float avgLength = float(points.size()-1)/float(n); + + QPointListList pll; + for ( uint i=0; i<n; ++i ) + { + QPointList pl; + // Get the points between (pos) and (pos+avgLength) + const int endPos = roundDouble( avgLength*(i+1) ); + const int startPos = roundDouble( avgLength*i ); + const QPointList::iterator end = ++points.at(endPos); + for ( QPointList::iterator it = points.at(startPos); it != end; ++it ) + { + pl += toCanvas(*it); + } + pll += pl; + } + return pll; +} + + +void ConRouter::checkACell( int x, int y, Cell *prev, int prevX, int prevY, int nextScore ) +{ + if ( !p_icnDocument->isValidCellReference(x,y) ) return; + Cell * const c = &(*cellsPtr)[x][y]; + if ( c->permanent ) return; + int newScore = nextScore + c->CIpenalty + c->Cpenalty; + + // Check for changing direction + if ( x != prevX && prev->prevX == prevX ) newScore += 5; + else if ( y != prevY && prev->prevY == prevY ) newScore += 5; + + if ( c->bestScore < newScore ) return; + + // We only want to change the previous cell if the score is different, + // or the score is the same but this cell allows the connector + // to travel in the same direction + + if ( c->bestScore == newScore && + x != prevX && + y != prevY ) return; + + c->bestScore = newScore; + c->prevX = prevX; + c->prevY = prevY; + + if ( !c->addedToLabels ) + { + c->addedToLabels = true; + Point point; + point.x = x; + point.y = y; + point.prevX = prevX; + point.prevY = prevY; + TempLabelMap::iterator it = tempLabels.insert( std::make_pair(newScore,point) ); + c->point = &it->second; + } + else + { + c->point->prevX = prevX; + c->point->prevY = prevY; + } +} + +void ConRouter::checkCell( int x, int y ) +{ + Cell * const c = &(*cellsPtr)[x][y]; + + c->permanent = true; + const int nextScore = c->bestScore+1; + + // Check the surrounding cells (up, left, right, down) + if ( y > 0 ) checkACell( x, y-1, c, x, y, nextScore ); + if ( x > 0 ) checkACell( x-1, y, c, x, y, nextScore ); + if ( x+1 < xcells ) checkACell( x+1, y, c, x, y, nextScore ); + if ( y+1 < ycells ) checkACell( x, y+1, c, x, y, nextScore ); +} + + +bool ConRouter::needsRouting( int sx, int sy, int ex, int ey ) const +{ + if ( m_cellPointList.size() < 2 ) + { + // Better be on the safe side... + return true; + } + + const int scx = fromCanvas(sx); + const int scy = fromCanvas(sy); + const int ecx = fromCanvas(ex); + const int ecy = fromCanvas(ey); + + const int psx = m_cellPointList.first().x(); + const int psy = m_cellPointList.first().y(); + const int pex = m_cellPointList.last().x(); + const int pey = m_cellPointList.last().y(); + + return (psx != scx || psy != scy || pex != ecx || pey != ecy ) && + (pex != scx || pey != scy || psx != ecx || psy != ecy ); +} + + +void ConRouter::setRoutePoints( const QPointList &pointList ) +{ + m_cellPointList = pointList; + removeDuplicatePoints(); +} + + +void ConRouter::setPoints( const QPointList &pointList, bool reverse ) +{ + if ( pointList.size() == 0 ) + return; + + QPointList cellPointList; + + QPoint prevCellPoint = fromCanvas(*pointList.begin()); + cellPointList.append(prevCellPoint); + const QPointList::const_iterator end = pointList.end(); + for ( QPointList::const_iterator it = pointList.begin(); it != end; ++it ) + { + QPoint cellPoint = fromCanvas(*it); + + while ( prevCellPoint != cellPoint ) + { + cellPointList.append(prevCellPoint); + + if ( prevCellPoint.x() < cellPoint.x() ) prevCellPoint.setX( prevCellPoint.x()+1 ); + else if ( prevCellPoint.x() > cellPoint.x() ) prevCellPoint.setX( prevCellPoint.x()-1 ); + if ( prevCellPoint.y() < cellPoint.y() ) prevCellPoint.setY( prevCellPoint.y()+1 ); + else if ( prevCellPoint.y() > cellPoint.y() ) prevCellPoint.setY( prevCellPoint.y()-1 ); + }; + + prevCellPoint = cellPoint; + } + cellPointList.append(prevCellPoint); + + if (reverse) + { + m_cellPointList.clear(); + const QPointList::iterator begin = cellPointList.begin(); + for ( QPointList::iterator it = cellPointList.fromLast(); it != begin; --it ) + { + m_cellPointList += *it; + } + m_cellPointList += *begin; + } + else { + m_cellPointList = cellPointList; + } + + removeDuplicatePoints(); +} + + +void ConRouter::translateRoute( int dx, int dy ) +{ + if ( dx == 0 && dy == 0 ) { + return; + } + + m_lcx += dx; + m_lcy += dy; + +// const QPoint ds = QPoint( fromCanvas(dx), fromCanvas(dy) ); + const QPoint ds = QPoint( dx/8, dy/8 ); + + QPointList::iterator end = m_cellPointList.end(); + for ( QPointList::iterator it = m_cellPointList.begin(); it != end; ++it ) + { + (*it) += ds; + } + + removeDuplicatePoints(); +} + + +void ConRouter::mapRoute( int sx, int sy, int ex, int ey ) +{ + const int scx = fromCanvas(sx); + const int scy = fromCanvas(sy); + const int ecx = fromCanvas(ex); + const int ecy = fromCanvas(ey); + + if ( !p_icnDocument->isValidCellReference( scx, scy ) || + !p_icnDocument->isValidCellReference( ecx, ecy ) ) + { + return; + } + + m_cellPointList.clear(); + m_lcx = ecx; + m_lcy = ecy; + + + // First, lets try some common connector routes (which will not necesssarily + // be shortest, but they will be neat, and cut down on overall CPU usage) + // If that fails, we will resort to a shortest-route algorithm to find an + // appropriate route. + + // Connector configuration: Line + { + bool ok = checkLineRoute( scx, scy, ecx, ecy, 4*ICNDocument::hs_connector, 0 ); + if (ok) { + return; + } else { + m_cellPointList.clear(); + } + } + + // Corner 1 + { + bool ok = checkLineRoute( scx, scy, ecx, ecy, 2*ICNDocument::hs_connector, 0 ); + if (!ok) { + m_cellPointList.clear(); + } else { + ok = checkLineRoute( scx, scy, ecx, ecy, ICNDocument::hs_connector-1, 0 ); + if (ok) { + return; + } else { + m_cellPointList.clear(); + } + } + } + + // Corner 2 + { + bool ok = checkLineRoute( scx, scy, ecx, ecy, 2*ICNDocument::hs_connector, 0 ); + if (!ok) { + m_cellPointList.clear(); + } else { + ok = checkLineRoute( scx, scy, ecx, ecy, ICNDocument::hs_connector-1, 0 ); + if (ok) { + return; + } else { + m_cellPointList.clear(); + } + } + } + + // It seems we must resort to brute-force route-checking + { + cellsPtr = p_icnDocument->cells(); + cellsPtr->reset(); + + xcells = p_icnDocument->canvas()->width()/8; + ycells = p_icnDocument->canvas()->height()/8; + + // Now to map out the shortest routes to the cells + Cell * const startCell = &(*cellsPtr)[ecx][ecy]; + startCell->permanent = true; + startCell->bestScore = 0; + startCell->prevX = -1; + startCell->prevY = -1; + + tempLabels.clear(); + checkCell( ecx, ecy ); + + // Daniel: I changed it from a do while to a while otherwise + // in rare cases the iterator can end up as end(). + while ( tempLabels.size() > 0 && !(*cellsPtr)[scx][scy].permanent ) + { + TempLabelMap::iterator it = tempLabels.begin(); + checkCell( it->second.x, it->second.y ); + tempLabels.erase(it); + } + + // Now, retrace the shortest route from the endcell to get out points :) + int x = scx, y = scy; + bool ok = true; + do + { + m_cellPointList.append( QPoint( x, y ) ); + int newx = (*cellsPtr)[x][y].prevX; + int newy = (*cellsPtr)[x][y].prevY; + if ( newx == x && newy == y ) { + ok = false; + } + x = newx; + y = newy; + } + while ( p_icnDocument->isValidCellReference(x,y) && x != -1 && y != -1 && ok ); + + // And append the last point... + m_cellPointList.append( QPoint( ecx, ecy ) ); + } + + removeDuplicatePoints(); +} + + +bool ConRouter::checkLineRoute( int scx, int scy, int ecx, int ecy, int maxConScore, int maxCIScore ) +{ + if ( (scx != ecx) && (scy != ecy) ) { + return false; + } + + const bool isHorizontal = scy == ecy; + + int start=0, end=0, x=0, y=0, dd=0; + if (isHorizontal) + { + dd = (scx<ecx)?1:-1; + start = scx; + end = ecx+dd; + y = scy; + } + else + { + dd = (scy<ecy)?1:-1; + start = scy; + end = ecy+dd; + x = scx; + } + + Cells *cells = p_icnDocument->cells(); + + if (isHorizontal) + { + for ( int x = start; x!=end; x+=dd ) + { + if ( std::abs((double)(x-start))>1 && std::abs((double)(x-end))>1 && ((*cells)[x][y].CIpenalty > maxCIScore || (*cells)[x][y].Cpenalty > maxConScore) ) + { + return false; + } else { + m_cellPointList.append( QPoint( x, y ) ); + } + } + } + else + { + for ( int y = start; y!=end; y+=dd ) + { + if ( std::abs((double)(y-start))>1 && std::abs((double)(y-end))>1 && ((*cells)[x][y].CIpenalty > maxCIScore || (*cells)[x][y].Cpenalty > maxConScore) ) + { + return false; + } else { + m_cellPointList.append( QPoint( x, y ) ); + } + } + } + + m_cellPointList.prepend( QPoint( scx, scy ) ); + m_cellPointList.append( QPoint( ecx, ecy ) ); + removeDuplicatePoints(); + return true; +} + + +void ConRouter::removeDuplicatePoints() +{ + QPoint prev(-1,-1); + + const QPointList::iterator end = m_cellPointList.end(); + for ( QPointList::iterator it = m_cellPointList.begin(); it != end; ++it ) + { + if ( *it == prev ) { + *it = QPoint(-1,-1); + } else { + prev = *it; + } + } + m_cellPointList.remove( QPoint(-1,-1) ); +} diff --git a/src/conrouter.h b/src/conrouter.h new file mode 100644 index 0000000..2a522b7 --- /dev/null +++ b/src/conrouter.h @@ -0,0 +1,102 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef CONROUTER_H +#define CONROUTER_H + +#include "cells.h" + +#include <qpoint.h> +#include <qvaluelist.h> + +class ICNDocument; +class Cell; + +typedef QValueList<QPoint> QPointList; +typedef QValueList<QPointList> QPointListList; + +/** +Abstraction for the routing of a connector. + +NB: As a general rule of thumb, the point references stored as members of this +class are in Cell-space (i.e. 8^2 x smaller than Canvas-space), and the +interfacing functions take or give point references in Canvas-space (unless +otherwise indicated). + +@author David Saxton +*/ +class ConRouter +{ +public: + ConRouter( ICNDocument *cv ); + ~ConRouter(); + + /** + * What this class is all about - finding a route, from (sx,sy) to (ex,ey). + */ + void mapRoute( int sx, int sy, int ex, int ey ); + /** + * Translates the precalculated routepoints by the given amount + */ + void translateRoute( int dx, int dy ); + /** + * Sets the route to the given canvas points + * @param reverse if true, the points in pointList will be reversed + */ + void setPoints( const QPointList &pointList, bool reverse = false ); + /** + * Sets the route to the given route points + */ + void setRoutePoints( const QPointList &pointList ); + /** + * @returns true if the start or end points differ from that of the current route + */ + bool needsRouting( int sx, int sy, int ex, int ey ) const; + /** + * Returns the list of canvas points + */ + QPointList pointList( bool reverse ) const; + /** + * Returns a pointer to the internall cellPointList + */ + QPointList *cellPointList() { return &m_cellPointList; } + /** + * This will return two lists of Canvas points from the splitting of the + * route at the Canvas point "pos". The internall stored points are not + * affected. + */ + QPointListList splitPoints( const QPoint &pos ) const; + /** + * This will return a list of Canvas pointLists from the route, divided + * into n parts (at n-1 equally spaced places). + */ + QPointListList dividePoints( uint n ) const; + +protected: + /** + * Check a line of the ICNDocument cells for a valid route + */ + bool checkLineRoute( int scx, int scy, int ecx, int ecy, int maxConScore, int maxCIScore ); + void checkACell( int x, int y, Cell *prev, int prevX, int prevY, int nextScore ); + void checkCell( int x, int y ); // Gets the shortest route from the final cell + /** + * Remove duplicated points from the route + */ + void removeDuplicatePoints(); + + int xcells, ycells; + int m_lcx, m_lcy; // Last x / y from mapRoute, if we need a point on the route + Cells *cellsPtr; + TempLabelMap tempLabels; + ICNDocument *p_icnDocument; + QPointList m_cellPointList; +}; + +#endif diff --git a/src/core/Makefile.am b/src/core/Makefile.am new file mode 100644 index 0000000..145add8 --- /dev/null +++ b/src/core/Makefile.am @@ -0,0 +1,6 @@ +INCLUDES = -I$(top_srcdir)/src -I$(top_srcdir)/src/drawparts -I$(top_srcdir)/src/electronics -I$(top_srcdir)/src/electronics/components -I$(top_srcdir)/src/electronics/simulation -I$(top_srcdir)/src/flowparts -I$(top_srcdir)/src/gui -I$(top_srcdir)/src/languages -I$(top_srcdir)/src/mechanics -I$(top_srcdir)/src/micro $(all_includes) +METASOURCES = AUTO +libcore_la_LDFLAGS = $(all_libraries) +noinst_LTLIBRARIES = libcore.la +kde_kcfg_DATA = ktechlab.kcfg +libcore_la_SOURCES = ktlconfig.kcfgc main.cpp diff --git a/src/core/ktechlab.kcfg b/src/core/ktechlab.kcfg new file mode 100644 index 0000000..de1c18f --- /dev/null +++ b/src/core/ktechlab.kcfg @@ -0,0 +1,290 @@ +<kcfg> + <kcfgfile name="ktechlabrc"/> + + <group name="General"> + <entry name="MaxUndo" type="Int"> + <label>Maximum number of undo steps</label> + <default>100</default> + </entry> + <entry name="RestoreDocumentsOnStartup" type="Bool"> + <label>Restore Documents on Startup</label> + <default>true</default> + </entry> + <entry name="RaiseItemSelectors" type="Bool"> + <label>Raise appropriate Item Selector on document creation</label> + <default>true</default> + </entry> + <entry name="RaiseMessagesLog" type="Bool"> + <label>Raise Messages Log on Compiling</label> + <default>true</default> + </entry> + <entry name="ShowVoltageBars" type="Bool"> + <label>Show Voltage Bars</label> + <default>true</default> + </entry> + <entry name="RefreshRate" type="Int"> + <label>Refresh Rate</label> + <default>50</default> <!-- Make sure this default value is synched with that in settingsdlg.cpp. TODO: Only store this value in one place --> + </entry> + <entry name="GridColor" type="Color"> + <label>Color of grid lines</label> + <default>#E8E8E8</default> + </entry> + <!--<entry name="BgColor" type="Color"> + <label>Background color</label> + <default>white</default> + </entry>--> + <entry name="ShowGrid" type="Bool"> + <label>Whether the grid should be shown.</label> + <default>true</default> + </entry> + <entry name="ReuseSameViewForOutput" type="Bool"> + <label>Whether the same output should be use for generation of code, etc</label> + <default>false</default> + </entry> + </group> + + <group name="AsmFormatter"> + <entry name="IndentAsmName" type="Int"> + <label>Indentation of Instruction Names</label> + <default>4</default> + </entry> + <entry name="IndentAsmData" type="Int"> + <label>Indentation of Instruction Data</label> + <default>14</default> + </entry> + <entry name="IndentComment" type="Int"> + <label>Indentation of Comments</label> + <default>40</default> + </entry> + <entry name="IndentEqu" type="Int"> + <label>Indentation of 'equ'</label> + <default>14</default> + </entry> + <entry name="IndentEquValue" type="Int"> + <label>Indentation of 'equ' Value</label> + <default>20</default> + </entry> + <entry name="AutoFormatMBOutput" type="Bool"> + <label>Automatically format Microbe output</label> + <default>false</default> + </entry> + </group> + + <group name="Logic"> + <entry name="LogicRisingTrigger" type="Double"> + <label>Rising Trigger Threshold</label> + <default>2.5</default> + </entry> + <entry name="LogicFallingTrigger" type="Double"> + <label>Falling Trigger Threshold</label> + <default>2</default> + </entry> + <entry name="LogicOutputHigh" type="Double"> + <label>Logic Output High</label> + <default>5</default> + </entry> + <entry name="LogicOutputHighImpedance" type="Double"> + <label>Logic Output High Impedance</label> + <default>15</default> + </entry> + <entry name="LogicOutputLowImpedance" type="Double"> + <label>Logic Output Low Impedance</label> + <default>0</default> + </entry> + </group> + + <group name="Gpasm"> + <entry name="HexFormat" type="Enum"> + <label>Hex Format</label> + <choices> + <choice name="inhx32"/> + <choice name="inhx8m"/> + <choice name="inhx8s"/> + <choice name="inhx16"/> + </choices> + <default>inhx32</default> + </entry> + <entry name="Radix" type="Enum"> + <label>Radix</label> + <choices> + <choice name="Decimal"/> + <choice name="Binary"/> + <choice name="Octal"/> + <choice name="Hexadecimal"/> + </choices> + <default>Decimal</default> + </entry> + <entry name="GpasmWarningLevel" type="Enum"> + <label>GpasmWarning Level</label> + <choices> + <choice name="All"/> + <choice name="Warnings"/> + <choice name="Errors"/> + </choices> + <default>All</default> + </entry> + <entry name="IgnoreCase" type="Bool"> + <label>Ignore Case</label> + <default>true</default> + </entry> + <entry name="DosFormat" type="Bool"> + <label>Dos Formatting</label> + <default>false</default> + </entry> + <entry name="MiscGpasmOptions" type="String"> + <label>Other Options</label> + <default></default> + </entry> + </group> + + <group name="SDCC"> + <entry name="SDCC_nostdlib" type="Bool"> + <label>Don't search in the standard library directory</label> + <default>false</default> + </entry> + <entry name="SDCC_nostdinc" type="Bool"> + <label>Don't search in the standard include directory</label> + <default>false</default> + </entry> + <entry name="SDCC_less_pedantic" type="Bool"> + <label>Disable pedantic warnings</label> + <default>false</default> + </entry> + <entry name="SDCC_std_c89" type="Bool"> + <label>Strictly follow the C89 standard</label> + <default>false</default> + </entry> + <entry name="SDCC_std_c99" type="Bool"> + <label>Strictly follow the C99 standard</label> + <default>false</default> + </entry> + + <entry name="SDCC_stack_auto" type="Bool"> + <label>Stack automatic variables</label> + <default>false</default> + </entry> + <entry name="SDCC_int_long_reent" type="Bool"> + <label>Integer libraries have been compiled as reentrant</label> + <default>false</default> + </entry> + <entry name="SDCC_float_reent" type="Bool"> + <label>Floating point library is has been compiled as reentrant</label> + <default>false</default> + </entry> + <entry name="SDCC_fommit_frame_pointer" type="Bool"> + <label>Leave out the frame pointer</label> + <default>false</default> + </entry> + <entry name="SDCC_no_xinit_opt" type="Bool"> + <label>Don't memcpy initialized data from code space to xdata space</label> + <default>false</default> + </entry> + <entry name="SDCC_all_callee_saves" type="Bool"> + <label>Callee will always save registers used</label> + <default>false</default> + </entry> + + <entry name="SDCC_nooverlay" type="Bool"> + <label>Don't overlay parameters and local variables</label> + <default>false</default> + </entry> + <entry name="SDCC_nogcse" type="Bool"> + <label>Disable the GCSE optimization</label> + <default>false</default> + </entry> + <entry name="SDCC_nolabelopt" type="Bool"> + <label>Don't optimize labels</label> + <default>false</default> + </entry> + <entry name="SDCC_noinvariant" type="Bool"> + <label>Disable optimization of invariants</label> + <default>false</default> + </entry> + <entry name="SDCC_noinduction" type="Bool"> + <label>Disable loop variable induction</label> + <default>false</default> + </entry> + <entry name="SDCC_no_peep" type="Bool"> + <label>Disable peep-hole optimization</label> + <default>false</default> + </entry> + <entry name="SDCC_noloopreverse" type="Bool"> + <label>Don't do loop reversal optimization</label> + <default>false</default> + </entry> + <entry name="SDCC_opt_code_size" type="Bool"> + <label>Optimize for compact code</label> + <default>false</default> + </entry> + <entry name="SDCC_opt_code_speed" type="Bool"> + <label>Optimize for fast code</label> + <default>false</default> + </entry> + <entry name="SDCC_peep_asm" type="Bool"> + <label>Pass inline assembler code through peep hole optimizer</label> + <default>false</default> + </entry> + <entry name="SDCC_nojtbound" type="Bool"> + <label>Don't generate boundary check for jump tables</label> + <default>false</default> + </entry> + + <entry name="SDCC_nodefaultlibs" type="Bool"> + <label>Don't use default libraries</label> + <default>false</default> + </entry> + <entry name="SDCC_pno_banksel" type="Bool"> + <label>Don't generate BANKSEL directives</label> + <default>false</default> + </entry> + <entry name="SDCC_pstack_model_large" type="Bool"> + <label>Use large stack model</label> + <default>false</default> + </entry> + <entry name="SDCC_debug_xtra" type="Bool"> + <label>Show more debug info in assembly output</label> + <default>false</default> + </entry> + <entry name="SDCC_denable_peeps" type="Bool"> + <label>Explicit enable of peepholes</label> + <default>false</default> + </entry> + <entry name="SDCC_calltree" type="Bool"> + <label>Dump call tree in .calltree file</label> + <default>false</default> + </entry> + <entry name="SDCC_fstack" type="Bool"> + <label>Enable stack optimizations</label> + <default>false</default> + </entry> + <entry name="SDCC_optimize_goto" type="Bool"> + <label>Try to use conditional BRA instead of GOTO</label> + <default>false</default> + </entry> + <entry name="SDCC_optimize_cmp" type="Bool"> + <label>Try to optimize some compares</label> + <default>false</default> + </entry> + <entry name="SDCC_optimize_df" type="Bool"> + <label>Thorough data flow analysis</label> + <default>false</default> + </entry> + + <entry name="MiscSDCCOptions" type="String"> + <label>Other Options</label> + <default></default> + </entry> + </group> + + <group name="PicProgramming"> + <entry name="PicProgrammerProgram" type="String"> + <label>The application to use to program the PIC.</label> + <default>picp</default> + </entry> + <entry name="PicProgrammerPort" type="String"> + <label>The device (serial, parallel, etc...) used to program the PIC.</label> + <default/> + </entry> + </group> +</kcfg> diff --git a/src/core/ktlconfig.kcfgc b/src/core/ktlconfig.kcfgc new file mode 100644 index 0000000..f056eb6 --- /dev/null +++ b/src/core/ktlconfig.kcfgc @@ -0,0 +1,3 @@ +File=ktechlab.kcfg +ClassName=KTLConfig +Singleton=true diff --git a/src/core/main.cpp b/src/core/main.cpp new file mode 100644 index 0000000..0c43a35 --- /dev/null +++ b/src/core/main.cpp @@ -0,0 +1,58 @@ +/*************************************************************************** + * Copyright (C) 2003-2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "ktechlab.h" + +#include <dcopclient.h> +#include <kaboutdata.h> +#include <kapplication.h> +#include <kcmdlineargs.h> +#include <kconfig.h> +#include <klocale.h> + +static const char description[] = + I18N_NOOP("An IDE for microcontrollers and electronics"); + +static const char version[] = "0.3"; + +static KCmdLineOptions options[] = +{ + { "+[URL]", I18N_NOOP( "Document to open." ), 0 }, + KCmdLineLastOption +}; + + +int main(int argc, char **argv) +{ + KAboutData about("ktechlab", I18N_NOOP("KTechlab"), version, description, + KAboutData::License_GPL, "(C) 2003-2005, The KTechlab developers", "", "http://ktechlab.org", "ktechlab-devel@lists.sourceforge.net" ); + about.addAuthor( "David Saxton", 0, "david@bluehaze.org" ); + about.addAuthor( "Daniel Clarke", 0, "daniel.jc@gmail.com" ); + about.addCredit( "Couriousous", "JK flip-flop, asyncronous preset/reset in the D flip-flop." ); + about.addCredit( "John Myers", "Rotary Switch" ); + about.addCredit( "Ali Akcaagac", "Glib friendliness." ); + about.addCredit( "David Leggett", "Website hosting and feedback during early development." ); + KCmdLineArgs::init(argc, argv, &about); + KCmdLineArgs::addCmdLineOptions(options); + KApplication app; + + // register ourselves as a dcop client + app.dcopClient()->registerAs(app.name(), false); + + KTechlab *ktechlab = new KTechlab(); + + KCmdLineArgs *args = KCmdLineArgs::parsedArgs(); + for ( int i=0; i < args->count(); ++i ) + ktechlab->load( args->url(i) ); + + ktechlab->show(); + args->clear(); // Free up some memory + return app.exec(); +} diff --git a/src/debugmanager.cpp b/src/debugmanager.cpp new file mode 100644 index 0000000..425232c --- /dev/null +++ b/src/debugmanager.cpp @@ -0,0 +1,89 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "config.h" +#ifndef NO_GPSIM + +#include "debugmanager.h" +#include "docmanager.h" +#include "gpsimprocessor.h" +#include "textdocument.h" + +#include <kdebug.h> +#include <kstaticdeleter.h> + + +//BEGIN class DebugManager +DebugManager * DebugManager::m_pSelf = 0l; +static KStaticDeleter<DebugManager> staticDebugManagerDeleter; + +DebugManager * DebugManager::self() +{ + if (!m_pSelf) + staticDebugManagerDeleter.setObject( m_pSelf, new DebugManager ); + return m_pSelf; +} + + +DebugManager::DebugManager() + : QObject() +{ +} + + +DebugManager::~DebugManager() +{ +} + + +void DebugManager::registerGpsim( GpsimProcessor * gpsim ) +{ + if (!gpsim) + return; + + m_processors << gpsim; + + const QStringList files = gpsim->sourceFileList(); + QStringList::const_iterator end = files.end(); + for ( QStringList::const_iterator it = files.begin(); it != end; ++it ) + { + if ( TextDocument * doc = dynamic_cast<TextDocument*>(DocManager::self()->findDocument(*it)) ) + { + if ( !doc->debuggerIsRunning() ) + doc->setDebugger( gpsim->currentDebugger(), false ); + } + } +} + + +void DebugManager::urlOpened( TextDocument * td ) +{ + if ( td->debuggerIsRunning() ) + return; + + m_processors.remove( (GpsimProcessor*)0l ); + GpsimProcessorList::iterator end = m_processors.end(); + for ( GpsimProcessorList::iterator it = m_processors.begin(); it != end; ++it ) + { + if ( !(*it)->sourceFileList().contains( td->url().path() ) ) + continue; + + (*it)->setDebugMode( (td->guessedCodeType() == TextDocument::ct_asm) ? GpsimDebugger::AsmDebugger : GpsimDebugger::HLLDebugger ); + + td->setDebugger( (*it)->currentDebugger(), false ); + return; + } +} +//END class DebugManager + + +#include "debugmanager.moc" + +#endif diff --git a/src/debugmanager.h b/src/debugmanager.h new file mode 100644 index 0000000..2d48eac --- /dev/null +++ b/src/debugmanager.h @@ -0,0 +1,54 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "config.h" +#ifndef NO_GPSIM + +#ifndef DEBUGMANAGER_H +#define DEBUGMANAGER_H + +#include <qguardedptr.h> +#include <qmap.h> +#include <qobject.h> + +class GpsimProcessor; +class TextDocument; + +typedef QValueList< QGuardedPtr<GpsimProcessor> > GpsimProcessorList; + +/** +@author David Saxton +*/ +class DebugManager : public QObject +{ + Q_OBJECT + public: + static DebugManager * self(); + ~DebugManager(); + + void registerGpsim( GpsimProcessor * gpsim ); + /** + * Called from TextDocument when it opens a URL so that it can be + * connected up to any processors that refer to its url. + */ + void urlOpened( TextDocument * td ); + + protected: + GpsimProcessorList m_processors; + + private: + DebugManager(); + static DebugManager * m_pSelf; + +}; + +#endif + +#endif diff --git a/src/docmanager.cpp b/src/docmanager.cpp new file mode 100644 index 0000000..bf49985 --- /dev/null +++ b/src/docmanager.cpp @@ -0,0 +1,500 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "circuitdocument.h" +#include "docmanager.h" +#include "docmanageriface.h" +#include "flowcodedocument.h" +#include "iteminterface.h" +#include "itemselector.h" +#include "ktechlab.h" +#include "core/ktlconfig.h" +#include "mechanicsdocument.h" +#include "textdocument.h" +#include "textview.h" +#include "viewcontainer.h" + +#include <klocale.h> +#include <kmessagebox.h> +#include <ktabwidget.h> +#include <qfile.h> + +#include <assert.h> + +DocManager * DocManager::m_pSelf = 0l; + +DocManager * DocManager::self( KTechlab * ktechlab ) +{ + if ( !m_pSelf ) + { + assert(ktechlab); + m_pSelf = new DocManager(ktechlab); + } + return m_pSelf; +} + + +DocManager::DocManager( KTechlab * ktechlab ) + : QObject( ktechlab ), + p_ktechlab(ktechlab) +{ + p_focusedView = 0l; + m_countCircuit = 0; + m_countFlowCode = 0; + m_countMechanics = 0; + m_countOther = 0; + p_connectedDocument = 0l; + m_nextDocumentID = 1; + m_pIface = new DocManagerIface(this); +} + + +DocManager::~DocManager() +{ +} + + +bool DocManager::closeAll() +{ + const DocumentList::iterator end = m_documentList.end(); + while ( !m_documentList.isEmpty() ) + { + Document *document = m_documentList.first(); + if ( document->fileClose() ) + { + m_documentList.remove(document); + removeDocumentAssociations(document); + } + else + return false; + } + return true; +} + + +void DocManager::gotoTextLine( const KURL &url, int line ) +{ + TextDocument * doc = dynamic_cast<TextDocument*>( openURL(url) ); + if (!doc) + return; + + doc->textView()->gotoLine(line); +} + + +Document* DocManager::openURL( const KURL &url, ViewArea *viewArea ) +{ + if ( url.isEmpty() ) + return 0l; + + if ( url.isLocalFile() ) + { + QFile file(url.path()); + if ( file.open(IO_ReadOnly) == false ) + { + KMessageBox::sorry( 0l, i18n("Could not open '%1'").arg( url.prettyURL() ) ); + return 0l; + } + file.close(); + } + + // If the document is already open, and a specific view area hasn't been + // specified, then just return that document - otherwise, create a new + // view in the viewarea + Document *document = findDocument(url); + if ( document ) + { + if ( viewArea ) + createNewView( document, viewArea ); + else + giveDocumentFocus( document, viewArea ); + return document; + } + + QString fileName = url.fileName(); + QString extension = fileName.right( fileName.length() - fileName.findRev('.') ); + + if ( extension == ".circuit" ) + return openCircuitFile( url, viewArea ); + + else if ( extension == ".flowcode" ) + return openFlowCodeFile( url, viewArea ); + + else if ( extension == ".mechanics" ) + return openMechanicsFile( url, viewArea ); + + else + return openTextFile( url, viewArea ); +} + + +Document *DocManager::getFocusedDocument() const +{ + Document * doc = p_focusedView ? p_focusedView->document() : 0l; + return (doc && !doc->isDeleted()) ? doc : 0l; +} + + +void DocManager::giveDocumentFocus( Document * toFocus, ViewArea * viewAreaForNew ) +{ + if ( !toFocus ) + return; + + if ( View * activeView = toFocus->activeView() ) + { + p_ktechlab->tabWidget()->showPage( activeView->viewContainer() ); + activeView->setFocused(); + activeView->viewContainer()->setFocused(); + } + else if ( viewAreaForNew ) + createNewView( toFocus, viewAreaForNew ); +} + + +QString DocManager::untitledName( int type ) +{ + QString name; + switch(type) + { + case Document::dt_circuit: + { + if ( m_countCircuit>1 ) + name = i18n("Untitled (Circuit %1)").arg(QString::number(m_countCircuit)); + else + name = i18n("Untitled (Circuit)"); + m_countCircuit++; + break; + } + case Document::dt_flowcode: + { + if ( m_countFlowCode>1 ) + name = i18n("Untitled (FlowCode %1)").arg(QString::number(m_countFlowCode)); + else + name = i18n("Untitled (FlowCode)"); + m_countFlowCode++; + break; + } + case Document::dt_mechanics: + { + if ( m_countMechanics>1 ) + name = i18n("Untitled (Mechanics %1)").arg(QString::number(m_countMechanics)); + else + name = i18n("Untitled (Mechanics)"); + m_countMechanics++; + break; + } + default: + { + if ( m_countOther>1 ) + name = i18n("Untitled (%1)").arg(QString::number(m_countOther)); + else + name = i18n("Untitled"); + m_countOther++; + break; + } + } + return name; +} + + +Document *DocManager::findDocument( const KURL &url ) const +{ + // First, look in the associated documents + if ( m_associatedDocuments.contains(url) ) + return m_associatedDocuments[url]; + + // Not found, so look in the known documents + const DocumentList::const_iterator end = m_documentList.end(); + for ( DocumentList::const_iterator it = m_documentList.begin(); it != end; ++it ) + { + if ( (*it)->url() == url ) + return *it; + } + + return 0l; +} + + +void DocManager::associateDocument( const KURL &url, Document *document ) +{ + if (!document) + return; + + m_associatedDocuments[url] = document; +} + + +void DocManager::removeDocumentAssociations( Document *document ) +{ + bool doneErase; + do + { + doneErase = false; + const URLDocumentMap::iterator end = m_associatedDocuments.end(); + for ( URLDocumentMap::iterator it = m_associatedDocuments.begin(); it != end; ++it ) + { + if ( it.data() == document ) + { + doneErase = true; + m_associatedDocuments.erase(it); + break; + } + } + } + while (doneErase); +} + + +void DocManager::handleNewDocument( Document *document, ViewArea *viewArea ) +{ + if ( !document || m_documentList.contains(document) ) + return; + + m_documentList.append(document); + document->setDCOPID(m_nextDocumentID++); + + connect( document, SIGNAL(modifiedStateChanged()), p_ktechlab, SLOT(slotDocModifiedChanged()) ); + connect( document, SIGNAL(fileNameChanged(const KURL&)), p_ktechlab, SLOT(slotDocModifiedChanged()) ); + connect( document, SIGNAL(fileNameChanged(const KURL&)), p_ktechlab, SLOT(addRecentFile(const KURL&)) ); + connect( document, SIGNAL(destroyed(QObject* )), this, SLOT(documentDestroyed(QObject* )) ); + connect( document, SIGNAL(viewFocused(View* )), this, SLOT(slotViewFocused(View* )) ); + connect( document, SIGNAL(viewUnfocused()), this, SLOT(slotViewUnfocused()) ); + + createNewView( document, viewArea ); +} + + +View *DocManager::createNewView( Document *document, ViewArea *viewArea ) +{ + if (!document) + return 0l; + + View *view = 0l; + + if (viewArea) + view = document->createView( viewArea->viewContainer(), viewArea->id() ); + + else + { + ViewContainer *viewContainer = new ViewContainer( document->caption(), p_ktechlab ); + view = document->createView( viewContainer, 0 ); + p_ktechlab->addWindow(viewContainer); + } + + view->setFocused(); + return view; +} + + +void DocManager::documentDestroyed( QObject *obj ) +{ + Document *doc = static_cast<Document*>(obj); + m_documentList.remove(doc); + removeDocumentAssociations(doc); + disableContextActions(); +} + + +void DocManager::slotViewFocused( View *view ) +{ + ViewContainer * vc = static_cast<ViewContainer*>(p_ktechlab->tabWidget()->currentPage()); + if (!vc) + view = 0l; + + if (!view) + return; + + // This function can get called with a view that is not in the current view + // container (such as when the user right clicks and then the popup is + // destroyed - not too sure why, but this is the easiest way to fix it). + if ( view->viewContainer() != vc ) + view = vc->activeView(); + + if ( !view || (View*)p_focusedView == view ) + return; + + if (p_focusedView) + slotViewUnfocused(); + + p_focusedView = view; + + if ( TextView * textView = dynamic_cast<TextView*>((View*)p_focusedView) ) + p_ktechlab->factory()->addClient( textView->kateView() ); + else + p_ktechlab->factory()->addClient( p_focusedView ); + + Document *document = view->document(); + + connect( document, SIGNAL(undoRedoStateChanged()), p_ktechlab, SLOT(slotDocUndoRedoChanged()) ); + p_connectedDocument = document; + + if ( document->type() == Document::dt_circuit || + document->type() == Document::dt_flowcode || + document->type() == Document::dt_mechanics ) + { + ItemDocument *cvb = static_cast<ItemDocument*>(view->document()); + ItemInterface::self()->slotItemDocumentChanged(cvb); + } + + p_ktechlab->slotDocUndoRedoChanged(); + p_ktechlab->slotDocModifiedChanged(); + p_ktechlab->requestUpdateCaptions(); +} + + +void DocManager::slotViewUnfocused() +{ + p_ktechlab->removeGUIClients(); + disableContextActions(); + + if (!p_focusedView) + return; + + if (p_connectedDocument) + { + disconnect( p_connectedDocument, SIGNAL(undoRedoStateChanged()), p_ktechlab, SLOT(slotDocUndoRedoChanged()) ); + p_connectedDocument = 0l; + } + + ItemInterface::self()->slotItemDocumentChanged(0l); + p_focusedView = 0l; + +// p_ktechlab->setCaption( 0 ); + p_ktechlab->requestUpdateCaptions(); +} + + +void DocManager::disableContextActions() +{ + p_ktechlab->action("file_save")->setEnabled(false); + p_ktechlab->action("file_save_as")->setEnabled(false); + p_ktechlab->action("file_close")->setEnabled(false); + p_ktechlab->action("file_print")->setEnabled(false); + p_ktechlab->action("edit_undo")->setEnabled(false); + p_ktechlab->action("edit_redo")->setEnabled(false); + p_ktechlab->action("edit_cut")->setEnabled(false); + p_ktechlab->action("edit_copy")->setEnabled(false); + p_ktechlab->action("edit_paste")->setEnabled(false); + p_ktechlab->action("view_split_leftright")->setEnabled(false); + p_ktechlab->action("view_split_topbottom")->setEnabled(false); +} + + +TextDocument *DocManager::createTextDocument() +{ + TextDocument *document = TextDocument::constructTextDocument( untitledName(Document::dt_text), p_ktechlab ); + handleNewDocument(document); + return document; +} + + +CircuitDocument *DocManager::createCircuitDocument() +{ + CircuitDocument *document = new CircuitDocument( untitledName(Document::dt_circuit), p_ktechlab ); + handleNewDocument(document); + if ( KTLConfig::raiseItemSelectors() ) + p_ktechlab->showToolView( p_ktechlab->toolView( ComponentSelector::toolViewIdentifier() ) ); + return document; +} + + +FlowCodeDocument *DocManager::createFlowCodeDocument() +{ + FlowCodeDocument *document = new FlowCodeDocument( untitledName(Document::dt_flowcode), p_ktechlab ); + handleNewDocument(document); + if ( KTLConfig::raiseItemSelectors() ) + p_ktechlab->showToolView( p_ktechlab->toolView( FlowPartSelector::toolViewIdentifier() ) ); + return document; +} + + +MechanicsDocument *DocManager::createMechanicsDocument() +{ + MechanicsDocument *document = new MechanicsDocument( untitledName(Document::dt_mechanics), p_ktechlab ); + handleNewDocument(document); + if ( KTLConfig::raiseItemSelectors() ) + p_ktechlab->showToolView( p_ktechlab->toolView( MechanicsSelector::toolViewIdentifier() ) ); + return document; +} + + +CircuitDocument *DocManager::openCircuitFile( const KURL &url, ViewArea *viewArea ) +{ + CircuitDocument *document = new CircuitDocument( url.fileName().remove(url.directory()), p_ktechlab ); + + if ( !document->openURL(url) ) + { + KMessageBox::sorry( 0l, i18n("Could not open Circuit file \"%1\"").arg(url.prettyURL()) ); + document->deleteLater(); + return 0l; + } + + handleNewDocument( document, viewArea ); + emit fileOpened(url); + return document; +} + + +FlowCodeDocument *DocManager::openFlowCodeFile( const KURL &url, ViewArea *viewArea ) +{ + FlowCodeDocument *document = new FlowCodeDocument( url.fileName().remove(url.directory()), p_ktechlab ); + + if ( !document->openURL(url) ) + { + KMessageBox::sorry( 0l, i18n("Could not open FlowCode file \"%1\"").arg(url.prettyURL()) ); + document->deleteLater(); + return 0l; + } + + handleNewDocument( document, viewArea ); + emit fileOpened(url); + return document; +} + + +MechanicsDocument *DocManager::openMechanicsFile( const KURL &url, ViewArea *viewArea ) +{ + MechanicsDocument *document = new MechanicsDocument( url.fileName().remove(url.directory()), p_ktechlab ); + + if ( !document->openURL(url) ) + { + KMessageBox::sorry( 0l, i18n("Could not open Mechanics file \"%1\"").arg(url.prettyURL()) ); + document->deleteLater(); + return 0l; + } + + handleNewDocument( document, viewArea ); + emit fileOpened(url); + return document; + +} + + +TextDocument *DocManager::openTextFile( const KURL &url, ViewArea *viewArea ) +{ + TextDocument *document = TextDocument::constructTextDocument( url.fileName().remove(url.directory()), p_ktechlab ); + + if (!document) + return 0l; + + if ( !document->openURL(url) ) + { + KMessageBox::sorry( 0l, i18n("Could not open text file \"%1\"").arg(url.prettyURL()) ); + document->deleteLater(); + return 0l; + } + + handleNewDocument( document, viewArea ); + emit fileOpened(url); + return document; +} + + +#include "docmanager.moc" diff --git a/src/docmanager.h b/src/docmanager.h new file mode 100644 index 0000000..0ba300e --- /dev/null +++ b/src/docmanager.h @@ -0,0 +1,169 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef DOCMANAGER_H +#define DOCMANAGER_H + +#include <kurl.h> +#include <qguardedptr.h> + +class CircuitDocument; +class DocManager; +class DocManagerIface; +class Document; +class FlowCodeDocument; +class KTechlab; +class MechanicsDocument; +class TextDocument; +class View; +class ViewArea; + +class KAction; + +typedef QValueList<Document*> DocumentList; +typedef QMap< KURL, Document* > URLDocumentMap; +typedef QValueList<KAction*> KActionList; + +/** +@author David Saxton +*/ +class DocManager : public QObject +{ +Q_OBJECT +public: + static DocManager * self( KTechlab * ktechlab = 0l ); + ~DocManager(); + + /** + * Attempts to close all open documents, returning true if successful + */ + bool closeAll(); + /** + * Goes to the given line in the given text file (if the file exists) + */ + void gotoTextLine( const KURL &url, int line ); + /** + * Attempts to open the document at the given url. + * @param ViewArea if non-null, will open the new view into the ViewArea + */ + Document* openURL( const KURL &url, ViewArea *viewArea = 0l ); + /** + * Returns the focused View + */ + View *getFocusedView() const { return p_focusedView; } + /** + * Returns the focused Document (the document of the focused view) + */ + Document *getFocusedDocument() const; + /** + * Get a unique name, e.g. Untitled (circuit) - n" depending on the types + * of Document and whether it is the first one or not + * @param type Document::DocumentType - type of Document + */ + QString untitledName( int type ); + /** + * Checks to see if a document with the given URL is already open, and + * returns a pointer to that Document if so - otherwises returns null + * @see associateDocument + */ + Document *findDocument( const KURL &url ) const; + /** + * Associates a url with a pointer to a document. When findFile is called + * with the given url, it will return a pointer to this document if it still + * exists. + * @see findDocument + */ + void associateDocument( const KURL &url, Document *document ); + /** + * Gives the given document focus. If it has no open views, one will be + * created for it if viewAreaForNew is non-null + */ + void giveDocumentFocus( Document * toFocus, ViewArea * viewAreaForNew = 0l ); + void removeDocumentAssociations( Document *document ); + void disableContextActions(); + +public slots: + /** + * Creates an empty text document (with an open view) + */ + TextDocument *createTextDocument(); + /** + * Creates an empty circuit document (with an open view), and shows the + * component selector. + */ + CircuitDocument *createCircuitDocument(); + /** + * Creates an empty flowcode document (with an open view), and shows the + * flowpart selector. + */ + FlowCodeDocument *createFlowCodeDocument(); + /** + * Creates an empty mechanics document (with an open view), and shows the + * mechanics selector. + */ + MechanicsDocument *createMechanicsDocument(); + +signals: + /** + * Emitted when a file is successfully opened + */ + void fileOpened( const KURL &url ); + +protected slots: + /** + * Does the appropriate enabling / disabling of actions, connections, etc + */ + void slotViewFocused( View *view ); + /** + * Does the appropriate enabling / disabling of actions, connections, etc + */ + void slotViewUnfocused(); + void documentDestroyed( QObject *obj ); + +protected: + /** + * This function should be called after creating a new document to add it + * to the appropriate lists and connect it up as appropriate + */ + void handleNewDocument( Document *document, ViewArea *viewArea = 0l ); + /** + * Takes the document, creates a new view and shoves it in a new + * ViewContainer + */ + View *createNewView( Document *document, ViewArea *viewArea = 0l ); + CircuitDocument *openCircuitFile( const KURL &url, ViewArea *viewArea = 0l ); + FlowCodeDocument *openFlowCodeFile( const KURL &url, ViewArea *viewArea = 0l ); + MechanicsDocument *openMechanicsFile( const KURL &url, ViewArea *viewArea = 0l ); + TextDocument *openTextFile( const KURL &url, ViewArea *viewArea = 0l ); + + DocumentList m_documentList; + URLDocumentMap m_associatedDocuments; + + // Keeps track of how many + // new files have been made + // for the purpose of making + // titles of the form Untitled (n) + int m_countCircuit; + int m_countFlowCode; + int m_countMechanics; + int m_countOther; + + KTechlab * const p_ktechlab; + QGuardedPtr<View> p_focusedView; + QGuardedPtr<Document> p_connectedDocument; + DocManagerIface *m_pIface; + unsigned m_nextDocumentID; + +private: + DocManager( KTechlab *ktechlab ); + static DocManager * m_pSelf; +}; + +#endif diff --git a/src/docmanageriface.cpp b/src/docmanageriface.cpp new file mode 100644 index 0000000..2f19511 --- /dev/null +++ b/src/docmanageriface.cpp @@ -0,0 +1,69 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "docmanager.h" +#include "docmanageriface.h" +#include "document.h" + + +DocManagerIface::DocManagerIface( DocManager * docManager ) + : DCOPObject("DocumentManager") +{ + m_pDocManager = docManager; +} + + +DocManagerIface::~DocManagerIface() +{ +} + +bool DocManagerIface::closeAll( ) +{ + return m_pDocManager->closeAll(); +} + +DCOPRef DocManagerIface::openURL( const QString & url ) +{ + return docToRef( m_pDocManager->openURL(url) ); +} + +void DocManagerIface::gotoTextLine( const QString & url, int line ) +{ + m_pDocManager->gotoTextLine( url, line ); +} + +DCOPRef DocManagerIface::createTextDocument( ) +{ + return docToRef( (Document*)m_pDocManager->createTextDocument() ); +} + +DCOPRef DocManagerIface::createCircuitDocument( ) +{ + return docToRef( (Document*)m_pDocManager->createCircuitDocument() ); +} + +DCOPRef DocManagerIface::createFlowCodeDocument( ) +{ + return docToRef( (Document*)m_pDocManager->createFlowCodeDocument() ); +} + +DCOPRef DocManagerIface::createMechanicsDocument( ) +{ + return docToRef( (Document*)m_pDocManager->createMechanicsDocument() ); +} + +DCOPRef DocManagerIface::docToRef( Document * document ) +{ + if (document) + return DCOPRef(document->dcopObject()); + return DCOPRef(); +} + + diff --git a/src/docmanageriface.h b/src/docmanageriface.h new file mode 100644 index 0000000..f788dc2 --- /dev/null +++ b/src/docmanageriface.h @@ -0,0 +1,68 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef DOCMANAGERIFACE_H +#define DOCMANAGERIFACE_H + +#include <dcopobject.h> +#include <dcopref.h> + +class DocManager; +class Document; + +/** +@author David Saxton +*/ +class DocManagerIface : public DCOPObject +{ + K_DCOP + + public: + DocManagerIface( DocManager * docManager ); + ~DocManagerIface(); + + k_dcop: + /** + * Attempt to close all documents, returning true if successful. + */ + bool closeAll(); + /** + * Attempt to open the given URL. + */ + DCOPRef openURL( const QString & url ); + /** + * Attempt to open the text file at the given url (if it isn't already + * open), and then go to that line in the text file. + */ + void gotoTextLine( const QString & url, int line ); + /** + * Creates a new Text document. + */ + DCOPRef createTextDocument(); + /** + * Creates a new Circuit document. + */ + DCOPRef createCircuitDocument(); + /** + * Creates a new FlowCode document. + */ + DCOPRef createFlowCodeDocument(); + /** + * Creates a new Mechanics document. + */ + DCOPRef createMechanicsDocument(); + + protected: + DCOPRef docToRef( Document * document ); + + DocManager * m_pDocManager; +}; + +#endif diff --git a/src/document.cpp b/src/document.cpp new file mode 100644 index 0000000..cc43f00 --- /dev/null +++ b/src/document.cpp @@ -0,0 +1,205 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "document.h" +#include "documentiface.h" +#include "ktechlab.h" +#include "projectmanager.h" +#include "view.h" +#include "viewcontainer.h" + +#include <kfiledialog.h> +#include <klocale.h> +#include <kmessagebox.h> +#include <ktabwidget.h> + +Document::Document( const QString &caption, KTechlab *ktechlab, const char *name ) + : QObject( ktechlab, name ), + b_modified(false), + p_ktechlab(ktechlab), + p_activeView(0l), + m_caption(caption), + m_bAddToProjectOnSave(false), + m_pDocumentIface(0l), + m_dcopID(0), + m_nextViewID(0), + m_bDeleted(false) +{ + if (p_ktechlab) + connect( p_ktechlab, SIGNAL(configurationChanged()), this, SLOT(slotUpdateConfiguration()) ); +} + + +Document::~Document() +{ + m_bDeleted = true; + + ViewList viewsToDelete = m_viewList; + const ViewList::iterator end = viewsToDelete.end(); + for ( ViewList::iterator it = viewsToDelete.begin(); it != end; ++it ) + (*it)->deleteLater(); +} + + +void Document::handleNewView( View *view ) +{ + if ( !view || m_viewList.contains(view) ) + return; + + m_viewList.append(view); + view->setDCOPID(m_nextViewID++); + view->setCaption(m_caption); + connect( view, SIGNAL(destroyed(QObject* )), this, SLOT(slotViewDestroyed(QObject* )) ); + connect( view, SIGNAL(viewFocused(View* )), this, SLOT(slotViewFocused(View* )) ); + connect( view, SIGNAL(viewUnfocused()), this, SIGNAL(viewUnfocused()) ); + view->show(); + view->setFocused(); +} + + +void Document::slotViewDestroyed( QObject *obj ) +{ + View *view = static_cast<View*>(obj); + + m_viewList.remove(view); + + if ( p_activeView == (QGuardedPtr<View>)view ) + { + p_activeView = 0l; + emit viewUnfocused(); + } + + if ( m_viewList.isEmpty() ) + deleteLater(); +} + + +void Document::slotViewFocused(View *view) +{ + if (!view) + return; + + p_activeView = view; + emit viewFocused(view); +} + + +void Document::setCaption( const QString &caption ) +{ + m_caption = caption; + const ViewList::iterator end = m_viewList.end(); + for ( ViewList::iterator it = m_viewList.begin(); it != end; ++it ) + (*it)->setCaption(caption); +} + + +bool Document::getURL( const QString &types ) +{ + KURL url = KFileDialog::getSaveURL( QString::null, types, p_ktechlab, i18n("Save Location")); + + if ( url.isEmpty() ) + return false; + + if ( QFile::exists( url.path() ) ) + { + int query = KMessageBox::warningYesNo( p_ktechlab, + i18n( "A file named \"%1\" already exists. Are you sure you want to overwrite it?" ).arg( url.fileName() ), + i18n( "Overwrite File?" ), + i18n( "Overwrite" ), + KStdGuiItem::cancel() ); + if ( query == KMessageBox::No ) + return false; + } + + setURL(url); + + return true; +} + + +bool Document::fileClose() +{ + if ( isModified() ) + { + // If the filename is empty then it must be an untitled file. + QString name = m_url.fileName().isEmpty() ? caption() : m_url.fileName(); + + if ( ViewContainer * viewContainer = (activeView() ? activeView()->viewContainer() : 0l) ) + p_ktechlab->tabWidget()->setCurrentPage( p_ktechlab->tabWidget()->indexOf(viewContainer) ); + + int choice = KMessageBox::warningYesNoCancel( p_ktechlab, + i18n("The document \'%1\' has been modified.\nDo you want to save it?").arg(name), + i18n("Save Document?"), + i18n("Save"), + i18n("Discard") ); + + if ( choice == KMessageBox::Cancel ) + return false; + if ( choice == KMessageBox::Yes ) + fileSave(); + } + + deleteLater(); + return true; +} + + +void Document::setModified( bool modified ) +{ + if ( b_modified == modified ) + return; + + b_modified = modified; + + if (!m_bDeleted) + emit modifiedStateChanged(); +} + + +void Document::setURL( const KURL &url ) +{ + if ( m_url == url ) + return; + + bool wasEmpty = m_url.isEmpty(); + m_url = url; + + if ( wasEmpty && m_bAddToProjectOnSave && ProjectManager::self()->currentProject() ) + ProjectManager::self()->currentProject()->addFile(m_url); + + emit fileNameChanged(url); + + if (p_ktechlab) + { + p_ktechlab->addRecentFile(url); + p_ktechlab->requestUpdateCaptions(); + } +} + +DCOPObject * Document::dcopObject( ) const +{ + return m_pDocumentIface; +} + +void Document::setDCOPID( unsigned id ) +{ + if ( m_dcopID == id ) + return; + + m_dcopID = id; + if ( m_pDocumentIface ) + { + QCString docID; + docID.setNum( dcopID() ); + m_pDocumentIface->setObjId( "Document#" + docID ); + } +} + +#include "document.moc" diff --git a/src/document.h b/src/document.h new file mode 100644 index 0000000..eb465ad --- /dev/null +++ b/src/document.h @@ -0,0 +1,243 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef DOCUMENT_H +#define DOCUMENT_H + +#include <kurl.h> +#include <qguardedptr.h> + +class DCOPObject; +class Document; +class DocumentIface; +class KTechlab; +class View; +class ViewContainer; + +typedef QValueList<QGuardedPtr<View> > ViewList; + +/** +@author David Saxton +*/ +class Document : public QObject +{ +Q_OBJECT +public: + enum DocumentType + { + dt_none, // Used to denote document type not known / specified / etc, when appropriate + dt_flowcode, + dt_circuit, + dt_mechanics, + dt_text, + dt_pinMapEditor + }; + Document( const QString &caption, KTechlab *ktechlab, const char *name = 0 ); + virtual ~Document(); + /** + * If the user has created a new document from the new file dialog, and + * wants to add it to the project, then this must wait until this file is + * given a url. Set this to true to add the file to the active project when + * it is first saved. + */ + void setAddToProjectOnSave( bool add ) { m_bAddToProjectOnSave = add; } + /** + * Caption of document, e.g. "Untitled 2" + */ + QString caption() const { return m_caption; } + /** + * Set the caption of the document, to be displayed in the tab bar when + * active + */ + void setCaption( const QString &caption ); + /** + * Return the dcop object for this document + */ + DCOPObject * dcopObject() const; + /** + * Returns the dcop suffix for this document - a unique ID for the current + * app session. DCOP name will be "Document#dcopID()" + */ + unsigned dcopID() const { return m_dcopID; } + /** + * Sets the dcop suffix. The DCOP object for this document will be renamed. + * @see dcopID + */ + void setDCOPID( unsigned id ); + /** + * Returns the active view, which is the last view to be used to edit in + */ + View *activeView() const { return p_activeView; } + ViewList viewList() const { return m_viewList; } + /** + * Returns the type of document. + * @see Document::DocumentType + */ + DocumentType type() const { return m_type; } + /** + * Returns the number of open views. + */ + uint numberOfViews() const { return m_viewList.size(); } + /** + * Create a view that will display the document data. In all reimplemented + * functions, you must call handleNewView after creating the view, so that + * the appropriate slots, pointers, etc can all be initialised. + */ + virtual View *createView( ViewContainer *viewContainer, uint viewAreaId, const char *name = 0l ) = 0; + /** + * Returns the url of the file that the Document refers to + */ + const KURL& url() const { return m_url; } + /** + * Prompts the user for a url, with the given types for the filter. + * If user accepts, returns true, and set the url to the new url. + */ + bool getURL( const QString &types ); + /** + * Attempts to open a url, and returns true if succesful. + * You must reinherit this function. + */ + virtual bool openURL( const KURL &url ) = 0; + /** + * Sets the url of the file that this Document refers to + */ + void setURL( const KURL &url ); + /** + * Sets whether the file is modified or not. Will emit modifiedStateChanged + * if state changes. You must emit this signal if you reinherit this + */ + virtual void setModified( bool modified ); + /** + * Returns the modification state since last-save. + */ + virtual bool isModified() const { return b_modified; } + /** + * Returns true if undo is avilable. + */ + virtual bool isUndoAvailable() const { return false; } + /** + * Returns true if redo is avilable. + */ + virtual bool isRedoAvailable() const { return false; } + /** + * Saves the file to a new name. + */ + virtual void fileSaveAs() = 0; + /** + * Attempts to close the file without saving, prompting the user if the + * file has been modified. If succesful, calls QObject::deleteLater(), and + * returns true (otherwise returns false). + */ + virtual bool fileClose(); + /** + * Saves the file. + */ + virtual void fileSave() = 0; + /** + * Prints the file. + */ + virtual void print() {}; + /** + * Cuts whatever is selected. + */ + virtual void cut() {}; + /** + * Copies whatever is selected. + */ + virtual void copy() {}; + /** + * Attempts to paste whatever is in the clipboard. + */ + virtual void paste() {}; + /** + * Undo the last operation. You should reinherit this function. + */ + virtual void undo() {}; + /** + * Redo the undone last operation. You should reinherit this function. + */ + virtual void redo() {}; + /** + * Selects everything in the view. + */ + virtual void selectAll() {}; + + virtual void convertToMicrobe() {}; + virtual void convertToHex() {}; + virtual void convertToPIC() {}; + virtual void convertToAssembly() {}; + virtual void debugRun() {}; + virtual void debugInterrupt() {}; + virtual void debugStop() {}; + virtual void debugStep() {}; + KTechlab *ktechlab() const { return p_ktechlab; } + bool isDeleted() const { return m_bDeleted; } + +protected slots: + /** + * Called when the user changes the configuration. + */ + virtual void slotUpdateConfiguration() {}; + +#define protected public +signals: + /** + * Emitted when an operation has been performed that + * has caused the stack of available undo/redo operations to + * have changed + */ + void undoRedoStateChanged(); +#undef protected + +signals: + /** + * Emitted when the Document goes from modified to unmodified, + * or vice-versa + */ + void modifiedStateChanged(); + /** + * Emitted when the name of the file that the Document refers to + * is changed. + */ + void fileNameChanged( const KURL &url ); + + void viewFocused( View *view ); + void viewUnfocused(); + +private slots: + void slotViewDestroyed( QObject *obj ); + void slotViewFocused( View *view ); + +protected: + /** + * You must call this function after creating a new view + */ + virtual void handleNewView( View *view ); + + bool b_modified; + KTechlab *p_ktechlab; + QGuardedPtr<View> p_activeView; + DocumentType m_type; + ViewList m_viewList; + QString m_caption; + bool m_bAddToProjectOnSave; + DocumentIface * m_pDocumentIface; + unsigned m_dcopID; + unsigned m_nextViewID; + + // Set to true by the document et subclasses destructors, used to avoid + // doing stuff that might lead to crash when being deleted. + bool m_bDeleted; + +private: + KURL m_url; +}; + +#endif diff --git a/src/documentiface.cpp b/src/documentiface.cpp new file mode 100644 index 0000000..dd7e0eb --- /dev/null +++ b/src/documentiface.cpp @@ -0,0 +1,445 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "circuitdocument.h" +#include "connector.h" +#include "cnitem.h" +#include "documentiface.h" +#include "flowcodedocument.h" +#include "itemlibrary.h" +#include "libraryitem.h" +#include "mechanicsdocument.h" +#include "textdocument.h" +#include "view.h" + + +//BEGIN class DocumentIface +DocumentIface::DocumentIface( Document * document ) + : DCOPObject("Document") +{ + m_pDocument = document; +} + + +DocumentIface::~DocumentIface() +{ +} + +void DocumentIface::selectAll( ) +{ + m_pDocument->selectAll(); +} + +void DocumentIface::redo( ) +{ + m_pDocument->redo(); +} + +void DocumentIface::undo( ) +{ + m_pDocument->undo(); +} + +void DocumentIface::paste( ) +{ + m_pDocument->paste(); +} + +void DocumentIface::copy( ) +{ + m_pDocument->copy(); +} + +void DocumentIface::cut( ) +{ + m_pDocument->cut(); +} + +void DocumentIface::print( ) +{ + m_pDocument->print(); +} + + +bool DocumentIface::close( ) +{ + return m_pDocument->fileClose(); +} + +void DocumentIface::saveAs( ) +{ + m_pDocument->fileSaveAs(); +} + +void DocumentIface::save( ) +{ + m_pDocument->fileSave(); +} + +bool DocumentIface::isRedoAvailable( ) +{ + return m_pDocument->isRedoAvailable(); +} + +bool DocumentIface::isUndoAvailable( ) +{ + return m_pDocument->isUndoAvailable(); +} + +bool DocumentIface::isModified( ) +{ + return m_pDocument->isModified(); +} + +bool DocumentIface::openURL( const QString & url ) +{ + return m_pDocument->openURL(url); +} + +QString DocumentIface::url( ) +{ + return m_pDocument->url().url(); +} + +uint DocumentIface::numberOfViews( ) +{ + return m_pDocument->numberOfViews(); +} + +DCOPRef DocumentIface::activeView( ) +{ + return viewToRef( m_pDocument->activeView() ); +} + +QString DocumentIface::caption( ) const +{ + return m_pDocument->caption(); +} + +DCOPRef DocumentIface::viewToRef( View * view ) +{ + return DCOPRef( view->dcopObject() ); +} +//END class DocumentIface + + + +//BEGIN class FlowCodeDocumentIface +FlowCodeDocumentIface::FlowCodeDocumentIface( FlowCodeDocument * document ) + : ICNDocumentIface(document) +{ + m_pFlowCodeDocument = document; +} + +void FlowCodeDocumentIface::setPicType( const QString & id ) +{ + m_pFlowCodeDocument->setPicType(id); +} + +void FlowCodeDocumentIface::convertToMicrobe() +{ + m_pFlowCodeDocument->convertToMicrobe(); +} + +void FlowCodeDocumentIface::convertToHex() +{ + m_pFlowCodeDocument->convertToHex(); +} + +void FlowCodeDocumentIface::convertToPIC() +{ + m_pFlowCodeDocument->convertToPIC(); +} + +void FlowCodeDocumentIface::convertToAssembly() +{ + m_pFlowCodeDocument->convertToAssembly(); +} +//END class FlowCodeDocumentIface + + + +//BEGIN class CircuitDocumentIface +CircuitDocumentIface::CircuitDocumentIface( CircuitDocument * document ) + : ICNDocumentIface(document) +{ + m_pCircuitDocument = document; +} + +void CircuitDocumentIface::setOrientation0( ) +{ + m_pCircuitDocument->setOrientation0(); +} + +void CircuitDocumentIface::setOrientation90( ) +{ + m_pCircuitDocument->setOrientation90(); +} + +void CircuitDocumentIface::setOrientation180( ) +{ + m_pCircuitDocument->setOrientation180(); +} + +void CircuitDocumentIface::setOrientation270( ) +{ + m_pCircuitDocument->setOrientation270(); +} + +void CircuitDocumentIface::rotateCounterClockwise( ) +{ + m_pCircuitDocument->rotateCounterClockwise(); +} + +void CircuitDocumentIface::rotateClockwise( ) +{ + m_pCircuitDocument->rotateClockwise(); +} + +void CircuitDocumentIface::flip( ) +{ + m_pCircuitDocument->itemFlip(); +} + +void CircuitDocumentIface::displayEquations( ) +{ + m_pCircuitDocument->displayEquations(); +} + +void CircuitDocumentIface::createSubcircuit( ) +{ + m_pCircuitDocument->createSubcircuit(); +} +//END class CircuitDocumentIface + + + +//BEGIN class ICNDocumentIface +ICNDocumentIface::ICNDocumentIface( ICNDocument * document ) + : ItemDocumentIface(document) +{ + m_pICNDocument = document; +} + +void ICNDocumentIface::exportToImage( ) +{ + m_pICNDocument->exportToImage(); +} + +QCStringList ICNDocumentIface::nodeIDs( const QString & id ) +{ + CNItem * item = m_pICNDocument->cnItemWithID(id); + + QCStringList ids; + if ( !item ) + return ids; + + const NodeMap nm = item->nodeMap(); + const NodeMap::const_iterator end = nm.end(); + for ( NodeMap::const_iterator it = nm.begin(); it != end; ++it ) + ids.append( it.key().ascii() ); + + return ids; +} + +QString ICNDocumentIface::makeConnection( const QString & item1, const QString & node1, const QString & item2, const QString & node2 ) +{ + CNItem * i1 = m_pICNDocument->cnItemWithID(item1); + CNItem * i2 = m_pICNDocument->cnItemWithID(item2); + + if ( !i1 || !i2 ) + return QString::null; + + Node * n1 = m_pICNDocument->nodeWithID( i1->nodeId(node1) ); + Node * n2 = m_pICNDocument->nodeWithID( i2->nodeId(node2) ); + + if ( !n1 || !n2 ) + return QString::null; + + Connector * connector = m_pICNDocument->createConnector( n1, n2 ); + return connector ? connector->id() : QString::null; +} + +void ICNDocumentIface::selectConnector( const QString & id ) +{ + m_pICNDocument->select( m_pICNDocument->connectorWithID(id) ); +} + +void ICNDocumentIface::unselectConnector( const QString & id ) +{ + m_pItemDocument->unselect( m_pICNDocument->connectorWithID(id) ); +} +//END class ICNDocumentIface + + + +//BEGIN class ItemDocumentIface +ItemDocumentIface::ItemDocumentIface( ItemDocument * document ) + : DocumentIface(document) +{ + m_pItemDocument = document; +} + +QCStringList ItemDocumentIface::validItemIDs( ) +{ + QCStringList validIDs; + + LibraryItemList * allItems = itemLibrary()->items(); + const LibraryItemList::iterator end = allItems->end(); + for ( LibraryItemList::iterator it = allItems->begin(); it != end; ++it ) + { + QString id = (*it)->activeID(); + if ( m_pItemDocument->isValidItem(id) ) + validIDs << id.utf8(); + } + return validIDs; +} + +QString ItemDocumentIface::addItem( const QString & id, int x, int y ) +{ + Item * item = m_pItemDocument->addItem( id, QPoint( x, y ), true ); + return item ? item->id() : QString::null; +} + +void ItemDocumentIface::selectItem( const QString & id ) +{ + m_pItemDocument->select( m_pItemDocument->itemWithID(id) ); +} + +void ItemDocumentIface::unselectItem( const QString & id ) +{ + m_pItemDocument->unselect( m_pItemDocument->itemWithID(id) ); +} + +void ItemDocumentIface::deleteSelection( ) +{ + m_pItemDocument->deleteSelection(); +} + +void ItemDocumentIface::clearHistory( ) +{ + m_pItemDocument->clearHistory(); +} + +void ItemDocumentIface::unselectAll( ) +{ + m_pItemDocument->unselectAll(); +} + +void ItemDocumentIface::alignHorizontally( ) +{ + m_pItemDocument->alignHorizontally(); +} + +void ItemDocumentIface::alignVertically( ) +{ + m_pItemDocument->alignVertically(); +} + +void ItemDocumentIface::distributeHorizontally( ) +{ + m_pItemDocument->distributeHorizontally(); +} + +void ItemDocumentIface::distributeVertically( ) +{ + m_pItemDocument->distributeVertically(); +} +//END class ItemDocumentIface + + + +//BEGIN class TextDocumentIface +TextDocumentIface::TextDocumentIface( TextDocument * document ) + : DocumentIface(document) +{ + m_pTextDocument = document; +} + +void TextDocumentIface::debugStepOver( ) +{ + m_pTextDocument->debugStepOver(); +} + +void TextDocumentIface::debugStepOut( ) +{ + m_pTextDocument->debugStepOut(); +} + +void TextDocumentIface::debugStep( ) +{ + m_pTextDocument->debugStep(); +} + +void TextDocumentIface::debugStop( ) +{ + m_pTextDocument->debugStop(); +} + +void TextDocumentIface::debugInterrupt( ) +{ + m_pTextDocument->debugInterrupt(); +} + +void TextDocumentIface::debugRun( ) +{ + m_pTextDocument->debugRun(); +} + +bool TextDocumentIface::isDebugging( ) +{ +#ifndef NO_GPSIM + return m_pTextDocument->debuggerIsRunning(); +#else + return false; +#endif +} + +void TextDocumentIface::clearBookmarks( ) +{ + m_pTextDocument->clearBookmarks(); +} + +void TextDocumentIface::convertToAssembly( ) +{ + m_pTextDocument->convertToAssembly(); +} + +void TextDocumentIface::convertToPIC( ) +{ + m_pTextDocument->convertToPIC(); +} + +void TextDocumentIface::convertToHex( ) +{ + m_pTextDocument->convertToHex(); +} + +void TextDocumentIface::convertToMicrobe( ) +{ + m_pTextDocument->convertToMicrobe(); +} + +void TextDocumentIface::formatAssembly( ) +{ + m_pTextDocument->formatAssembly(); +} +//END class TextDocumentIface + + + +//BEGIN class MechanicsDocumentIface +MechanicsDocumentIface::MechanicsDocumentIface( MechanicsDocument * document ) + : ItemDocumentIface(document) +{ + m_pMechanicsDocument = document; +} +//END class MechanicsDocumentIface + diff --git a/src/documentiface.h b/src/documentiface.h new file mode 100644 index 0000000..5f32cfe --- /dev/null +++ b/src/documentiface.h @@ -0,0 +1,193 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef DOCUMENTIFACE_H +#define DOCUMENTIFACE_H + +#include "config.h" + +#include <dcopobject.h> +#include <dcopref.h> + +class CircuitDocument; +class Document; +class FlowCodeDocument; +class ICNDocument; +class ItemDocument; +class MechanicsDocument; +class TextDocument; +class View; + +/** +@author David Saxton +*/ +class DocumentIface : public DCOPObject +{ + K_DCOP + + public: + DocumentIface( Document * document ); + virtual ~DocumentIface(); + + k_dcop: + QString caption() const; + DCOPRef activeView(); + uint numberOfViews(); +// View *createView( ViewContainer *viewContainer, uint viewAreaId, const char *name = 0l ); + QString url(); + bool openURL( const QString & url ); + bool isModified(); + bool isUndoAvailable(); + bool isRedoAvailable(); + void save(); + void saveAs(); + bool close(); + void print(); + void cut(); + void copy(); + void paste(); + void undo(); + void redo(); + void selectAll(); + + protected: + DCOPRef viewToRef( View * view ); + + Document * m_pDocument; +}; + +class TextDocumentIface : public DocumentIface +{ + K_DCOP + + public: + TextDocumentIface( TextDocument * document ); + + k_dcop: + void formatAssembly(); + void convertToMicrobe(); + void convertToHex(); + void convertToPIC(); + void convertToAssembly(); + void clearBookmarks(); + bool isDebugging(); + void debugRun(); + void debugInterrupt(); + void debugStop(); + void debugStep(); + void debugStepOver(); + void debugStepOut(); + + protected: + TextDocument * m_pTextDocument; +}; + +class ItemDocumentIface : public DocumentIface +{ + K_DCOP + + public: + ItemDocumentIface( ItemDocument * document ); + + k_dcop: + QCStringList validItemIDs(); + /** + * Create an item with the given id (e.g. "ec/resistor") at the given + * position. + * @return name of item (assigned to it by KTechlab) + */ + QString addItem( const QString & id, int x, int y ); + void selectItem( const QString & id ); + void unselectItem( const QString & id ); + void clearHistory(); + void unselectAll(); + void alignHorizontally(); + void alignVertically(); + void distributeHorizontally(); + void distributeVertically(); + void deleteSelection(); + + protected: + ItemDocument * m_pItemDocument; +}; + +class MechanicsDocumentIface : public ItemDocumentIface +{ + K_DCOP + + public: + MechanicsDocumentIface( MechanicsDocument * document ); + + protected: + MechanicsDocument * m_pMechanicsDocument; +}; + +class ICNDocumentIface : public ItemDocumentIface +{ + K_DCOP + + public: + ICNDocumentIface( ICNDocument * document ); + + k_dcop: + void exportToImage(); + QCStringList nodeIDs( const QString & id ); + /** + * Makes a connection from node1 on item1 to node2 on item2 + */ + QString makeConnection( const QString & item1, const QString & node1, const QString & item2, const QString & node2 ); + void selectConnector( const QString & id ); + void unselectConnector( const QString & id ); + + protected: + ICNDocument * m_pICNDocument; +}; + +class CircuitDocumentIface : public ICNDocumentIface +{ + K_DCOP + + public: + CircuitDocumentIface( CircuitDocument * document ); + + k_dcop: + void setOrientation0(); + void setOrientation90(); + void setOrientation180(); + void setOrientation270(); + void rotateCounterClockwise(); + void rotateClockwise(); + void flip(); + void displayEquations(); + void createSubcircuit(); + + protected: + CircuitDocument * m_pCircuitDocument; +}; + +class FlowCodeDocumentIface : public ICNDocumentIface +{ + K_DCOP + + public: + FlowCodeDocumentIface( FlowCodeDocument * document ); + void convertToMicrobe(); + void convertToHex(); + void convertToPIC(); + void convertToAssembly(); + + k_dcop: + void setPicType( const QString & id ); + + protected: + FlowCodeDocument * m_pFlowCodeDocument; +}; + +#endif diff --git a/src/drawparts/Makefile.am b/src/drawparts/Makefile.am new file mode 100644 index 0000000..f3c1c25 --- /dev/null +++ b/src/drawparts/Makefile.am @@ -0,0 +1,6 @@ +INCLUDES = -I$(top_srcdir)/src -I$(top_srcdir)/src/electronics -I$(top_srcdir)/src/electronics/components -I$(top_srcdir)/src/electronics/simulation -I$(top_srcdir)/src/flowparts -I$(top_srcdir)/src/gui -I$(top_srcdir)/src/languages -I$(top_srcdir)/src/mechanics -I$(top_srcdir)/src/micro $(all_includes) +METASOURCES = AUTO +noinst_LTLIBRARIES = libdrawparts.la +libdrawparts_la_SOURCES = drawpart.cpp dpline.cpp solidshape.cpp dptext.cpp +noinst_HEADERS = drawpart.h dpline.h solidshape.h dptext.h + diff --git a/src/drawparts/dpline.cpp b/src/drawparts/dpline.cpp new file mode 100644 index 0000000..59fa789 --- /dev/null +++ b/src/drawparts/dpline.cpp @@ -0,0 +1,227 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "dpline.h" +#include "libraryitem.h" +#include "resizeoverlay.h" +#include "variant.h" + +#include <cmath> +#include <kiconloader.h> +#include <klocale.h> +#include <qpainter.h> + + +//BEGIN class DPLine +Item* DPLine::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new DPLine( itemDocument, newItem, id ); +} + +LibraryItem* DPLine::libraryItem() +{ + return new LibraryItem( + QString("dp/line"), + i18n("Line"), + i18n("Other"), + KGlobal::iconLoader()->loadIcon( "text", KIcon::Small ), + LibraryItem::lit_drawpart, + DPLine::construct ); +} + +DPLine::DPLine( ItemDocument *itemDocument, bool newItem, const char *id ) + : DrawPart( itemDocument, newItem, id ? id : "line" ) +{ + m_pLineOverlay = new LineOverlay(this); + m_name = i18n("Line"); + m_desc = i18n("Select the line to position the end points"); + + createProperty( "line-color", Variant::Type::Color ); + property("line-color")->setCaption( i18n("Line Color") ); + property("line-color")->setValue(Qt::black); + + createProperty( "line-width", Variant::Type::Int ); + property("line-width")->setCaption( i18n("Line Width") ); + property("line-width")->setMinValue(1); + property("line-width")->setMaxValue(1000); + property("line-width")->setValue(1); + + createProperty( "line-style", Variant::Type::PenStyle ); + property("line-style")->setCaption( i18n("Line Style") ); + property("line-style")->setAdvanced(true); + setDataPenStyle( "line-style", Qt::SolidLine ); + + createProperty( "cap-style", Variant::Type::PenCapStyle ); + property("cap-style")->setCaption( i18n("Cap Style") ); + property("cap-style")->setAdvanced(true); + setDataPenCapStyle( "cap-style", Qt::FlatCap ); +} + +DPLine::~DPLine() +{ +} + +void DPLine::setSelected( bool yes ) +{ + if ( yes == isSelected() ) + return; + + DrawPart::setSelected(yes); + m_pLineOverlay->showResizeHandles(yes); +} + + +void DPLine::dataChanged() +{ + setPen( QPen( dataColor("line-color"), + unsigned( dataInt("line-width") ), + getDataPenStyle("line-style"), + getDataPenCapStyle("cap-style"), + Qt::MiterJoin ) ); + + postResize(); // in case the pen width has changed + update(); +} + + +void DPLine::postResize() +{ + int x1 = offsetX(); + int y1 = offsetY(); + int x2 = x1+width(); + int y2 = y1+height(); + + QPointArray p(4); + int pw = pen().width(); + int dx = QABS(x1-x2); + int dy = QABS(y1-y2); + pw = pw*4/3+2; // approx pw*sqrt(2) + int px = x1<x2 ? -pw : pw ; + int py = y1<y2 ? -pw : pw ; + if ( dx && dy && (dx > dy ? (dx*2/dy <= 2) : (dy*2/dx <= 2)) ) { + // steep + if ( px == py ) { + p[0] = QPoint(x1 ,y1+py); + p[1] = QPoint(x2-px,y2 ); + p[2] = QPoint(x2 ,y2-py); + p[3] = QPoint(x1+px,y1 ); + } else { + p[0] = QPoint(x1+px,y1 ); + p[1] = QPoint(x2 ,y2-py); + p[2] = QPoint(x2-px,y2 ); + p[3] = QPoint(x1 ,y1+py); + } + } else if ( dx > dy ) { + // horizontal + p[0] = QPoint(x1+px,y1+py); + p[1] = QPoint(x2-px,y2+py); + p[2] = QPoint(x2-px,y2-py); + p[3] = QPoint(x1+px,y1-py); + } else { + // vertical + p[0] = QPoint(x1+px,y1+py); + p[1] = QPoint(x2+px,y2-py); + p[2] = QPoint(x2-px,y2-py); + p[3] = QPoint(x1-px,y1+py); + } + setItemPoints( p, false ); +} + + +void DPLine::drawShape( QPainter & p ) +{ + int x1 = int(x()+offsetX()); + int y1 = int(y()+offsetY()); + int x2 = x1+width(); + int y2 = y1+height(); + + p.drawLine( x1, y1, x2, y2 ); +} +//END class DPLine + + +//BEGIN class DPArrow +Item* DPArrow::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new DPArrow( itemDocument, newItem, id ); +} + +LibraryItem* DPArrow::libraryItem() +{ + return new LibraryItem( + QString("dp/arrow"), + i18n("Arrow"), + i18n("Other"), + KGlobal::iconLoader()->loadIcon( "text", KIcon::Small ), + LibraryItem::lit_drawpart, + DPArrow::construct ); +} + +DPArrow::DPArrow( ItemDocument *itemDocument, bool newItem, const char *id ) + : DPLine( itemDocument, newItem, id ? id : "arrow" ) +{ + m_name = i18n("Arrow"); + + // We don't want to use the square cap style as it screws up drawing our arrow head + QStringList allowed = property("cap-style")->allowed(); + allowed.remove( DrawPart::penCapStyleToName( Qt::SquareCap ) ); + property("cap-style")->setAllowed(allowed); +} + +DPArrow::~DPArrow() +{ +} + + +void DPArrow::drawShape( QPainter & p ) +{ + int x1 = int(x()+offsetX()); + int y1 = int(y()+offsetY()); + int x2 = x1+width(); + int y2 = y1+height(); + + p.drawLine( x1, y1, x2, y2 ); + + double dx = x2-x1; + double dy = y2-y1; + + if ( dx == 0. && dy == 0. ) + return; + + double pi = 3.14159265358979323846264; + double arrow_angle = ( dx == 0 ? (dy>0?(pi/2.):(-pi/2.)) : std::atan(dy/dx) ); + if ( dx < 0 ) + arrow_angle += pi; + + double head_angle = 0.6; // Angle of arrowhead + double head_length = 10.; + + // Position of arrowhead + int x3 = int( x2 + head_length*std::cos( pi + arrow_angle - head_angle ) ); + int y3 = int( y2 + head_length*std::sin( pi + arrow_angle - head_angle ) ); + int x4 = int( x2 + head_length*std::cos( pi + arrow_angle + head_angle ) ); + int y4 = int( y2 + head_length*std::sin( pi + arrow_angle + head_angle ) ); + + // Draw arrowhead + QPen pen = p.pen(); + pen.setCapStyle( Qt::RoundCap ); + p.setPen(pen); + p.setBrush(pen.color()); + QPointArray pa(3); + pa[0] = QPoint( x2, y2 ); + pa[1] = QPoint( x3, y3 ); + pa[2] = QPoint( x4, y4 ); + p.drawPolygon(pa); + p.drawPolyline(pa); +// p.drawLine( x2, y2, x3, y3 ); +// p.drawLine( x2, y2, x4, y4 ); +} +//END class DPLine + diff --git a/src/drawparts/dpline.h b/src/drawparts/dpline.h new file mode 100644 index 0000000..e47aca8 --- /dev/null +++ b/src/drawparts/dpline.h @@ -0,0 +1,56 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef DPLINE_H +#define DPLINE_H + +#include "drawpart.h" + +class LineOverlay; + +/** +@author David Saxton +*/ +class DPLine : public DrawPart +{ + public: + DPLine( ItemDocument *itemDocument, bool newItem, const char *id = 0L ); + ~DPLine(); + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + + virtual void setSelected( bool yes ); + + protected: + virtual void postResize(); + virtual void dataChanged(); + virtual void drawShape( QPainter &p ); + + LineOverlay * m_pLineOverlay; +}; + +/** +@author David Saxton +*/ +class DPArrow : public DPLine +{ + public: + DPArrow( ItemDocument *itemDocument, bool newItem, const char *id = 0L ); + ~DPArrow(); + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + + protected: + virtual void drawShape( QPainter &p ); +}; + +#endif diff --git a/src/drawparts/dptext.cpp b/src/drawparts/dptext.cpp new file mode 100644 index 0000000..ebb239a --- /dev/null +++ b/src/drawparts/dptext.cpp @@ -0,0 +1,133 @@ +/*************************************************************************** + * Copyright (C) 2003-2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "dptext.h" +#include "itemdocument.h" +#include "libraryitem.h" +#include "resizeoverlay.h" + +#include <kiconloader.h> +#include <klocale.h> +#include <qpainter.h> + +Item* DPText::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new DPText( itemDocument, newItem, id ); +} + +LibraryItem* DPText::libraryItem() +{ + QStringList idList; + idList << "dp/text" << "dp/canvas_text" << "canvas_text"; + + return new LibraryItem( + idList, + i18n("Canvas Text"), + i18n("Other"), + KGlobal::iconLoader()->loadIcon( "text", KIcon::Small ), + LibraryItem::lit_drawpart, + DPText::construct ); +} + +DPText::DPText( ItemDocument *itemDocument, bool newItem, const char *id ) + : DrawPart( itemDocument, newItem, (id) ? id : "canvas_text" ) +{ + m_rectangularOverlay = new RectangularOverlay(this); + m_name = i18n("Text"); + m_desc = i18n("Doubleclick the Text Item to set the text"); + + createProperty( "text", Variant::Type::Multiline ); + property("text")->setValue( i18n("Text") ); + + createProperty( "background", Variant::Type::Bool ); + property("background")->setValue(false); + property("background")->setCaption( i18n("Display Background") ); + property("background")->setAdvanced(true); + + createProperty( "background-color", Variant::Type::Color ); + property("background-color")->setValue(Qt::white); + property("background-color")->setCaption( i18n("Background Color") ); + property("background-color")->setAdvanced(true); + + createProperty( "frame-color", Variant::Type::Color ); + property("frame-color")->setValue(Qt::black); + property("frame-color")->setCaption( i18n("Frame Color") ); + property("frame-color")->setAdvanced(true); + + createProperty( "text-color", Variant::Type::Color ); + property("text-color")->setValue(Qt::black); + property("text-color")->setCaption( i18n("Text Color") ); +} + +DPText::~DPText() +{ +} + +void DPText::setSelected( bool yes ) +{ + if ( yes == isSelected() ) + return; + + DrawPart::setSelected(yes); + m_rectangularOverlay->showResizeHandles(yes); +} + + +void DPText::dataChanged() +{ + m_caption = dataString("text"); + b_displayBackground = dataBool("background"); + m_backgroundColor = dataColor("background-color"); + m_textColor = dataColor("text-color"); + m_frameColor = dataColor("frame-color"); + update(); +} + + +void DPText::postResize() +{ + setItemPoints( QPointArray(m_sizeRect), false ); +} + + +QSize DPText::minimumSize() const +{ + return QSize( 48, 24 ); +} + + +void DPText::drawShape( QPainter &p ) +{ + QRect bound = m_sizeRect; + bound.setWidth( bound.width()-2 ); + bound.setHeight( bound.height()-2 ); + bound.moveBy( int(x()+1), int(y()+1) ); + + if (b_displayBackground) + { + p.save(); + p.setPen( QPen( m_frameColor, 1, Qt::DotLine) ); + p.setBrush(m_backgroundColor); + p.drawRect(bound); + p.restore(); + } + + const int pad = 6; + + bound.setLeft( bound.left()+pad ); + bound.setTop( bound.top()+pad ); + bound.setRight( bound.right()-pad ); + bound.setBottom( bound.bottom()-pad ); + + p.setPen(m_textColor); + p.setFont( font() ); + p.drawText( bound, (Qt::WordBreak | Qt::AlignHCenter | Qt::AlignVCenter), m_caption ); +} + diff --git a/src/drawparts/dptext.h b/src/drawparts/dptext.h new file mode 100644 index 0000000..a6f5a4a --- /dev/null +++ b/src/drawparts/dptext.h @@ -0,0 +1,48 @@ +/*************************************************************************** + * Copyright (C) 2003-2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef CANVASTEXT_H +#define CANVASTEXT_H + +#include "drawpart.h" + +/** +@short Represents editable text on the canvas +@author David Saxton +*/ +class DPText : public DrawPart +{ +public: + DPText( ItemDocument *itemDocument, bool newItem, const char *id = 0L ); + ~DPText(); + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + static LibraryItem *libraryItemOld(); + + virtual void setSelected( bool yes ); + + virtual QSize minimumSize() const; + +protected: + virtual void postResize(); + +private: + virtual void drawShape( QPainter &p ); + void dataChanged(); + QString m_caption; + bool b_displayBackground; + QColor m_textColor; + QColor m_backgroundColor; + QColor m_frameColor; + RectangularOverlay *m_rectangularOverlay; +}; + +#endif diff --git a/src/drawparts/drawpart.cpp b/src/drawparts/drawpart.cpp new file mode 100644 index 0000000..6d9708d --- /dev/null +++ b/src/drawparts/drawpart.cpp @@ -0,0 +1,264 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "itemdocument.h" +#include "itemdocumentdata.h" +#include "drawpart.h" +#include "variant.h" + +#include <klocale.h> +#include <qbitarray.h> + +DrawPart::DrawPart( ItemDocument *itemDocument, bool newItem, const char *id ) + : Item( itemDocument, newItem, id ) +{ + itemDocument->registerItem(this); +} + + +DrawPart::~DrawPart() +{ +} + + +int DrawPart::rtti() const +{ + return ItemDocument::RTTI::DrawPart; +} + + +Variant * DrawPart::createProperty( const QString & id, Variant::Type::Value type ) +{ + if ( type == Variant::Type::PenStyle ) + { + QStringList penStyles; + penStyles << DrawPart::penStyleToName(Qt::SolidLine) << DrawPart::penStyleToName(Qt::DashLine) + << DrawPart::penStyleToName(Qt::DotLine) << DrawPart::penStyleToName(Qt::DashDotLine) + << DrawPart::penStyleToName(Qt::DashDotDotLine); + + Variant * v = createProperty( id, Variant::Type::String ); + v->setType( Variant::Type::PenStyle ); + v->setAllowed(penStyles); + return v; + } + + if ( type == Variant::Type::PenCapStyle ) + { + QStringList penCapStyles; + penCapStyles << DrawPart::penCapStyleToName(Qt::FlatCap) << DrawPart::penCapStyleToName(Qt::SquareCap) + << DrawPart::penCapStyleToName(Qt::RoundCap); + + Variant * v = createProperty( id, Variant::Type::String ); + v->setType( Variant::Type::PenCapStyle ); + v->setAllowed(penCapStyles); + return v; + } + + return Item::createProperty( id, type ); +} + + +Qt::PenStyle DrawPart::getDataPenStyle( const QString & id ) +{ + return nameToPenStyle( dataString(id) ); +} +Qt::PenCapStyle DrawPart::getDataPenCapStyle( const QString & id ) +{ + return nameToPenCapStyle( dataString(id) ); +} +void DrawPart::setDataPenStyle( const QString & id, Qt::PenStyle value ) +{ + property(id)->setValue( penStyleToName(value) ); +} +void DrawPart::setDataPenCapStyle( const QString & id, Qt::PenCapStyle value ) +{ + property(id)->setValue( penCapStyleToName(value) ); +} + + +ItemData DrawPart::itemData() const +{ + ItemData itemData = Item::itemData(); + + const VariantDataMap::const_iterator end = m_variantData.end(); + for ( VariantDataMap::const_iterator it = m_variantData.begin(); it != end; ++it ) + { + switch( it.data()->type() ) + { + case Variant::Type::PenStyle: + itemData.dataString[it.key()] = penStyleToID( nameToPenStyle( it.data()->value().toString() ) ); + break; + case Variant::Type::PenCapStyle: + itemData.dataString[it.key()] = penCapStyleToID( nameToPenCapStyle( it.data()->value().toString() ) ); + break; + case Variant::Type::String: + case Variant::Type::FileName: + case Variant::Type::Port: + case Variant::Type::Pin: + case Variant::Type::VarName: + case Variant::Type::Combo: + case Variant::Type::Select: + case Variant::Type::Multiline: + case Variant::Type::Int: + case Variant::Type::Double: + case Variant::Type::Color: + case Variant::Type::Bool: + case Variant::Type::Raw: + case Variant::Type::SevenSegment: + case Variant::Type::KeyPad: + case Variant::Type::None: + // All of these are handled by Item + break; + } + } + + return itemData; +} + + +void DrawPart::restoreFromItemData( const ItemData &itemData ) +{ + Item::restoreFromItemData(itemData); + + const QStringMap::const_iterator stringEnd = itemData.dataString.end(); + for ( QStringMap::const_iterator it = itemData.dataString.begin(); it != stringEnd; ++it ) + { + VariantDataMap::iterator vit = m_variantData.find(it.key()); + if ( vit == m_variantData.end() ) + continue; + + if ( vit.data()->type() == Variant::Type::PenStyle ) + setDataPenStyle( it.key(), idToPenStyle( it.data() ) ); + + else if ( vit.data()->type() == Variant::Type::PenCapStyle ) + setDataPenCapStyle( it.key(), idToPenCapStyle( it.data() ) ); + } +} + + + +QString DrawPart::penStyleToID( Qt::PenStyle style ) +{ + switch (style) + { + case Qt::SolidLine: + return "SolidLine"; + case Qt::NoPen: + return "NoPen"; + case Qt::DashLine: + return "DashLine"; + case Qt::DotLine: + return "DotLine"; + case Qt::DashDotLine: + return "DashDotLine"; + case Qt::DashDotDotLine: + return "DashDotDotLine"; + case Qt::MPenStyle: + default: + return ""; // ?! + } +} +Qt::PenStyle DrawPart::idToPenStyle( const QString & id ) +{ + if ( id == "NoPen" ) + return Qt::NoPen; + if ( id == "DashLine" ) + return Qt::DashLine; + if ( id == "DotLine" ) + return Qt::DotLine; + if ( id == "DashDotLine" ) + return Qt::DashDotLine; + if ( id == "DashDotDotLine" ) + return Qt::DashDotDotLine; + return Qt::SolidLine; +} +QString DrawPart::penCapStyleToID( Qt::PenCapStyle style ) +{ + switch (style) + { + case Qt::FlatCap: + return "FlatCap"; + case Qt::SquareCap: + return "SquareCap"; + case Qt::RoundCap: + return "RoundCap"; + case Qt::MPenCapStyle: + default: + return ""; // ?! + } +} +Qt::PenCapStyle DrawPart::idToPenCapStyle( const QString & id ) +{ + if ( id == "SquareCap" ) + return Qt::SquareCap; + if ( id == "RoundCap" ) + return Qt::RoundCap; + return Qt::FlatCap; +} + +QString DrawPart::penStyleToName( Qt::PenStyle style ) +{ + switch (style) + { + case Qt::SolidLine: + return i18n("Solid"); + case Qt::NoPen: + return i18n("None"); + case Qt::DashLine: + return i18n("Dash"); + case Qt::DotLine: + return i18n("Dot"); + case Qt::DashDotLine: + return i18n("Dash Dot"); + case Qt::DashDotDotLine: + return i18n("Dash Dot Dot"); + case Qt::MPenStyle: + default: + return ""; // ?! + } +} +Qt::PenStyle DrawPart::nameToPenStyle( const QString & name ) +{ + if ( name == i18n("None") ) + return Qt::NoPen; + if ( name == i18n("Dash") ) + return Qt::DashLine; + if ( name == i18n("Dot") ) + return Qt::DotLine; + if ( name == i18n("Dash Dot") ) + return Qt::DashDotLine; + if ( name == i18n("Dash Dot Dot") ) + return Qt::DashDotDotLine; + return Qt::SolidLine; +} +QString DrawPart::penCapStyleToName( Qt::PenCapStyle style ) +{ + switch (style) + { + case Qt::FlatCap: + return i18n("Flat"); + case Qt::SquareCap: + return i18n("Square"); + case Qt::RoundCap: + return i18n("Round"); + case Qt::MPenCapStyle: + default: + return ""; // ?! + } +} +Qt::PenCapStyle DrawPart::nameToPenCapStyle( const QString & name ) +{ + if ( name == i18n("Square") ) + return Qt::SquareCap; + if ( name == i18n("Round") ) + return Qt::RoundCap; + return Qt::FlatCap; +} + diff --git a/src/drawparts/drawpart.h b/src/drawparts/drawpart.h new file mode 100644 index 0000000..8dc9f50 --- /dev/null +++ b/src/drawparts/drawpart.h @@ -0,0 +1,68 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef DRAWPART_H +#define DRAWPART_H + +#include <item.h> + +class ICNDocument; +class ItemDocument; +class LibraryItem; +class RectangularOverlay; + +/** +@author David Saxton +*/ +class DrawPart : public Item +{ + public: + enum DrawAction + { + // Note: values are used for popup menu + da_text = 0, + da_line = 1, + da_arrow = 2, + da_rectangle = 3, + da_ellipse = 4 + }; + + DrawPart( ItemDocument *itemDocument, bool newItem, const char *id ); + virtual ~DrawPart(); + + int rtti() const; + + virtual bool canResize() const { return true; } + + virtual Variant * createProperty( const QString & id, Variant::Type::Value type ); + + Qt::PenStyle getDataPenStyle( const QString & id ); + Qt::PenCapStyle getDataPenCapStyle( const QString & id ); + + void setDataPenStyle( const QString & id, Qt::PenStyle value ); + void setDataPenCapStyle( const QString & id, Qt::PenCapStyle value ); + + virtual ItemData itemData() const; + virtual void restoreFromItemData( const ItemData &itemData ); + + // Convention for following functions: name is i18n'd name of style, id is the english one + + static QString penStyleToID( Qt::PenStyle style ); + static Qt::PenStyle idToPenStyle( const QString & id ); + static QString penCapStyleToID( Qt::PenCapStyle style ); + static Qt::PenCapStyle idToPenCapStyle( const QString & id ); + + static QString penStyleToName( Qt::PenStyle style ); + static Qt::PenStyle nameToPenStyle( const QString & name ); + static QString penCapStyleToName( Qt::PenCapStyle style ); + static Qt::PenCapStyle nameToPenCapStyle( const QString & name ); +}; + +#endif diff --git a/src/drawparts/solidshape.cpp b/src/drawparts/solidshape.cpp new file mode 100644 index 0000000..e60efef --- /dev/null +++ b/src/drawparts/solidshape.cpp @@ -0,0 +1,192 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "solidshape.h" +#include "libraryitem.h" +#include "resizeoverlay.h" + +#include <cmath> +#include <kiconloader.h> +#include <klocale.h> +#include <qpainter.h> + + +//BEGIN class DPRectangle +Item * DPRectangle::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new DPRectangle( itemDocument, newItem, id ); +} + +LibraryItem* DPRectangle::libraryItem() +{ + return new LibraryItem( + QString("dp/rectangle"), + i18n("Rectangle"), + i18n("Other"), + KGlobal::iconLoader()->loadIcon( "text", KIcon::Small ), + LibraryItem::lit_drawpart, + DPRectangle::construct ); +} + +DPRectangle::DPRectangle( ItemDocument *itemDocument, bool newItem, const char *id ) + : DrawPart( itemDocument, newItem, id ? id : "rectangle" ) +{ + m_pRectangularOverlay = new RectangularOverlay(this); + m_name = i18n("Rectangle"); + + createProperty( "background", Variant::Type::Bool ); + property("background")->setValue(false); + property("background")->setCaption( i18n("Display Background") ); + property("background")->setAdvanced(true); + + createProperty( "background-color", Variant::Type::Color ); + property("background-color")->setValue(Qt::white); + property("background-color")->setCaption( i18n("Background Color") ); + property("background-color")->setAdvanced(true); + + createProperty( "line-color", Variant::Type::Color ); + property("line-color")->setValue(Qt::black); + property("line-color")->setCaption( i18n("Line Color") ); + property("line-color")->setAdvanced(true); + + createProperty( "line-width", Variant::Type::Int ); + property("line-width")->setCaption( i18n("Line Width") ); + property("line-width")->setMinValue(1); + property("line-width")->setMaxValue(1000); + property("line-width")->setValue(1); + property("line-width")->setAdvanced(true); + + createProperty( "line-style", Variant::Type::PenStyle ); + property("line-style")->setAdvanced(true); + setDataPenStyle( "line-style", Qt::SolidLine ); +} + +DPRectangle::~DPRectangle() +{ +} + +void DPRectangle::setSelected( bool yes ) +{ + if ( yes == isSelected() ) + return; + + DrawPart::setSelected(yes); + m_pRectangularOverlay->showResizeHandles(yes); +} + + +void DPRectangle::dataChanged() +{ + bool displayBackground = dataBool("background"); + QColor line_color = dataColor("line-color"); + unsigned width = unsigned( dataInt("line-width") ); + Qt::PenStyle style = getDataPenStyle("line-style"); + + setPen( QPen( line_color, width, style ) ); + + if (displayBackground) + setBrush( dataColor("background-color") ); + else + setBrush( Qt::NoBrush ); + + postResize(); + update(); +} + + +QSize DPRectangle::minimumSize() const +{ + int side = QMAX(16, pen().width()+2); + return QSize( side, side ); +} + + +void DPRectangle::postResize() +{ + setItemPoints( m_sizeRect, false ); +} + + +QRect DPRectangle::drawRect() const +{ + int lw = pen().width(); + + if ( lw > m_sizeRect.width() ) + lw = m_sizeRect.width(); + + if ( lw > m_sizeRect.height() ) + lw = m_sizeRect.height(); + + return QRect( int(x() + m_sizeRect.x()+lw/2), int(y() + m_sizeRect.y()+lw/2), + m_sizeRect.width()-lw, m_sizeRect.height()-lw ); +} + + +void DPRectangle::drawShape( QPainter & p ) +{ + p.drawRect(drawRect()); +} +//END class DPRectangle + + +//BEGIN class DPEllipse +Item * DPEllipse::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new DPEllipse( itemDocument, newItem, id ); +} + +LibraryItem* DPEllipse::libraryItem() +{ + return new LibraryItem( + QString("dp/ellipse"), + i18n("Ellipse"), + i18n("Other"), + KGlobal::iconLoader()->loadIcon( "text", KIcon::Small ), + LibraryItem::lit_drawpart, + DPEllipse::construct ); +} + +DPEllipse::DPEllipse( ItemDocument *itemDocument, bool newItem, const char *id ) + : DPRectangle( itemDocument, newItem, id ? id : "ellipse" ) +{ + m_name = i18n("Ellipse"); +} + +DPEllipse::~DPEllipse() +{ +} + + +void DPEllipse::postResize() +{ + QRect br = m_sizeRect; + + // Make octagon that roughly covers ellipse + QPointArray pa(8); + pa[0] = QPoint( br.x() + br.width()/4, br.y() ); + pa[1] = QPoint( br.x() + 3*br.width()/4, br.y() ); + pa[2] = QPoint( br.x() + br.width(), br.y() + br.height()/4 ); + pa[3] = QPoint( br.x() + br.width(), br.y() + 3*br.height()/4 ); + pa[4] = QPoint( br.x() + 3*br.width()/4, br.y() + br.height() ); + pa[5] = QPoint( br.x() + br.width()/4, br.y() + br.height() ); + pa[6] = QPoint( br.x(), br.y() + 3*br.height()/4 ); + pa[7] = QPoint( br.x(), br.y() + br.height()/4 ); + + setItemPoints( pa, false ); +} + + +void DPEllipse::drawShape( QPainter & p ) +{ + p.drawEllipse(drawRect()); +} +//END class SolidShape + + diff --git a/src/drawparts/solidshape.h b/src/drawparts/solidshape.h new file mode 100644 index 0000000..317f417 --- /dev/null +++ b/src/drawparts/solidshape.h @@ -0,0 +1,68 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef SOLIDSHAPE_H +#define SOLIDSHAPE_H + +#include <drawpart.h> + +class RectangularOverlay; + +/** +@short Represents a drawable rectangle on the canvas +@author David Saxton +*/ +class DPRectangle : public DrawPart +{ + public: + DPRectangle( ItemDocument *itemDocument, bool newItem, const char *id = 0L ); + ~DPRectangle(); + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + + virtual void setSelected( bool yes ); + + virtual QSize minimumSize() const; + + protected: + virtual void drawShape( QPainter &p ); + void dataChanged(); + virtual void postResize(); + + /** Returns the rectangle to draw in, taking into account the line + * width */ + QRect drawRect() const; + + private: + RectangularOverlay *m_pRectangularOverlay; +}; + +/** +@short Represents a drawable rectangle on the canvas +@author David Saxton +*/ +class DPEllipse : public DPRectangle +{ + public: + DPEllipse( ItemDocument *itemDocument, bool newItem, const char *id = 0L ); + ~DPEllipse(); + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + + protected: + virtual void postResize(); + + private: + virtual void drawShape( QPainter &p ); +}; + +#endif diff --git a/src/electronics/Makefile.am b/src/electronics/Makefile.am new file mode 100644 index 0000000..c15e689 --- /dev/null +++ b/src/electronics/Makefile.am @@ -0,0 +1,18 @@ +INCLUDES = -I$(top_srcdir)/src -I$(top_srcdir)/src/electronics \ + -I$(top_srcdir)/src/electronics/components -I$(top_srcdir)/src/electronics/simulation \ + -I$(top_srcdir)/src/flowparts -I$(top_srcdir)/src/gui -I$(top_srcdir)/src/languages \ + -I$(top_srcdir)/src/mechanics -I$(top_srcdir)/src/micro $(glib_cflags) $(all_includes) + +METASOURCES = AUTO + +SUBDIRS = simulation components + +noinst_LTLIBRARIES = libelectronics.la + +libelectronics_la_SOURCES = component.cpp subcircuits.cpp gpsimprocessor.cpp \ + switch.cpp pin.cpp wire.cpp ecnode.cpp port.cpp + +libelectronics_la_LIBADD = \ + $(top_builddir)/src/electronics/simulation/libelements.la $(top_builddir)/src/electronics/components/libcomponents.la + +noinst_HEADERS = gpsimprocessor.h switch.h pin.h wire.h ecnode.h port.h diff --git a/src/electronics/component.cpp b/src/electronics/component.cpp new file mode 100644 index 0000000..f9f3c12 --- /dev/null +++ b/src/electronics/component.cpp @@ -0,0 +1,1017 @@ +/*************************************************************************** + * Copyright (C) 2004-2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "canvasitemparts.h" +#include "circuitdocument.h" +#include "component.h" +#include "src/core/ktlconfig.h" +#include "ecnode.h" +#include "itemdocumentdata.h" +#include "node.h" +#include "pin.h" +#include "simulator.h" + +#include "bjt.h" +#include "capacitance.h" +#include "cccs.h" +#include "ccvs.h" +#include "currentsignal.h" +#include "currentsource.h" +#include "diode.h" +#include "inductance.h" +#include "logic.h" +#include "opamp.h" +#include "resistance.h" +#include "switch.h" +#include "vccs.h" +#include "vcvs.h" +#include "voltagepoint.h" +#include "voltagesignal.h" +#include "voltagesource.h" + +#include <cmath> +#include <kdebug.h> +#include <qbitarray.h> +#include <qpainter.h> +#include <qwidget.h> +#include <qwmatrix.h> + +const int dipWidth = 112; +const int pairSep = 32; + +// Degrees per radian +static const double DPR = 57.29577951308232087665461840231273527024; + +Component::Component( ICNDocument *icnDocument, bool newItem, const QString &id ) + : CNItem( icnDocument, newItem, id ), + m_angleDegrees(0), + b_flipped(false) +{ + m_pCircuitDocument = dynamic_cast<CircuitDocument*>(icnDocument); + + for ( int i=0; i<4; ++i ) + { + m_pPNode[i] = 0l; + m_pNNode[i] = 0l; + } + + // Get configuration options + slotUpdateConfiguration(); + + // And finally register this :-) + icnDocument->registerItem(this); +} + +Component::~Component() +{ + removeElements(); + Simulator::self()->detachComponent(this); +} + +void Component::removeItem( ) +{ + if (b_deleted) + return; + Simulator::self()->detachComponent(this); + CNItem::removeItem(); +} + +void Component::removeElements( bool setPinsInterIndependent ) +{ + const ElementMapList::iterator end = m_elementMapList.end(); + for ( ElementMapList::iterator it = m_elementMapList.begin(); it != end; ++it ) + { + Element * e = (*it).e; + if (e) + { + emit elementDestroyed(e); + e->componentDeleted(); + } + } + m_elementMapList.clear(); + + const SwitchList::iterator swEnd = m_switchList.end(); + for ( SwitchList::iterator it = m_switchList.begin(); it != swEnd; ++it ) + { + Switch * sw = *it; + if ( !sw ) + continue; + + emit switchDestroyed( sw ); + delete sw; + } + m_switchList.clear(); + + if ( setPinsInterIndependent ) + setAllPinsInterIndependent(); +} + + +void Component::removeElement( Element * element, bool setPinsInterIndependent ) +{ + if (!element) + return; + + emit elementDestroyed(element); + element->componentDeleted(); + + const ElementMapList::iterator end = m_elementMapList.end(); + for ( ElementMapList::iterator it = m_elementMapList.begin(); it != end; ) + { + ElementMapList::iterator next = it; + ++next; + + if ( (*it).e == element ) + m_elementMapList.remove(it); + + it = next; + } + + if ( setPinsInterIndependent ) + rebuildPinInterDepedence(); +} + + +void Component::removeSwitch( Switch * sw ) +{ + if ( !sw ) + return; + + emit switchDestroyed( sw ); + delete sw; + m_switchList.remove(sw); + m_pCircuitDocument->requestAssignCircuits(); +} + + +void Component::setNodalCurrents() +{ + const ElementMapList::iterator end = m_elementMapList.end(); + for ( ElementMapList::iterator it = m_elementMapList.begin(); it != end; ++it ) + { + ElementMap m = (*it); + for ( int i=0; i<4; i++ ) + { + if ( m.n[i] ) { + m.n[i]->mergeCurrent( m.e->m_cnodeI[i] ); + } + } + } +} + + +void Component::initPainter( QPainter &p ) +{ + CNItem::initPainter(p); + + if ( !b_flipped && (m_angleDegrees%360 == 0) ) + return; + + p.save(); + + p.translate( int(x()), int(y()) ); + if (b_flipped) + p.scale( -1, 1 ); + + p.rotate(m_angleDegrees); + p.translate( -int(x()), -int(y()) ); +} + + +void Component::deinitPainter( QPainter &p ) +{ + if ( !b_flipped && (m_angleDegrees%360 == 0) ) + return; + + p.restore(); +} + + +void Component::setAngleDegrees( int degrees ) +{ + updateConnectorPoints(false); + m_angleDegrees = degrees; + itemPointsChanged(); + updateAttachedPositioning(); + p_icnDocument->requestRerouteInvalidatedConnectors(); +} + + +void Component::setFlipped( bool flipped ) +{ + updateConnectorPoints(false); + b_flipped = flipped; + itemPointsChanged(); + updateAttachedPositioning(); + p_icnDocument->requestRerouteInvalidatedConnectors(); +} + + +void Component::itemPointsChanged() +{ + QPointArray transformedPoints = transMatrix( m_angleDegrees, b_flipped, 0, 0, false ).map(m_itemPoints); +// transformedPoints.translate( int(x()), int(y()) ); + setPoints(transformedPoints); +} + + +void Component::restoreFromItemData( const ItemData &itemData ) +{ + CNItem::restoreFromItemData(itemData); + + setAngleDegrees( int(itemData.angleDegrees) ); + setFlipped(itemData.flipped); +} + + +ItemData Component::itemData() const +{ + ItemData itemData = CNItem::itemData(); + itemData.angleDegrees = m_angleDegrees; + itemData.flipped = b_flipped; + return itemData; +} + + +QWMatrix Component::transMatrix( int angleDegrees, bool flipped, int x, int y, bool inverse ) +{ + QWMatrix m; + m.translate( x, y ); + if (inverse) + { + m.rotate(-angleDegrees); + if (flipped) + m.scale( -1, 1 ); + } + else + { + if (flipped) + m.scale( -1, 1 ); + m.rotate(angleDegrees); + } + m.translate( -x, -y ); + m.setTransformationMode( QWMatrix::Areas ); + return m; +} + + +void Component::finishedCreation() +{ + CNItem::finishedCreation(); + updateAttachedPositioning(); +} + + +void Component::updateAttachedPositioning() +{ + if (b_deleted || !m_bDoneCreation) + return; + + //BEGIN Transform the nodes + const NodeMap::iterator end = m_nodeMap.end(); + for ( NodeMap::iterator it = m_nodeMap.begin(); it != end; ++it ) + { + if ( !it.data().node ) + kdError() << k_funcinfo << "Node in nodemap is null" << endl; + else + { + int nx = int((std::cos(m_angleDegrees/DPR) * it.data().x) - (std::sin(m_angleDegrees/DPR) * it.data().y)); + int ny = int((std::sin(m_angleDegrees/DPR) * it.data().x) + (std::cos(m_angleDegrees/DPR) * it.data().y)); + + if (b_flipped) + nx = -nx; + +#define round_8(x) (((x) > 0) ? int(((x)+4)/8)*8 : int(((x)-4)/8)*8) + nx = round_8(nx); + ny = round_8(ny); +#undef round_8 + + int newDir = (((m_angleDegrees + it.data().orientation)%360)+360)%360; + if (b_flipped) + newDir = (((180-newDir)%360)+360)%360; + + it.data().node->move( nx+x(), ny+y() ); + it.data().node->setOrientation( (Node::node_dir)newDir ); + } + } + //END Transform the nodes + + + //BEGIN Transform the GuiParts + QWMatrix m; + + if (b_flipped) + m.scale( -1, 1 ); + m.rotate(m_angleDegrees); + m.setTransformationMode( QWMatrix::Areas ); + + const TextMap::iterator textMapEnd = m_textMap.end(); + for ( TextMap::iterator it = m_textMap.begin(); it != textMapEnd; ++it ) + { + QRect newPos = m.mapRect( it.data()->recommendedRect() ); + it.data()->move( newPos.x() + x(), newPos.y() + y() ); + it.data()->setGuiPartSize( newPos.width(), newPos.height() ); + it.data()->setAngleDegrees(m_angleDegrees); + } + const WidgetMap::iterator widgetMapEnd = m_widgetMap.end(); + for ( WidgetMap::iterator it = m_widgetMap.begin(); it != widgetMapEnd; ++it ) + { + QRect newPos = m.mapRect( it.data()->recommendedRect() ); + it.data()->move( newPos.x() + x(), newPos.y() + y() ); + it.data()->setGuiPartSize( newPos.width(), newPos.height() ); + it.data()->setAngleDegrees(m_angleDegrees); + } + //END Transform the GuiParts +} + + +void Component::drawPortShape( QPainter & p ) +{ + int h = height(); + int w = width() - 1; + int _x = int( x() + offsetX() ); + int _y = int( y() + offsetY() ); + + double roundSize = 8; + double slantIndent = 8; + + const double pi = 3.1415926536; + const double DPR = 180./pi; + double inner = std::atan(h/slantIndent); // Angle for slight corner + double outer = pi-inner; // Angle for sharp corner + + int inner16 = int(16*inner*DPR); + int outer16 = int(16*outer*DPR); + + p.save(); + p.setPen( Qt::NoPen ); + p.drawPolygon( areaPoints() ); + p.restore(); + + initPainter( p ); + + // Left line + p.drawLine( int(_x), int(_y+roundSize/2), int(_x), int(_y+h-roundSize/2) ); + + // Right line + p.drawLine( int(_x+w), int(_y-slantIndent+h-roundSize/2), int(_x+w), int(_y+slantIndent+roundSize/2) ); + + // Bottom line + p.drawLine( int(_x+(1-std::cos(outer))*(roundSize/2)), int(_y+h+(std::sin(outer)-1)*(roundSize/2)), + int(_x+w+(std::cos(inner)-1)*(roundSize/2)), int(_y+h-slantIndent+(std::sin(inner)-1)*(roundSize/2)) ); + + // Top line + p.drawLine( int(_x+w+(std::cos(outer)-1)*(roundSize/2)), int(_y+slantIndent+(1-std::sin(inner))*(roundSize/2)), + int(_x+(1-std::cos(inner))*(roundSize/2)), int(_y+(1-std::sin(outer))*(roundSize/2)) ); + + + // Top left + p.drawArc( int(_x), int(_y), int(roundSize), int(roundSize), 90*16, outer16 ); + + // Bottom left + p.drawArc( int(_x), int(_y+h-roundSize), int(roundSize), int(roundSize), 180*16, outer16 ); + + // Top right + p.drawArc( int(_x+w-roundSize), int(_y+slantIndent), int(roundSize), int(roundSize), 0, inner16 ); + + // Bottom right + p.drawArc( int(_x+w-roundSize), int(_y-slantIndent+h-roundSize), int(roundSize), int(roundSize), 270*16, inner16 ); + + deinitPainter( p ); +} + + +void Component::initDIP( const QStringList & pins ) +{ + const int numPins = pins.size(); + const int numSide = numPins/2 + numPins%2; + + // Pins along left + for ( int i=0; i<numSide; i++ ) + { + if ( !pins[i].isEmpty() ) + { + const int nodeX = -8+offsetX(); + const int nodeY = (i+1)*16+offsetY(); + ECNode *node = ecNodeWithID(pins[i]); + if (node) + { + m_nodeMap[pins[i]].x = nodeX; + m_nodeMap[pins[i]].y = nodeY; + m_nodeMap[pins[i]].orientation = (Node::node_dir)0; + } + else + createPin( nodeX, nodeY, 0, pins[i] ); + } + } + // Pins along right + for ( int i=numSide; i<numPins; i++ ) + { + if ( !pins[i].isEmpty() ) + { + const int nodeX = width()+8+offsetX(); + const int nodeY = (2*numSide-i)*16+offsetY(); + ECNode *node = ecNodeWithID(pins[i]); + if (node) + { + m_nodeMap[pins[i]].x = nodeX; + m_nodeMap[pins[i]].y = nodeY; + m_nodeMap[pins[i]].orientation = (Node::node_dir)180; + } + else + createPin( nodeX, nodeY, 180, pins[i] ); + } + } + + updateAttachedPositioning(); +} + +void Component::initDIPSymbol( const QStringList & pins, int _width ) +{ + const int numPins = pins.size(); + const int numSide = numPins/2 + numPins%2; + + setSize( -(_width-(_width%16))/2, -(numSide+1)*8, _width, (numSide+1)*16, true ); + + QWidget tmpWidget; + QPainter p(&tmpWidget); + + p.setFont(m_font); + + // Pins along left + for ( int i=0; i<numSide; i++ ) + { + if ( !pins[i].isEmpty() ) + { + const QString text = *pins.at(i); + + const int _top = (i+1)*16-8 + offsetY(); + const int _width = width()/2 - 6; + const int _left = 6 + offsetX(); + const int _height = 16; + + QRect br = p.boundingRect( QRect( _left, _top, _width, _height ), Qt::AlignLeft, text ); + addDisplayText( text, br, text ); + } + } + // Pins along right + for ( int i=numSide; i<numPins; i++ ) + { + if ( !pins[i].isEmpty() ) + { + const QString text = *pins.at(i); + + const int _top = (2*numSide-i)*16 - 8 + offsetY(); + const int _width = width()/2 - 6; + const int _left = (width()/2) + offsetX(); + const int _height = 16; + + QRect br = p.boundingRect( QRect( _left, _top, _width, _height ), Qt::AlignRight, text ); + addDisplayText( text, br, text ); + } + } + + updateAttachedPositioning(); +} + + +// QString createNode( double _x, double _y, int shape, int orientation, const QString &name, int type, bool isInput = true ); + +void Component::init1PinLeft( int h1 ) +{ + if ( h1 == -1 ) h1 = offsetY()+height()/2; + + m_pNNode[0] = createPin( offsetX()-8, h1, 0, "n1" ); +} + +void Component::init2PinLeft( int h1, int h2 ) +{ + if ( h1 == -1 ) h1 = offsetY()+8; + if ( h2 == -1 ) h2 = offsetY()+height()-8; + + m_pNNode[0] = createPin( offsetX()-8, h1, 0, "n1" ); + m_pNNode[1] = createPin( offsetX()-8, h2, 0, "n2" ); +} + +void Component::init3PinLeft( int h1, int h2, int h3 ) +{ + if ( h1 == -1 ) h1 = offsetY()+8; + if ( h2 == -1 ) h2 = offsetY()+height()/2; + if ( h3 == -1 ) h3 = offsetY()+height()-8; + + m_pNNode[0] = createPin( offsetX()-8, h1, 0, "n1" ); + m_pNNode[1] = createPin( offsetX()-8, h2, 0, "n2" ); + m_pNNode[2] = createPin( offsetX()-8, h3, 0, "n3" ); +} + +void Component::init4PinLeft( int h1, int h2, int h3, int h4 ) +{ + if ( h1 == -1 ) h1 = offsetY()+8; + if ( h2 == -1 ) h2 = offsetY()+24; + if ( h3 == -1 ) h3 = offsetY()+height()-24; + if ( h4 == -1 ) h4 = offsetY()+height()-8; + + m_pNNode[0] = createPin( offsetX()-8, h1, 0, "n1" ); + m_pNNode[1] = createPin( offsetX()-8, h2, 0, "n2" ); + m_pNNode[2] = createPin( offsetX()-8, h3, 0, "n3" ); + m_pNNode[3] = createPin( offsetX()-8, h4, 0, "n4" ); +} + +void Component::init1PinRight( int h1 ) +{ + if ( h1 == -1 ) h1 = offsetY()+height()/2; + + m_pPNode[0] = createPin( offsetX()+width()+8, h1, 180, "p1" ); +} + + +void Component::init2PinRight( int h1, int h2 ) +{ + if ( h1 == -1 ) h1 = offsetY()+8; + if ( h2 == -1 ) h2 = offsetY()+height()-8; + + m_pPNode[0] = createPin( offsetX()+width()+8, h1, 180, "p1" ); + m_pPNode[1] = createPin( offsetX()+width()+8, h2, 180, "p2" ); +} + +void Component::init3PinRight( int h1, int h2, int h3 ) +{ + if ( h1 == -1 ) h1 = offsetY()+8; + if ( h2 == -1 ) h2 = offsetY()+height()/2; + if ( h3 == -1 ) h3 = offsetY()+height()-8; + + m_pPNode[0] = createPin( offsetX()+width()+8, h1, 180, "p1" ); + m_pPNode[1] = createPin( offsetX()+width()+8, h2, 180, "p2" ); + m_pPNode[2] = createPin( offsetX()+width()+8, h3, 180, "p3" ); +} + +void Component::init4PinRight( int h1, int h2, int h3, int h4 ) +{ + if ( h1 == -1 ) h1 = offsetY()+8; + if ( h2 == -1 ) h2 = offsetY()+24; + if ( h3 == -1 ) h3 = offsetY()+height()-24; + if ( h4 == -1 ) h4 = offsetY()+height()-8; + + m_pPNode[0] = createPin( offsetX()+width()+8, h1, 180, "p1" ); + m_pPNode[1] = createPin( offsetX()+width()+8, h2, 180, "p2" ); + m_pPNode[2] = createPin( offsetX()+width()+8, h3, 180, "p3" ); + m_pPNode[3] = createPin( offsetX()+width()+8, h4, 180, "p4" ); +} + + +ECNode* Component::ecNodeWithID( const QString &ecNodeId ) +{ + return dynamic_cast<ECNode*>( p_icnDocument->nodeWithID( nodeId(ecNodeId) ) ); +} + + +void Component::slotUpdateConfiguration() +{ + const LogicConfig logicConfig = LogicIn::getConfig(); + + const ElementMapList::iterator end = m_elementMapList.end(); + for ( ElementMapList::iterator it = m_elementMapList.begin(); it != end; ++it ) + { + if ( LogicIn * logicIn = dynamic_cast<LogicIn*>((*it).e) ) + logicIn->setLogic(logicConfig); + } +} + + + +BJT * Component::createBJT( ECNode *c, ECNode *b, ECNode *e, bool isNPN ) +{ return createBJT( c->pin(), b->pin(), e->pin(), isNPN ); } +Capacitance * Component::createCapacitance( ECNode *n0, ECNode *n1, double capacitance ) +{ return createCapacitance( n0->pin(), n1->pin(), capacitance ); } +CCCS * Component::createCCCS( ECNode *n0, ECNode *n1, ECNode *n2, ECNode *n3, double gain ) +{ return createCCCS( n0->pin(), n1->pin(), n2->pin(), n3->pin(), gain ); } +CCVS * Component::createCCVS( ECNode *n0, ECNode *n1, ECNode *n2, ECNode *n3, double gain ) +{ return createCCVS( n0->pin(), n1->pin(), n2->pin(), n3->pin(), gain ); } +CurrentSignal * Component::createCurrentSignal( ECNode *n0, ECNode *n1, double current ) +{ return createCurrentSignal( n0->pin(), n1->pin(), current ); } +CurrentSource * Component::createCurrentSource( ECNode *n0, ECNode *n1, double current ) +{ return createCurrentSource( n0->pin(), n1->pin(), current ); } +Diode * Component::createDiode( ECNode *n0, ECNode *n1 ) +{ return createDiode( n0->pin(), n1->pin() ); } +Inductance * Component::createInductance( ECNode *n0, ECNode *n1, double inductance ) +{ return createInductance( n0->pin(), n1->pin(), inductance ); } +LogicIn * Component::createLogicIn( ECNode *node ) +{ return createLogicIn( node->pin() ); } +LogicOut * Component::createLogicOut( ECNode *node, bool isHigh ) +{ return createLogicOut( node->pin(), isHigh ); } +OpAmp * Component::createOpAmp( ECNode * nonInverting, ECNode * out, ECNode * inverting ) +{ return createOpAmp( nonInverting->pin(), out->pin(), inverting->pin() ); } +Resistance * Component::createResistance( ECNode *n0, ECNode *n1, double resistance ) +{ return createResistance( n0->pin(), n1->pin(), resistance ); } +Switch * Component::createSwitch( ECNode *n0, ECNode *n1, bool open ) +{ return createSwitch( n0->pin(), n1->pin(), open ); } +VCCS * Component::createVCCS( ECNode *n0, ECNode *n1, ECNode *n2, ECNode *n3, double gain ) +{ return createVCCS( n0->pin(), n1->pin(), n2->pin(), n3->pin(), gain ); } +VCVS * Component::createVCVS( ECNode *n0, ECNode *n1, ECNode *n2, ECNode *n3, double gain ) +{ return createVCVS( n0->pin(), n1->pin(), n2->pin(), n3->pin(), gain ); } +VoltagePoint * Component::createVoltagePoint( ECNode *n0, double voltage ) +{ return createVoltagePoint( n0->pin(), voltage ); } +VoltageSignal * Component::createVoltageSignal( ECNode *n0, ECNode *n1, double voltage ) +{ return createVoltageSignal( n0->pin(), n1->pin(), voltage ); } +VoltageSource * Component::createVoltageSource( ECNode *n0, ECNode *n1, double voltage ) +{ return createVoltageSource( n0->pin(), n1->pin(), voltage ); } + +BJT* Component::createBJT( Pin *cN, Pin *bN, Pin *eN, bool isNPN ) +{ + BJT *e = new BJT(isNPN); + + QValueList<Pin*> pins; + pins << bN << cN << eN; + + ElementMapList::iterator it = handleElement( e, pins ); + setInterDependent( it, pins ); + return e; +} + +Capacitance* Component::createCapacitance( Pin *n0, Pin *n1, double capacitance ) +{ + Capacitance *e = new Capacitance( capacitance, 1./LINEAR_UPDATE_RATE ); + + QValueList<Pin*> pins; + pins << n0 << n1; + + ElementMapList::iterator it = handleElement( e, pins ); + setInterDependent( it, pins ); + return e; +} + +CCCS* Component::createCCCS( Pin *n0, Pin *n1, Pin *n2, Pin *n3, double gain ) +{ + CCCS *e = new CCCS(gain); + + QValueList<Pin*> pins; + pins << n0 << n1 << n2 << n3; + + ElementMapList::iterator it = handleElement( e, pins ); + setInterDependent( it, pins ); + return e; +} + +CCVS* Component::createCCVS( Pin *n0, Pin *n1, Pin *n2, Pin *n3, double gain ) +{ + CCVS *e = new CCVS(gain); + + QValueList<Pin*> pins; + pins << n0 << n1 << n2 << n3; + + ElementMapList::iterator it = handleElement( e, pins ); + setInterCircuitDependent( it, pins ); + + pins.clear(); + pins << n0 << n1; + setInterGroundDependent( it, pins ); + + pins.clear(); + pins << n2 << n3; + setInterGroundDependent( it, pins ); + + return e; +} + +CurrentSignal* Component::createCurrentSignal( Pin *n0, Pin *n1, double current ) +{ + CurrentSignal *e = new CurrentSignal( 1./LINEAR_UPDATE_RATE, current ); + + QValueList<Pin*> pins; + pins << n0 << n1; + + ElementMapList::iterator it = handleElement( e, pins ); + setInterDependent( it, pins ); + return e; +} + +CurrentSource* Component::createCurrentSource( Pin *n0, Pin *n1, double current ) +{ + CurrentSource *e = new CurrentSource(current); + + QValueList<Pin*> pins; + pins << n0 << n1; + + ElementMapList::iterator it = handleElement( e, pins ); + setInterDependent( it, pins ); + return e; +} + +Diode* Component::createDiode( Pin *n0, Pin *n1 ) +{ + Diode *e = new Diode(); + + QValueList<Pin*> pins; + pins << n0 << n1; + + ElementMapList::iterator it = handleElement( e, pins ); + setInterDependent( it, pins ); + return e; +} + +Inductance* Component::createInductance( Pin *n0, Pin *n1, double inductance ) +{ + Inductance *e = new Inductance( inductance, 1./LINEAR_UPDATE_RATE ); + + QValueList<Pin*> pins; + pins << n0 << n1; + + ElementMapList::iterator it = handleElement( e, pins ); + setInterDependent( it, pins ); + return e; +} + +LogicIn *Component::createLogicIn( Pin *node ) +{ + LogicIn *e = new LogicIn(LogicIn::getConfig()); + + QValueList<Pin*> pins; + pins << node; + + ElementMapList::iterator it = handleElement( e, pins ); + return e; +} + +LogicOut *Component::createLogicOut( Pin *node, bool isHigh ) +{ + LogicOut *e = new LogicOut( LogicIn::getConfig(), isHigh); + + QValueList<Pin*> pins; + pins << node; + + ElementMapList::iterator it = handleElement( e, pins ); + setInterDependent( it, pins ); + return e; +} + +OpAmp * Component::createOpAmp( Pin * nonInverting, Pin * inverting, Pin * out ) +{ + OpAmp * e = new OpAmp(); + + QValueList<Pin*> pins; + pins << nonInverting << inverting << out; + + ElementMapList::iterator it = handleElement( e, pins ); + setInterDependent( it, pins ); + return e; +} + +Resistance* Component::createResistance( Pin *n0, Pin *n1, double resistance ) +{ + Resistance *e = new Resistance(resistance); + + QValueList<Pin*> pins; + pins << n0 << n1; + + ElementMapList::iterator it = handleElement( e, pins ); + setInterDependent( it, pins ); + return e; +} + +Switch* Component::createSwitch( Pin *n0, Pin *n1, bool open ) +{ + // Note that a Switch is not really an element (although in many cases it + // behaves very much like one). + + Switch * e = new Switch( this, n0, n1, open ? Switch::Open : Switch::Closed ); + m_switchList.append(e); + n0->addSwitch( e ); + n1->addSwitch( e ); + emit switchCreated( e ); + return e; +} + +VCCS* Component::createVCCS( Pin *n0, Pin *n1, Pin *n2, Pin *n3, double gain ) +{ + VCCS *e = new VCCS(gain); + + QValueList<Pin*> pins; + pins << n0 << n1 << n2 << n3; + + ElementMapList::iterator it = handleElement( e, pins ); + setInterDependent( it, pins ); + return e; +} + +VCVS* Component::createVCVS( Pin *n0, Pin *n1, Pin *n2, Pin *n3, double gain ) +{ + VCVS *e = new VCVS(gain); + + QValueList<Pin*> pins; + pins << n0 << n1 << n2 << n3; + + ElementMapList::iterator it = handleElement( e, pins ); + setInterCircuitDependent( it, pins ); + + pins.clear(); + pins << n0 << n1; + setInterGroundDependent( it, pins ); + + pins.clear(); + pins << n2 << n3; + setInterGroundDependent( it, pins ); + return e; +} + +VoltagePoint* Component::createVoltagePoint( Pin *n0, double voltage ) +{ + VoltagePoint *e = new VoltagePoint(voltage); + + QValueList<Pin*> pins; + pins << n0; + + ElementMapList::iterator it = handleElement( e, pins ); + setInterDependent( it, pins ); + return e; +} + +VoltageSignal* Component::createVoltageSignal( Pin *n0, Pin *n1, double voltage ) +{ + VoltageSignal *e = new VoltageSignal( 1./LINEAR_UPDATE_RATE, voltage ); + + QValueList<Pin*> pins; + pins << n0 << n1; + + ElementMapList::iterator it = handleElement( e, pins ); + setInterDependent( it, pins ); + return e; +} + +VoltageSource* Component::createVoltageSource( Pin *n0, Pin *n1, double voltage ) +{ + VoltageSource *e = new VoltageSource(voltage); + + QValueList<Pin*> pins; + pins << n0 << n1; + + ElementMapList::iterator it = handleElement( e, pins ); + setInterDependent( it, pins ); + return e; +} + + +ElementMapList::iterator Component::handleElement( Element *e, const QValueList<Pin*> & pins ) +{ + if (!e) + return m_elementMapList.end(); + + ElementMap em; + em.e = e; + int at = 0; + QValueList<Pin*>::ConstIterator end = pins.end(); + for ( QValueList<Pin*>::ConstIterator it = pins.begin(); it != end; ++it ) + { + (*it)->addElement(e); + em.n[at++] = *it; + } + + ElementMapList::iterator it = m_elementMapList.append(em); + + emit elementCreated(e); + return it; +} + + +void Component::setInterDependent( ElementMapList::iterator it, const QValueList<Pin*> & pins ) +{ + setInterCircuitDependent( it, pins ); + setInterGroundDependent( it, pins ); +} + + +void Component::setInterCircuitDependent( ElementMapList::iterator it, const QValueList<Pin*> & pins ) +{ + QValueList<Pin*>::ConstIterator end = pins.end(); + for ( QValueList<Pin*>::ConstIterator it1 = pins.begin(); it1 != end; ++it1 ) + { + for ( QValueList<Pin*>::ConstIterator it2 = pins.begin(); it2 != end; ++it2 ) + { + (*it1)->addCircuitDependentPin( *it2 ); + } + } + + (*it).interCircuitDependent.append( pins ); +} + + +void Component::setInterGroundDependent( ElementMapList::iterator it, const QValueList<Pin*> & pins ) +{ + QValueList<Pin*>::ConstIterator end = pins.end(); + for ( QValueList<Pin*>::ConstIterator it1 = pins.begin(); it1 != end; ++it1 ) + { + for ( QValueList<Pin*>::ConstIterator it2 = pins.begin(); it2 != end; ++it2 ) + { + (*it1)->addGroundDependentPin( *it2 ); + } + } + + (*it).interGroundDependent.append( pins ); +} + + +void Component::rebuildPinInterDepedence() +{ + setAllPinsInterIndependent(); + + // Rebuild dependencies + ElementMapList::iterator emlEnd = m_elementMapList.end(); + for ( ElementMapList::iterator it = m_elementMapList.begin(); it != emlEnd; ++it ) + { + // Many copies of the pin lists as these will be affected when we call setInter*Dependent + PinListList list = (*it).interCircuitDependent; + + PinListList::iterator depEnd = list.end(); + for ( PinListList::iterator depIt = list.begin(); depIt != depEnd; ++depIt ) + setInterCircuitDependent( it, *depIt ); + + list = (*it).interGroundDependent; + + depEnd = list.end(); + for ( PinListList::iterator depIt = list.begin(); depIt != depEnd; ++depIt ) + setInterGroundDependent( it, *depIt ); + } +} + + +void Component::setAllPinsInterIndependent() +{ + NodeMap::iterator nmEnd = m_nodeMap.end(); + for ( NodeMap::iterator it = m_nodeMap.begin(); it != nmEnd; ++it ) + { + PinVector pins = (static_cast<ECNode*>(it.data().node))->pins(); + PinVector::iterator pinsEnd = pins.end(); + for ( PinVector::iterator pinsIt = pins.begin(); pinsIt != pinsEnd; ++pinsIt ) + { + if ( *pinsIt ) + (*pinsIt)->removeDependentPins(); + } + } +} + + +void Component::initElements( const uint stage ) +{ + /// @todo this function is ugly and messy and needs tidying up + + const ElementMapList::iterator end = m_elementMapList.end(); + + if ( stage == 1 ) + { + for ( ElementMapList::iterator it = m_elementMapList.begin(); it != end; ++it ) + { + (*it).e->add_initial_dc(); + } + return; + } + + for ( ElementMapList::iterator it = m_elementMapList.begin(); it != end; ++it ) + { + ElementMap m = (*it); + + if ( m.n[3] ) { + m.e->setCNodes( m.n[0]->eqId(), m.n[1]->eqId(), m.n[2]->eqId(), m.n[3]->eqId() ); + } + else if ( m.n[2] ) { + m.e->setCNodes( m.n[0]->eqId(), m.n[1]->eqId(), m.n[2]->eqId() ); + } + else if ( m.n[1] ) { + m.e->setCNodes( m.n[0]->eqId(), m.n[1]->eqId() ); + } + else if ( m.n[0] ) { + m.e->setCNodes( m.n[0]->eqId() ); + } + } + + for ( ElementMapList::iterator it = m_elementMapList.begin(); it != end; ++it ) + { + (*it).e->add_map(); + } +} + + +ECNode * Component::createPin( double x, double y, int orientation, const QString & name ) +{ + return dynamic_cast<ECNode*>( createNode( x, y, orientation, name, Node::ec_pin ) ); +} + + +//BEGIN class ElementMap +ElementMap::ElementMap() +{ + e = 0; + for ( int i = 0; i < 4; ++i ) + n[i] = 0; +} +//END class ElementMap + + +#include "component.moc" + + diff --git a/src/electronics/component.h b/src/electronics/component.h new file mode 100644 index 0000000..d45d835 --- /dev/null +++ b/src/electronics/component.h @@ -0,0 +1,362 @@ +/*************************************************************************** + * Copyright (C) 2003 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef COMPONENT_H +#define COMPONENT_H + +#include "cnitem.h" + +#include <qvaluelist.h> + +class ICNDocument; +class CircuitDocument; +class ECNode; +class ECSubcircuit; +class Element; +class Node; +class Pin; + +class BJT; +class Capacitance; +class CCCS; +class CCVS; +class CurrentSignal; +class CurrentSource; +class Diode; +class Inductance; +class LogicIn; +class LogicOut; +class OpAmp; +class Resistance; +class Switch; +class Transformer; +class VCCS; +class VCVS; +class VoltagePoint; +class VoltageSignal; +class VoltageSource; + +typedef QValueList<ECNode*> ECNodeList; +typedef QValueList<Element*> ElementList; +typedef QValueList<Switch*> SwitchList; + +typedef QValueList< QValueList<Pin*> > PinListList; + +/** +Contains vital information about the elements in the component. +*/ +class ElementMap +{ + public: + ElementMap(); + + Element * e; // The element + Pin * n[4]; // The Pins associated with the CNodes in the element + + /// @see Component::setInterCircuitDependent + PinListList interCircuitDependent; + + /// @see Component::setInterGroundDependent + PinListList interGroundDependent; +}; + +typedef QValueList<ElementMap> ElementMapList; + +/** +@short Bass class for all electrical components +@author David Saxton +*/ +class Component : public CNItem +{ + Q_OBJECT + public: + Component( ICNDocument *icnDocument, bool newItem, const QString &id ); + virtual ~Component(); + + ECNode* createPin( double _x, double _y, int orientation, const QString &name ); + /** + * Reinherit this function to disallow rotation of the component. + */ + virtual bool canRotate() const { return true; } + /** + * Angle of orientation + */ + int angleDegrees() const { return m_angleDegrees; } + /** + * Sets the angle (in degrees) + */ + void setAngleDegrees( int degrees ); + /** + * Whether or not the item is flipped + */ + bool flipped() const { return b_flipped; } + /** + * Sets whether or not the item is flipped + */ + void setFlipped( bool flipped ); + /** + * After calculating nodal voltages, each component will be + * called to tell its nodes what the current flowing *into* + * the component is. + */ + void setNodalCurrents(); + /** + * @return pointer to the CircuitDocument that we're in. + */ + CircuitDocument * circuitDocument() const { return m_pCircuitDocument; } + void initElements( const uint stage ); + virtual void finishedCreation(); + /** + * If reinherit (and use) the stepNonLogic function, then you must also + * reinherit this function so that it returns true. Else your component + * will not get called. + */ + virtual bool doesStepNonLogic() const { return false; } + virtual void stepNonLogic() {}; + /** + * Returns the translation matrix used for painting et al + * @param orientation The orientation to use + * @param x x co-ordinate of the center of the object to be mapped + * @param y y co-ordinate of the center of the object to be mapped + * @param inverse If false, maps the unrotated item to a rotated one, else mapped->unmapped + */ + static QWMatrix transMatrix( int angleDegrees, bool flipped, int x, int y, bool inverse = false ); + /** + * @return Information about the component in an ItemData struct. + */ + virtual ItemData itemData() const; + /** + * Restores the state of the component from the ItemData struct. + */ + virtual void restoreFromItemData( const ItemData &itemData ); + + BJT * createBJT( Pin *c, Pin *b, Pin *e, bool isNPN = true ); + BJT * createBJT( ECNode *c, ECNode *b, ECNode *e, bool isNPN = true ); + + Capacitance * createCapacitance( Pin *n0, Pin *n1, double capacitance ); + Capacitance * createCapacitance( ECNode *n0, ECNode *n1, double capacitance ); + + CCCS * createCCCS( Pin *n0, Pin *n1, Pin *n2, Pin *n3, double gain ); + CCCS * createCCCS( ECNode *n0, ECNode *n1, ECNode *n2, ECNode *n3, double gain ); + + CCVS * createCCVS( Pin *n0, Pin *n1, Pin *n2, Pin *n3, double gain ); + CCVS * createCCVS( ECNode *n0, ECNode *n1, ECNode *n2, ECNode *n3, double gain ); + + CurrentSignal * createCurrentSignal( Pin *n0, Pin *n1, double current ); + CurrentSignal * createCurrentSignal( ECNode *n0, ECNode *n1, double current ); + + CurrentSource * createCurrentSource( Pin *n0, Pin *n1, double current ); + CurrentSource * createCurrentSource( ECNode *n0, ECNode *n1, double current ); + + Diode * createDiode( Pin *n0, Pin *n1 ); + Diode * createDiode( ECNode *n0, ECNode *n1 ); + + Inductance * createInductance( Pin *n0, Pin *n1, double inductance ); + Inductance * createInductance( ECNode *n0, ECNode *n1, double inductance ); + + LogicIn * createLogicIn( Pin *node ); + LogicIn * createLogicIn( ECNode *node ); + + LogicOut * createLogicOut( Pin *node, bool isHigh ); + LogicOut * createLogicOut( ECNode *node, bool isHigh ); + + OpAmp * createOpAmp( Pin * nonInverting, Pin * out, Pin * inverting ); + OpAmp * createOpAmp( ECNode * nonInverting, ECNode * out, ECNode * inverting ); + + Resistance * createResistance( Pin *n0, Pin *n1, double resistance ); + Resistance * createResistance( ECNode *n0, ECNode *n1, double resistance ); + + Switch * createSwitch( Pin *n0, Pin *n1, bool open ); + Switch * createSwitch( ECNode *n0, ECNode *n1, bool open ); + + VCCS * createVCCS( Pin *n0, Pin *n1, Pin *n2, Pin *n3, double gain ); + VCCS * createVCCS( ECNode *n0, ECNode *n1, ECNode *n2, ECNode *n3, double gain ); + + VCVS * createVCVS( Pin *n0, Pin *n1, Pin *n2, Pin *n3, double gain ); + VCVS * createVCVS( ECNode *n0, ECNode *n1, ECNode *n2, ECNode *n3, double gain ); + + VoltagePoint * createVoltagePoint( Pin *n0, double voltage ); + VoltagePoint * createVoltagePoint( ECNode *n0, double voltage ); + + VoltageSignal * createVoltageSignal( Pin *n0, Pin *n1, double voltage ); + VoltageSignal * createVoltageSignal( ECNode *n0, ECNode *n1, double voltage ); + + VoltageSource * createVoltageSource( Pin *n0, Pin *n1, double voltage ); + VoltageSource * createVoltageSource( ECNode *n0, ECNode *n1, double voltage ); + + + ECNode* ecNodeWithID( const QString &ecNodeId ); + + /** + * Safely delete an element - in this case, calls element->componentDeleted, + * and removes it from the element list. + * @param setPinsInterIndependent whether to call + * setPinsInterIndependent. The call is time-consuming, and unnecessary + * if the pins from which the element was originally attached will be/ + * were removed, or they will become interdependent again. + */ + void removeElement( Element * element, bool setPinsInterIndependent ); + /** + * Safely remove a switch. + */ + void removeSwitch( Switch * sw ); + /** + * Removes all elements and switches. + * @param setPinsInterIndependent whether to bother calling + * setPinsInterIndependent. This is false when calling from the + * destructor, or when the dependency information is the same. + */ + void removeElements( bool setPinsInterIndependent = false ); + /** + * @return the list of switches that this component uses. + */ + SwitchList switchList() const { return m_switchList; } + + signals: + /** + * Emitted when an element is created. + */ + void elementCreated( Element * element ); + /** + * Emitted when an element is destroyed. + */ + void elementDestroyed( Element * element ); + /** + * Emitted when a switch. is created + */ + void switchCreated( Switch * sw ); + /** + * Emitted when a switch is destroyed. + */ + void switchDestroyed( Switch * sw ); + + public slots: + virtual void slotUpdateConfiguration(); + virtual void removeItem(); + + protected: + /** + * Convenience functionality provided for components in a port shape + * (such as ParallelPortComponent and SerialPortComponent). + */ + void drawPortShape( QPainter & p ); + virtual void itemPointsChanged(); + virtual void updateAttachedPositioning(); + virtual void initPainter( QPainter &p ); + /** + * Untranforms the painter from the matrix. This *must* be called after doing + * initPainter( QPainter &p ); + */ + virtual void deinitPainter( QPainter &p ); + /** + * This creates a set of nodes with their internal IDs set to those in QStringList pins. + * The pins are in a DIP arrangement, and are spaced width() apart. + */ + void initDIP( const QStringList & pins ); + /** + * Creates the DIP symbol: + * @li constructs rectangular shape + * @li puts on text labels in appropriate positions from QStringList pins + */ + void initDIPSymbol( const QStringList & pins, int width ); + /** + * Create 1 pin on the left of the component, placed half way down if h1 is + * -1 - else at the position of h1. + */ + void init1PinLeft( int h1 = -1 ); + /** + * Create 2 pins on the left of the component, either spread out, or at the + * given heights. + */ + void init2PinLeft( int h1 = -1, int h2 = -1 ); + /** + * Create 3 pins on the left of the component, either spread out, or at the + * given heights. + */ + void init3PinLeft( int h1 = -1, int h2 = -1, int h3 = -1 ); + /** + * Create 4 pins on the left of the component, either spread out, or at the + * given heights. + */ + void init4PinLeft( int h1 = -1, int h2 = -1, int h3 = -1, int h4 = -1 ); + /** + * Create 1 pin on the right of the component, placed half way down if h1 is + * -1 - else at the position of h1. + */ + void init1PinRight( int h1 = -1 ); + /** + * Create 2 pins on the right of the component, either spread out, or at the + * given heights. + */ + void init2PinRight( int h1 = -1, int h2 = -1 ); + /** + * Create 3 pins on the right of the component, either spread out, or at the + * given heights. + */ + void init3PinRight( int h1 = -1, int h2 = -1, int h3 = -1 ); + /** + * Create 4 pins on the right of the component, either spread out, or at the + * given heights. + */ + void init4PinRight( int h1 = -1, int h2 = -1, int h3 = -1, int h4 = -1 ); + /** + * When we remove an element, we have to rebuild the list of inter-dependent + * nodes. (when adding elements, we just call setInterDependent). + */ + void rebuildPinInterDepedence(); + + // Pointers to commonly used nodes + ECNode * m_pPNode[4]; + ECNode * m_pNNode[4]; + + QGuardedPtr<CircuitDocument> m_pCircuitDocument; + int m_angleDegrees; + bool b_flipped; + + private: + /** + * Convenience function for calling both setInterCircuitDependent and + * setInterGroundDependent. + * @param it Which pins are inter-dependent needs to be recorded in case + * this information is later needed in rebuildPinInterDependence. + */ + void setInterDependent( ElementMapList::iterator it, const QValueList<Pin*> & pins ); + /** + * Sets all pins independent of each other. + */ + void setAllPinsInterIndependent(); + /** + * The given pins will affect the simulation of each other. Therefore, they + * will need to be simulated in the same circuit. + */ + void setInterCircuitDependent( ElementMapList::iterator it, const QValueList<Pin*> & pins ); + /** + * If any of the given pins are ground, then that will affect whether + * any of the other pins can be ground. + */ + void setInterGroundDependent( ElementMapList::iterator it, const QValueList<Pin*> & pins ); + /** + * List of ElementMaps; which contain information on the pins associated + * with the element as well as the dependence between the pins for that + * element. + * @see ElementMap + */ + ElementMapList m_elementMapList; + /** + * The switches used by the component. + */ + SwitchList m_switchList; + /** + * @return an iterator to the element in m_elementMapList + */ + ElementMapList::iterator handleElement( Element *e, const QValueList<Pin*> & pins ); +}; + +#endif diff --git a/src/electronics/components/Makefile.am b/src/electronics/components/Makefile.am new file mode 100644 index 0000000..c388cf6 --- /dev/null +++ b/src/electronics/components/Makefile.am @@ -0,0 +1,33 @@ +INCLUDES = -I$(top_srcdir)/src \ + -I$(top_srcdir)/src/electronics -I$(top_srcdir)/src/electronics/components \ + -I$(top_srcdir)/src/electronics/simulation -I$(top_srcdir)/src/gui -I$(top_srcdir)/src/languages \ + -I$(top_srcdir)/src/micro $(glib_cflags) $(all_includes) + +METASOURCES = AUTO +noinst_HEADERS = ecresistor.h ecled.h ecdiode.h ecsevensegment.h eckeypad.h \ + eccapacitor.h ec555.h eccurrentsource.h ecfixedvoltage.h ecbcdto7segment.h \ + ecsignallamp.h ecclockinput.h ecpotentiometer.h ecopamp.h ecvoltagesource.h \ + eccurrentsignal.h ecvoltagesignal.h ecground.h multiinputgate.h fulladder.h multiplexer.h \ + demultiplexer.h externalconnection.h ecsubcircuit.h meter.h probe.h resistordip.h \ + addac.h dependentsource.h flipflop.h toggleswitch.h pushswitch.h ram.h \ + discretelogic.h piccomponent.h piccomponentpin.h binarycounter.h bidirled.h \ + matrixdisplay.h bussplitter.h matrixdisplaydriver.h magnitudecomparator.h \ + serialportcomponent.h parallelportcomponent.h inductor.h ecbjt.h rotoswitch.h + +noinst_LTLIBRARIES = libcomponents.la +libcomponents_la_SOURCES = ecresistor.cpp ecled.cpp ecdiode.cpp \ + ecsevensegment.cpp eckeypad.cpp eccapacitor.cpp ec555.cpp eccurrentsource.cpp \ + ecfixedvoltage.cpp ecbcdto7segment.cpp ecsignallamp.cpp ecclockinput.cpp \ + ecpotentiometer.cpp ecopamp.cpp ecvoltagesource.cpp eccurrentsignal.cpp ecvoltagesignal.cpp \ + ecground.cpp multiinputgate.cpp fulladder.cpp multiplexer.cpp demultiplexer.cpp \ + externalconnection.cpp ecsubcircuit.cpp meter.cpp probe.cpp resistordip.cpp addac.cpp \ + dependentsource.cpp flipflop.cpp toggleswitch.cpp pushswitch.cpp ram.cpp discretelogic.cpp \ + piccomponent.cpp piccomponentpin.cpp binarycounter.cpp bidirled.cpp matrixdisplay.cpp \ + bussplitter.cpp matrixdisplaydriver.cpp magnitudecomparator.cpp serialportcomponent.cpp \ + parallelportcomponent.cpp inductor.cpp ecbjt.cpp rotoswitch.cpp + +libcomponents_la_PCH = AUTO + + +libcomponents_la_LIBADD =\ + $(top_builddir)/src/electronics/simulation/libelements.la diff --git a/src/electronics/components/addac.cpp b/src/electronics/components/addac.cpp new file mode 100644 index 0000000..a58e2d8 --- /dev/null +++ b/src/electronics/components/addac.cpp @@ -0,0 +1,281 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "addac.h" +#include "ecnode.h" +#include "logic.h" +#include "libraryitem.h" +#include "pin.h" +#include "voltagepoint.h" + +#include <cmath> +#include <kiconloader.h> +#include <klocale.h> + + +Item* ADC::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new ADC( (ICNDocument*)itemDocument, newItem, id ); +} + + +Item* DAC::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new DAC( (ICNDocument*)itemDocument, newItem, id ); +} + + +LibraryItem* ADC::libraryItem() +{ + return new LibraryItem( + "ec/adc", + i18n("Analog-Digital"), + i18n("Integrated Circuits"), + "ic1.png", + LibraryItem::lit_component, + ADC::construct + ); +} + + +LibraryItem* DAC::libraryItem() +{ + return new LibraryItem( + "ec/dac", + i18n("Digital-Analog"), + i18n("Integrated Circuits"), + "ic1.png", + LibraryItem::lit_component, + DAC::construct + ); +} + + +//BEGIN class ADDAC +ADDAC::ADDAC( ICNDocument *icnDocument, bool newItem, const char *id ) + : Component( icnDocument, newItem, id ) +{ + m_numBits = 0; + m_range = 0; + + createProperty( "numBits", Variant::Type::Int ); + property("numBits")->setCaption( i18n("Number Bits") ); + property("numBits")->setMinValue(2); + property("numBits")->setMaxValue(max_ADDAC_bits); + property("numBits")->setValue(2); + + createProperty( "range", Variant::Type::Double ); + property("range")->setCaption( i18n("Input Range") ); + property("range")->setUnit("V"); + property("range")->setMinValue(-1e12); + property("range")->setMaxValue(1e12); + property("range")->setValue(5); +} + +ADDAC::~ADDAC() +{ +} + + +void ADDAC::dataChanged() +{ + m_range = dataDouble("range"); + initPins(); +} + +//END class ADDAC + + + + +//BEGIN class ADC +ADC::ADC( ICNDocument *icnDocument, bool newItem, const char *id ) + : ADDAC( icnDocument, newItem, (id) ? id : "adc" ) +{ + m_name = i18n("ADC"); + m_desc = i18n("Converts an analog signal into a digital output."); + + for ( int i=0; i<max_ADDAC_bits; ++i ) + m_logic[i] = 0l; + + m_realNode = 0l; +} + +ADC::~ADC() +{ +} + + +void ADC::stepNonLogic() +{ + double floatBitValue = m_realNode->pin()->voltage() * (std::pow( 2, double(m_numBits) )-1.) / m_range; + double roundBitValue = std::floor( floatBitValue+0.5 ); + + if ( roundBitValue < 0 ) + { + for ( int i = 0; i<m_numBits; ++i ) + m_logic[i]->setHigh(false); + return; + } + + uint roundedBitValue = uint(roundBitValue); + for ( int i = 0; i<m_numBits; ++i ) + m_logic[i]->setHigh( roundedBitValue & ( 1 << i ) ); +} + + +void ADC::initPins() +{ + int numBits = dataInt("numBits"); + + if ( numBits < 2 ) + numBits = 2; + else if ( numBits > max_ADDAC_bits ) + numBits = max_ADDAC_bits; + + if ( numBits == m_numBits ) + return; + + QStringList pins; + + int inPos = (numBits-1+(numBits%2))/2; + for ( int i=0; i<inPos; ++i ) + pins += ""; + + pins += "In"; + + for ( int i=inPos+1; i<numBits; ++i ) + pins += ""; + + for ( int i=numBits-1; i>=0; --i ) + pins += QString::number(i); + + initDIPSymbol( pins, 64 ); + initDIP(pins); + + if (!m_realNode) + m_realNode = ecNodeWithID("In"); + + + if ( numBits > m_numBits ) + { + for ( int i=m_numBits; i<numBits; ++i ) + { + ECNode *node = ecNodeWithID( QString::number(i) ); + m_logic[i] = createLogicOut( node, false ); + } + } + else + { + for ( int i=numBits; i<m_numBits; ++i ) + { + QString id = QString::number(i); + removeDisplayText(id); + removeElement( m_logic[i], false ); + removeNode(id); + m_logic[i] = 0l; + } + } + + m_numBits = numBits; +} +//END class ADC + + + + +//BEGIN class DAC +DAC::DAC( ICNDocument *icnDocument, bool newItem, const char *id ) + : ADDAC( icnDocument, newItem, (id) ? id : "dac" ) +{ + m_name = i18n("DAC"); + m_desc = i18n("Converts a digital input to an analog output signal."); + + for ( int i=0; i<max_ADDAC_bits; ++i ) + m_logic[i] = 0l; + + m_voltagePoint = 0l; +} + + +DAC::~DAC() +{ +} + + +void DAC::stepNonLogic() +{ + uint value = 0; + for ( int i=0; i<m_numBits; ++i ) + value |= ( m_logic[i]->isHigh() ? 1 : 0 ) << i; + +// double valueAsDouble = double(value); +// double powChange = std::pow( double(m_numBits), 2 )-1.; +// m_voltagePoint->setVoltage( m_range * valueAsDouble / powChange ); + m_voltagePoint->setVoltage( m_range * double(value) / (std::pow( 2, double(m_numBits) )-1.) ); +} + + +void DAC::initPins() +{ + int numBits = dataInt("numBits"); + + if ( numBits < 2 ) + numBits = 2; + else if ( numBits > max_ADDAC_bits ) + numBits = max_ADDAC_bits; + + if ( numBits == m_numBits ) + return; + + QStringList pins; + + for ( int i=0; i<numBits; ++i ) + pins += QString::number(i); + + int inPos = (numBits+1+(numBits%2))/2; + for ( int i=numBits-1; i>=inPos; --i ) + pins += ""; + + pins += "Out"; + + for ( int i=inPos-2; i>=0; --i ) + pins += ""; + + initDIPSymbol( pins, 64 ); + initDIP(pins); + + if (!m_voltagePoint) + m_voltagePoint = createVoltagePoint( ecNodeWithID("Out"), 0. ); + + if ( numBits > m_numBits ) + { + for ( int i=m_numBits; i<numBits; ++i ) + { + ECNode *node = ecNodeWithID( QString::number(i) ); + m_logic[i] = createLogicIn(node); + } + } + else + { + for ( int i=numBits; i<m_numBits; ++i ) + { + QString id = QString::number(i); + removeDisplayText(id); + removeElement( m_logic[i], false ); + removeNode(id); + m_logic[i] = 0l; + } + } + + m_numBits = numBits; +} +//END class DAC + diff --git a/src/electronics/components/addac.h b/src/electronics/components/addac.h new file mode 100644 index 0000000..3d69e88 --- /dev/null +++ b/src/electronics/components/addac.h @@ -0,0 +1,93 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef ADDAC_H +#define ADDAC_H + +#include "component.h" + +const int max_ADDAC_bits = 32; + +/** +@author David Saxton +*/ +class ADDAC : public Component +{ +public: + public: + ADDAC( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~ADDAC(); + virtual bool canFlip() const { return true; } + + protected: + void dataChanged(); + /** + * Add / remove pins according to the number of outputs the user has requested + */ + virtual void initPins() = 0; + + int m_numBits; + double m_range; +}; + + +/** +@author David Saxton + */ +class ADC : public ADDAC +{ + public: + ADC( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~ADC(); + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + + virtual void stepNonLogic(); + virtual bool doesStepNonLogic() const { return true; } + + protected: + /** + * Add / remove pins according to the number of outputs the user has requested + */ + virtual void initPins(); + + LogicOut *m_logic[max_ADDAC_bits]; + ECNode *m_realNode; +}; + + +/** +@author David Saxton + */ +class DAC : public ADDAC +{ + public: + DAC( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~DAC(); + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + + virtual void stepNonLogic(); + virtual bool doesStepNonLogic() const { return true; } + + protected: + /** + * Add / remove pins according to the number of outputs the user has requested + */ + virtual void initPins(); + + LogicIn *m_logic[max_ADDAC_bits]; + VoltagePoint *m_voltagePoint; +}; + + +#endif diff --git a/src/electronics/components/bidirled.cpp b/src/electronics/components/bidirled.cpp new file mode 100644 index 0000000..92e242d --- /dev/null +++ b/src/electronics/components/bidirled.cpp @@ -0,0 +1,157 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "bidirled.h" +#include "colorcombo.h" +#include "diode.h" +#include "ecled.h" +#include "ecnode.h" +#include "libraryitem.h" +#include "simulator.h" + +#include <klocale.h> +#include <qpainter.h> + +Item* BiDirLED::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new BiDirLED( (ICNDocument*)itemDocument, newItem, id ); +} + +LibraryItem* BiDirLED::libraryItem() +{ + return new LibraryItem ( + "ec/bidir_led", + i18n("Bidirectional LED"), + i18n("Outputs"), + "bidirled.png", + LibraryItem::lit_component, + BiDirLED::construct + ); +} + +BiDirLED::BiDirLED( ICNDocument *icnDocument, bool newItem, const char *id ) + : Component( icnDocument, newItem, id ? id : "bidir_led" ) +{ + m_name = i18n("Bidirectional LED"); + m_desc = i18n("Bidrectional Light Emitting Diode"); + m_bDynamicContent = true; + + setSize( -8, -16, 16, 32 ); + init1PinLeft(); + init1PinRight(); + setSize( -8, -24, 24, 40 ); + + m_pDiode[0] = createDiode( m_pNNode[0], m_pPNode[0] ); + m_pDiode[1] = createDiode( m_pPNode[0], m_pNNode[0] ); + + avg_brightness[0] = avg_brightness[1] = 255; + lastUpdatePeriod = 0.; + r[0]=r[1]=g[0]=g[1]=b[0]=b[1]=0; + last_brightness[0] = last_brightness[1] = 255; + + createProperty( "0-color1", Variant::Type::Color ); + property("0-color1")->setCaption( i18n("Color 1") ); + property("0-color1")->setColorScheme( ColorCombo::LED ); + + createProperty( "0-color2", Variant::Type::Color ); + property("0-color2")->setCaption( i18n("Colour 2") ); + property("0-color2")->setColorScheme( ColorCombo::LED ); +} + +BiDirLED::~BiDirLED() +{ +} + +void BiDirLED::dataChanged() +{ + QString colors[] = { "0-color1", "0-color2" }; + for ( unsigned i = 0; i < 2; i++ ) + { + QColor color = dataColor(colors[i]); + r[i] = color.red(); + g[i] = color.green(); + b[i] = color.blue(); + r[i] /= 0x100; + g[i] /= 0x100; + b[i] /= 0x100; + } +} + +void BiDirLED::stepNonLogic() +{ + double interval = 1./LINEAR_UPDATE_RATE; + lastUpdatePeriod += interval; + + for ( unsigned i = 0; i < 2; i++ ) + avg_brightness[i] += ECLed::brightness(m_pDiode[i]->current())*interval; +} + +void BiDirLED::drawShape( QPainter &p ) +{ + initPainter(p); + + for ( unsigned i = 0; i < 2; i++ ) + { + uint _b; + if ( lastUpdatePeriod == 0. ) + _b = last_brightness[i]; + + else + { + _b = uint(avg_brightness[i]/lastUpdatePeriod); + last_brightness[i] = _b; + } + avg_brightness[i] = 0.; + + p.setBrush( QColor( uint(255-(255-_b)*(1-r[i])), uint(255-(255-_b)*(1-g[i])), uint(255-(255-_b)*(1-b[i])) ) ); + + + QPointArray pa(3); + if ( i == 0 ) + { + pa[0] = QPoint( 8, -8 ); + pa[1] = QPoint( -8, -16 ); + pa[2] = QPoint( -8, 0 ); + } + else + { + pa[0] = QPoint( -8, 8 ); + pa[1] = QPoint( 8, 0 ); + pa[2] = QPoint( 8, 16 ); + } + + pa.translate( int(x()), int(y()) ); + p.drawPolygon(pa); + p.drawPolyline(pa); + } + lastUpdatePeriod = 0.; + + // Draw the arrows indicating it's a LED + int _x = (int)x()-2; + int _y = (int)y()-21; + + p.drawLine( _x+9, _y+3, _x+12, _y ); // Tail of left arrow + p.drawLine( _x+12, _y, _x+10, _y ); // Left edge of left arrow tip + p.drawLine( _x+12, _y, _x+12, _y+2 ); // Right edge of left arrow tip + + p.drawLine( _x+12, _y+6, _x+15, _y+3 ); // Tail of right arrow + p.drawLine( _x+15, _y+3, _x+13, _y+3 ); // Left edge of right arrow tip + p.drawLine( _x+15, _y+3, _x+15, _y+5 ); // Right edge of right arrow tip + + p.drawLine( _x+10, _y, _x+15, _y+5 ); // Diagonal line that forms base of both arrow tips + + _x = int(x()); + _y = int(y()); + p.drawLine( _x+8, _y-16, _x+8, _y ); + p.drawLine( _x-8, _y, _x-8, _y+16 ); + + deinitPainter(p); +} + diff --git a/src/electronics/components/bidirled.h b/src/electronics/components/bidirled.h new file mode 100644 index 0000000..e80f4e0 --- /dev/null +++ b/src/electronics/components/bidirled.h @@ -0,0 +1,45 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef BIDIRLED_H +#define BIDIRLED_H + +#include <component.h> + +/** +@author David Saxton +*/ +class BiDirLED : public Component +{ + public: + BiDirLED( ICNDocument * icnDocument, bool newItem, const char *id = 0L ); + ~BiDirLED(); + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + + virtual void dataChanged(); + virtual void stepNonLogic(); + virtual bool doesStepNonLogic() const { return true; } + + private: + virtual void drawShape( QPainter &p ); + + double r[2]; + double g[2]; + double b[2]; + + double avg_brightness[2]; + uint last_brightness[2]; + double lastUpdatePeriod; + Diode *m_pDiode[2]; +}; + +#endif diff --git a/src/electronics/components/binarycounter.cpp b/src/electronics/components/binarycounter.cpp new file mode 100644 index 0000000..f278104 --- /dev/null +++ b/src/electronics/components/binarycounter.cpp @@ -0,0 +1,186 @@ +/*************************************************************************** + * Copyright (C) 2003-2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "binarycounter.h" + +#include "logic.h" +#include "libraryitem.h" + +#include <kiconloader.h> +#include <klocale.h> + +Item* BinaryCounter::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new BinaryCounter( (ICNDocument*)itemDocument, newItem, id ); +} + +LibraryItem* BinaryCounter::libraryItem() +{ + QStringList ids; + ids << "ec/binary_counter" << "ec/4_bit_binary_counter"; + return new LibraryItem( + ids, + i18n("Binary Counter"), + i18n("Integrated Circuits"), + "ic1.png", + LibraryItem::lit_component, + BinaryCounter::construct + ); +} + +BinaryCounter::BinaryCounter( ICNDocument *icnDocument, bool newItem, const char *id ) + : Component( icnDocument, newItem, id ? id : "binary_counter" ) +{ + m_name = i18n("Binary Counter"); + m_desc = i18n("Holds an internal count, which changes when the clock input <i>></i> pin is pulsed.<br><br>" + "Normal operation: <i>en</i> (Enable) and <i>u/d</i> (Up/Down) are held high, <i>r</i> (Reset) is low."); + + enLogic = inLogic = rLogic = udLogic = 0L; + + b_reset = false; + b_triggerHigh = true; + b_oldIn = false; + m_value = 0; + b_en = false; + b_ud = false; + m_numBits = 0; + m_maxValue = false; + m_bDoneLogicIn = false; + + createProperty( "trig", Variant::Type::Select ); + property("trig")->setCaption( i18n("Trigger Edge") ); + property("trig")->setAllowed( QStringList::split( ',', "Rising,Falling" ) ); + property("trig")->setValue("Rising"); + + createProperty( "bitcount", Variant::Type::Int ); + property("bitcount")->setCaption( i18n("Bit Count") ); + property("bitcount")->setMinValue(1); + property("bitcount")->setMaxValue(26); + property("bitcount")->setValue(4); +} + + +BinaryCounter::~BinaryCounter() +{ +} + + +void BinaryCounter::dataChanged() +{ + initPins( dataInt("bitcount") ); + + b_triggerHigh = dataString("trig") == "Rising"; + setDisplayText( ">", b_triggerHigh ? "^>" : "_>" ); +} + + +void BinaryCounter::initPins( unsigned numBits ) +{ + if ( m_numBits == numBits ) + return; + + QStringList pins; + pins << "en" << ">" << "u/d" << "r"; + + for ( int i = 0; i < QABS(4-int(numBits)); i++ ) + pins << ""; + + for ( int i = numBits-1; i >= 0; i-- ) + pins << QChar('A'+i); + + initDIPSymbol( pins, 64 ); + initDIP(pins); + + if ( m_numBits < numBits ) + { + for ( unsigned i = m_numBits; i < numBits; i++ ) + m_pLogicOut[i] = createLogicOut( ecNodeWithID( QChar('A'+i) ), false ); + } + else + { + for ( unsigned i = numBits; i < m_numBits; i++ ) + { + QString id = QChar('A'+i); + removeElement( m_pLogicOut[i], false ); + removeDisplayText(id); + removeNode(id); + } + } + + m_numBits = numBits; + m_maxValue = (1<<m_numBits)-1; + + if (!m_bDoneLogicIn) + { + enLogic = createLogicIn( ecNodeWithID("en") ); + inLogic = createLogicIn( ecNodeWithID(">") ); + rLogic = createLogicIn( ecNodeWithID("r") ); + udLogic = createLogicIn( ecNodeWithID("u/d") ); + + enLogic->setCallback( this, (CallbackPtr)(&BinaryCounter::enStateChanged) ); + inLogic->setCallback( this, (CallbackPtr)(&BinaryCounter::inStateChanged) ); + rLogic->setCallback( this, (CallbackPtr)(&BinaryCounter::rStateChanged) ); + udLogic->setCallback( this, (CallbackPtr)(&BinaryCounter::udStateChanged) ); + + m_bDoneLogicIn = true; + } + + outputValue(); +} + + +void BinaryCounter::inStateChanged( bool state ) +{ + if ( (state != b_oldIn) && b_en && !b_reset && state == b_triggerHigh ) + { + m_value += (b_ud) ? 1 : -1; + + if ( m_value < 0 ) + m_value = m_maxValue; + + else if ( m_value > m_maxValue ) + m_value = 0; + + outputValue(); + } + + b_oldIn = state; +} + + +void BinaryCounter::rStateChanged( bool state ) +{ + b_reset = state; + if (b_reset) + { + m_value = 0; + outputValue(); + } +} + + +void BinaryCounter::enStateChanged( bool state ) +{ + b_en = state; +} + + +void BinaryCounter::udStateChanged( bool state ) +{ + b_ud = state; +} + + +void BinaryCounter::outputValue() +{ + for ( unsigned i = 0; i < m_numBits; i++ ) + m_pLogicOut[i]->setHigh( m_value & (1 << i) ); +} + diff --git a/src/electronics/components/binarycounter.h b/src/electronics/components/binarycounter.h new file mode 100644 index 0000000..b15762d --- /dev/null +++ b/src/electronics/components/binarycounter.h @@ -0,0 +1,63 @@ +/*************************************************************************** + * Copyright (C) 2003-2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef EC4BITCOUNTER_H +#define EC4BITCOUNTER_H + +#include "component.h" +#include "logic.h" + +/** +Simple logic counter. 4 Inputs, 4 Outputs. + +Outputs (A-D) represent the stored value (0-15). +The inputs are: +@li en - Enable incrementing of value +@li in - Input (trigger high) +@li r - Reset stored value to 0 +@li ud - Up/Down increment + +@short 4 Bit Binary Counter +@author David Saxton +*/ +class BinaryCounter : public CallbackClass, public Component +{ +public: + BinaryCounter( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~BinaryCounter(); + + virtual bool canFlip() const { return true; } + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + +protected: + void inStateChanged( bool state ); // Input + void rStateChanged( bool state ); // Reset + void enStateChanged( bool state ); // Enable + void udStateChanged( bool state ); // Up/Down + void outputValue(); + void dataChanged(); + void initPins( unsigned numBits ); + + LogicIn *enLogic, *inLogic, *rLogic, *udLogic; + LogicOut * m_pLogicOut[26]; + + unsigned m_numBits; + bool b_triggerHigh; + bool b_en; // Enable + bool b_ud; // Up/Down + bool b_oldIn; + bool b_reset; + long m_value; + long m_maxValue; + bool m_bDoneLogicIn; +}; + +#endif diff --git a/src/electronics/components/bussplitter.cpp b/src/electronics/components/bussplitter.cpp new file mode 100644 index 0000000..62fbb39 --- /dev/null +++ b/src/electronics/components/bussplitter.cpp @@ -0,0 +1,133 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "bussplitter.h" +#include "ecnode.h" +#include "libraryitem.h" +#include "wire.h" + +#include <klocale.h> +#include <qpainter.h> + +Item* BusSplitter::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new BusSplitter( (ICNDocument*)itemDocument, newItem, id ); +} + + +LibraryItem* BusSplitter::libraryItem() +{ + return new LibraryItem( + "ec/bus", + i18n("Bus"), + i18n("Connections"), + "bus.png", + LibraryItem::lit_component, + BusSplitter::construct ); +} + + +const unsigned MAX_BUS_SIZE = 10000; + + +BusSplitter::BusSplitter( ICNDocument *icnDocument, bool newItem, const char *id ) + : Component( icnDocument, newItem, id ? id : "Bus" ) +{ + m_name = i18n("Bus Splitter"); + m_desc = i18n("Merges several connections into one."); + + m_busSize = 0; + init1PinLeft(); + m_pInNode = m_pNNode[0]; + + createProperty( "size", Variant::Type::Int ); + property("size")->setCaption( i18n("Size") ); + property("size")->setMinValue(1); + property("size")->setMaxValue(MAX_BUS_SIZE); + property("size")->setValue(8); +} + + +BusSplitter::~BusSplitter() +{ +} + + +void BusSplitter::dataChanged() +{ + unsigned busSize = dataInt("size"); + + if ( busSize < 1 ) + busSize = 1; + + else if ( busSize > MAX_BUS_SIZE ) + busSize = MAX_BUS_SIZE; + + if ( busSize == m_busSize ) + return; + + m_pInNode->setNumPins(busSize); + + if ( busSize > m_busSize ) + { + m_pWires.resize(busSize); + for ( unsigned i = m_busSize; i < unsigned(busSize); i++ ) + { + Pin * pin = createPin( 16, 0, 180, outNodeID(i) )->pin(); + m_pWires[i] = new Wire( m_pInNode->pin(i), pin ); + } + } + else + { + for ( unsigned i = busSize; i < unsigned(m_busSize); i++ ) + { + removeNode( outNodeID(i) ); + delete m_pWires[i]; + } + m_pWires.resize(busSize); + } + m_busSize = busSize; + + // Position pins + setSize( 0, -int(m_busSize+1)*8, 8, int(m_busSize+1)*16, true ); + for ( int i = 0; i < int(m_busSize); i++ ) + m_nodeMap[ outNodeID(i) ].y = 16*i - int(m_busSize+1)*8 + 24; + m_nodeMap["n1"].y = -int(m_busSize+1)*8 + 8; + + updateAttachedPositioning(); +} + + +QString BusSplitter::outNodeID( unsigned node ) const +{ + return QString("out_%1").arg(QString::number(node)); +} + + +void BusSplitter::drawShape( QPainter &p ) +{ + initPainter(p); + +// QPen pen(p.pen()); +// pen.setWidth(); +// p.setPen(pen); + + int _x = int(x()); + int _y = int(y()); + + QRect r = m_sizeRect; + r.moveBy( _x, _y ); + p.drawRect(r); + + deinitPainter(p); +} + + + diff --git a/src/electronics/components/bussplitter.h b/src/electronics/components/bussplitter.h new file mode 100644 index 0000000..ccd1994 --- /dev/null +++ b/src/electronics/components/bussplitter.h @@ -0,0 +1,45 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef BUSSPLITTER_H +#define BUSSPLITTER_H + +#include <component.h> + +#include <qguardedptr.h> +#include <qvaluevector.h> + +class Wire; + +/** +@author David Saxton +*/ +class BusSplitter : public Component +{ + public: + BusSplitter( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~BusSplitter(); + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + + virtual bool canFlip() const { return true; } + + protected: + QString outNodeID( unsigned node ) const; + virtual void dataChanged(); + virtual void drawShape( QPainter &p ); + + unsigned m_busSize; + QValueVector<QGuardedPtr<Wire> > m_pWires; + ECNode * m_pInNode; +}; + +#endif diff --git a/src/electronics/components/demultiplexer.cpp b/src/electronics/components/demultiplexer.cpp new file mode 100644 index 0000000..8a01808 --- /dev/null +++ b/src/electronics/components/demultiplexer.cpp @@ -0,0 +1,175 @@ +/*************************************************************************** + * Copyright (C) 2004-2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "demultiplexer.h" + +#include "logic.h" +#include "libraryitem.h" + +#include <kiconloader.h> +#include <klocale.h> + +#include <cmath> + +Item* Demultiplexer::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new Demultiplexer( (ICNDocument*)itemDocument, newItem, id ); +} + +LibraryItem* Demultiplexer::libraryItem() +{ + return new LibraryItem( + "ec/demultiplexer", + i18n("Demultiplexer"), + i18n("Integrated Circuits"), + "ic1.png", + LibraryItem::lit_component, + Demultiplexer::construct + ); +} + +Demultiplexer::Demultiplexer( ICNDocument *icnDocument, bool newItem, const char *id ) + : Component( icnDocument, newItem, id ? id : "demultiplexer" ) +{ + m_name = i18n("Demultiplexer"); + m_desc = i18n("Seperates the input data stream into components. The value of the input is passed to the \"X\" output selected by the binary number given by the \"A\" inputs."); + + m_input = 0l; + + createProperty( "addressSize", Variant::Type::Int ); + property("addressSize")->setCaption( i18n("Address Size") ); + property("addressSize")->setMinValue(1); + property("addressSize")->setMaxValue(8); + property("addressSize")->setValue(1); + + // For backwards compatibility + createProperty( "numInput", Variant::Type::Int ); + property("numInput")->setMinValue(-1); + property("numInput")->setValue(-1); + property("numInput")->setHidden(true); +} + +Demultiplexer::~Demultiplexer() +{ +} + + +void Demultiplexer::dataChanged() +{ + if ( hasProperty("numInput") && dataInt("numInput") != -1 ) + { + int addressSize = int( std::ceil( std::log( (double)dataInt("numInput") ) / std::log(2.0) ) ); + property("numInput")->setValue(-1); + + if ( addressSize < 1 ) + addressSize = 1; + else if ( addressSize > 8 ) + addressSize = 8; + + // This function will get called again when we set the value of numInput + property("addressSize")->setValue(addressSize); + return; + } + + if ( hasProperty("numInput") ) + { + m_variantData["numInput"]->deleteLater(); + m_variantData.remove("numInput"); + } + + initPins( unsigned(dataInt("addressSize")) ); +} + + +void Demultiplexer::inStateChanged( bool /*state*/ ) +{ + unsigned long long pos = 0; + for ( unsigned i = 0; i < m_aLogic.size(); ++i ) + { + if ( m_aLogic[i]->isHigh() ) + pos += 1 << i; + } + for ( unsigned i = 0; i < m_xLogic.size(); ++i ) + m_xLogic[i]->setHigh( (pos == i) && m_input->isHigh() ); +} + + +void Demultiplexer::initPins( unsigned newAddressSize ) +{ + unsigned oldAddressSize = m_aLogic.size(); + unsigned long long oldXLogicCount = m_xLogic.size(); + unsigned long long newXLogicCount = 1 << newAddressSize; + + if ( newXLogicCount == oldXLogicCount ) + return; + + QStringList pins; + + for ( unsigned i=0; i<newAddressSize; ++i ) + pins += "A"+QString::number(i); + for ( unsigned i=newAddressSize; i<(newXLogicCount+(newXLogicCount%2))/2; ++i ) + pins += ""; + pins += "X"; + for ( unsigned i=(newXLogicCount+(newXLogicCount%2))/2+1; i<newXLogicCount; ++i ) + pins += ""; + for ( int i=newXLogicCount-1; i>=0; --i ) + pins += "X"+QString::number(i); + + initDIPSymbol( pins, 64 ); + initDIP(pins); + + ECNode *node; + + if (!m_input) + { + node = ecNodeWithID("X"); + m_input = createLogicIn(node); + m_input->setCallback( this, (CallbackPtr)(&Demultiplexer::inStateChanged) ); + } + + if ( newXLogicCount > oldXLogicCount ) + { + m_xLogic.resize(newXLogicCount); + for ( unsigned i = oldXLogicCount; i < newXLogicCount; ++i ) + { + node = ecNodeWithID("X"+QString::number(i)); + m_xLogic.insert( i, createLogicOut(node,false) ); + } + + m_aLogic.resize(newAddressSize); + for ( unsigned i = oldAddressSize; i < newAddressSize; ++i ) + { + node = ecNodeWithID("A"+QString::number(i)); + m_aLogic.insert( i, createLogicIn(node) ); + m_aLogic[i]->setCallback( this, (CallbackPtr)(&Demultiplexer::inStateChanged) ); + } + } + else + { + for ( unsigned i = newXLogicCount; i < oldXLogicCount; ++i ) + { + QString id = "X"+QString::number(i); + removeDisplayText(id); + removeElement( m_xLogic[i], false ); + removeNode(id); + } + m_xLogic.resize(newXLogicCount); + + for ( unsigned i = newAddressSize; i < oldAddressSize; ++i ) + { + QString id = "A"+QString::number(i); + removeDisplayText(id); + removeElement( m_aLogic[i], false ); + removeNode(id); + } + m_aLogic.resize(newAddressSize); + } +} + diff --git a/src/electronics/components/demultiplexer.h b/src/electronics/components/demultiplexer.h new file mode 100644 index 0000000..a2b52dd --- /dev/null +++ b/src/electronics/components/demultiplexer.h @@ -0,0 +1,46 @@ +/*************************************************************************** + * Copyright (C) 2004-2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef DEMULTIPLEXER_H +#define DEMULTIPLEXER_H + +#include "component.h" +#include "logic.h" + +#include <qptrvector.h> + +/** +@author David Saxton +*/ +class Demultiplexer : public CallbackClass, public Component +{ +public: + Demultiplexer( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~Demultiplexer(); + + virtual bool canFlip() const { return true; } + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + +protected: + void dataChanged(); + /** + * Add / remove pins according to the number of inputs the user has requested + */ + void initPins( unsigned addressSize ); + + void inStateChanged( bool newState ); + + QPtrVector<LogicIn> m_aLogic; + QPtrVector<LogicOut> m_xLogic; + LogicIn * m_input; +}; + +#endif diff --git a/src/electronics/components/dependentsource.cpp b/src/electronics/components/dependentsource.cpp new file mode 100644 index 0000000..764cc53 --- /dev/null +++ b/src/electronics/components/dependentsource.cpp @@ -0,0 +1,314 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "dependentsource.h" +#include "ecnode.h" +#include "libraryitem.h" +#include "pin.h" + +#include "cccs.h" +#include "ccvs.h" +#include "vccs.h" +#include "vcvs.h" + +#include <klocale.h> +#include <qpainter.h> + +//BEGIN class DependentSource +DependentSource::DependentSource( ICNDocument *icnDocument, bool newItem, const char *id ) + : Component( icnDocument, newItem, id ) +{ + setSize( -16, -16, 32, 32 ); + + init2PinLeft(); + init2PinRight(); + + createProperty( "gain", Variant::Type::Double ); + property("gain")->setCaption( i18n("Gain") ); + property("gain")->setValue(1.0); + + addDisplayText( "gain", QRect( -16, -32, 32, 16 ), "" ); +} + + +DependentSource::~DependentSource() +{ +} + + +void DependentSource::drawOutline( QPainter & p ) +{ + const int _x = (int)x()-16; + const int _y = (int)y()-32; + + // Top rectangle + p.drawRect( _x, _y+19, width(), 11 ); + + p.save(); + bool canSetCol = (p.pen().color() != Qt::color0) && (p.pen().color() != Qt::color1); + + // Bottom lines + if (canSetCol) + p.setPen( m_pNNode[1]->isSelected() ? m_selectedCol : Qt::black ); + p.drawLine( _x, _y+40, _x+8, _y+40 ); // Left inny + + if (canSetCol) + p.setPen( m_pPNode[1]->isSelected() ? m_selectedCol : Qt::black ); + p.drawLine( _x+width(), _y+40, _x+24, _y+40 ); // Right inny + + p.restore(); + + // Bottom diamond + QPointArray pa4(4); + pa4[0] = QPoint( _x+6, _y+40 ); + pa4[1] = QPoint( _x+16, _y+32 ); + pa4[2] = QPoint( _x+26, _y+40 ); + pa4[3] = QPoint( _x+16, _y+48 ); + p.drawPolygon(pa4); +} + + +void DependentSource::drawTopArrow( QPainter & p ) +{ + const int _x = (int)x()-16; + const int _y = (int)y()-32; + + if ( p.pen().color() == m_selectedCol ) + p.setPen(Qt::black); + + if ( p.brush().color() == m_brushCol ) + p.setBrush(Qt::black); + + p.drawLine( _x+8, _y+24, _x+24, _y+24 ); + + QPointArray pa3(3); + pa3[0] = QPoint( _x+24, _y+24 ); + pa3[1] = QPoint( _x+19, _y+21 ); + pa3[2] = QPoint( _x+19, _y+27 ); + p.drawPolygon(pa3); +} + + +void DependentSource::drawBottomArrow( QPainter & p ) +{ + const int _x = (int)x()-16; + const int _y = (int)y()-32; + + if ( p.pen().color() == m_selectedCol ) + p.setPen(Qt::black); + + if ( p.brush().color() == m_brushCol ) + p.setBrush(Qt::black); + + p.drawLine( _x+11, _y+40, _x+21, _y+40 ); + + QPointArray pa3(3); + pa3[0] = QPoint( _x+21, _y+40 ); + pa3[1] = QPoint( _x+16, _y+37 ); + pa3[2] = QPoint( _x+16, _y+43 ); + p.drawPolygon(pa3); +} +//END class DependentSource + + +//BEGIN class ECCCCS +Item* ECCCCS::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new ECCCCS( (ICNDocument*)itemDocument, newItem, id ); +} + +LibraryItem* ECCCCS::libraryItem() +{ + return new LibraryItem( + QString("ec/cccs"), + i18n("CCCS"), + i18n("Sources"), + "cccs.png", + LibraryItem::lit_component, + ECCCCS::construct ); +} + +ECCCCS::ECCCCS( ICNDocument *icnDocument, bool newItem, const char *id ) + : DependentSource( icnDocument, newItem, id ? id : "cccs" ) +{ + m_name = i18n("Current Controlled Currrent Source"); + m_cccs = createCCCS( m_pNNode[0], m_pPNode[0], m_pNNode[1], m_pPNode[1], 1. ); + m_pNNode[1]->pin()->setGroundType( Pin::gt_medium ); +} + +ECCCCS::~ECCCCS() +{ +} + +void ECCCCS::dataChanged() +{ + double gain = dataDouble("gain"); + + QString display = QString::number( gain / getMultiplier(gain), 'g', 3 ) + getNumberMag(gain) + QChar(' '); + setDisplayText( "gain", display ); + + m_cccs->setGain(gain); +} + +void ECCCCS::drawShape( QPainter &p ) +{ + initPainter(p); + drawOutline(p); + drawTopArrow(p); + drawBottomArrow(p); + deinitPainter(p); +} +//END class ECCCCS + + +//BEGIN class ECCCVS +Item* ECCCVS::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new ECCCVS( (ICNDocument*)itemDocument, newItem, id ); +} + +LibraryItem* ECCCVS::libraryItem() +{ + return new LibraryItem( + QString("ec/ccvs"), + i18n("CCVS"), + i18n("Sources"), + "ccvs.png", + LibraryItem::lit_component, + ECCCVS::construct ); +} + +ECCCVS::ECCCVS( ICNDocument *icnDocument, bool newItem, const char *id ) + : DependentSource( icnDocument, newItem, id ? id : "ccvs" ) +{ + m_name = i18n("Current Controlled Voltage Source"); + m_ccvs = createCCVS( m_pNNode[0], m_pPNode[0], m_pNNode[1], m_pPNode[1], 1. ); + m_pNNode[1]->pin()->setGroundType( Pin::gt_medium ); +} + +ECCCVS::~ECCCVS() +{ +} + +void ECCCVS::dataChanged() +{ + double gain = dataDouble("gain"); + + QString display = QString::number( gain / getMultiplier(gain), 'g', 3 ) + getNumberMag(gain) + QChar(' '); + setDisplayText( "gain", display ); + + m_ccvs->setGain(gain); +} + +void ECCCVS::drawShape( QPainter &p ) +{ + initPainter(p); + drawOutline(p); + drawTopArrow(p); + deinitPainter(p); +} +//END class ECCCVS + + +//BEGIN class ECVCCS +Item* ECVCCS::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new ECVCCS( (ICNDocument*)itemDocument, newItem, id ); +} + +LibraryItem* ECVCCS::libraryItem() +{ + return new LibraryItem( + QString("ec/vccs"), + i18n("VCCS"), + i18n("Sources"), + "vccs.png", + LibraryItem::lit_component, + ECVCCS::construct ); +} + +ECVCCS::ECVCCS( ICNDocument *icnDocument, bool newItem, const char *id ) + : DependentSource( icnDocument, newItem, id ? id : "vccs" ) +{ + m_name = i18n("Voltage Controlled Current Source"); + m_vccs = createVCCS( m_pNNode[0], m_pPNode[0], m_pNNode[1], m_pPNode[1], 1. ); + m_pNNode[1]->pin()->setGroundType( Pin::gt_medium ); +} + +ECVCCS::~ECVCCS() +{ +} + +void ECVCCS::dataChanged() +{ + double gain = dataDouble("gain"); + + QString display = QString::number( gain / getMultiplier(gain), 'g', 3 ) + getNumberMag(gain) + QChar(' '); + setDisplayText( "gain", display ); + + m_vccs->setGain(gain); +} + +void ECVCCS::drawShape( QPainter &p ) +{ + initPainter(p); + drawOutline(p); + drawBottomArrow(p); + deinitPainter(p); +} +//END class ECVCCS + + +//BEGIN class ECVCVS +Item* ECVCVS::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new ECVCVS( (ICNDocument*)itemDocument, newItem, id ); +} + +LibraryItem* ECVCVS::libraryItem() +{ + return new LibraryItem( + QString("ec/vcvs"), + i18n("VCVS"), + i18n("Sources"), + "vcvs.png", + LibraryItem::lit_component, + ECVCVS::construct ); +} + +ECVCVS::ECVCVS( ICNDocument *icnDocument, bool newItem, const char *id ) + : DependentSource( icnDocument, newItem, id ? id : "vcvs" ) +{ + m_name = i18n("Voltage Controlled Voltage Source"); + m_vcvs = createVCVS( m_pNNode[0], m_pPNode[0], m_pNNode[1], m_pPNode[1], 1. ); + m_pNNode[1]->pin()->setGroundType( Pin::gt_medium ); +} + +ECVCVS::~ECVCVS() +{ +} + +void ECVCVS::dataChanged() +{ + double gain = dataDouble("gain"); + + QString display = QString::number( gain / getMultiplier(gain), 'g', 3 ) + getNumberMag(gain) + QChar(' '); + setDisplayText( "gain", display ); + + m_vcvs->setGain(gain); +} + +void ECVCVS::drawShape( QPainter &p ) +{ + initPainter(p); + drawOutline(p); + deinitPainter(p); +} +//END class ECVCVS diff --git a/src/electronics/components/dependentsource.h b/src/electronics/components/dependentsource.h new file mode 100644 index 0000000..2bccbb7 --- /dev/null +++ b/src/electronics/components/dependentsource.h @@ -0,0 +1,112 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef DEPENDENTSOURCE_H +#define DEPENDENTSOURCE_H + +#include "component.h" + +/** +@author David Saxton +*/ +class DependentSource : public Component +{ + public: + DependentSource( ICNDocument *icnDocument, bool newItem, const char *id ); + ~DependentSource(); + virtual bool canFlip() const { return true; } + + protected: + void drawOutline( QPainter & p ); + void drawTopArrow( QPainter & p ); + void drawBottomArrow( QPainter & p ); +}; + +/** +@short Current Controlled Current Source +@author David Saxton +*/ +class ECCCCS : public DependentSource +{ + public: + ECCCCS( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~ECCCCS(); + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + + protected: + virtual void dataChanged(); + virtual void drawShape( QPainter &p ); + + CCCS *m_cccs; +}; + +/** +@short Current Controlled Voltage Source +@author David Saxton +*/ +class ECCCVS : public DependentSource +{ + public: + ECCCVS( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~ECCCVS(); + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + + protected: + virtual void dataChanged(); + virtual void drawShape( QPainter &p ); + + CCVS *m_ccvs; +}; + +/** +@short Voltage Controlled Current Source +@author David Saxton +*/ +class ECVCCS : public DependentSource +{ + public: + ECVCCS( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~ECVCCS(); + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + + protected: + virtual void dataChanged(); + virtual void drawShape( QPainter &p ); + + VCCS *m_vccs; +}; + +/** +@short Voltage Controlled Voltage Source +@author David Saxton +*/ +class ECVCVS : public DependentSource +{ + public: + ECVCVS( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~ECVCVS(); + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + + protected: + virtual void dataChanged(); + virtual void drawShape( QPainter &p ); + + VCVS *m_vcvs; +}; + +#endif diff --git a/src/electronics/components/discretelogic.cpp b/src/electronics/components/discretelogic.cpp new file mode 100644 index 0000000..e2e284e --- /dev/null +++ b/src/electronics/components/discretelogic.cpp @@ -0,0 +1,308 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "canvasitemparts.h" +#include "discretelogic.h" +#include "ecnode.h" +#include "logic.h" +#include "libraryitem.h" +#include "simulator.h" + +#include <klocale.h> +#include <qpainter.h> + + +//BEGIN class Inverter +Item* Inverter::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new Inverter( (ICNDocument*)itemDocument, newItem, id ); +} + +LibraryItem* Inverter::libraryItem() +{ + QStringList ids; + ids << "ec/inverter" << "ec/not"; + return new LibraryItem( + ids, + i18n("Inverter"), + i18n("Logic"), + "not.png", + LibraryItem::lit_component, + Inverter::construct ); +} + +Inverter::Inverter( ICNDocument *icnDocument, bool newItem, const char *id ) + : Component( icnDocument, newItem, id ? id : "not" ) +{ + m_name = i18n("Inverter"); + m_desc = i18n("The output is the logical inverse of the logic-input state."); + setSize( -8, -8, 16, 16 ); + + init1PinLeft(); + init1PinRight(); + + m_pIn = createLogicIn(m_pNNode[0]); + m_pOut = createLogicOut( m_pPNode[0], true ); + + m_pIn->setCallback( this, (CallbackPtr)(&Inverter::inStateChanged) ); + inStateChanged(false); +} + + +Inverter::~Inverter() +{ +} + + +void Inverter::inStateChanged( bool newState ) +{ + (static_cast<LogicOut*>(m_pOut))->setHigh( !newState ); +} + + +void Inverter::drawShape( QPainter &p ) +{ + initPainter(p); + int _x = (int)x()-8; + int _y = (int)y()-8; + QPointArray pa(3); + pa[0] = QPoint( _x, _y ); + pa[1] = QPoint( _x+width()-6, _y+(height()/2) ); + pa[2] = QPoint( _x, _y+height() ); + p.drawPolygon(pa); + p.drawPolyline(pa); + p.drawEllipse( _x+width()-6, _y+(height()/2)-3, 7, 7 ); + deinitPainter(p); +} +//END class Inverter + + +//BEGIN class Buffer +Item* Buffer::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new Buffer( (ICNDocument*)itemDocument, newItem, id ); +} + +LibraryItem* Buffer::libraryItem() +{ + return new LibraryItem( + QString::QString("ec/buffer"), + i18n("Buffer"), + i18n("Logic"), + "buffer.png", + LibraryItem::lit_component, + Buffer::construct ); +} + +Buffer::Buffer( ICNDocument *icnDocument, bool newItem, const char *id ) + : Component( icnDocument, newItem, id ? id : "buffer" ) +{ + m_name = i18n("Buffer"); + m_desc = i18n("Cleans the logic input, with the output high or low depending on input trigger levels."); + setSize( -8, -8, 16, 16 ); + + init1PinLeft(); + init1PinRight(); + + m_pIn = createLogicIn(m_pNNode[0]); + m_pOut = createLogicOut( m_pPNode[0], true ); + + m_pIn->setCallback( this, (CallbackPtr)(&Buffer::inStateChanged) ); + inStateChanged(false); +} + + +Buffer::~Buffer() +{ +} + + +void Buffer::inStateChanged( bool newState ) +{ + m_pOut->setHigh(newState); +} + + +void Buffer::drawShape( QPainter &p ) +{ + initPainter(p); + int _x = (int)x()-8; + int _y = (int)y()-8; + QPointArray pa(3); + pa[0] = QPoint( _x, _y ); + pa[1] = QPoint( _x+width(), _y+(height()/2) ); + pa[2] = QPoint( _x, _y+height() ); + p.drawPolygon(pa); + p.drawPolyline(pa); + deinitPainter(p); +} +//END class Buffer + + +//BEGIN class ECLogicInput +Item* ECLogicInput::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new ECLogicInput( (ICNDocument*)itemDocument, newItem, id ); +} + +LibraryItem* ECLogicInput::libraryItem() +{ + return new LibraryItem( + QString::QString("ec/logic_input"), + i18n("Logic Input"), + i18n("Logic"), + "logic_input.png", + LibraryItem::lit_component, + ECLogicInput::construct ); +} + +ECLogicInput::ECLogicInput( ICNDocument *icnDocument, bool newItem, const char *id ) + : Component( icnDocument, newItem, (id) ? id : "logic_input" ) +{ + m_name = i18n("Logic Input"); + m_desc = i18n("Provides a user-adjustable logic state.<br><br>" + "Click to pulse high, or drag the mouse off to keep the output high."); + setSize( -8, -8, 16, 16 ); + + b_state = false; + addButton( "button", QRect( -24, -8, 16, 16 ), "", true ); + + createProperty( "useToggle", Variant::Type::Bool ); + property("useToggle")->setCaption( i18n("Use Toggle") ); + property("useToggle")->setValue(true); + + init1PinRight(); + + m_pOut = createLogicOut( m_pPNode[0], false ); +} + + +ECLogicInput::~ECLogicInput() +{ +} + + +void ECLogicInput::dataChanged() +{ + button("button")->setToggle( dataBool("useToggle") ); +} + + +void ECLogicInput::drawShape( QPainter &p ) +{ + initPainter(p); + if (b_state) + p.setBrush( QColor( 255, 166, 0 ) ); + else + p.setBrush( Qt::white ); + p.drawEllipse( (int)x()-4, (int)y()-6, 12, 12 ); + deinitPainter(p); +} + + +void ECLogicInput::buttonStateChanged( const QString &, bool state ) +{ + b_state = state; + m_pOut->setHigh(b_state); +} +//END class ECLogicInput + + +//BEGIN class ECLogicOutput +Item* ECLogicOutput::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new ECLogicOutput( (ICNDocument*)itemDocument, newItem, id ); +} + +LibraryItem* ECLogicOutput::libraryItem() +{ + return new LibraryItem( + QString::QString("ec/logic_output"), + i18n("Logic Output"), + i18n("Logic"), + "logic_output.png", + LibraryItem::lit_component, + ECLogicOutput::construct ); +} + +ECLogicOutput::ECLogicOutput( ICNDocument *icnDocument, bool newItem, const char *id ) + : Component( icnDocument, newItem, (id) ? id : "logic_output" ) +{ + m_name = i18n("Logic Output"); + m_desc = i18n("Shows the logic-state of the input."); + setSize( -8, -8, 16, 16 ); + + init1PinLeft(); + m_pIn = createLogicIn(m_pNNode[0]); + + m_pSimulator = Simulator::self(); + + m_lastDrawState = 0.0; + m_lastSwitchTime = m_lastDrawTime = m_pSimulator->time(); + m_highTime = 0; + m_bLastState = false; + m_bDynamicContent = true; + + m_pIn->setCallback( this, (CallbackPtr)(&ECLogicOutput::inStateChanged) ); +} + +ECLogicOutput::~ECLogicOutput() +{ +} + +void ECLogicOutput::inStateChanged( bool newState ) +{ + if ( m_bLastState == newState ) + return; + + unsigned long long newTime = m_pSimulator->time(); + unsigned long long dt = newTime - m_lastSwitchTime; + + m_lastSwitchTime = newTime; + + m_bLastState = newState; + if (!newState) + { + // Gone from high to low + m_highTime += dt; + } +} + + +void ECLogicOutput::drawShape( QPainter &p ) +{ + unsigned long long newTime = m_pSimulator->time(); + unsigned long long runTime = newTime - m_lastDrawTime; + m_lastDrawTime = newTime; + + if (m_bLastState) + { + // Logic in is currently high + m_highTime += newTime - m_lastSwitchTime; + } + + double state; + + if ( runTime == 0 ) + state = m_lastDrawState; + + else + state = m_lastDrawState = double(m_highTime)/double(runTime); + + initPainter(p); + p.setBrush( QColor( 255, uint(255-state*(255-166)), uint((1-state)*255) ) ); + p.drawEllipse( int(x()-8), int(y()-8), width(), height() ); + deinitPainter(p); + + m_lastSwitchTime = newTime; + m_highTime = 0; +} +//END class ECLogicOutput + diff --git a/src/electronics/components/discretelogic.h b/src/electronics/components/discretelogic.h new file mode 100644 index 0000000..82775bf --- /dev/null +++ b/src/electronics/components/discretelogic.h @@ -0,0 +1,110 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef DISCRETELOGIC_H +#define DISCRETELOGIC_H + +#include "component.h" +#include "logic.h" + +class Simulator; + +/** +@short Boolean NOT +@author David Saxton +*/ +class Inverter : public CallbackClass, public Component +{ + public: + Inverter( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~Inverter(); + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + + protected: + void inStateChanged( bool newState ); + virtual void drawShape( QPainter &p ); + + LogicIn * m_pIn; + LogicOut * m_pOut; +}; + +/** +@short Buffer +@author David Saxton +*/ +class Buffer : public CallbackClass, public Component +{ +public: + Buffer( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~Buffer(); + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + +private: + void inStateChanged( bool newState ); + virtual void drawShape( QPainter &p ); + + LogicIn * m_pIn; + LogicOut * m_pOut; +}; + +/** +@short Boolean logic input +@author David Saxton +*/ +class ECLogicInput : public Component +{ +public: + ECLogicInput( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~ECLogicInput(); + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + + virtual void buttonStateChanged( const QString &id, bool state ); + +private: + virtual void dataChanged(); + virtual void drawShape( QPainter &p ); + LogicOut * m_pOut; + bool b_state; +}; + +/** +@short Boolean logic output +@author David Saxton +*/ +class ECLogicOutput : public CallbackClass, public Component +{ + public: + ECLogicOutput( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~ECLogicOutput(); + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + + protected: + void inStateChanged( bool newState ); + virtual void drawShape( QPainter &p ); + + unsigned long long m_lastDrawTime; + unsigned long long m_lastSwitchTime; + unsigned long long m_highTime; + bool m_bLastState; + + double m_lastDrawState; + LogicIn * m_pIn; + Simulator * m_pSimulator; +}; + +#endif diff --git a/src/electronics/components/ec555.cpp b/src/electronics/components/ec555.cpp new file mode 100644 index 0000000..eccf2bd --- /dev/null +++ b/src/electronics/components/ec555.cpp @@ -0,0 +1,172 @@ +/*************************************************************************** + * Copyright (C) 2003 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "ec555.h" + +#include "ecnode.h" +#include "libraryitem.h" +#include "pin.h" +#include "resistance.h" + +#include <kiconloader.h> +#include <klocale.h> +#include <qpainter.h> + +Item* EC555::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new EC555( (ICNDocument*)itemDocument, newItem, id ); +} + +LibraryItem* EC555::libraryItem() +{ + return new LibraryItem( + QString::QString("ec/555"), + i18n("555"), + i18n("Integrated Circuits"), + "ic1.png", + LibraryItem::lit_component, + EC555::construct + ); +} + +EC555::EC555( ICNDocument *icnDocument, bool newItem, const char *id ) + : Component( icnDocument, newItem, (id) ? id : "555" ) +{ + m_name = i18n("555"); + m_desc = i18n("Common timer IC"); +// m_pins = QStringList::split( ',', "Gnd,Trg,Out,Res,CV,Th,Dis,Vcc" ); +// m_pins = QStringList::split( ',', "Dis,Th,Trg,Gnd,CV,Out,Res,Vcc" ); + + old_com1 = false; + old_com2 = false; + old_q = false; + + setSize( -32, -32, 64, 64 ); + + + // Pins down left + + // Pin 7 + discharge = createPin( -40, -16, 0, "Dis" )->pin(); + addDisplayText( "dis", QRect( -32, -24, 24, 16 ), "Dis" ); + + // Pin 6 + threshold = createPin( -40, 0, 0, "Th" )->pin(); + addDisplayText( "th", QRect( -32, -8, 24, 16 ), "Th" ); + + // Pin 2 + trigger = createPin( -40, 16, 0, "Trg" )->pin(); + addDisplayText( "trg", QRect( -32, 8, 24, 16 ), "Trg" ); + + + + // Top two + + // Pin 8 + vcc = createPin( -16, -40, 90, "Vcc" )->pin(); + addDisplayText( "vcc", QRect( -24, -32, 16, 8 ), "+" ); + + // Pin 4 + reset = createPin( 16, -40, 90, "Res" )->pin(); + addDisplayText( "res", QRect( 8, -28, 16, 16 ), "Res" ); + + + + // Bottom two + + // Pin 1 + ground = createPin( -16, 40, 270, "Gnd" )->pin(); + addDisplayText( "gnd", QRect( -24, 20, 16, 8 ), "-" ); + + // Pin 5 + control = createPin( 16, 40, 270, "CV" )->pin(); + addDisplayText( "cv", QRect( 8, 12, 16, 16 ), "CV" ); + + + + // Output on right + + // Pin 3 + output = createPin( 40, 0, 180, "Out" )->pin(); + addDisplayText( "out", QRect( 8, -8, 16, 16 ), "Out" ); + + + + m_r1 = createResistance( vcc, control, 5e3 ); + m_r23 = createResistance( control, ground, 1e4 ); + m_po_sink = createResistance( output, ground, 0. ); + m_po_source = createResistance( output, vcc, 0. ); + m_po_source->setConductance(0.); + m_r_discharge = createResistance( discharge, ground, 0. ); +} + + +EC555::~EC555() +{ +} + +void EC555::stepNonLogic() +{ + double v_threshold = threshold->voltage(); + double v_control = control->voltage(); + double v_ground = ground->voltage(); + double v_trigger = trigger->voltage(); + double v_reset = reset->voltage(); + double v_vcc = vcc->voltage(); + + double v_r = (v_control+v_ground)/2; + + bool com1 = ( v_threshold == v_control ) ? old_com1 : ( v_threshold < v_control ); + bool com2 = ( v_r == v_trigger ) ? old_com2 : ( v_r > v_trigger ); + bool reset = ( v_reset >= (v_control-v_ground)/2 + v_ground ); + old_com1 = com1; + old_com2 = com2; + + bool r = ( !reset || !com1 ); + bool s = com2; + + bool q = old_q; + if ( v_vcc - v_ground >= 2.5 ) + { + if ( s && !r ) + { + q = true; + } + else if ( r && !s ) + { + q = false; + } + } + else + { + q = false; + } + old_q = q; + + + m_r_discharge->setConductance(0.); + + if (q) + { + m_po_source->setResistance(10.); + m_po_sink->setConductance(0.); + } + else + { + m_po_source->setConductance(0.); + m_po_sink->setResistance(10.); + if ( v_ground+0.7 <= v_vcc ) + { + m_r_discharge->setResistance(10.); + } + } +} + + diff --git a/src/electronics/components/ec555.h b/src/electronics/components/ec555.h new file mode 100644 index 0000000..edeb2a5 --- /dev/null +++ b/src/electronics/components/ec555.h @@ -0,0 +1,56 @@ +/*************************************************************************** + * Copyright (C) 2003 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef EC555_H +#define EC555_H + +#include "component.h" + +#include <qstringlist.h> + +/** +@short 555 IC +@author David Saxton +*/ +class EC555 : public Component +{ +public: + EC555( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~EC555(); + + virtual bool canFlip() const { return true; } + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + + virtual void stepNonLogic(); + virtual bool doesStepNonLogic() const { return true; } + +private: + Pin * ground; + Pin * trigger; + Pin * output; + Pin * reset; + Pin * control; + Pin * threshold; + Pin * discharge; + Pin * vcc; + + Resistance *m_r1; + Resistance *m_r23; + Resistance *m_po_sink; + Resistance *m_po_source; + Resistance *m_r_discharge; + + bool old_com1; + bool old_com2; + bool old_q; +}; + +#endif diff --git a/src/electronics/components/ecbcdto7segment.cpp b/src/electronics/components/ecbcdto7segment.cpp new file mode 100644 index 0000000..fb210ea --- /dev/null +++ b/src/electronics/components/ecbcdto7segment.cpp @@ -0,0 +1,158 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "ecbcdto7segment.h" + +#include "logic.h" +#include "libraryitem.h" + +#include <kiconloader.h> +#include <klocale.h> + +// Values for a,b,c,d,e,f,g of common-anode 7 segment display +bool numbers[16][7] = + { { 1, 1, 1, 1, 1, 1, 0 }, // 0 + { 0, 1, 1, 0, 0, 0, 0 }, // 1 + { 1, 1, 0, 1, 1, 0, 1 }, // 2 + { 1, 1, 1, 1, 0, 0, 1 }, // 3 + { 0, 1, 1, 0 ,0, 1, 1 }, // 4 + { 1, 0, 1, 1, 0, 1, 1 }, // 5 + { 1, 0, 1, 1, 1, 1, 1 }, // 6 + { 1, 1, 1, 0, 0, 0, 0 }, // 7 + { 1, 1, 1, 1, 1, 1, 1 }, // 8 + { 1, 1, 1, 0, 0, 1, 1 }, // 9 + { 1, 1, 1, 0, 1, 1, 1 }, // A + { 0, 0, 1, 1, 1, 1, 1 }, // b + { 1, 0, 0, 1, 1, 1, 0 }, // C + { 0, 1, 1, 1, 1, 0, 1 }, // d + { 1, 0, 0, 1, 1, 1, 1 }, // E + { 1, 0, 0, 0, 1, 1, 1 } }; // F + +Item* ECBCDTo7Segment::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new ECBCDTo7Segment( (ICNDocument*)itemDocument, newItem, id ); +} + +LibraryItem* ECBCDTo7Segment::libraryItem() +{ + return new LibraryItem( + QString::QString("ec/bcd_to_seven_segment"), + i18n("BCD to 7 Segment"), + i18n("Integrated Circuits"), + "ic2.png", + LibraryItem::lit_component, + ECBCDTo7Segment::construct + ); +} + +ECBCDTo7Segment::ECBCDTo7Segment( ICNDocument *icnDocument, bool newItem, const char *id ) + : Component( icnDocument, newItem, (id) ? id : "bcd_to_seven_segment" ) +{ + m_name = i18n("BCD to Seven Segment"); + m_desc = i18n("Converts a binary-coded-input to a form displayable by a seven segment display.<br><br>" + "Normal operation: <i>lt</i> (Lamp Test) and the <i>rb</i> (Ripple Blanking) are held high, <i>en</i> (Enable) is held low."); + + ALogic = BLogic = CLogic = DLogic = 0L; + ltLogic = rbLogic = enLogic = 0L; + + for ( int i=0; i<7; i++ ) + { + outLogic[i] = 0L; + oldOut[i] = false; + } + + QStringList pins = QStringList::split( ',', "A,B,C,D,,lt,rb,en,d,e,f,g,,a,b,c", true ); + initDIPSymbol( pins, 48 ); + initDIP(pins); + + ALogic = createLogicIn( ecNodeWithID("A") ); + BLogic = createLogicIn( ecNodeWithID("B") ); + CLogic = createLogicIn( ecNodeWithID("C") ); + DLogic = createLogicIn( ecNodeWithID("D") ); + ltLogic = createLogicIn( ecNodeWithID("lt") ); + rbLogic = createLogicIn( ecNodeWithID("rb") ); + enLogic = createLogicIn( ecNodeWithID("en") ); + + ALogic->setCallback( this, (CallbackPtr)(&ECBCDTo7Segment::inStateChanged) ); + BLogic->setCallback( this, (CallbackPtr)(&ECBCDTo7Segment::inStateChanged) ); + CLogic->setCallback( this, (CallbackPtr)(&ECBCDTo7Segment::inStateChanged) ); + DLogic->setCallback( this, (CallbackPtr)(&ECBCDTo7Segment::inStateChanged) ); + ltLogic->setCallback( this, (CallbackPtr)(&ECBCDTo7Segment::inStateChanged) ); + rbLogic->setCallback( this, (CallbackPtr)(&ECBCDTo7Segment::inStateChanged) ); + enLogic->setCallback( this, (CallbackPtr)(&ECBCDTo7Segment::inStateChanged) ); + + for ( uint i=0; i<7; ++i ) + { + outLogic[i] = createLogicOut( ecNodeWithID( QChar('a'+i) ), false ); + outLogic[i]->setCallback( this, (CallbackPtr)(&ECBCDTo7Segment::inStateChanged) ); + } + inStateChanged(false); +} + +ECBCDTo7Segment::~ECBCDTo7Segment() +{ +} + +void ECBCDTo7Segment::inStateChanged(bool) +{ + bool A = ALogic->isHigh(); + bool B = BLogic->isHigh(); + bool C = CLogic->isHigh(); + bool D = DLogic->isHigh(); + bool lt = ltLogic->isHigh(); // Lamp test + bool rb = rbLogic->isHigh(); // Ripple Blank + bool en = enLogic->isHigh(); // Enable (store) + + int n = A + 2*B + 4*C + 8*D; +// if ( n > 9 ) n = 0; + + bool out[7]; + + if (lt) // Lamp test + { + if (rb) // Ripple blanking + { + if (en) // Enable (store) + { + for ( int i=0; i<7; i++ ) + { + out[i] = oldOut[i]; + } + } + else + { + for ( int i=0; i<7; i++ ) + { + out[i] = numbers[n][i]; + oldOut[i] = out[i]; + } + } + } + else + { + for ( int i=0; i<7; i++ ) + { + out[i] = false; + } + } + } + else + { + for ( int i=0; i<7; i++ ) + { + out[i] = true; + } + } + + for ( int i=0; i<7; i++ ) + { + outLogic[i]->setHigh( out[i] ); + } +} diff --git a/src/electronics/components/ecbcdto7segment.h b/src/electronics/components/ecbcdto7segment.h new file mode 100644 index 0000000..03d4c2b --- /dev/null +++ b/src/electronics/components/ecbcdto7segment.h @@ -0,0 +1,41 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef ECBCDTO7SEGMENT_H +#define ECBCDTO7SEGMENT_H + +#include "component.h" +#include "logic.h" + +/** +@short Converts a BCD input to 7-Segment Output +@author David Saxton +*/ +class ECBCDTo7Segment : public CallbackClass, public Component +{ +public: + ECBCDTo7Segment( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~ECBCDTo7Segment(); + + virtual bool canFlip() const { return true; } + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + +private: + void inStateChanged( bool newState ); + LogicIn *ALogic, *BLogic, *CLogic, *DLogic; + LogicIn *ltLogic, *rbLogic, *enLogic; + LogicOut *outLogic[7]; + + // Old values + bool oldOut[7]; +}; + +#endif diff --git a/src/electronics/components/ecbjt.cpp b/src/electronics/components/ecbjt.cpp new file mode 100644 index 0000000..b4b59f2 --- /dev/null +++ b/src/electronics/components/ecbjt.cpp @@ -0,0 +1,153 @@ +/*************************************************************************** + * Copyright (C) 2003-2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "bjt.h" +#include "ecbjt.h" +#include "ecnode.h" +#include "libraryitem.h" + +#include <kiconloader.h> +#include <klocale.h> +#include <qpainter.h> + +Item * ECBJT::constructNPN( ItemDocument * itemDocument, bool newItem, const char * id ) +{ + return new ECBJT( true, (ICNDocument*)itemDocument, newItem, id ); +} + + +Item * ECBJT::constructPNP( ItemDocument * itemDocument, bool newItem, const char * id ) +{ + return new ECBJT( false, (ICNDocument*)itemDocument, newItem, id ); +} + + +LibraryItem* ECBJT::libraryItemNPN() +{ + return new LibraryItem( + "ec/npnbjt", + i18n("NPN"), + i18n("Discrete"), + "npn.png", + LibraryItem::lit_component, + ECBJT::constructNPN ); +} + + +LibraryItem* ECBJT::libraryItemPNP() +{ + return new LibraryItem( + "ec/pnpbjt", + i18n("PNP"), + i18n("Discrete"), + "pnp.png", + LibraryItem::lit_component, + ECBJT::constructPNP ); +} + + +ECBJT::ECBJT( bool isNPN, ICNDocument * icnDocument, bool newItem, const char * id ) + : Component( icnDocument, newItem, id ? id : (isNPN ? "npnbjt" : "pnpbjt") ) +{ + m_bIsNPN = isNPN; + if ( m_bIsNPN ) + m_name = i18n("NPN Transistor"); + else + m_name = i18n("PNP Transistor"); + + setSize( -8, -8, 16, 16 ); + m_pBJT = createBJT( createPin( 8, -16, 90, "c" ), createPin( -16, 0, 0, "b" ), createPin( 8, 16, 270, "e" ), m_bIsNPN ); + + BJTSettings s; // will be created with the default settings + + Variant * v = createProperty( "I_S", Variant::Type::Double ); + v->setCaption("Saturation Current"); + v->setUnit("A"); + v->setMinValue(1e-20); + v->setMaxValue(1e-0); + v->setValue( s.I_S ); + v->setAdvanced(true); + + v = createProperty( "N_F", Variant::Type::Double ); + v->setCaption( i18n("Forward Coefficient") ); + v->setMinValue(1e0); + v->setMaxValue(1e1); + v->setValue( s.N_F ); + v->setAdvanced(true); + + v = createProperty( "N_R", Variant::Type::Double ); + v->setCaption( i18n("Reverse Coefficient") ); + v->setMinValue(1e0); + v->setMaxValue(1e1); + v->setValue( s.N_R ); + v->setAdvanced(true); + + v = createProperty( "B_F", Variant::Type::Double ); + v->setCaption( i18n("Forward Beta") ); + v->setMinValue(1e-1); + v->setMaxValue(1e3); + v->setValue( s.B_F ); + v->setAdvanced(true); + + v = createProperty( "B_R", Variant::Type::Double ); + v->setCaption( i18n("Reverse Beta") ); + v->setMinValue(1e-1); + v->setMaxValue(1e3); + v->setValue( s.B_R ); + v->setAdvanced(true); +} + +ECBJT::~ECBJT() +{ +} + + +void ECBJT::dataChanged() +{ + BJTSettings s; + s.I_S = dataDouble( "I_S" ); + s.N_F = dataDouble( "N_F" ); + s.N_R = dataDouble( "N_R" ); + s.B_F = dataDouble( "B_F" ); + s.B_R = dataDouble( "B_R" ); + + m_pBJT->setBJTSettings( s ); +} + + +void ECBJT::drawShape( QPainter &p ) +{ + const int _x = int(x()); + const int _y = int(y()); + + initPainter(p); + + p.drawLine( _x-8, _y-8, _x-8, _y+8 ); + p.drawLine( _x+8, _y-8, _x-8, _y ); + p.drawLine( _x+8, _y+8, _x-8, _y ); + + QPointArray pa(3); + if ( m_bIsNPN ) + { + pa[0] = QPoint( _x+6, _y+7 ); + pa[1] = QPoint( _x+2, _y+8 ); + pa[2] = QPoint( _x+6, _y+3 ); + } + else + { + pa[0] = QPoint( _x-7, _y+1 ); + pa[1] = QPoint( _x-4, _y+5 ); + pa[2] = QPoint( _x-2, _y ); + } + p.setBrush( p.pen().color() ); + p.drawPolygon(pa); + + deinitPainter(p); +} diff --git a/src/electronics/components/ecbjt.h b/src/electronics/components/ecbjt.h new file mode 100644 index 0000000..17a63e9 --- /dev/null +++ b/src/electronics/components/ecbjt.h @@ -0,0 +1,42 @@ +/*************************************************************************** + * Copyright (C) 2003-2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef ECBJT_H +#define ECBJT_H + +#include "component.h" + +class BJT; + +/** +@short Simulates a BJT +@author David Saxton +*/ +class ECBJT : public Component +{ + public: + ECBJT( bool isNPN, ICNDocument *icnDocument, bool newItem, const char * id = 0L ); + ~ECBJT(); + virtual bool canFlip() const { return true; } + + static Item * constructNPN( ItemDocument * itemDocument, bool newItem, const char * id ); + static Item * constructPNP( ItemDocument * itemDocument, bool newItem, const char * id ); + static LibraryItem * libraryItemNPN(); + static LibraryItem * libraryItemPNP(); + + protected: + virtual void dataChanged(); + virtual void drawShape( QPainter &p ); + + bool m_bIsNPN; + BJT * m_pBJT; +}; + +#endif diff --git a/src/electronics/components/eccapacitor.cpp b/src/electronics/components/eccapacitor.cpp new file mode 100644 index 0000000..76a09b0 --- /dev/null +++ b/src/electronics/components/eccapacitor.cpp @@ -0,0 +1,92 @@ +/*************************************************************************** + * Copyright (C) 2003 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "capacitance.h" +#include "eccapacitor.h" +#include "ecnode.h" +#include "libraryitem.h" + +#include <klocale.h> +#include <qpainter.h> + +Item* ECCapacitor::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new ECCapacitor( (ICNDocument*)itemDocument, newItem, id ); +} + +LibraryItem* ECCapacitor::libraryItem() +{ + return new LibraryItem( + "ec/capacitor", + i18n("Capacitor"), + i18n("Discrete"), + "capacitor.png", + LibraryItem::lit_component, + ECCapacitor::construct + ); +} + +ECCapacitor::ECCapacitor( ICNDocument *icnDocument, bool newItem, const char *id ) + : Component( icnDocument, newItem, id ? id : "capacitor" ) +{ + m_name = i18n("Capacitor"); + m_desc = i18n("Stores electrical charge.<br><br>" + "The voltage across the capacitor and capacitance are related by <i>Charge = Capacitance x Voltage</i>."); + setSize( -8, -8, 16, 16 ); + + init1PinLeft(); + init1PinRight(); + + m_capacitance = createCapacitance( m_pNNode[0], m_pPNode[0], 0.001 ); + + createProperty( "Capacitance", Variant::Type::Double ); + property("Capacitance")->setCaption( i18n("Capacitance") ); + property("Capacitance")->setUnit("F"); + property("Capacitance")->setMinValue(1e-12); + property("Capacitance")->setMaxValue(1e12); + property("Capacitance")->setValue(1e-3); + + addDisplayText( "capacitance", QRect( -8, -24, 16, 16 ), "", false ); +} + +ECCapacitor::~ECCapacitor() +{ +} + +void ECCapacitor::dataChanged() +{ + double capacitance = dataDouble("Capacitance"); + + QString display = QString::number( capacitance / getMultiplier(capacitance), 'g', 3 ) + getNumberMag(capacitance) + "F"; + setDisplayText( "capacitance", display ); + + m_capacitance->setCapacitance(capacitance); +} + +void ECCapacitor::drawShape( QPainter &p ) +{ + initPainter(p); + + int _y = (int)y()-8; + int _x = (int)x()-8; + + QPen pen; + pen.setWidth(1); + pen.setColor( p.pen().color() ); + p.setPen(pen); + p.drawRect( _x, _y, 5, 16 ); + p.drawRect( _x+11, _y, 5, 16 ); + + + deinitPainter(p); +// p.drawPolyline( areaPoints() ); +} + + diff --git a/src/electronics/components/eccapacitor.h b/src/electronics/components/eccapacitor.h new file mode 100644 index 0000000..4dd0ee6 --- /dev/null +++ b/src/electronics/components/eccapacitor.h @@ -0,0 +1,40 @@ +/*************************************************************************** + * Copyright (C) 2003 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef ECCAPACITOR_H +#define ECCAPACITOR_H + +#include "component.h" + +class Capacitance; +class ECNode; + +/** +@short Capacitor +Simple capacitor +@author David Saxton +*/ +class ECCapacitor : public Component +{ +public: + ECCapacitor( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~ECCapacitor(); + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + +private: + void dataChanged(); + virtual void drawShape( QPainter &p ); + + Capacitance * m_capacitance; +}; + +#endif diff --git a/src/electronics/components/ecclockinput.cpp b/src/electronics/components/ecclockinput.cpp new file mode 100644 index 0000000..7f1ec9c --- /dev/null +++ b/src/electronics/components/ecclockinput.cpp @@ -0,0 +1,185 @@ +/*************************************************************************** + * Copyright (C) 2003-2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "ecclockinput.h" + +#include "logic.h" +#include "libraryitem.h" +#include "simulator.h" + +#include <klocale.h> +#include <qpainter.h> + +#include <cmath> + +static inline uint roundDouble( const double x ) +{ + return uint(std::floor(x+0.5)); +} + +Item* ECClockInput::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new ECClockInput( (ICNDocument*)itemDocument, newItem, id ); +} + +LibraryItem* ECClockInput::libraryItem() +{ + return new LibraryItem( + QString("ec/clock_input"), + i18n("Clock Input"), + i18n("Logic"), + "clockinput.png", + LibraryItem::lit_component, + ECClockInput::construct ); +} + +ECClockInput::ECClockInput( ICNDocument *icnDocument, bool newItem, const char *id ) + : Component( icnDocument, newItem, (id) ? id : "clock_input" ) +{ + m_name = i18n("Clock Input"); + m_desc = i18n("A square-wave generator, outputing logical high/low at repeating time intervals."); + setSize( -16, -8, 32, 16 ); + + m_lastSetTime = 0; + m_time = 0; + m_high_time = 0; + m_low_time = 0; + m_period = 0; + m_bSetStepCallbacks = true; + m_pSimulator = Simulator::self(); + + for ( unsigned i = 0; i < 1000; i++ ) + { + ComponentCallback * ccb = new ComponentCallback( this, (VoidCallbackPtr)(&ECClockInput::stepCallback) ); + m_pComponentCallback[i] = new LinkedList<ComponentCallback>(ccb); + } + + init1PinRight(); + m_pOut = createLogicOut( m_pPNode[0], false ); + + createProperty( "low-time", Variant::Type::Double ); + property("low-time")->setUnit("S"); + property("low-time")->setCaption( i18n("Low Time") ); + property("low-time")->setMinValue(1.0/LOGIC_UPDATE_RATE); + property("low-time")->setValue(0.5); + + createProperty( "high-time", Variant::Type::Double ); + property("high-time")->setUnit("S"); + property("high-time")->setCaption( i18n("High Time") ); + property("high-time")->setMinValue(1.0/LOGIC_UPDATE_RATE); + property("high-time")->setValue(0.5); + + addDisplayText( "freq", QRect( -16, -24, 32, 14 ), "", false ); +} + + +ECClockInput::~ECClockInput() +{ + for ( unsigned i = 0; i < 1000; i++ ) + { + delete m_pComponentCallback[i]->data(); + delete m_pComponentCallback[i]; + } +} + + +void ECClockInput::dataChanged() +{ + m_high_time = roundDouble(dataDouble("high-time")*LOGIC_UPDATE_RATE); + m_low_time = roundDouble(dataDouble("low-time")*LOGIC_UPDATE_RATE); + m_period = m_low_time+m_high_time; + + const double frequency = 1./(dataDouble("high-time")+dataDouble("low-time")); + QString display = QString::number( frequency / getMultiplier(frequency), 'g', 3 ) + getNumberMag(frequency) + "Hz"; + setDisplayText( "freq", display ); + + bool setStepCallbacks = m_period > 100; + if ( setStepCallbacks != m_bSetStepCallbacks ) + { + m_bSetStepCallbacks = setStepCallbacks; + if (setStepCallbacks) + m_pSimulator->detachComponentCallbacks(this); + else + m_pSimulator->attachComponentCallback( this, (VoidCallbackPtr)(&ECClockInput::stepLogic) ); + } + + m_bLastStepCallbackOut = false; + m_lastSetTime = m_pSimulator->time(); +} + + +void ECClockInput::stepLogic() +{ + m_pOut->setHigh( m_time>m_low_time ); + + if ( ++m_time > m_period ) { + m_time -= int(m_time/m_period)*m_period; + } +} + + +void ECClockInput::stepCallback() +{ + m_pOut->setHigh(m_bLastStepCallbackOut); + m_bLastStepCallbackOut = !m_bLastStepCallbackOut; +} + + +void ECClockInput::stepNonLogic() +{ + if (!m_bSetStepCallbacks) + return; + + bool addingHigh = !m_bLastStepCallbackOut; + + //TODO 100 number shouldn't be hard-coded + long long lowerTime = m_pSimulator->time(); + long long upperTime = lowerTime + 100; + + long long upTo = m_lastSetTime; + + while ( upTo + (addingHigh?m_high_time:m_low_time) < upperTime ) + { + upTo += addingHigh ? m_high_time : m_low_time; + addingHigh = !addingHigh; + + long long at = upTo-lowerTime; + if ( at >= 0 && at < 100 ) + m_pSimulator->addStepCallback( at, m_pComponentCallback[at] ); + } + + m_lastSetTime = upTo; +} + + +void ECClockInput::drawShape( QPainter &p ) +{ + initPainter(p); + + int _x = (int)x()-10; + int _y = (int)y()-8; + + p.drawRect( _x-6, _y, 32, 16 ); + + p.drawLine( _x, _y+8, _x, _y+4 ); + p.drawLine( _x, _y+4, _x+4, _y+4 ); + p.drawLine( _x+4, _y+4, _x+4, _y+12 ); + p.drawLine( _x+4, _y+12, _x+8, _y+12 ); + p.drawLine( _x+8, _y+12, _x+8, _y+4 ); + p.drawLine( _x+8, _y+4, _x+12, _y+4 ); + p.drawLine( _x+12, _y+4, _x+12, _y+12 ); + p.drawLine( _x+12, _y+12, _x+16, _y+12 ); + p.drawLine( _x+16, _y+12, _x+16, _y+4 ); + p.drawLine( _x+16, _y+4, _x+20, _y+4 ); + p.drawLine( _x+20, _y+4, _x+20, _y+8 ); + + deinitPainter(p); +} + diff --git a/src/electronics/components/ecclockinput.h b/src/electronics/components/ecclockinput.h new file mode 100644 index 0000000..c7f7d14 --- /dev/null +++ b/src/electronics/components/ecclockinput.h @@ -0,0 +1,56 @@ +/*************************************************************************** + * Copyright (C) 2003-2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef ECCLOCKINPUT_H +#define ECCLOCKINPUT_H + +#include "component.h" + +class ComponentCallback; +class Simulator; + +template <typename T> +class LinkedList; + +/** +@short Boolean clock input +@author David Saxton +*/ +class ECClockInput : public Component +{ +public: + ECClockInput( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~ECClockInput(); + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + + void stepCallback(); + void stepLogic(); + virtual void stepNonLogic(); + virtual bool doesStepNonLogic() const { return true; } + +protected: + virtual void drawShape( QPainter &p ); + void dataChanged(); + + uint m_time; + uint m_high_time; + uint m_low_time; + uint m_period; + long long m_lastSetTime; + LogicOut * m_pOut; + bool m_bSetStepCallbacks; + bool m_bLastStepCallbackOut; + Simulator * m_pSimulator; + LinkedList<ComponentCallback> * m_pComponentCallback[1000]; +}; + +#endif diff --git a/src/electronics/components/eccurrentsignal.cpp b/src/electronics/components/eccurrentsignal.cpp new file mode 100644 index 0000000..e6dc351 --- /dev/null +++ b/src/electronics/components/eccurrentsignal.cpp @@ -0,0 +1,92 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "currentsignal.h" +#include "eccurrentsignal.h" +#include "ecnode.h" +#include "libraryitem.h" +#include "pin.h" +#include "simulator.h" + +#include <klocale.h> +#include <qpainter.h> + +Item* ECCurrentSignal::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new ECCurrentSignal( (ICNDocument*)itemDocument, newItem, id ); +} + +LibraryItem* ECCurrentSignal::libraryItem() +{ + return new LibraryItem( + QString("ec/ac_current"), + i18n("Current Signal"), + i18n("Sources"), + "currentsignal.png", + LibraryItem::lit_component, + ECCurrentSignal::construct ); +} + +ECCurrentSignal::ECCurrentSignal( ICNDocument *icnDocument, bool newItem, const char *id ) + : Component( icnDocument, newItem, (id) ? id : "current_signal" ) +{ + m_name = i18n("Current Signal"); + m_desc = i18n("Provides a variety of current signals"); + setSize( -8, -8, 16, 16 ); + + init1PinLeft(); + init1PinRight(); + + m_pNNode[0]->pin()->setGroundType( Pin::gt_low ); + m_currentSignal = createCurrentSignal( m_pNNode[0], m_pPNode[0], 0. ); + m_currentSignal->setStep( 1./LINEAR_UPDATE_RATE, ElementSignal::st_sinusoidal, 50. ); + + createProperty( "1-frequency", Variant::Type::Double ); + property("1-frequency")->setCaption( i18n("Frequency") ); + property("1-frequency")->setUnit("Hz"); + property("1-frequency")->setMinValue(1e-9); + property("1-frequency")->setMaxValue(1e3); + property("1-frequency")->setValue(50.0); + + createProperty( "1-current", Variant::Type::Double ); + property("1-current")->setCaption( i18n("Current Range") ); + property("1-current")->setUnit("A"); + property("1-current")->setMinValue(-1e12); + property("1-current")->setMaxValue(1e12); + property("1-current")->setValue(0.02); + + addDisplayText( "~", QRect( -8, -8, 16, 16 ), "~" ); + addDisplayText( "current", QRect( -16, -24, 32, 16 ), "" ); +} + + +ECCurrentSignal::~ECCurrentSignal() +{ +} + +void ECCurrentSignal::dataChanged() +{ + const double current = dataDouble("1-current"); + const double frequency = dataDouble("1-frequency"); + + QString display = QString::number( current / getMultiplier(current), 'g', 3 ) + getNumberMag(current) + "A"; + setDisplayText( "current", display ); + + m_currentSignal->setStep( 1./LINEAR_UPDATE_RATE, ElementSignal::st_sinusoidal, frequency ); + m_currentSignal->setCurrent(current); +} + +void ECCurrentSignal::drawShape( QPainter &p ) +{ + initPainter(p); + p.drawEllipse( (int)x()-8, (int)y()-8, width(), height() ); + deinitPainter(p); +} + diff --git a/src/electronics/components/eccurrentsignal.h b/src/electronics/components/eccurrentsignal.h new file mode 100644 index 0000000..88dd276 --- /dev/null +++ b/src/electronics/components/eccurrentsignal.h @@ -0,0 +1,36 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef ECCURRENTSIGNAL_H +#define ECCURRENTSIGNAL_H + +#include "component.h" + +/** +@short Provides a current signal (sinusoidal, square, etc) +@author David Saxton +*/ +class ECCurrentSignal : public Component +{ +public: + ECCurrentSignal( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~ECCurrentSignal(); + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + +protected: + virtual void drawShape( QPainter &p ); + void dataChanged(); + + CurrentSignal *m_currentSignal; +}; + +#endif diff --git a/src/electronics/components/eccurrentsource.cpp b/src/electronics/components/eccurrentsource.cpp new file mode 100644 index 0000000..76ecf3e --- /dev/null +++ b/src/electronics/components/eccurrentsource.cpp @@ -0,0 +1,94 @@ +/*************************************************************************** + * Copyright (C) 2003 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + + +#include "currentsource.h" +#include "eccurrentsource.h" +#include "ecnode.h" +#include "libraryitem.h" +#include "pin.h" + +#include <klocale.h> +#include <qpainter.h> + +Item* ECCurrentSource::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new ECCurrentSource( (ICNDocument*)itemDocument, newItem, id ); +} + +LibraryItem* ECCurrentSource::libraryItem() +{ + return new LibraryItem( + QString::QString("ec/current_source"), + i18n("Current Source"), + i18n("Sources"), + "current_source.png", + LibraryItem::lit_component, + ECCurrentSource::construct ); +} + +ECCurrentSource::ECCurrentSource( ICNDocument *icnDocument, bool newItem, const char *id ) + : Component( icnDocument, newItem, (id) ? id : "current_source" ) +{ + m_name = i18n("Current Source"); + m_desc = i18n("Provides a fixed current source."); + setSize( -16, -8, 24, 24 ); + + init1PinLeft(8); + init1PinRight(8); + m_pNNode[0]->pin()->setGroundType( Pin::gt_low ); + + m_currentSource = createCurrentSource( m_pNNode[0], m_pPNode[0], 0. ); + + createProperty( "current", Variant::Type::Double ); + property("current")->setCaption( i18n("Current") ); + property("current")->setUnit("A"); + property("current")->setMinValue(-1e12); + property("current")->setMaxValue(1e12); + property("current")->setValue(0.02); + + addDisplayText("current", QRect( -16, -16, 24, 0 ), "" ); +} + + +ECCurrentSource::~ECCurrentSource() +{ +} + +void ECCurrentSource::dataChanged() +{ + double current = dataDouble("current"); + m_currentSource->setCurrent(current); + + QString display = QString::number( current / getMultiplier(current), 'g', 3 ) + getNumberMag(current) + "A"; + setDisplayText("current", display ); +} + +void ECCurrentSource::drawShape( QPainter &p ) +{ + initPainter(p); + + int _x = (int)x()-16; + int _y = (int)y()-24; + + // Top arrow indicating current direction + p.drawLine( _x+width(), _y+19, _x, _y+19 ); + p.drawLine( _x+width(), _y+19, _x+width()-3, _y+16 ); + p.drawLine( _x+width(), _y+19, _x+width()-3, _y+22 ); + + // Double circules + p.drawEllipse( _x, _y+24, 16, 16 ); + p.drawEllipse( _x+8, _y+24, 16, 16 ); + + deinitPainter(p); +} + + + diff --git a/src/electronics/components/eccurrentsource.h b/src/electronics/components/eccurrentsource.h new file mode 100644 index 0000000..6332eba --- /dev/null +++ b/src/electronics/components/eccurrentsource.h @@ -0,0 +1,36 @@ +/*************************************************************************** + * Copyright (C) 2003 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef ECCURRENTSOURCE_H +#define ECCURRENTSOURCE_H + +#include "component.h" + +/** +@short Fixed current source +@author David Saxton +*/ +class ECCurrentSource : public Component +{ +public: + ECCurrentSource( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~ECCurrentSource(); + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + +private: + virtual void drawShape( QPainter &p ); + void dataChanged(); + + CurrentSource *m_currentSource; +}; + +#endif diff --git a/src/electronics/components/ecdiode.cpp b/src/electronics/components/ecdiode.cpp new file mode 100644 index 0000000..1d63bce --- /dev/null +++ b/src/electronics/components/ecdiode.cpp @@ -0,0 +1,120 @@ +/*************************************************************************** + * Copyright (C) 2003,2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "diode.h" +#include "ecdiode.h" +#include "ecnode.h" +#include "libraryitem.h" + +#include <klocale.h> +#include <qpainter.h> + +Item* ECDiode::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new ECDiode( (ICNDocument*)itemDocument, newItem, id ); +} + +LibraryItem* ECDiode::libraryItem() +{ + return new LibraryItem( + "ec/diode", + i18n("Diode"), + i18n("Discrete"), + "diode.png", + LibraryItem::lit_component, + ECDiode::construct ); +} + +ECDiode::ECDiode( ICNDocument *icnDocument, bool newItem, const char *id ) + : Component( icnDocument, newItem, id ? id : "diode" ) +{ + m_name = i18n("Diode"); + m_desc = i18n("Allows current to flow in the direction indicated by the arrow when a certain voltage difference has been reached."); + + setSize( -8, -8, 16, 16 ); + + init1PinLeft(); + init1PinRight(); + + m_diode = createDiode( m_pNNode[0], m_pPNode[0] ); + + DiodeSettings ds; // it will have the default properties that we use + + createProperty( "I_S", Variant::Type::Double ); + property("I_S")->setCaption("Saturation Current"); + property("I_S")->setUnit("A"); + property("I_S")->setMinValue(1e-20); + property("I_S")->setMaxValue(1e-0); + property("I_S")->setValue( ds.I_S ); + property("I_S")->setAdvanced(true); + + createProperty( "N", Variant::Type::Double ); + property("N")->setCaption( i18n("Emission Coefficient") ); + property("N")->setMinValue(1e0); + property("N")->setMaxValue(1e1); + property("N")->setValue( ds.N ); + property("N")->setAdvanced(true); + + createProperty( "V_B", Variant::Type::Double ); + property("V_B")->setCaption( i18n("Breakdown Voltage") ); + property("V_B")->setUnit("V"); + property("V_B")->setMinAbsValue(1e-5); + property("V_B")->setMaxValue(1e10); + property("V_B")->setValue( ds.V_B ); + property("V_B")->setAdvanced(true); + +// createProperty( "R", Variant::Type::Double ); +// property("R")->setCaption( i18n("Series Resistance") ); +// property("R")->setUnit( QChar(0x3a9) ); +// property("R")->setMinValue(1e-5); +// property("R")->setMaxValue(1e0); +// property("R")->setValue( ds.R ); +// property("R")->setAdvanced(true); +} + + +ECDiode::~ECDiode() +{ +} + + +void ECDiode::dataChanged() +{ + DiodeSettings ds; + + ds.I_S = dataDouble("I_S"); + ds.V_B = dataDouble("V_B"); + ds.N = dataDouble("N"); +// ds.R = dataDouble("R"); + + m_diode->setDiodeSettings( ds ); +} + + +void ECDiode::drawShape( QPainter & p ) +{ + initPainter(p); + + int _x = int(x()); + int _y = int(y()); + + QPointArray pa(3); + pa[0] = QPoint( 8, 0 ); + pa[1] = QPoint( -8, -8 ); + pa[2] = QPoint( -8, 8 ); + pa.translate( _x, _y ); + p.drawPolygon(pa); + p.drawPolyline(pa); + + p.drawLine( _x+8, _y-8, _x+8, _y+8 ); + + deinitPainter(p); +} + diff --git a/src/electronics/components/ecdiode.h b/src/electronics/components/ecdiode.h new file mode 100644 index 0000000..fa8d034 --- /dev/null +++ b/src/electronics/components/ecdiode.h @@ -0,0 +1,35 @@ +/*************************************************************************** + * Copyright (C) 2003,2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef ECDIODE_H +#define ECDIODE_H + +#include "component.h" + +/** +@short Simple diode +@author David Saxton +*/ +class ECDiode : public Component +{ +public: + ECDiode( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~ECDiode(); + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + +protected: + void drawShape( QPainter & p ); + void dataChanged(); + Diode *m_diode; +}; + +#endif diff --git a/src/electronics/components/ecfixedvoltage.cpp b/src/electronics/components/ecfixedvoltage.cpp new file mode 100644 index 0000000..06eb707 --- /dev/null +++ b/src/electronics/components/ecfixedvoltage.cpp @@ -0,0 +1,77 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "ecfixedvoltage.h" + +#include "ecnode.h" +#include "voltagepoint.h" +#include "libraryitem.h" + +#include <klocale.h> +#include <qpainter.h> + +Item* ECFixedVoltage::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new ECFixedVoltage( (ICNDocument*)itemDocument, newItem, id ); +} +LibraryItem* ECFixedVoltage::libraryItem() +{ + return new LibraryItem( + "ec/fixed_voltage", + i18n("Fixed Voltage"), + i18n("Sources"), + "voltage.png", + LibraryItem::lit_component, + ECFixedVoltage::construct ); +} + +ECFixedVoltage::ECFixedVoltage( ICNDocument *icnDocument, bool newItem, const char *id ) + : Component( icnDocument, newItem, id ? id : "fixed_voltage" ) +{ + m_name = i18n("Fixed Voltage"); + m_desc = i18n("Provides a fixed voltage point to connect components to."); + setSize( -8, -8, 16, 16 ); + + init1PinRight(); + m_voltagePoint = createVoltagePoint( m_pPNode[0], 5.0 ); + + addDisplayText( "voltage", QRect( -24, -20, width()+32, 12 ), "" ); + + createProperty( "voltage", Variant::Type::Double ); + property("voltage")->setUnit("V"); + property("voltage")->setCaption( i18n("Voltage") ); + property("voltage")->setMinValue(-1e15); + property("voltage")->setMaxValue(1e15); + property("voltage")->setValue(5.0); +} + +ECFixedVoltage::~ECFixedVoltage() +{ +} + +void ECFixedVoltage::dataChanged() +{ + const double voltage = dataDouble("voltage"); + QString display = QString::number( voltage / getMultiplier(voltage), 'g', 3 ) + getNumberMag(voltage) + "V"; + setDisplayText( "voltage", display ); + m_voltagePoint->setVoltage(voltage); +} + +void ECFixedVoltage::drawShape( QPainter &p ) +{ + initPainter(p); + int _x = int(x()); + int _y = int(y()); + p.drawEllipse( _x-4, _y-4, 8, 8 ); + p.setPen( m_pPNode[0]->isSelected() ? m_selectedCol : Qt::black ); + p.drawLine( _x+4, _y, _x+8, _y ); + deinitPainter(p); +} + diff --git a/src/electronics/components/ecfixedvoltage.h b/src/electronics/components/ecfixedvoltage.h new file mode 100644 index 0000000..ba20358 --- /dev/null +++ b/src/electronics/components/ecfixedvoltage.h @@ -0,0 +1,35 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef ECFIXEDVOLTAGE_H +#define ECFIXEDVOLTAGE_H + +#include "component.h" + +/** +@short Fixed voltage source +@author David Saxton +*/ +class ECFixedVoltage : public Component +{ +public: + ECFixedVoltage( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~ECFixedVoltage(); + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + +private: + virtual void drawShape( QPainter &p ); + void dataChanged(); + VoltagePoint *m_voltagePoint; +}; + +#endif diff --git a/src/electronics/components/ecground.cpp b/src/electronics/components/ecground.cpp new file mode 100644 index 0000000..efbfc41 --- /dev/null +++ b/src/electronics/components/ecground.cpp @@ -0,0 +1,66 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "ecground.h" + +#include "ecnode.h" +#include "libraryitem.h" +#include "pin.h" + +#include <klocale.h> +#include <qpainter.h> + +Item* ECGround::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new ECGround( (ICNDocument*)itemDocument, newItem, id ); +} +LibraryItem* ECGround::libraryItem() +{ + return new LibraryItem( + QString::QString("ec/ground"), + i18n("Ground (0V)"), + i18n("Sources"), + "ground.png", + LibraryItem::lit_component, + ECGround::construct ); +} + +ECGround::ECGround( ICNDocument *icnDocument, bool newItem, const char *id ) + : Component( icnDocument, newItem, (id) ? id : "ground" ) +{ + m_name = i18n("Ground"); + m_desc = i18n("Ground (0V) point"); + setSize( -8, -8, 16, 16 ); + init1PinRight(); + m_pPNode[0]->pin()->setGroundType( Pin::gt_always ); + setAngleDegrees(270); +} + +ECGround::~ECGround() +{ +} + +void ECGround::drawShape( QPainter &p ) +{ + initPainter(p); + int _x = (int)x()-8; + int _y = (int)y()-8; + QPen pen; + pen.setWidth(2); + pen.setColor( p.pen().color() ); + p.setPen(pen); + p.drawLine( _x+15, _y, _x+15, _y+16 ); + p.drawLine( _x+10, _y+3, _x+10, _y+13 ); + p.drawLine( _x+5, _y+6, _x+5, _y+10 ); + deinitPainter(p); +} + + + diff --git a/src/electronics/components/ecground.h b/src/electronics/components/ecground.h new file mode 100644 index 0000000..0cf97bc --- /dev/null +++ b/src/electronics/components/ecground.h @@ -0,0 +1,33 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef ECGROUND_H +#define ECGROUND_H + +#include "component.h" + +/** +@short Fixed voltage source +@author David Saxton +*/ +class ECGround : public Component +{ +public: + ECGround( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~ECGround(); + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + +private: + virtual void drawShape( QPainter &p ); +}; + +#endif diff --git a/src/electronics/components/eckeypad.cpp b/src/electronics/components/eckeypad.cpp new file mode 100644 index 0000000..89d2659 --- /dev/null +++ b/src/electronics/components/eckeypad.cpp @@ -0,0 +1,199 @@ +/*************************************************************************** + * Copyright (C) 2003,2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "canvasitemparts.h" +#include "eckeypad.h" +#include "libraryitem.h" +#include "switch.h" + +#include "ecnode.h" +#include <klocale.h> + +Item* ECKeyPad::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new ECKeyPad( (ICNDocument*)itemDocument, newItem, id ); +} + +LibraryItem* ECKeyPad::libraryItem() +{ + return new LibraryItem( + QString("ec/keypad"), + i18n("Keypad"), + i18n("Switches"), + "keypad.png", + LibraryItem::lit_component, + ECKeyPad::construct ); +} + +const QString text[4][9] = + { { "1","2","3","A","E","I","M","Q","U" }, + { "4","5","6","B","F","J","N","R","V" }, + { "7","8","9","C","G","K","O","S","W" }, + { "*","0","#","D","H","L","P","T","X" } }; + +ECKeyPad::ECKeyPad( ICNDocument *icnDocument, bool newItem, const char *id ) + : Component( icnDocument, newItem, (id) ? id : "keypad" ) +{ + m_name = i18n("Keypad"); + m_desc = i18n("Provides a numeric array of Push-to-Make switches, with 4 rows and a configurable number of columns."); + + createProperty( "useToggles", Variant::Type::Bool ); + property("useToggles")->setCaption( i18n("Use Toggles") ); + property("useToggles")->setValue(false); + + createProperty( "numCols", Variant::Type::Int ); + property("numCols")->setCaption( i18n("Columns") ); + property("numCols")->setMinValue(3); + property("numCols")->setMaxValue(9); + property("numCols")->setValue(3); + + Variant * v = createProperty( "bounce", Variant::Type::Bool ); + v->setCaption("Bounce"); + v->setAdvanced(true); + v->setValue(false); + + v = createProperty( "bounce_period", Variant::Type::Double ); + v->setCaption("Bounce Period"); + v->setAdvanced(true); + v->setUnit("s"); + v->setValue(5e-3); + + for ( int i = 0; i < 4; i++ ) + createPin( 0, -32+i*24, 0, QString("row_%1").arg(QString::number(i)) ); + + m_numCols = 0; +} + + +ECKeyPad::~ECKeyPad() +{ +} + + +QString ECKeyPad::buttonID( int row, int col ) const +{ + return QString("b_%1_%2").arg(QString::number(row)).arg(QString::number(col)); +} + + +int ECKeyPad::sideLength( unsigned numButtons ) const +{ + return 8 + 24*numButtons; +} + + +void ECKeyPad::dataChanged() +{ + initPins( dataInt("numCols") ); + + bool useToggle = dataBool("useToggles"); + bool bounce = dataBool("bounce"); + int bouncePeriod_ms = int(dataDouble("bounce_period")*1e3); + + for ( unsigned i = 0; i < 4; i++ ) + { + for ( unsigned j = 0; j < m_numCols; j++ ) + { + button( buttonID(i,j) )->setToggle(useToggle); + m_switch[i][j]->setBounce( bounce, bouncePeriod_ms ); + } + } +} + + +void ECKeyPad::initPins( unsigned numCols ) +{ + if ( numCols < 3 ) + numCols = 3; + else if ( numCols > 9 ) + numCols = 9; + + if ( numCols == m_numCols ) + return; + + int w = sideLength(numCols); + int h = sideLength(4); + setSize( -int(w/16)*8, -int(h/16)*8, w, h, true ); + + if ( numCols > m_numCols ) + { + // Adding columns + + for ( unsigned i = 0; i < 4; i++ ) + { + for ( unsigned j = m_numCols; j < numCols; j++ ) + addButton( buttonID(i,j), QRect( 0, 0, 20, 20 ), text[i][j] ); + } + + ECNode * cols[numCols]; + + for ( unsigned j = m_numCols; j < numCols; j++ ) + cols[j] = createPin( 0, 64, 270, "col_" + QString::number(j) ); + + for ( unsigned i = 0; i < 4; i++ ) + { + ECNode * row = ecNodeWithID("row_"+QString::number(i)); + for ( unsigned j = m_numCols; j < numCols; j++ ) + m_switch[i][j] = createSwitch( cols[j], row, true ); + } + } + else + { + // Remove columns + + for ( unsigned i = 0; i < 4; i++ ) + { + for ( unsigned j = numCols; j < m_numCols; j++ ) + removeWidget( buttonID(i,j) ); + } + + for ( unsigned j = numCols; j < m_numCols; j++ ) + removeNode( "col_" + QString::number(j) ); + + for ( unsigned i = 0; i < 4; i++ ) + { + for ( unsigned j = m_numCols; j < numCols; j++ ) + removeSwitch( m_switch[i][j] ); + } + } + + //BEGIN Update Positions + m_numCols = numCols; + + for ( int i = 0; i < 4; i++ ) + { + for ( int j = 0; j < int(m_numCols); j++ ) + { + widgetWithID( buttonID(i,j) )->setOriginalRect( + QRect( offsetX() + 6 + 24*j, offsetY() + 6 + 24*i, 20, 20 ) ); + } + } + + for ( int i = 0; i < 4; i++ ) + m_nodeMap["row_" + QString::number(i)].x = width()+offsetX(); + + for ( int j = 0; j < int(m_numCols); j++ ) + m_nodeMap["col_" + QString::number(j)].x = 24*j+offsetX()+16; + + updateAttachedPositioning(); + //END Update Positions +} + + +void ECKeyPad::buttonStateChanged( const QString &id, bool state ) +{ + if ( !id.startsWith("b_") ) + return; + + QStringList tags = QStringList::split( '_', id ); + const int i = tags[1].toInt(); + const int j = tags[2].toInt(); + m_switch[i][j]->setState( state ? Switch::Closed : Switch::Open ); +} diff --git a/src/electronics/components/eckeypad.h b/src/electronics/components/eckeypad.h new file mode 100644 index 0000000..18a497c --- /dev/null +++ b/src/electronics/components/eckeypad.h @@ -0,0 +1,42 @@ +/*************************************************************************** + * Copyright (C) 2003,2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef ECKEYPAD_H +#define ECKEYPAD_H + +#include "component.h" + +/** +@short 4x3 PTM Keypad +@author David Saxton +*/ +class ECKeyPad : public Component +{ + public: + ECKeyPad( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~ECKeyPad(); + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + + virtual void buttonStateChanged( const QString &id, bool state ); + virtual bool canFlip() const { return true; } + + protected: + virtual void dataChanged(); + void initPins( unsigned numCols); + QString buttonID( int row, int col ) const; + int sideLength( unsigned numButtons ) const; + + Switch *m_switch[4][11]; + unsigned m_numCols; +}; + +#endif diff --git a/src/electronics/components/ecled.cpp b/src/electronics/components/ecled.cpp new file mode 100644 index 0000000..c865de6 --- /dev/null +++ b/src/electronics/components/ecled.cpp @@ -0,0 +1,134 @@ +/*************************************************************************** + * Copyright (C) 2003 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "colorcombo.h" +#include "diode.h" +#include "ecled.h" +#include "ecnode.h" +#include "libraryitem.h" +#include "simulator.h" + +#include <klocale.h> +#include <qpainter.h> + +Item* ECLed::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new ECLed( (ICNDocument*)itemDocument, newItem, id ); +} + +LibraryItem* ECLed::libraryItem() +{ + return new LibraryItem( + QString::QString("ec/led"), + i18n("LED"), + i18n("Outputs"), + "led.png", + LibraryItem::lit_component, + ECLed::construct + ); +} + +ECLed::ECLed( ICNDocument *icnDocument, bool newItem, const char *id ) + : ECDiode( icnDocument, newItem, (id) ? id : "led" ) +{ + m_bDynamicContent = true; + m_name = i18n("LED"); + m_desc = i18n("Light Emitting Diode"); + setSize( -8, -16, 24, 24, true ); + avg_brightness = 255; + lastUpdatePeriod = 0.; + r=g=b=0; + last_brightness = 255; + + createProperty( "0-color", Variant::Type::Color ); + property("0-color")->setCaption( i18n("Color") ); + property("0-color")->setColorScheme( ColorCombo::LED ); +} + +ECLed::~ECLed() +{ +} + +void ECLed::dataChanged() +{ + QColor color = dataColor("0-color"); + r = color.red(); + g = color.green(); + b = color.blue(); + r /= 0x100; + g /= 0x100; + b /= 0x100; +} + +void ECLed::stepNonLogic() +{ + double interval = 1./LINEAR_UPDATE_RATE; + avg_brightness += brightness(m_diode->current())*interval; + lastUpdatePeriod += interval; +} + +void ECLed::drawShape( QPainter &p ) +{ + int _x = int(x()); + int _y = int(y()); + + initPainter(p); + + //BEGIN draw "Diode" part + uint _b; + if ( lastUpdatePeriod == 0. ) + _b = last_brightness; + else + { + _b = (uint)(avg_brightness/lastUpdatePeriod); + last_brightness = _b; + } + avg_brightness = 0.; + lastUpdatePeriod = 0.; + + p.setBrush( QColor( uint(255-(255-_b)*(1-r)), uint(255-(255-_b)*(1-g)), uint(255-(255-_b)*(1-b)) ) ); + + QPointArray pa(3); + pa[0] = QPoint( 8, 0 ); + pa[1] = QPoint( -8, -8 ); + pa[2] = QPoint( -8, 8 ); + pa.translate( _x, _y ); + p.drawPolygon(pa); + p.drawPolyline(pa); + + p.drawLine( _x+8, _y-8, _x+8, _y+8 ); + //END draw "Diode" part + + + + //BEGIN draw "Arrows" part + p.drawLine( _x+7, _y-10, _x+10, _y-13 ); // Tail of left arrow + p.drawLine( _x+10, _y-13, _x+8, _y-13 ); // Left edge of left arrow tip + p.drawLine( _x+10, _y-13, _x+10, _y-11 ); // Right edge of left arrow tip + + p.drawLine( _x+10, _y-7, _x+13, _y-10 ); // Tail of right arrow + p.drawLine( _x+13, _y-10, _x+11, _y-10 ); // Left edge of right arrow tip + p.drawLine( _x+13, _y-10, _x+13, _y-8 ); // Right edge of right arrow tip + + p.drawLine( _x+8, _y-13, _x+13, _y-8 ); // Diagonal line that forms base of both arrow tips + //END draw "Arrows" part1 + + + deinitPainter(p); +} + + +uint ECLed::brightness( double i ) +{ + if ( i > 0.018 ) return 0; + if ( i < 0.002 ) return 255; + return (uint)(255*(1-((i-0.002)/0.016))); +} + diff --git a/src/electronics/components/ecled.h b/src/electronics/components/ecled.h new file mode 100644 index 0000000..ce24598 --- /dev/null +++ b/src/electronics/components/ecled.h @@ -0,0 +1,48 @@ +/*************************************************************************** + * Copyright (C) 2003 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef ECLED_H +#define ECLED_H + +#include "component.h" +#include "ecdiode.h" + +/** +@short Simulates a LED +@author David Saxton +*/ +class ECLed : public ECDiode +{ +public: + ECLed( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~ECLed(); + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + /** + * Returns the brightness for the given current, from 255 (off) -> 0 (on) + */ + static uint brightness( double i ); + + virtual void dataChanged(); + virtual void stepNonLogic(); + virtual bool doesStepNonLogic() const { return true; } + +private: + virtual void drawShape( QPainter &p ); + + double r, g, b; + + double avg_brightness; + uint last_brightness; + double lastUpdatePeriod; +}; + +#endif diff --git a/src/electronics/components/ecopamp.cpp b/src/electronics/components/ecopamp.cpp new file mode 100644 index 0000000..dbb7457 --- /dev/null +++ b/src/electronics/components/ecopamp.cpp @@ -0,0 +1,85 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "ecopamp.h" + +#include "ecnode.h" +#include "libraryitem.h" + +#include <klocale.h> +#include <qpainter.h> + +Item* ECOpAmp::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new ECOpAmp( (ICNDocument*)itemDocument, newItem, id ); +} + + +LibraryItem* ECOpAmp::libraryItem() +{ + return new LibraryItem( + "ec/opamp", + i18n("Op Amp"), + i18n("Integrated Circuits"), + "opamp.png", + LibraryItem::lit_component, + ECOpAmp::construct ); +} + + +ECOpAmp::ECOpAmp( ICNDocument *icnDocument, bool newItem, const char *id ) + : Component( icnDocument, newItem, id ? id : "opamp" ) +{ + m_name = i18n("Operational Amplifier"); + m_desc = i18n("Ideal amplifier"); + + QPointArray pa(3); + pa[0] = QPoint( -16, -16 ); + pa[1] = QPoint( 16, 0 ); + pa[2] = QPoint( -16, 16 ); + setItemPoints( pa, true ); + + init2PinLeft( -8, 8 ); + init1PinRight(); + createOpAmp( m_pNNode[0], m_pPNode[0], m_pNNode[1] ); +} + + +ECOpAmp::~ECOpAmp() +{ +} + + +void ECOpAmp::drawShape( QPainter & p ) +{ + initPainter(p); + + int _x = int(x()); + int _y = int(y()); + + QPointArray pa(3); + pa[0] = QPoint( _x-16, _y-16 ); + pa[1] = QPoint( _x+16, _y ); + pa[2] = QPoint( _x-16, _y+16 ); + + p.drawPolygon(pa); + p.drawPolyline(pa); + + // Plus symbol + p.drawLine( _x-9, _y-8, _x-9, _y-2 ); + p.drawLine( _x-12, _y-5, _x-6, _y-5 ); + + // Minus symbol + p.drawLine( _x-11, _y+6, _x-7, _y+6 ); + + deinitPainter(p); +} + + diff --git a/src/electronics/components/ecopamp.h b/src/electronics/components/ecopamp.h new file mode 100644 index 0000000..6b6b147 --- /dev/null +++ b/src/electronics/components/ecopamp.h @@ -0,0 +1,35 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef ECOPAMP_H +#define ECOPAMP_H + +#include "component.h" + +/** +@short Operational Amplifier +@author David Saxton +*/ +class ECOpAmp : public Component +{ + public: + ECOpAmp( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~ECOpAmp(); + + virtual bool canFlip() const { return true; } + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + + protected: + virtual void drawShape( QPainter & p ); +}; + +#endif diff --git a/src/electronics/components/ecpotentiometer.cpp b/src/electronics/components/ecpotentiometer.cpp new file mode 100644 index 0000000..db0fedd --- /dev/null +++ b/src/electronics/components/ecpotentiometer.cpp @@ -0,0 +1,119 @@ +/*************************************************************************** + * Copyright (C) 2003-2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "canvasitemparts.h" +#include "ecnode.h" +#include "ecpotentiometer.h" +#include "libraryitem.h" +#include "resistance.h" + +#include <klocale.h> +#include <qpainter.h> +#include <qstyle.h> + +Item* ECPotentiometer::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new ECPotentiometer( (ICNDocument*)itemDocument, newItem, id ); +} + +LibraryItem* ECPotentiometer::libraryItem() +{ + return new LibraryItem( + "ec/potentiometer", + i18n("Potentiometer"), + i18n("Discrete"), + "potentiometer.png", + LibraryItem::lit_component, + ECPotentiometer::construct ); +} + +ECPotentiometer::ECPotentiometer( ICNDocument *icnDocument, bool newItem, const char *id ) + : Component( icnDocument, newItem, id ? id : "potentiometer" ) +{ + m_name = i18n("Potentiometer"); + m_desc =i18n("Consists of a resistor connected to the end pins, with a central pin connected at an adjustable point along the resistor"); + setSize( -16, -16, 40, 32 ); + + m_p1 = createPin( 32, 0, 180, "p1" ); + + m_sliderProp = 0.0; + m_resistance = 5000.; + m_r1 = createResistance( createPin( -8, -24, 90, "n1" ), m_p1, 1. ); + m_r2 = createResistance( createPin( -8, 24, 270, "n2" ), m_p1, 1. ); + + Slider * s = addSlider( "slider", 0, 100, 5, 50, Qt::Vertical, QRect( 0, -16, 16, 32 ) ); + m_pSlider = static_cast<QSlider*>(s->widget()); + + createProperty( "resistance", Variant::Type::Double ); + property("resistance")->setCaption( i18n("Resistance") ); + property("resistance")->setUnit( QChar(0x3a9) ); + property("resistance")->setMinValue(1e-6); + property("resistance")->setValue(1e5); + + addDisplayText( "res", QRect( -56, -8, 40, 16 ), "" ); +} + +ECPotentiometer::~ECPotentiometer() +{ +} + +void ECPotentiometer::dataChanged() +{ + m_resistance = dataDouble("resistance"); + + QString display = QString::number( m_resistance / getMultiplier(m_resistance), 'g', 3 ) + getNumberMag(m_resistance) + QChar(0x3a9); + setDisplayText( "res", display ); + + sliderValueChanged( "slider", slider("slider")->value() ); +} + +void ECPotentiometer::sliderValueChanged( const QString &id, int newValue ) +{ + if ( id != "slider" ) + return; + + m_sliderProp = (newValue-50.0)/100.0; + + m_r1->setResistance( m_resistance*(double)newValue/100. ); + m_r2->setResistance( m_resistance*(double)(100.-newValue)/100. ); +} + +void ECPotentiometer::drawShape( QPainter &p ) +{ + initPainter(p); + int _x = int(x()); + int _y = int(y()); + + p.drawRect( _x-14, _y-16, 12, 32 ); + + QPointArray pa(3); + pa[0] = QPoint( 0, 0 ); + pa[1] = QPoint( 4, -3 ); + pa[2] = QPoint( 4, 3 ); + + int space = m_pSlider->style().pixelMetric( QStyle::PM_SliderSpaceAvailable, m_pSlider ); + int base_y = _y + (( angleDegrees() == 0 || angleDegrees() == 270 ) ? 1 : -1) * int( space * m_sliderProp ); + + pa.translate( _x+16, base_y ); + + QColor c = m_p1->isSelected() ? m_selectedCol : black; + + p.setPen(c); + p.setBrush(c); + p.drawPolygon(pa); + + p.drawLine( _x+20, base_y, _x+24, base_y ); + p.drawLine( _x+24, base_y, _x+24, _y ); + + deinitPainter(p); +} + + + diff --git a/src/electronics/components/ecpotentiometer.h b/src/electronics/components/ecpotentiometer.h new file mode 100644 index 0000000..e7ca83b --- /dev/null +++ b/src/electronics/components/ecpotentiometer.h @@ -0,0 +1,43 @@ +/*************************************************************************** + * Copyright (C) 2003-2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef ECPOTENTIOMETER_H +#define ECPOTENTIOMETER_H + +#include "component.h" + +class QSlider; + +/** +@short Potentiometer +@author David Saxton +*/ +class ECPotentiometer : public Component +{ +public: + ECPotentiometer( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~ECPotentiometer(); + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + + virtual void sliderValueChanged( const QString &id, int newValue ); + +private: + void dataChanged(); + virtual void drawShape( QPainter &p ); + + ECNode * m_p1; + Resistance *m_r1, *m_r2; + double m_resistance; + double m_sliderProp; + QSlider * m_pSlider; +}; +#endif diff --git a/src/electronics/components/ecresistor.cpp b/src/electronics/components/ecresistor.cpp new file mode 100644 index 0000000..e4ee7a3 --- /dev/null +++ b/src/electronics/components/ecresistor.cpp @@ -0,0 +1,76 @@ +/*************************************************************************** + * Copyright (C) 2003 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "ecresistor.h" + +#include "libraryitem.h" +#include "resistance.h" + +#include <klocale.h> +#include <qpainter.h> + +Item* ECResistor::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new ECResistor( (ICNDocument*)itemDocument, newItem, id ); +} + +LibraryItem* ECResistor::libraryItem() +{ + return new LibraryItem( + "ec/resistor", + i18n("Resistor"), + i18n("Discrete"), + "resistor.png", + LibraryItem::lit_component, + ECResistor::construct ); +} + +ECResistor::ECResistor( ICNDocument *icnDocument, bool newItem, const char *id ) + : Component( icnDocument, newItem, id ? id : "resistor" ) +{ + m_name = i18n("Resistor"); + m_desc = i18n("Limits the flow of current, obeying Ohms Law"); + setSize( -16, -8, 32, 16 ); + + init1PinLeft(); + init1PinRight(); + m_resistance = createResistance( m_pPNode[0], m_pNNode[0], 1. ); + + createProperty( "resistance", Variant::Type::Double ); + property("resistance")->setCaption( i18n("Resistance") ); + property("resistance")->setUnit( QChar(0x3a9) ); + property("resistance")->setValue(1e4); + property("resistance")->setMinValue(1e-6); + + addDisplayText( "res", QRect( -16, -22, 32, 12 ), "", false ); +} + +ECResistor::~ECResistor() +{ +} + +void ECResistor::dataChanged() +{ + double resistance = dataDouble("resistance"); + + QString display = QString::number( resistance / getMultiplier(resistance), 'g', 3 ) + getNumberMag(resistance) + QChar(0x3a9); + setDisplayText( "res", display ); + + m_resistance->setResistance(resistance); +} + +void ECResistor::drawShape( QPainter &p ) +{ + initPainter(p); + p.drawRect( (int)x()-16, (int)y()-6, width(), 12 ); + deinitPainter(p); +} + + diff --git a/src/electronics/components/ecresistor.h b/src/electronics/components/ecresistor.h new file mode 100644 index 0000000..17e799e --- /dev/null +++ b/src/electronics/components/ecresistor.h @@ -0,0 +1,36 @@ +/*************************************************************************** + * Copyright (C) 2003 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef ECRESISTOR_H +#define ECRESISTOR_H + +#include "component.h" + +/** +@short Simple resistor +@author David Saxton +*/ +class ECResistor : public Component +{ + public: + ECResistor( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~ECResistor(); + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + + protected: + virtual void dataChanged(); + virtual void drawShape( QPainter & p ); + + Resistance * m_resistance; +}; + +#endif diff --git a/src/electronics/components/ecsevensegment.cpp b/src/electronics/components/ecsevensegment.cpp new file mode 100644 index 0000000..ec35776 --- /dev/null +++ b/src/electronics/components/ecsevensegment.cpp @@ -0,0 +1,210 @@ +/*************************************************************************** + * Copyright (C) 2003-2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "colorcombo.h" +#include "diode.h" +#include "ecled.h" +#include "ecnode.h" +#include "ecsevensegment.h" +#include "libraryitem.h" +#include "simulator.h" + +#include <klocale.h> +#include <qpainter.h> +#include <qstring.h> + +Item* ECSevenSegment::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new ECSevenSegment( (ICNDocument*)itemDocument, newItem, id ); +} + +LibraryItem* ECSevenSegment::libraryItem() +{ + return new LibraryItem( + "ec/seven_segment", + i18n("Seven Segment"), + i18n("Outputs"), + "seven_segment.png", + LibraryItem::lit_component, + ECSevenSegment::construct ); +} + +ECSevenSegment::ECSevenSegment( ICNDocument *icnDocument, bool newItem, const char *id ) + : Component( icnDocument, newItem, id ? id : "seven_segment" ) +{ + m_name = i18n("Seven Segment LED"); + m_desc = i18n("A seven segment display with a decimal point. This can be configured to either have a common cathode or a common anode."); + m_bDynamicContent = true; + + QStringList pins = QStringList::split( ',', "g,f,e,d,"+QString(QChar(0xB7))+",c,b,a" ); + + createProperty( "0-color", Variant::Type::Color ); + property("0-color")->setCaption( i18n("Color") ); + property("0-color")->setColorScheme( ColorCombo::LED ); + + createProperty( "diode-polarity", Variant::Type::Select ); + property("diode-polarity")->setCaption( i18n("Configuration") ); + property("diode-polarity")->setAllowed( QStringList::split(',',"Common Cathode,Common Anode") ); + property("diode-polarity")->setValue("Common Cathode"); + + for ( int i=0; i<8; i++ ) + { + m_diodes[i] = 0L; + m_nodes[i] = 0L; + avg_brightness[i] = 0.; + last_brightness[i] = 255; + } + m_nNode = 0L; + + lastUpdatePeriod = 0.; + + initDIPSymbol( pins, 64 ); + initDIP(pins); + + m_nNode = createPin( width()/2+offsetX(), height()+8+offsetY(), 270, "-v" ); + + for ( int i=0; i<7; i++ ) + m_nodes[i] = ecNodeWithID( QChar('a'+i) ); + + m_nodes[7] = ecNodeWithID(QChar(0xB7)); + + m_bCommonCathode = false; // Force update +} + + +ECSevenSegment::~ECSevenSegment() +{ +} + + +void ECSevenSegment::dataChanged() +{ + QColor color = dataColor("0-color"); + r = color.red(); + g = color.green(); + b = color.blue(); + r /= 0x100; + g /= 0x100; + b /= 0x100; + + bool commonCathode = dataString("diode-polarity") == "Common Cathode"; + if ( commonCathode != m_bCommonCathode ) + { + m_bCommonCathode = commonCathode; + for ( int i=0; i<7; i++ ) + { + removeElement( m_diodes[i], false ); + if (commonCathode) + m_diodes[i] = createDiode( m_nodes[i], m_nNode ); + else + m_diodes[i] = createDiode( m_nNode, m_nodes[i] ); + } + + removeElement( m_diodes[7], false ); + if (commonCathode) + m_diodes[7] = createDiode( m_nodes[7], m_nNode ); + else + m_diodes[7] = createDiode( m_nNode, m_nodes[7] ); + } + + update(); +} + + +void ECSevenSegment::stepNonLogic() +{ + double interval = 1./LINEAR_UPDATE_RATE; + + for ( int i=0; i<8; i++ ) { + avg_brightness[i] += ECLed::brightness( m_diodes[i]->current() )*interval; + } + + lastUpdatePeriod += interval; +} + +void ECSevenSegment::drawShape( QPainter &p ) +{ + CNItem::drawShape(p); + + initPainter(p); + + const int _width = 20; + const int _height = 32; + + const int x1 = (int)x()+offsetX() + (width()-_width)/2 - 1; + const int x2 = x1 + _width; + const int y1 = (int)y()+offsetY() + (height()-_height)/2; + const int y2 = y1 + _height/2; + const int y3 = y1 + _height; + const int ds = 2; // "Slope" + +// QPen pen; +// pen.setWidth(2); +// pen.setCapStyle(Qt::RoundCap); +// p.setPen(pen); + + if ( lastUpdatePeriod != 0. ) + { + for ( uint i=0; i<8; ++i ) + { + last_brightness[i] = (uint)(avg_brightness[i]/lastUpdatePeriod); + } + } + + double _b; + + // Top + _b = last_brightness[0]; + p.setPen( QPen( QColor( uint(255-(255-_b)*(1-r)), uint(255-(255-_b)*(1-g)), uint(255-(255-_b)*(1-b)) ), 2 ) ); + p.drawLine( x1+3+ds, y1+0, x2-3+ds, y1+0 ); + + // Top right + _b = last_brightness[1]; + p.setPen( QPen( QColor( uint(255-(255-_b)*(1-r)), uint(255-(255-_b)*(1-g)), uint(255-(255-_b)*(1-b)) ), 2 ) ); + p.drawLine( x2+0+ds, y1+3, x2+0, y2-3 ); + + // Bottom right + _b = last_brightness[2]; + p.setPen( QPen( QColor( uint(255-(255-_b)*(1-r)), uint(255-(255-_b)*(1-g)), uint(255-(255-_b)*(1-b)) ), 2 ) ); + p.drawLine( x2+0, y2+3, x2+0-ds, y3-3 ); + + // Bottom + _b = last_brightness[3]; + p.setPen( QPen( QColor( uint(255-(255-_b)*(1-r)), uint(255-(255-_b)*(1-g)), uint(255-(255-_b)*(1-b)) ), 2 ) ); + p.drawLine( x2-3-ds, y3+0, x1+3-ds, y3+0 ); + + // Bottom left + _b = last_brightness[4]; + p.setPen( QPen( QColor( uint(255-(255-_b)*(1-r)), uint(255-(255-_b)*(1-g)), uint(255-(255-_b)*(1-b)) ), 2 ) ); + p.drawLine( x1+0-ds, y3-3, x1+0, y2+3 ); + + // Top left + _b = last_brightness[5]; + p.setPen( QPen( QColor( uint(255-(255-_b)*(1-r)), uint(255-(255-_b)*(1-g)), uint(255-(255-_b)*(1-b)) ), 2 ) ); + p.drawLine( x1+0, y2-3, x1+0+ds, y1+3 ); + + // Middle + _b = last_brightness[6]; + p.setPen( QPen( QColor( uint(255-(255-_b)*(1-r)), uint(255-(255-_b)*(1-g)), uint(255-(255-_b)*(1-b)) ), 2 ) ); + p.drawLine( x1+3, y2+0, x2-3, y2+0 ); + + // Decimal point + _b = last_brightness[7]; + p.setBrush( QBrush( QColor( uint(255-(255-_b)*(1-r)), uint(255-(255-_b)*(1-g)), uint(255-(255-_b)*(1-b)) ) ) ); + p.setPen( Qt::NoPen ); + p.drawPie( x2+3, y3-2, 3, 3, 0, 16*360 ); + + lastUpdatePeriod = 0.; + for ( uint i=0; i<8; ++i ) { + avg_brightness[i] = 0.; + } + + deinitPainter(p); +} diff --git a/src/electronics/components/ecsevensegment.h b/src/electronics/components/ecsevensegment.h new file mode 100644 index 0000000..7041c9a --- /dev/null +++ b/src/electronics/components/ecsevensegment.h @@ -0,0 +1,50 @@ +/*************************************************************************** + * Copyright (C) 2003-2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef ECSEVENSEGMENT_H +#define ECSEVENSEGMENT_H + +#include "component.h" + +class Diode; +class ECNode; + +/** +@short Seven segment display component +@author David Saxton +*/ +class ECSevenSegment : public Component +{ +public: + ECSevenSegment( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~ECSevenSegment(); + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + + virtual void stepNonLogic(); + virtual bool doesStepNonLogic() const { return true; } + virtual void dataChanged(); + virtual bool canFlip() const { return true; } + +private: + virtual void drawShape( QPainter &p ); + + bool m_bCommonCathode; + double lastUpdatePeriod; + double avg_brightness[8]; + uint last_brightness[8]; + Diode *m_diodes[8]; + ECNode *m_nodes[8]; + ECNode *m_nNode; + double r, g, b; +}; + +#endif diff --git a/src/electronics/components/ecsignallamp.cpp b/src/electronics/components/ecsignallamp.cpp new file mode 100644 index 0000000..c7034f7 --- /dev/null +++ b/src/electronics/components/ecsignallamp.cpp @@ -0,0 +1,86 @@ +/*************************************************************************** + * Copyright (C) 2003-2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + + +#include "ecnode.h" +#include "ecsignallamp.h" +#include "element.h" +#include "libraryitem.h" +#include "pin.h" + +#include <klocale.h> +#include <qpainter.h> + +Item* ECSignalLamp::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new ECSignalLamp( (ICNDocument*)itemDocument, newItem, id ); +} + +LibraryItem* ECSignalLamp::libraryItem() +{ + return new LibraryItem( + QString("ec/signal_lamp"), + i18n("Signal Lamp"), + i18n("Outputs"), + "signal_lamp.png", + LibraryItem::lit_component, + ECSignalLamp::construct ); +} + +ECSignalLamp::ECSignalLamp( ICNDocument *icnDocument, bool newItem, const char *id ) + : Component( icnDocument, newItem, (id) ? id : "signal_lamp" ) +{ + m_name = i18n("Signal Lamp"); + m_desc = i18n("A simple filament signal lamp, with a 100 ohms series resistance."); + setSize( -8, -8, 16, 16 ); + + init1PinLeft(); + init1PinRight(); + + createResistance( m_pPNode[0], m_pNNode[0], 100. ); + + advanceSinceUpdate = 0; + avgPower = 0.; + m_bDynamicContent = true; +} + +ECSignalLamp::~ECSignalLamp() +{ +} + +void ECSignalLamp::stepNonLogic() +{ + const double voltage = m_pPNode[0]->pin()->voltage()-m_pNNode[0]->pin()->voltage(); + avgPower = QABS(avgPower*advanceSinceUpdate + (voltage*voltage/100))/++advanceSinceUpdate; +} + +void ECSignalLamp::drawShape( QPainter &p ) +{ + initPainter(p); + + int _x = int(x()); + int _y = int(y()); + + // Calculate the brightness as a linear function of power, bounded below by + // 25 milliWatts and above by 500 milliWatts. + int brightness = (avgPower<0.025) ? 255 : ((avgPower>0.5) ? 0 : (int)(255*(1-((avgPower-0.025)/0.475)))); + advanceSinceUpdate = 0; + + p.setBrush( QColor( 255, 255, brightness ) ); + p.drawEllipse( _x-8, _y-8, 16, 16 ); + + // 2*sqrt(2) = 2.828427125... + int pos = 8 - int(16/2.828); + + p.drawLine( _x-8+pos, _y-8+pos, _x+8-pos, _y+8-pos ); + p.drawLine( _x+8-pos, _y-8+pos, _x-8+pos, _y+8-pos ); + + deinitPainter(p); +} diff --git a/src/electronics/components/ecsignallamp.h b/src/electronics/components/ecsignallamp.h new file mode 100644 index 0000000..e56d724 --- /dev/null +++ b/src/electronics/components/ecsignallamp.h @@ -0,0 +1,40 @@ +/*************************************************************************** + * Copyright (C) 2003-2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef ECSIGNALLAMP_H +#define ECSIGNALLAMP_H + +#include "component.h" + +class Resistance; + +/** +@short Signal Lamp - glows when current flows +@author David Saxton +*/ +class ECSignalLamp : public Component +{ +public: + ECSignalLamp( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~ECSignalLamp(); + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + + virtual void stepNonLogic(); + virtual bool doesStepNonLogic() const { return true; } + +private: + void drawShape( QPainter &p ); + double avgPower; + uint advanceSinceUpdate; +}; + +#endif diff --git a/src/electronics/components/ecsubcircuit.cpp b/src/electronics/components/ecsubcircuit.cpp new file mode 100644 index 0000000..a69e720 --- /dev/null +++ b/src/electronics/components/ecsubcircuit.cpp @@ -0,0 +1,130 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "canvasitemparts.h" +#include "circuitdocument.h" +#include "ecsubcircuit.h" +#include "node.h" +#include "libraryitem.h" +#include "subcircuits.h" + +#include <kdebug.h> +#include <klocale.h> +#include <qfile.h> + +Item* ECSubcircuit::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new ECSubcircuit( (ICNDocument*)itemDocument, newItem, id ); +} + +LibraryItem* ECSubcircuit::libraryItem() +{ + return new LibraryItem( + QString::QString("ec/subcircuit"), + QString::null, + QString::null, + QString::null, + LibraryItem::lit_subcircuit, + ECSubcircuit::construct ); +} + +ECSubcircuit::ECSubcircuit( ICNDocument *icnDocument, bool newItem, const char *id ) + : Component( icnDocument, newItem, (id) ? id : "subcircuit" ) +{ + m_name = i18n("Subcircuit"); + + createProperty( "id", Variant::Type::Int ); + property("id")->setMinValue(1); + property("id")->setMaxValue(1<<15); + property("id")->setValue(1); + property("id")->setHidden(true); +} + + +ECSubcircuit::~ECSubcircuit() +{ +} + + +void ECSubcircuit::removeItem() +{ + emit subcircuitDeleted(); + Component::removeItem(); +} + + +void ECSubcircuit::setNumExtCon( unsigned numExtCon ) +{ + m_conNames.resize(numExtCon); + + // Remove old pins + const NodeMap::iterator nodeMapEnd = m_nodeMap.end(); + for ( NodeMap::iterator it = m_nodeMap.begin(); it != nodeMapEnd; ++it ) + { + p_icnDocument->appendDeleteList( p_icnDocument->nodeWithID(it.data().id) ); + } + p_icnDocument->flushDeleteList(); + m_nodeMap.clear(); + + QStringList pins; + for ( unsigned i=0; i<numExtCon; ++i ) + { + pins += QString::number(i); + } + + initDIPSymbol( pins, 80 ); + + // We don't want the text that the dip symbol gave us as we initialize it later... + for ( unsigned i = 0; i < numExtCon; ++i ) + removeDisplayText( QString::number(i) ); + + initDIP(pins); +} + + +void ECSubcircuit::dataChanged() +{ + int subcircuitId = dataInt("id"); + if ( subcircuitId == -1 ) { + return; + } + emit subcircuitDeleted(); + Subcircuits::initECSubcircuit( subcircuitId, this ); +} + + +void ECSubcircuit::setExtConName( unsigned numId, const QString & name ) +{ + if ( numId > m_conNames.size() ) + return; + + m_conNames[numId] = name; +} + + +void ECSubcircuit::doneSCInit() +{ + QStringList pins; + for ( unsigned i = 0; i < m_conNames.size(); ++i ) + pins << m_conNames[i]; + initDIPSymbol( pins, 80 ); +} + + +void ECSubcircuit::drawShape( QPainter &p ) +{ + Component::drawShape(p); +} + + +#include "ecsubcircuit.moc" + + + diff --git a/src/electronics/components/ecsubcircuit.h b/src/electronics/components/ecsubcircuit.h new file mode 100644 index 0000000..eaf21ec --- /dev/null +++ b/src/electronics/components/ecsubcircuit.h @@ -0,0 +1,61 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef ECSUBCIRCUIT_H +#define ECSUBCIRCUIT_H + +#include <component.h> + +#include <qvaluevector.h> + +/** +"Container" component for subcircuits +@author David Saxton +*/ +class ECSubcircuit : public Component +{ +Q_OBJECT +public: + ECSubcircuit( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~ECSubcircuit(); + virtual bool canFlip() const { return true; } + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + + /** + * Create numExtCon nodes, deleting any old ones + */ + void setNumExtCon( unsigned numExtCon ); + /** + * Give the connecting node at position numId the given name + */ + void setExtConName( unsigned numId, const QString & name ); + /** + * Called from SubcircuitData once the subcircuit has been fully attached + */ + void doneSCInit(); + +public slots: + virtual void removeItem(); + +signals: + /** + * Emitted when the current subcircuit is deleted + */ + void subcircuitDeleted(); + +protected: + virtual void dataChanged(); + virtual void drawShape( QPainter &p ); + QValueVector<QString> m_conNames; +}; + +#endif diff --git a/src/electronics/components/ecvoltagesignal.cpp b/src/electronics/components/ecvoltagesignal.cpp new file mode 100644 index 0000000..c338f36 --- /dev/null +++ b/src/electronics/components/ecvoltagesignal.cpp @@ -0,0 +1,95 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "ecnode.h" +#include "ecvoltagesignal.h" +#include "libraryitem.h" +#include "pin.h" +#include "simulator.h" +#include "voltagesignal.h" + +#include <klocale.h> +#include <qpainter.h> + +const double conductance = 1e5; // Internal resistance + +Item* ECVoltageSignal::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new ECVoltageSignal( (ICNDocument*)itemDocument, newItem, id ); +} + +LibraryItem* ECVoltageSignal::libraryItem() +{ + return new LibraryItem( + QString("ec/voltage_signal"), + i18n("Voltage Signal"), + i18n("Sources"), + "voltagesignal.png", + LibraryItem::lit_component, + ECVoltageSignal::construct ); +} + +ECVoltageSignal::ECVoltageSignal( ICNDocument *icnDocument, bool newItem, const char *id ) + : Component( icnDocument, newItem, (id) ? id : "voltage_signal" ) +{ + m_name = i18n("Voltage Signal"); + m_desc = i18n("Provides a variety of voltage signals."); + setSize( -8, -8, 16, 16 ); + + init1PinLeft(); + init1PinRight(); + + m_pNNode[0]->pin()->setGroundType( Pin::gt_medium ); + m_voltageSignal = createVoltageSignal( m_pNNode[0], m_pPNode[0], 0. ); + m_voltageSignal->setStep( 1./LINEAR_UPDATE_RATE, ElementSignal::st_sinusoidal, 50. ); + + createProperty( "frequency", Variant::Type::Double ); + property("frequency")->setCaption( i18n("Frequency") ); + property("frequency")->setUnit("Hz"); + property("frequency")->setMinValue(1e-9); + property("frequency")->setMaxValue(1e3); + property("frequency")->setValue(50.0); + + createProperty( "voltage", Variant::Type::Double ); + property("voltage")->setCaption( i18n("Voltage Range") ); + property("voltage")->setUnit("V"); + property("voltage")->setMinValue(-1e12); + property("voltage")->setMaxValue(1e12); + property("voltage")->setValue(5.0); + + addDisplayText( "~", QRect( -8, -8, 16, 16 ), "~" ); + addDisplayText( "voltage", QRect( -16, -24, 32, 16 ), "" ); +} + + +ECVoltageSignal::~ECVoltageSignal() +{ +} + +void ECVoltageSignal::dataChanged() +{ + const double voltage = dataDouble("voltage"); + const double frequency = dataDouble("frequency"); + + QString display = QString::number( voltage / getMultiplier(voltage), 'g', 3 ) + getNumberMag(voltage) + "V"; + setDisplayText( "voltage", display ); + + m_voltageSignal->setStep( 1./LINEAR_UPDATE_RATE, ElementSignal::st_sinusoidal, frequency ); + m_voltageSignal->setVoltage(voltage); +} + + +void ECVoltageSignal::drawShape( QPainter &p ) +{ + initPainter(p); + p.drawEllipse( (int)x()-8, (int)y()-8, width(), height() ); + deinitPainter(p); +} + diff --git a/src/electronics/components/ecvoltagesignal.h b/src/electronics/components/ecvoltagesignal.h new file mode 100644 index 0000000..7102132 --- /dev/null +++ b/src/electronics/components/ecvoltagesignal.h @@ -0,0 +1,36 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef ECVOLTAGESIGNAL_H +#define ECVOLTAGESIGNAL_H + +#include "component.h" + +/** +@short Provides an alternating voltage source +@author David Saxton +*/ +class ECVoltageSignal : public Component +{ +public: + ECVoltageSignal( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~ECVoltageSignal(); + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + +protected: + virtual void drawShape( QPainter &p ); + void dataChanged(); + + VoltageSignal *m_voltageSignal; +}; + +#endif diff --git a/src/electronics/components/ecvoltagesource.cpp b/src/electronics/components/ecvoltagesource.cpp new file mode 100644 index 0000000..4b8c543 --- /dev/null +++ b/src/electronics/components/ecvoltagesource.cpp @@ -0,0 +1,93 @@ +/*************************************************************************** + * Copyright (C) 2003 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "ecvoltagesource.h" + +#include "ecnode.h" +#include "voltagesource.h" +#include "libraryitem.h" +#include "pin.h" + +#include <klocale.h> +#include <qpainter.h> + +const double conductance = 1e5; // Internal resistance + +Item* ECCell::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new ECCell( (ICNDocument*)itemDocument, newItem, id ); +} + +LibraryItem* ECCell::libraryItem() +{ + QStringList ids; + ids << "ec/battery" << "ec/cell"; + return new LibraryItem( + ids, + i18n("Battery"), + i18n("Sources"), + "cell.png", + LibraryItem::lit_component, + ECCell::construct ); +} + +ECCell::ECCell( ICNDocument *icnDocument, bool newItem, const char *id ) + : Component( icnDocument, newItem, (id) ? id : "cell" ) +{ + m_name = i18n("Battery"); + m_desc = i18n("Provides a potential-difference."); + setSize( -8, -8, 16, 16 ); + voltage = 0; + + init1PinLeft(); + init1PinRight(); + + m_pNNode[0]->pin()->setGroundType( Pin::gt_medium ); + m_voltageSource = createVoltageSource( m_pNNode[0], m_pPNode[0], voltage ); + + createProperty( "voltage", Variant::Type::Double ); + property("voltage")->setUnit("V"); + property("voltage")->setCaption( i18n("Voltage") ); + property("voltage")->setMinValue(-1e12); + property("voltage")->setMaxValue(1e12); + property("voltage")->setValue(5.0); + + addDisplayText( "voltage", QRect( -16, -24, 32, 16 ), "" ); +} + +ECCell::~ECCell() +{ +} + +void ECCell::dataChanged() +{ + voltage = dataDouble("voltage"); + m_voltageSource->setVoltage(voltage); + + QString display = QString::number( voltage / getMultiplier(voltage), 'g', 3 ) + getNumberMag(voltage) + "V"; + setDisplayText( "voltage", display ); +} + +void ECCell::drawShape( QPainter &p ) +{ + initPainter(p); + + int _x = (int)x()-8; + int _y = (int)y()-24; + + p.drawLine( _x, _y+20, _x, _y+28 ); + p.drawLine( _x+5, _y+16, _x+5, _y+32 ); + p.drawLine( _x+10, _y+20, _x+10, _y+28 ); + p.drawLine( _x+15, _y+16, _x+15, _y+32 ); + + deinitPainter(p); +// p.drawPolyline( areaPoints() ); +} + diff --git a/src/electronics/components/ecvoltagesource.h b/src/electronics/components/ecvoltagesource.h new file mode 100644 index 0000000..4ba87ef --- /dev/null +++ b/src/electronics/components/ecvoltagesource.h @@ -0,0 +1,37 @@ +/*************************************************************************** + * Copyright (C) 2003 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef ECCELL_H +#define ECCELL_H + +#include "component.h" + +/** +@short Electrical cell +Simple electrical cell that simulates a PD and internal resistance +@author David Saxton +*/ +class ECCell : public Component +{ +public: + ECCell( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~ECCell(); + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + +private: + void dataChanged(); + virtual void drawShape( QPainter &p ); + VoltageSource *m_voltageSource; + double voltage; +}; + +#endif diff --git a/src/electronics/components/externalconnection.cpp b/src/electronics/components/externalconnection.cpp new file mode 100644 index 0000000..596727a --- /dev/null +++ b/src/electronics/components/externalconnection.cpp @@ -0,0 +1,78 @@ +/*************************************************************************** + * Copyright (C) 2004 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "externalconnection.h" +#include "libraryitem.h" + +#include <klocale.h> +#include <qpainter.h> + +Item* ExternalConnection::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new ExternalConnection( (ICNDocument*)itemDocument, newItem, id ); +} + +LibraryItem* ExternalConnection::libraryItem() +{ + return new LibraryItem( + "ec/external_connection", + i18n("External Connection"), + i18n("Connections"), + "external_connection.png", + LibraryItem::lit_component, + ExternalConnection::construct ); +} + +ExternalConnection::ExternalConnection( ICNDocument *icnDocument, bool newItem, const char *id ) + : Component( icnDocument, newItem, id ? id : "external_connection" ) +{ + m_name = i18n("External Connection"); + m_desc = i18n("Point to connect the circuit to an external entity - e.g. a mechanical component or as part of a subcircuit."); + setSize( -8, -8, 16, 16 ); + + createProperty( "name", Variant::Type::Combo ); + property("name")->setCaption( i18n("Name") ); + property("name")->setValue("ExtCon"); + + init1PinLeft(); + + addDisplayText( "name", QRect( -24, 8, 3*width(), 16 ), "ExtCon" ); +} + +ExternalConnection::~ExternalConnection() +{ +} + + +void ExternalConnection::dataChanged() +{ + QString name = dataString("name"); + + QRect r( -width(), 16, 3*width(), 16 ); + setDisplayText( "name", name ); +} + + +void ExternalConnection::drawShape( QPainter &p ) +{ + initPainter(p); + int _x = (int)x()-8; + int _y = (int)y()-8; + p.drawEllipse( _x, _y, width(), height() ); + + p.drawLine( _x+3, _y+6, _x+12, _y+6 ); + p.drawLine( _x+8, _y+3, _x+12, _y+6 ); + + p.drawLine( _x+3, _y+9, _x+12, _y+9 ); + p.drawLine( _x+3, _y+9, _x+8, _y+12 ); + + deinitPainter(p); +} + diff --git a/src/electronics/components/externalconnection.h b/src/electronics/components/externalconnection.h new file mode 100644 index 0000000..9b733fc --- /dev/null +++ b/src/electronics/components/externalconnection.h @@ -0,0 +1,35 @@ +/*************************************************************************** + * Copyright (C) 2004 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef EXTERNALCONNECTION_H +#define EXTERNALCONNECTION_H + +#include <component.h> + +/** +For connecting to something "outside" - e.g. a mechanical component, or as part +of a circuit part +@author David Saxton +*/ +class ExternalConnection : public Component +{ +public: + ExternalConnection( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~ExternalConnection(); + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + +private: + void dataChanged(); + virtual void drawShape( QPainter &p ); +}; + +#endif diff --git a/src/electronics/components/flipflop.cpp b/src/electronics/components/flipflop.cpp new file mode 100644 index 0000000..5c55baf --- /dev/null +++ b/src/electronics/components/flipflop.cpp @@ -0,0 +1,347 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "flipflop.h" +#include "icndocument.h" +#include "logic.h" +#include "libraryitem.h" +#include "node.h" +#include "simulator.h" + +#include <kiconloader.h> +#include <klocale.h> +#include <qpainter.h> + + +//BEGIN class ECDFlipFlop +Item* ECDFlipFlop::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new ECDFlipFlop( (ICNDocument*)itemDocument, newItem, id ); +} + +LibraryItem* ECDFlipFlop::libraryItem() +{ + return new LibraryItem( + "ec/d_flipflop", + i18n("D Flip-Flop"), + i18n("Integrated Circuits"), + "ic3.png", + LibraryItem::lit_component, + ECDFlipFlop::construct ); +} + +ECDFlipFlop::ECDFlipFlop( ICNDocument *icnDocument, bool newItem, const char *id ) + : Component( icnDocument, newItem, (id) ? id : "d_flipflop" ) +{ + m_name = i18n("D-Type Flip-Flop"); + m_desc = i18n("The output state is set from the input state when the clock is pulsed."); + + setSize( -32, -24, 64, 48 ); + + init2PinLeft( -8, 8 ); + init2PinRight( -8, 8 ); + + m_prevD[0] = m_prevD[1] = false; + m_whichPrevD = 0; + m_prevDSimTime = 0; + m_pSimulator = Simulator::self(); + + m_bPrevClock = false; + m_pD = createLogicIn( m_pNNode[0] ); + m_pClock = createLogicIn( m_pNNode[1] ); + m_pQ = createLogicOut( m_pPNode[0], false ); + m_pQBar = createLogicOut( m_pPNode[1], false ); + + setp = createLogicIn( createPin( 0, -32, 90, "set" ) ); + rstp = createLogicIn( createPin( 0, 32, 270, "rst" ) ); + + addDisplayText( "D", QRect( -32, -16, 20, 16 ), "D" ); + addDisplayText( ">", QRect( -32, 0, 20, 16 ), ">" ); + addDisplayText( "Q", QRect( 12, -16, 20, 16 ), "Q" ); + addDisplayText( "Q'", QRect( 12, 0, 20, 16 ), "Q'" ); + addDisplayText( "Set", QRect( -16, -20, 32, 16 ), "Set" ); + addDisplayText( "Rst", QRect( -16, 4, 32, 16 ), "Rst" ); + + m_pD->setCallback( this, (CallbackPtr)(&ECDFlipFlop::inputChanged) ); + m_pClock->setCallback( this, (CallbackPtr)(&ECDFlipFlop::clockChanged) ); + setp->setCallback( this, (CallbackPtr)(&ECDFlipFlop::asyncChanged) ); + rstp->setCallback( this, (CallbackPtr)(&ECDFlipFlop::asyncChanged) ); + + inStateChanged(false); +} + +ECDFlipFlop::~ECDFlipFlop() +{ +} + +void ECDFlipFlop::asyncChanged(bool) +{ + bool set = setp->isHigh(); + bool rst = rstp->isHigh(); + if(set || rst) + { + m_pQ->setHigh(set); + m_pQBar->setHigh(rst); + } +} + +void ECDFlipFlop::inputChanged( bool newState ) +{ + unsigned long long simTime = m_pSimulator->time(); + if ( (simTime == m_prevDSimTime) && (newState == m_prevD[m_whichPrevD]) ) + return; + + m_prevDSimTime = simTime; + m_whichPrevD = 1-m_whichPrevD; + m_prevD[m_whichPrevD] = newState; +} + +void ECDFlipFlop::clockChanged( bool newState ) +{ + bool set = setp->isHigh(); + bool rst = rstp->isHigh(); + + bool fallingEdge = m_bPrevClock && !newState; + m_bPrevClock = newState; + + if( set || rst ) return; + + if (fallingEdge) + { + unsigned long long simTime = m_pSimulator->time(); + bool d = ( simTime == m_prevDSimTime ) ? m_prevD[1-m_whichPrevD] : m_prevD[m_whichPrevD]; + + m_pQ->setHigh(d); + m_pQBar->setHigh(!d); + } +} + +void ECDFlipFlop::inStateChanged(bool) +{ + // Only called when the flipflop is created. + m_pQ->setHigh(false); + m_pQBar->setHigh(true); +} +//END class ECDFlipFlop + + +//BEGIN class ECJKFlipFlop +Item* ECJKFlipFlop::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new ECJKFlipFlop( (ICNDocument*)itemDocument, newItem, id ); +} + +LibraryItem* ECJKFlipFlop::libraryItem() +{ + return new LibraryItem( + QString::QString("ec/jk_flipflop"), + i18n("JK Flip-Flop"), + i18n("Integrated Circuits"), + "ic3.png", + LibraryItem::lit_component, + ECJKFlipFlop::construct ); +} + +ECJKFlipFlop::ECJKFlipFlop( ICNDocument *icnDocument, bool newItem, const char *id ) + : Component( icnDocument, newItem, (id) ? id : "jk_flipflop" ) +{ + m_name = i18n("JK-Type Flip-Flop"); + m_desc = i18n("The output state is set according to J and K when the clock is pulsed."); + + setSize( -32, -32, 64, 64 ); + + init3PinLeft( -16, 0, 16 ); + init2PinRight( -16, 16 ); + + m_pJ = createLogicIn( m_pNNode[0] ); + m_pClock = createLogicIn( m_pNNode[1] ); + m_pK = createLogicIn( m_pNNode[2] ); + + m_pQ = createLogicOut( m_pPNode[0], false ); + m_pQBar = createLogicOut( m_pPNode[1], false ); + + setp = createLogicIn( createPin( 0, -40, 90, "set" ) ); + rstp = createLogicIn( createPin( 0, 40, 270, "rst" ) ); + + addDisplayText( "J", QRect( -32, -24, 20, 16 ), "J" ); + addDisplayText( ">", QRect( -32, -8, 20, 16 ), ">" ); + addDisplayText( "K", QRect( -32, 8, 20, 16 ), "K" ); + addDisplayText( "Q", QRect( 12, -24, 20, 16 ), "Q" ); + addDisplayText( "Q'", QRect( 12, 8, 20, 16 ), "Q'" ); + addDisplayText( "Set", QRect( -16, -28, 32, 16 ), "Set" ); + addDisplayText( "Rst", QRect( -16, 12, 32, 16 ), "Rst" ); + + m_pClock->setCallback( this, (CallbackPtr)(&ECJKFlipFlop::clockChanged) ); + setp->setCallback( this, (CallbackPtr)(&ECJKFlipFlop::asyncChanged) ); + rstp->setCallback( this, (CallbackPtr)(&ECJKFlipFlop::asyncChanged) ); + + inStateChanged(false); +} + +ECJKFlipFlop::~ECJKFlipFlop() +{ +} + +void ECJKFlipFlop::clockChanged(bool newvalue) +{ + bool j = m_pJ->isHigh(); + bool k = m_pK->isHigh(); + bool set = setp->isHigh(); + bool rst = rstp->isHigh(); + + if( set || rst ) return; + +// a JK flip-flop change state when clock do 1->0 + if (!newvalue && (j || k)) { + if ( j && k ) { + m_pQ->setHigh(!prev_state); + m_pQBar->setHigh(prev_state); + prev_state = !prev_state; + } else { + // (J=1 && K=0) || (J=0 && K=1) + m_pQ->setHigh(j); + m_pQBar->setHigh(k); + prev_state = j; + } + } +} + +void ECJKFlipFlop::asyncChanged(bool) +{ + bool set = setp->isHigh(); + bool rst = rstp->isHigh(); + + if (set || rst) { + m_pQ->setHigh(set); + m_pQBar->setHigh(rst); + prev_state = set; + } +} + +void ECJKFlipFlop::inStateChanged(bool) +{ + m_pQBar->setHigh(true); + m_pQ->setHigh(false); + prev_state = false; +} +//END class ECJKFlipFlop + + +//BEGIN class ECSRFlipFlop +Item* ECSRFlipFlop::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new ECSRFlipFlop( (ICNDocument*)itemDocument, newItem, id ); +} + +LibraryItem* ECSRFlipFlop::libraryItem() +{ + return new LibraryItem( + QString::QString("ec/sr_flipflop"), + i18n("SR Flip-Flop"), + i18n("Integrated Circuits"), + "ic3.png", + LibraryItem::lit_component, + ECSRFlipFlop::construct ); +} + +ECSRFlipFlop::ECSRFlipFlop( ICNDocument *icnDocument, bool newItem, const char *id ) + : Component( icnDocument, newItem, (id) ? id : "sr_flipflop" ) +{ + m_name = i18n("SR Flip-Flop"); + m_desc = i18n("The output is made high by holding <i>set</i> high, and low by holding <i>reset</i> high."); + + setSize( -24, -24, 48, 48 ); + + init2PinLeft( -8, 8 ); + init2PinRight( -8, 8 ); + + m_pS = createLogicIn( m_pNNode[0] ); + m_pR = createLogicIn( m_pNNode[1] ); + m_pQ = createLogicOut( m_pPNode[0], true ); + m_pQBar = createLogicOut( m_pPNode[1], false ); + + old_q1 = true; + old_q2 = false; + m_pQ->setHigh(old_q1); + m_pQBar->setHigh(old_q2); + + addDisplayText( "S", QRect( -24, -16, 20, 16 ), "S" ); + addDisplayText( "R", QRect( -24, 0, 20, 16 ), "R" ); + addDisplayText( "Q", QRect( 4, -16, 20, 16 ), "Q" ); + addDisplayText( "Q'", QRect( 4, 0, 20, 16 ), "Q'" ); + + m_pS->setCallback( this, (CallbackPtr)(&ECSRFlipFlop::inStateChanged) ); + m_pR->setCallback( this, (CallbackPtr)(&ECSRFlipFlop::inStateChanged) ); + m_pQ->setCallback( this, (CallbackPtr)(&ECSRFlipFlop::inStateChanged) ); + m_pQBar->setCallback( this, (CallbackPtr)(&ECSRFlipFlop::inStateChanged) ); +} + +ECSRFlipFlop::~ECSRFlipFlop() +{ +} + +void ECSRFlipFlop::inStateChanged(bool) +{ + // Q = v_q1, Q-bar = v_q2 + bool new_q1 = false; + bool new_q2 = false; + + bool s = m_pS->isHigh(); + bool r = m_pR->isHigh(); + bool q1 = m_pQ->isHigh(); + bool q2 = m_pQBar->isHigh(); + + // Easy ones to do :-) + if (!q1) new_q2 = true; + if (!q2) new_q1 = true; + + if ( q1 && q2 ) + { + if ( s && !r ) + { + new_q1 = true; + new_q2 = false; + } + else if ( !s && r ) + { + new_q1 = false; + new_q2 = true; + } + else if ( s && r ) + { + new_q1 = old_q1; + new_q2 = old_q2; + } + else if ( !s && !r ) + { + new_q1 = false; + new_q2 = false; + } + } + else if ( q1 && !q2 ) + { + // Note: We only need to set the value of v_q2 + if ( r && !s ) new_q2 = true; + else new_q2 = false; + } + else if ( !q1 && q2 ) + { + // Note: We only need to set the value of v_q1 + if ( s && !r ) new_q1 = true; + else new_q1 = false; + } + + old_q1 = new_q1; + old_q2 = new_q2; + + m_pQ->setHigh(new_q1); + m_pQBar->setHigh(new_q2); +} +//END class ECSRFlipFlop diff --git a/src/electronics/components/flipflop.h b/src/electronics/components/flipflop.h new file mode 100644 index 0000000..7b9d5b7 --- /dev/null +++ b/src/electronics/components/flipflop.h @@ -0,0 +1,107 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef FLIPFLOP_H +#define FLIPFLOP_H + +#include "component.h" +#include "logic.h" + +class Simulator; + +/** +@short Boolean D-Type Flip-Flop +@author David Saxton +*/ +class ECDFlipFlop : public CallbackClass, public Component +{ +public: + ECDFlipFlop( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~ECDFlipFlop(); + virtual bool canFlip() const { return true; } + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + +private: + void inputChanged( bool newState ); + void inStateChanged( bool newState ); + void asyncChanged(bool newState ); + void clockChanged(bool newState ); + + LogicIn *m_pD; + LogicIn *m_pClock; + LogicOut *m_pQ; + LogicOut *m_pQBar; + LogicIn *setp; + LogicIn *rstp; + bool m_bPrevClock; + + bool m_prevD[2]; + unsigned m_whichPrevD:1; + unsigned long long m_prevDSimTime; + Simulator * m_pSimulator; +}; + + +/** +@short Boolean JK-Type Flip-Flop +@author Couriousous +*/ +class ECJKFlipFlop : public CallbackClass, public Component +{ +public: + ECJKFlipFlop( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~ECJKFlipFlop(); + virtual bool canFlip() const { return true; } + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + +private: + void inStateChanged( bool newState ); + void asyncChanged(bool newState ); + void clockChanged(bool newState ); + bool prev_state; + LogicIn *m_pJ; + LogicIn *m_pClock; + LogicIn *m_pK; + LogicIn *setp; + LogicIn *rstp; + LogicOut *m_pQ; + LogicOut *m_pQBar; +}; + + +/** +@short Boolean Set-Reset Flip-Flop +@author David Saxton +*/ +class ECSRFlipFlop : public CallbackClass, public Component +{ +public: + ECSRFlipFlop( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~ECSRFlipFlop(); + virtual bool canFlip() const { return true; } + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + +protected: + void inStateChanged( bool newState ); + LogicIn * m_pS; + LogicIn * m_pR; + LogicOut * m_pQ; + LogicOut * m_pQBar; + bool old_q1; + bool old_q2; +}; + +#endif diff --git a/src/electronics/components/fulladder.cpp b/src/electronics/components/fulladder.cpp new file mode 100644 index 0000000..ad5e40c --- /dev/null +++ b/src/electronics/components/fulladder.cpp @@ -0,0 +1,91 @@ +/*************************************************************************** + * Copyright (C) 2004 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "fulladder.h" + +#include "logic.h" +#include "libraryitem.h" + +#include <kiconloader.h> +#include <klocale.h> + + +Item* FullAdder::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new FullAdder( (ICNDocument*)itemDocument, newItem, id ); +} + +LibraryItem* FullAdder::libraryItem() +{ + return new LibraryItem( + QString("ec/adder"), + i18n("Adder"), + i18n("Integrated Circuits"), + "ic1.png", + LibraryItem::lit_component, + FullAdder::construct + ); +} + +FullAdder::FullAdder( ICNDocument *icnDocument, bool newItem, const char *id ) + : Component( icnDocument, newItem, (id) ? id : "adder" ) +{ + m_name = i18n("Adder"); +// m_desc = i18n("Insert missing adder help here."); + + ALogic = BLogic = inLogic = 0l; + outLogic = SLogic = 0l; + + QStringList pins = QStringList::split( ',', "A,B,>,,S,C", true ); + initDIPSymbol( pins, 48 ); + initDIP(pins); + + ECNode *node; + + node = ecNodeWithID("S"); + SLogic = createLogicOut( node, false ); + + node = ecNodeWithID("C"); + outLogic = createLogicOut( node, false ); + + node = ecNodeWithID("A"); + ALogic = createLogicIn(node); + + node = ecNodeWithID("B"); + BLogic = createLogicIn(node); + + node = ecNodeWithID(">"); + inLogic = createLogicIn(node); + + + ALogic->setCallback( this, (CallbackPtr)(&FullAdder::inStateChanged) ); + BLogic->setCallback( this, (CallbackPtr)(&FullAdder::inStateChanged) ); + inLogic->setCallback( this, (CallbackPtr)(&FullAdder::inStateChanged) ); +} + +FullAdder::~FullAdder() +{ +} + + +void FullAdder::inStateChanged( bool /*state*/ ) +{ + const bool A = ALogic->isHigh(); + const bool B = BLogic->isHigh(); + const bool in = inLogic->isHigh(); + + const bool out = (!A && B && in) || (A && !B && in) || (A && B); + const bool S = (!A && !B && in) || (!A && B && !in) || (A && !B && !in) || (A && B && in); + + SLogic->setHigh(S); + outLogic->setHigh(out); +} + + diff --git a/src/electronics/components/fulladder.h b/src/electronics/components/fulladder.h new file mode 100644 index 0000000..d4277e5 --- /dev/null +++ b/src/electronics/components/fulladder.h @@ -0,0 +1,37 @@ +/*************************************************************************** + * Copyright (C) 2004 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef ECFullAdder_H +#define ECFullAdder_H + +#include "component.h" +#include "logic.h" + +/** +@author David Saxton +*/ +class FullAdder : public CallbackClass, public Component +{ +public: + FullAdder( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~FullAdder(); + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + virtual bool canFlip() const { return true; } + +protected: + void inStateChanged( bool newState ); + + LogicIn *ALogic, *BLogic, *inLogic; + LogicOut *outLogic, *SLogic; +}; + +#endif diff --git a/src/electronics/components/inductor.cpp b/src/electronics/components/inductor.cpp new file mode 100644 index 0000000..9e30b34 --- /dev/null +++ b/src/electronics/components/inductor.cpp @@ -0,0 +1,84 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "inductance.h" +#include "inductor.h" +#include "libraryitem.h" + +#include <klocale.h> +#include <qpainter.h> + +Item* Inductor::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new Inductor( (ICNDocument*)itemDocument, newItem, id ); +} + +LibraryItem* Inductor::libraryItem() +{ + return new LibraryItem( + "ec/inductor", + i18n("Inductor"), + i18n("Discrete"), + "inductor.png", + LibraryItem::lit_component, + Inductor::construct + ); +} + +Inductor::Inductor( ICNDocument *icnDocument, bool newItem, const char *id ) + : Component( icnDocument, newItem, id ? id : "inductor" ) +{ + m_name = i18n("Inductor"); +// m_desc = i18n("Stores electrical charge.<br><br>" +// "The voltage across the inductor and inductance are related by <i>Charge = Inductance x Voltage</i>."); + setSize( -16, -8, 32, 16 ); + + init1PinLeft(); + init1PinRight(); + + m_pInductance = createInductance( m_pNNode[0], m_pPNode[0], 0.001 ); + + createProperty( "Inductance", Variant::Type::Double ); + property("Inductance")->setCaption( i18n("Inductance") ); + property("Inductance")->setUnit("H"); + property("Inductance")->setMinValue(1e-12); + property("Inductance")->setMaxValue(1e12); + property("Inductance")->setValue(1e-3); + + addDisplayText( "inductance", QRect( -8, -24, 16, 16 ), "", false ); +} + +Inductor::~Inductor() +{ +} + +void Inductor::dataChanged() +{ + double inductance = dataDouble("Inductance"); + + QString display = QString::number( inductance / getMultiplier(inductance), 'g', 3 ) + getNumberMag(inductance) + "H"; + setDisplayText( "inductance", display ); + + m_pInductance->setInductance(inductance); +} + +void Inductor::drawShape( QPainter &p ) +{ + initPainter(p); + int _y = int(y()); + int _x = int(x()); + + p.drawArc( _x-16, _y-5, 11, 11, 0, 180*16 ); + p.drawArc( _x-5, _y-5, 11, 11, 0, 180*16 ); + p.drawArc( _x+6, _y-5, 11, 11, 0, 180*16 ); + + deinitPainter(p); +} + diff --git a/src/electronics/components/inductor.h b/src/electronics/components/inductor.h new file mode 100644 index 0000000..abb4eec --- /dev/null +++ b/src/electronics/components/inductor.h @@ -0,0 +1,35 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef INDUCTOR_H +#define INDUCTOR_H + +#include <component.h> + +/** +@author David Saxton +*/ +class Inductor : public Component +{ + public: + Inductor( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~Inductor(); + + static Item * construct( ItemDocument * itemDocument, bool newItem, const char * id ); + static LibraryItem * libraryItem(); + + private: + void dataChanged(); + virtual void drawShape( QPainter & p ); + + Inductance * m_pInductance; +}; + +#endif diff --git a/src/electronics/components/magnitudecomparator.cpp b/src/electronics/components/magnitudecomparator.cpp new file mode 100644 index 0000000..2659122 --- /dev/null +++ b/src/electronics/components/magnitudecomparator.cpp @@ -0,0 +1,206 @@ +/*************************************************************************** + * Copyright (C) 2005 by Fredy Yanardi * + * * + * This program 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. * + ***************************************************************************/ + +#include "libraryitem.h" +#include "logic.h" +#include "magnitudecomparator.h" +#include "variant.h" + +#include <cmath> +#include <klocale.h> + +Item* MagnitudeComparator::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new MagnitudeComparator( (ICNDocument*)itemDocument, newItem, id ); +} + +LibraryItem* MagnitudeComparator::libraryItem() +{ + return new LibraryItem( + QString("ec/magnitudecomparator"), + i18n("Magnitude Comparator"), + i18n("Integrated Circuits"), + "ic1.png", + LibraryItem::lit_component, + MagnitudeComparator::construct + ); +} + +MagnitudeComparator::MagnitudeComparator( ICNDocument *icnDocument, bool newItem, const char *id ) + : Component( icnDocument, newItem, id ? id : "magnitudecomparator" ) +{ + m_name = i18n("Magnitude Comparator"); +// m_desc = i18n("Compares to two binary number and generates input to indicate which binary number has greater magnitude. It has 3 cascading inputs: I0 for I<SUB>A > B</SUB>, I1 for I<SUB>A < B</SUB>, and I2 for I<SUB>A = B</SUB> and 3 outputs: O0 for O<SUB>A > B</SUB>, O1 for O<SUB>A < B</SUB>, and O2 for O<SUB>A = B</SUB>"); + m_desc = i18n("Compares two binary numbers and generates output to indicate which binary number has the greater magnitude. It has 3 cascading inputs:" + "<ul><li>I: A > B</li>" + "<li>I: A < B</li>" + "<li>I: A = B</li></ul>" + "and 3 outputs:" + "<ul><li>O: A > B</li>" + "<li>O: A < B</li>" + "<li>O: A = B</li></ul>"); + + createProperty( "numInput", Variant::Type::Int ); + property("numInput")->setCaption( i18n("Number Inputs") ); + property("numInput")->setMinValue(1); + property("numInput")->setMaxValue(8); + property("numInput")->setValue(4); + + m_oldABLogicCount = 0; + cascadingInputs = 3; + outputs = 3; + + firstTime = true; +} + +MagnitudeComparator::~MagnitudeComparator() +{ +} + + +void MagnitudeComparator::dataChanged() +{ + initPins(); +} + + +void MagnitudeComparator::inStateChanged() +{ + int i; + + for ( i = 0; i < 3; i++ ) + m_output[i]->setHigh(false); + +// for ( i = dataInt("numInput")-1; i >= 0; i-- ) { + for ( i = m_oldABLogicCount-1; i >= 0; i-- ) { + if (m_aLogic[i]->isHigh() && !m_bLogic[i]->isHigh()) + { + m_output[0]->setHigh(true); + return; + } + else if ( !m_aLogic[i]->isHigh() && m_bLogic[i]->isHigh() ) { + m_output[1]->setHigh(true); + return; + } + } + + if ( m_cLogic[2]->isHigh() ) + m_output[2]->setHigh(true); + else if ( m_cLogic[0]->isHigh() ) + if ( !m_cLogic[1]->isHigh() ) + m_output[0]->setHigh(true); + else + ; + else if ( m_cLogic[1]->isHigh() ) + m_output[1]->setHigh(true); + else { + m_output[0]->setHigh(true); + m_output[1]->setHigh(true); + } +} + + +void MagnitudeComparator::initPins() +{ + const double numInputs = dataInt("numInput"); + int newABLogicCount = (int)numInputs; + + if ( newABLogicCount == m_oldABLogicCount ) + return; + + QStringList leftPins; + int space = 3 - newABLogicCount; + for ( int i = 0; i < space; i++ ) + leftPins << ""; + for ( int i = 0; i < newABLogicCount; i++ ) + leftPins << QString("A%1").arg( QString::number(i) ); + for ( int i = 0; i < newABLogicCount; i++ ) + leftPins << QString("B%1").arg( QString::number(i) ); + for ( int i = 0; i < space; i++ ) + leftPins << ""; + + QStringList rightPins; + space = -space; + for ( int i = 0; i < space; i++ ) + rightPins << ""; + QString inNames[] = { "I: A>B", "I: A<B", "I: A=B" }; + rightPins << inNames[2] << inNames[1] << inNames[0]; + QString outNames[] = { "O: A>B", "O: A<B", "O: A=B" }; + rightPins << outNames[2] << outNames[1] << outNames[0]; + for ( int i = 0; i < space; i++ ) + rightPins << ""; + + QStringList pins = leftPins + rightPins; + + initDIPSymbol( pins, 88 ); + initDIP(pins); + + ECNode *node; + + if (firstTime) { + m_cLogic.resize(3); + for ( int i = 0; i < cascadingInputs; i++ ) + { + node = ecNodeWithID( inNames[i] ); + m_cLogic.insert( i, createLogicIn(node) ); + m_cLogic[i]->setCallback( this, (CallbackPtr)(&MagnitudeComparator::inStateChanged)); + } + + m_output.resize(3); + for ( int i = 0; i < outputs; i++ ) + { + node = ecNodeWithID( outNames[i] ); + m_output.insert( i, createLogicOut(node,false) ); + } + firstTime = false; + } + + if ( newABLogicCount > m_oldABLogicCount ) + { + m_aLogic.resize(newABLogicCount); + for ( int i=m_oldABLogicCount; i<newABLogicCount; ++i ) + { + node = ecNodeWithID("A"+QString::number(i)); + m_aLogic.insert( i, createLogicIn(node) ); + m_aLogic[i]->setCallback( this, (CallbackPtr)(&MagnitudeComparator::inStateChanged) ); + } + + m_bLogic.resize(newABLogicCount); + for ( int i=m_oldABLogicCount; i<newABLogicCount; ++i ) + { + node = ecNodeWithID("B"+QString::number(i)); + m_bLogic.insert( i, createLogicIn(node) ); + m_bLogic[i]->setCallback( this, (CallbackPtr)(&MagnitudeComparator::inStateChanged) ); + } + } + else + { + for ( int i=newABLogicCount; i<m_oldABLogicCount; ++i ) + { + QString id = "A"+QString::number(i); + removeDisplayText(id); + removeElement( m_aLogic[i], false ); + removeNode(id); + } + m_aLogic.resize(newABLogicCount); + for ( int i=newABLogicCount; i<m_oldABLogicCount; ++i ) + { + QString id = "B"+QString::number(i); + removeDisplayText(id); + removeElement( m_bLogic[i], false ); + removeNode(id); + } + m_bLogic.resize(newABLogicCount); + } + + m_oldABLogicCount = newABLogicCount; + inStateChanged(); +} + + diff --git a/src/electronics/components/magnitudecomparator.h b/src/electronics/components/magnitudecomparator.h new file mode 100644 index 0000000..57dc748 --- /dev/null +++ b/src/electronics/components/magnitudecomparator.h @@ -0,0 +1,50 @@ +/*************************************************************************** + * Copyright (C) 2005 by Fredy Yanardi * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef MAGNITUDECOMPARATOR_H +#define MAGNITUDECOMPARATOR_H + +#include "component.h" +#include "logic.h" + +#include <qbitarray.h> +#include <qptrvector.h> + +/** +@author Fredy Yanardi + */ +class MagnitudeComparator : public CallbackClass, public Component +{ + public: + MagnitudeComparator( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~MagnitudeComparator(); + + virtual bool canFlip() const { return true; } + static Item * construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem * libraryItem(); + + protected: + void initPins(); + virtual void dataChanged(); + void inStateChanged(); + + int m_oldABLogicCount; + int cascadingInputs; + int outputs; + bool firstTime; + + QBitArray m_data; + + QPtrVector<LogicIn> m_aLogic; + QPtrVector<LogicIn> m_bLogic; + QPtrVector<LogicIn> m_cLogic; + QPtrVector<LogicOut> m_output; +}; + +#endif diff --git a/src/electronics/components/matrixdisplay.cpp b/src/electronics/components/matrixdisplay.cpp new file mode 100644 index 0000000..dd40b6a --- /dev/null +++ b/src/electronics/components/matrixdisplay.cpp @@ -0,0 +1,291 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "colorcombo.h" +#include "diode.h" +#include "ecled.h" +#include "ecnode.h" +#include "libraryitem.h" +#include "matrixdisplay.h" +#include "simulator.h" + +#include <kdebug.h> +#include <klocale.h> +#include <qpainter.h> +#include <qstring.h> + +Item* MatrixDisplay::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new MatrixDisplay( (ICNDocument*)itemDocument, newItem, id ); +} + + +LibraryItem* MatrixDisplay::libraryItem() +{ + return new LibraryItem( + "ec/matrix_display", + i18n("Matrix Display"), + i18n("Outputs"), + "matrixdisplay.png", + LibraryItem::lit_component, + MatrixDisplay::construct ); +} + + +MatrixDisplay::MatrixDisplay( ICNDocument *icnDocument, bool newItem, const char *id ) + : Component( icnDocument, newItem, id ? id : "matrix_display" ) +{ + m_name = i18n("Matrix Display"); + m_desc = i18n("A matrix display of LEDs with a configurable number of columns and rows."); + m_bDynamicContent = true; + + //BEGIN Reset members + for ( unsigned i = 0; i < max_md_height; i++ ) + m_pRowNodes[i] = 0l; + for ( unsigned i = 0; i < max_md_width; i++ ) + m_pColNodes[i] = 0l; + + m_lastUpdatePeriod = 0.0; + m_r = m_g = m_b = 0.0; + m_bRowCathode = true; + m_numRows = 0; + m_numCols = 0; + //END Reset members + + createProperty( "0-rows", Variant::Type::Int ); + property("0-rows")->setCaption( i18n("Rows") ); + property("0-rows")->setMinValue(1); + property("0-rows")->setMaxValue(max_md_height); + property("0-rows")->setValue(7); + + createProperty( "1-cols", Variant::Type::Int ); + property("1-cols")->setCaption( i18n("Columns") ); + property("1-cols")->setMinValue(1); + property("1-cols")->setMaxValue(max_md_width); + property("1-cols")->setValue(5); + + createProperty( "color", Variant::Type::Color ); + property("color")->setCaption( i18n("Color") ); + property("color")->setColorScheme( ColorCombo::LED ); + + createProperty( "diode-configuration", Variant::Type::Select ); + property("diode-configuration")->setCaption( i18n("Configuration") ); + property("diode-configuration")->setAllowed( QStringList::split(',',"Row Cathode,Column Cathode") ); + property("diode-configuration")->setValue("Row Cathode"); + property("diode-configuration")->setAdvanced(true); +} + + +MatrixDisplay::~MatrixDisplay() +{ +} + + +void MatrixDisplay::dataChanged() +{ + QColor color = dataColor("color"); + m_r = double(color.red()) / double(0x100); + m_g = double(color.green()) / double(0x100); + m_b = double(color.blue()) / double(0x100); + + int numRows = dataInt("0-rows"); + int numCols = dataInt("1-cols"); + + bool ledsChanged = (numRows != int(m_numRows)) || (numCols != int(m_numCols)); + + if (ledsChanged) + initPins( numRows, numCols ); + + bool rowCathode = dataString("diode-configuration") == "Row Cathode"; + if ( (rowCathode != m_bRowCathode) || ledsChanged) + { + m_bRowCathode = rowCathode; + + for ( unsigned i = 0; i < m_numCols; i++ ) + { + for ( unsigned j = 0; j < m_numRows; j++ ) + { + removeElement( m_pDiodes[i][j], false ); + if (rowCathode) + m_pDiodes[i][j] = createDiode( m_pColNodes[i], m_pRowNodes[j] ); + else + m_pDiodes[i][j] = createDiode( m_pRowNodes[j], m_pColNodes[i] ); + } + } + } +} + + +void MatrixDisplay::initPins( unsigned numRows, unsigned numCols ) +{ + if ( (numRows == m_numRows) && (numCols == m_numCols) ) + return; + + if ( numRows > max_md_height ) + numRows = max_md_height; + + if ( numCols > max_md_width ) + numCols = max_md_width; + + m_lastUpdatePeriod = 0.0; + + //BEGIN Remove diodes + // All the diodes are going to be readded from dataChanged (where this + // function is called from), so easiest just to delete the diodes now and + // resize. + + for ( unsigned i = 0; i < m_numCols; i++ ) + { + for ( unsigned j = 0; j < m_numRows; j++ ) + removeElement( m_pDiodes[i][j], false ); + } + + m_avgBrightness.resize(numCols); + m_lastBrightness.resize(numCols); + m_pDiodes.resize(numCols); + + for ( unsigned i = 0; i < numCols; i++ ) + { + m_avgBrightness[i].resize(numRows); + m_lastBrightness[i].resize(numRows); + m_pDiodes[i].resize(numRows); + + for ( unsigned j = 0; j < numRows; j++ ) + { + m_avgBrightness[i][j] = 0.0; + m_lastBrightness[i][j] = 255; + m_pDiodes[i][j] = 0l; + } + + } + //END Remove diodes + + + //BEGIN Create or destroy pins + if ( numCols >= m_numCols ) + { + for ( unsigned i = m_numCols; i < numCols; i++ ) + m_pColNodes[i] = createPin( 0, 0, 270, colPinID(i) ); + } + else + { + for ( unsigned i = numCols; i < m_numCols; i++ ) + { + removeNode( colPinID(i) ); + m_pColNodes[i] = 0l; + } + } + m_numCols = numCols; + + if ( numRows >= m_numRows ) + { + for ( unsigned i = m_numRows; i < numRows; i++ ) + m_pRowNodes[i] = createPin( 0, 0, 0, rowPinID(i) ); + } + else + { + for ( unsigned i = numRows; i < m_numRows; i++ ) + { + removeNode( rowPinID(i) ); + m_pRowNodes[i] = 0l; + } + } + m_numRows = numRows; + //END Create or destroy pins + + + //BEGIN Position pins et al + setSize( -int(numCols+1)*8, -int(numRows+1)*8, int(numCols+1)*16, int(numRows+1)*16, true ); + + for ( int i = 0; i < int(m_numCols); i++ ) + { + m_nodeMap[colPinID(i)].x = offsetX() + 16 + 16*i; + m_nodeMap[colPinID(i)].y = offsetY() + height() + 8; + } + + for ( int i = 0; i < int(m_numRows); i++ ) + { + m_nodeMap[rowPinID(i)].x = offsetX() - 8; + m_nodeMap[rowPinID(i)].y = offsetY() + 16 + 16*i; + } + + updateAttachedPositioning(); + //END Position pins et al +} + + +QString MatrixDisplay::colPinID( int col ) const +{ + return QString("col_%1").arg(QString::number(col)); +} +QString MatrixDisplay::rowPinID( int row ) const +{ + return QString("row_%1").arg(QString::number(row)); +} + + +void MatrixDisplay::stepNonLogic() +{ + double interval = 1./LINEAR_UPDATE_RATE; + + for ( unsigned i = 0; i < m_numCols; i++ ) + { + for ( unsigned j = 0; j < m_numRows; j++ ) + m_avgBrightness[i][j] += ECLed::brightness( m_pDiodes[i][j]->current() )*interval; + } + + m_lastUpdatePeriod += interval; +} + + +void MatrixDisplay::drawShape( QPainter &p ) +{ + if ( isSelected() ) + p.setPen(m_selectedCol); + p.drawRect( boundingRect() ); + + initPainter(p); + + const int _x = int(x()+offsetX()); + const int _y = int(y()+offsetY()); + + // To avoid flicker, require at least a 10 ms sample before changing + // the brightness + double minUpdatePeriod = 0.0099; + + for ( int i = 0; i < int(m_numCols); i++ ) + { + for ( int j = 0; j < int(m_numRows); j++ ) + { + if ( m_lastUpdatePeriod > minUpdatePeriod ) + m_lastBrightness[i][j] = unsigned(m_avgBrightness[i][j]/m_lastUpdatePeriod); + + double _b = m_lastBrightness[i][j]; + + QColor brush = QColor( uint(255-(255-_b)*(1-m_r)), uint(255-(255-_b)*(1-m_g)), uint(255-(255-_b)*(1-m_b)) ); + p.setBrush(brush); + p.setPen( Qt::NoPen ); + p.drawEllipse( _x+10+i*16, _y+10+j*16, 12, 12 ); + } + } + + if ( m_lastUpdatePeriod > minUpdatePeriod ) + { + m_lastUpdatePeriod = 0.0; + + for ( unsigned i = 0; i < m_numCols; i++ ) + { + for ( unsigned j = 0; j < m_numRows; j++ ) + m_avgBrightness[i][j] = 0.0; + } + } + + deinitPainter(p); +} diff --git a/src/electronics/components/matrixdisplay.h b/src/electronics/components/matrixdisplay.h new file mode 100644 index 0000000..4851817 --- /dev/null +++ b/src/electronics/components/matrixdisplay.h @@ -0,0 +1,60 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef MATRIXDISPLAY_H +#define MATRIXDISPLAY_H + +#include <component.h> +#include <qvaluevector.h> + +const unsigned max_md_width = 100; +const unsigned max_md_height = 20; + +/** +@author David Saxton +*/ +class MatrixDisplay : public Component +{ + public: + MatrixDisplay( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~MatrixDisplay(); + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + + virtual bool canFlip() const { return true; } + virtual void stepNonLogic(); + virtual bool doesStepNonLogic() const { return true; } + + protected: + virtual void drawShape( QPainter &p ); + virtual void dataChanged(); + void initPins( unsigned numRows, unsigned numCols ); + QString colPinID( int col ) const; + QString rowPinID( int row ) const; + + + QValueVector< QValueVector<double> > m_avgBrightness; + QValueVector< QValueVector<unsigned> > m_lastBrightness; + QValueVector< QValueVector<Diode*> > m_pDiodes; + + ECNode * m_pRowNodes[max_md_height]; + ECNode * m_pColNodes[max_md_width]; + + double m_lastUpdatePeriod; + + double m_r, m_g, m_b; + bool m_bRowCathode; + + unsigned m_numRows; + unsigned m_numCols; +}; + +#endif diff --git a/src/electronics/components/matrixdisplaydriver.cpp b/src/electronics/components/matrixdisplaydriver.cpp new file mode 100644 index 0000000..da00bb7 --- /dev/null +++ b/src/electronics/components/matrixdisplaydriver.cpp @@ -0,0 +1,380 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "libraryitem.h" +#include "logic.h" +#include "matrixdisplaydriver.h" + +#include <klocale.h> +#include <qpainter.h> +#include <qstring.h> + +#include <assert.h> + +// Thank you Scott Dattalo! +// http://www.dattalo.com/gnupic/lcdfont.inc + +static char characterMap[256][5] = { +{ 0x00, 0x00, 0x00, 0x00, 0x00 }, //0 +{ 0x00, 0x00, 0x00, 0x00, 0x00 }, //1 +{ 0x00, 0x00, 0x00, 0x00, 0x00 }, //2 +{ 0x00, 0x00, 0x00, 0x00, 0x00 }, //3 +{ 0x00, 0x00, 0x00, 0x00, 0x00 }, //4 +{ 0x00, 0x00, 0x00, 0x00, 0x00 }, //5 +{ 0x00, 0x00, 0x00, 0x00, 0x00 }, //6 +{ 0x00, 0x00, 0x00, 0x00, 0x00 }, //7 +{ 0x00, 0x00, 0x00, 0x00, 0x00 }, //8 +{ 0x00, 0x00, 0x00, 0x00, 0x00 }, //9 +{ 0x00, 0x00, 0x00, 0x00, 0x00 }, //10 +{ 0x00, 0x00, 0x00, 0x00, 0x00 }, //11 +{ 0x00, 0x00, 0x00, 0x00, 0x00 }, //12 +{ 0x00, 0x00, 0x00, 0x00, 0x00 }, //13 +{ 0x00, 0x00, 0x00, 0x00, 0x00 }, //14 +{ 0x00, 0x00, 0x00, 0x00, 0x00 }, //15 +{ 0x00, 0x00, 0x00, 0x00, 0x00 }, //16 +{ 0x00, 0x00, 0x00, 0x00, 0x00 }, //17 +{ 0x00, 0x00, 0x00, 0x00, 0x00 }, //18 +{ 0x00, 0x00, 0x00, 0x00, 0x00 }, //19 +{ 0x00, 0x00, 0x00, 0x00, 0x00 }, //20 +{ 0x00, 0x00, 0x00, 0x00, 0x00 }, //21 +{ 0x00, 0x00, 0x00, 0x00, 0x00 }, //22 +{ 0x00, 0x00, 0x00, 0x00, 0x00 }, //23 +{ 0x00, 0x00, 0x00, 0x00, 0x00 }, //24 +{ 0x00, 0x00, 0x00, 0x00, 0x00 }, //25 +{ 0x00, 0x00, 0x00, 0x00, 0x00 }, //26 +{ 0x00, 0x00, 0x00, 0x00, 0x00 }, //27 +{ 0x00, 0x00, 0x00, 0x00, 0x00 }, //28 +{ 0x00, 0x00, 0x00, 0x00, 0x00 }, //29 +{ 0x00, 0x00, 0x00, 0x00, 0x00 }, //30 +{ 0x00, 0x00, 0x00, 0x00, 0x00 }, //31 +{ 0x00, 0x00, 0x00, 0x00, 0x00 }, //32 +{ 0x00, 0x00, 0x7d, 0x00, 0x00 }, //33 +{ 0x00, 0x70, 0x00, 0x70, 0x00 }, //34 +{ 0x14, 0x7f, 0x14, 0x7f, 0x14 }, //35 +{ 0x12, 0x2a, 0x7f, 0x2a, 0x24 }, //36 +{ 0x62, 0x64, 0x08, 0x13, 0x23 }, //37 +{ 0x36, 0x49, 0x55, 0x22, 0x05 }, //38 +{ 0x00, 0x50, 0x60, 0x00, 0x00 }, //39 +{ 0x00, 0x1c, 0x22, 0x41, 0x00 }, //40 +{ 0x00, 0x41, 0x22, 0x1c, 0x00 }, //41 +{ 0x14, 0x08, 0x3e, 0x08, 0x14 }, //42 +{ 0x08, 0x08, 0x3e, 0x08, 0x08 }, //43 +{ 0x00, 0x05, 0x06, 0x00, 0x00 }, //44 +{ 0x08, 0x08, 0x08, 0x08, 0x08 }, //45 +{ 0x00, 0x03, 0x03, 0x00, 0x00 }, //46 +{ 0x02, 0x04, 0x08, 0x10, 0x20 }, //47 +{ 0x3e, 0x45, 0x49, 0x51, 0x3e }, //48 +{ 0x00, 0x21, 0x7f, 0x01, 0x00 }, //49 +{ 0x21, 0x43, 0x45, 0x49, 0x31 }, //50 +{ 0x42, 0x41, 0x51, 0x69, 0x46 }, //51 +{ 0x0c, 0x14, 0x24, 0x7f, 0x04 }, //52 +{ 0x72, 0x51, 0x51, 0x51, 0x4e }, //53 +{ 0x1e, 0x29, 0x49, 0x49, 0x06 }, //54 +{ 0x40, 0x47, 0x48, 0x50, 0x60 }, //55 +{ 0x36, 0x49, 0x49, 0x49, 0x36 }, //56 +{ 0x30, 0x49, 0x49, 0x4a, 0x3c }, //57 +{ 0x00, 0x36, 0x36, 0x00, 0x00 }, //58 +{ 0x00, 0x35, 0x36, 0x00, 0x00 }, //59 +{ 0x08, 0x14, 0x22, 0x41, 0x00 }, //60 +{ 0x14, 0x14, 0x14, 0x14, 0x14 }, //61 +{ 0x41, 0x22, 0x14, 0x08, 0x00 }, //62 +{ 0x20, 0x40, 0x45, 0x48, 0x30 }, //63 +{ 0x26, 0x49, 0x4f, 0x41, 0x3e }, //64 +{ 0x3f, 0x44, 0x44, 0x44, 0x3f }, //65 +{ 0x7f, 0x49, 0x49, 0x49, 0x36 }, //66 +{ 0x3e, 0x41, 0x41, 0x41, 0x22 }, //67 +{ 0x7f, 0x41, 0x41, 0x41, 0x3e }, //68 +{ 0x7f, 0x49, 0x49, 0x49, 0x41 }, //69 +{ 0x7f, 0x48, 0x48, 0x48, 0x40 }, //70 +{ 0x3e, 0x41, 0x49, 0x49, 0x2f }, //71 +{ 0x7f, 0x08, 0x08, 0x08, 0x7f }, //72 +{ 0x00, 0x41, 0x7f, 0x41, 0x00 }, //73 +{ 0x02, 0x01, 0x41, 0x7e, 0x40 }, //74 +{ 0x7f, 0x08, 0x14, 0x22, 0x41 }, //75 +{ 0x7f, 0x01, 0x01, 0x01, 0x01 }, //76 +{ 0x7f, 0x40, 0x20, 0x40, 0x7f }, //77 +{ 0x7f, 0x10, 0x08, 0x04, 0x7f }, //78 +{ 0x3e, 0x41, 0x41, 0x41, 0x3e }, //79 +{ 0x7f, 0x48, 0x48, 0x48, 0x30 }, //80 +{ 0x3e, 0x41, 0x45, 0x42, 0x3d }, //81 +{ 0x7f, 0x48, 0x4c, 0x4a, 0x31 }, //82 +{ 0x31, 0x49, 0x49, 0x49, 0x46 }, //83 +{ 0x40, 0x40, 0x7f, 0x40, 0x40 }, //84 +{ 0x7e, 0x01, 0x01, 0x01, 0x7e }, //85 +{ 0x7c, 0x02, 0x01, 0x02, 0x7c }, //86 +{ 0x7e, 0x01, 0x0e, 0x01, 0x7e }, //87 +{ 0x63, 0x14, 0x08, 0x14, 0x63 }, //88 +{ 0x70, 0x08, 0x07, 0x08, 0x70 }, //89 +{ 0x43, 0x45, 0x49, 0x51, 0x61 }, //90 +{ 0x00, 0x7f, 0x41, 0x41, 0x00 }, //91 +{ 0x54, 0x34, 0x1f, 0x34, 0x54 }, //92 +{ 0x00, 0x41, 0x41, 0x7f, 0x00 }, //93 +{ 0x10, 0x20, 0x40, 0x20, 0x10 }, //94 +{ 0x01, 0x01, 0x01, 0x01, 0x01 }, //95 +{ 0x00, 0x40, 0x20, 0x10, 0x00 }, //96 +{ 0x02, 0x15, 0x15, 0x15, 0x0f }, //97 +{ 0x7f, 0x09, 0x11, 0x11, 0x0e }, //98 +{ 0x0e, 0x11, 0x11, 0x11, 0x02 }, //99 +{ 0x0e, 0x11, 0x11, 0x09, 0x7f }, //100 +{ 0x0e, 0x15, 0x15, 0x15, 0x0c }, //101 +{ 0x08, 0x3f, 0x48, 0x40, 0x20 }, //102 +{ 0x30, 0x49, 0x49, 0x49, 0x7e }, //103 +{ 0x7f, 0x08, 0x10, 0x10, 0x0f }, //104 +{ 0x00, 0x11, 0x5f, 0x01, 0x00 }, //105 +{ 0x02, 0x01, 0x21, 0x7e, 0x00 }, //106 +{ 0x7f, 0x04, 0x0a, 0x11, 0x00 }, //107 +{ 0x00, 0x41, 0x7f, 0x01, 0x00 }, //108 +{ 0x1f, 0x10, 0x0c, 0x10, 0x0f }, //109 +{ 0x1f, 0x08, 0x10, 0x10, 0x0f }, //110 +{ 0x0e, 0x11, 0x11, 0x11, 0x0e }, //111 +{ 0x1f, 0x14, 0x14, 0x14, 0x08 }, //112 +{ 0x08, 0x14, 0x14, 0x0c, 0x1f }, //113 +{ 0x1f, 0x08, 0x10, 0x10, 0x08 }, //114 +{ 0x09, 0x15, 0x15, 0x15, 0x12 }, //115 +{ 0x20, 0x7e, 0x21, 0x01, 0x02 }, //116 +{ 0x1e, 0x01, 0x01, 0x02, 0x1f }, //117 +{ 0x1c, 0x02, 0x01, 0x02, 0x1c }, //118 +{ 0x1e, 0x01, 0x06, 0x01, 0x1e }, //119 +{ 0x11, 0x0a, 0x04, 0x0a, 0x11 }, //120 +{ 0x18, 0x05, 0x05, 0x05, 0x1e }, //121 +{ 0x11, 0x13, 0x15, 0x19, 0x11 }, //122 +{ 0x00, 0x08, 0x36, 0x41, 0x00 }, //123 +{ 0x00, 0x00, 0x7f, 0x00, 0x00 }, //124 +{ 0x00, 0x41, 0x36, 0x08, 0x00 }, //125 +{ 0x08, 0x08, 0x2a, 0x1c, 0x08 }, //126 +{ 0x08, 0x1c, 0x2a, 0x08, 0x08 }, //127 +{ 0x00, 0x00, 0x00, 0x00, 0x00 }, //128 +{ 0x00, 0x00, 0x00, 0x00, 0x00 }, //129 +{ 0x00, 0x00, 0x00, 0x00, 0x00 }, //130 +{ 0x00, 0x00, 0x00, 0x00, 0x00 }, //131 +{ 0x00, 0x00, 0x00, 0x00, 0x00 }, //132 +{ 0x00, 0x00, 0x00, 0x00, 0x00 }, //133 +{ 0x00, 0x00, 0x00, 0x00, 0x00 }, //134 +{ 0x00, 0x00, 0x00, 0x00, 0x00 }, //135 +{ 0x00, 0x00, 0x00, 0x00, 0x00 }, //136 +{ 0x00, 0x00, 0x00, 0x00, 0x00 }, //137 +{ 0x00, 0x00, 0x00, 0x00, 0x00 }, //138 +{ 0x00, 0x00, 0x00, 0x00, 0x00 }, //139 +{ 0x00, 0x00, 0x00, 0x00, 0x00 }, //140 +{ 0x00, 0x00, 0x00, 0x00, 0x00 }, //141 +{ 0x00, 0x00, 0x00, 0x00, 0x00 }, //142 +{ 0x00, 0x00, 0x00, 0x00, 0x00 }, //143 +{ 0x00, 0x00, 0x00, 0x00, 0x00 }, //144 +{ 0x07, 0x05, 0x07, 0x00, 0x00 }, //145 +{ 0x00, 0x00, 0x78, 0x40, 0x40 }, //146 +{ 0x01, 0x01, 0x0f, 0x00, 0x00 }, //147 +{ 0x04, 0x02, 0x01, 0x00, 0x00 }, //148 +{ 0x00, 0x0c, 0x0c, 0x00, 0x00 }, //149 +{ 0x28, 0x28, 0x29, 0x2a, 0x3c }, //150 +{ 0x10, 0x11, 0x16, 0x14, 0x18 }, //151 +{ 0x02, 0x04, 0x0f, 0x10, 0x00 }, //152 +{ 0x0c, 0x08, 0x19, 0x09, 0x0e }, //153 +{ 0x09, 0x09, 0x0f, 0x09, 0x09 }, //154 +{ 0x09, 0x0a, 0x0c, 0x1f, 0x08 }, //155 +{ 0x08, 0x1f, 0x08, 0x0a, 0x0c }, //156 +{ 0x01, 0x09, 0x09, 0x0f, 0x01 }, //157 +{ 0x15, 0x15, 0x15, 0x1f, 0x00 }, //158 +{ 0x0c, 0x00, 0x0d, 0x01, 0x0e }, //159 +{ 0x04, 0x04, 0x04, 0x04, 0x04 }, //160 +{ 0x40, 0x41, 0x5e, 0x48, 0x70 }, //161 +{ 0x04, 0x08, 0x1f, 0x20, 0x40 }, //162 +{ 0x38, 0x20, 0x61, 0x22, 0x3c }, //163 +{ 0x11, 0x11, 0x1f, 0x11, 0x11 }, //164 +{ 0x22, 0x24, 0x28, 0x7f, 0x20 }, //165 +{ 0x21, 0x7e, 0x20, 0x21, 0x3e }, //166 +{ 0x28, 0x28, 0x7f, 0x28, 0x28 }, //167 +{ 0x08, 0x31, 0x21, 0x22, 0x3c }, //168 +{ 0x10, 0x60, 0x21, 0x3e, 0x20 }, //169 +{ 0x21, 0x21, 0x21, 0x21, 0x3f }, //170 +{ 0x20, 0x79, 0x22, 0x7c, 0x20 }, //171 +{ 0x29, 0x29, 0x01, 0x02, 0x1c }, //172 +{ 0x21, 0x22, 0x24, 0x2a, 0x31 }, //173 +{ 0x20, 0x7e, 0x21, 0x29, 0x31 }, //174 +{ 0x30, 0x09, 0x01, 0x02, 0x3c }, //175 +{ 0x08, 0x31, 0x29, 0x26, 0x3c }, //176 +{ 0x28, 0x29, 0x3e, 0x48, 0x08 }, //177 +{ 0x30, 0x00, 0x31, 0x02, 0x3c }, //178 +{ 0x10, 0x51, 0x5e, 0x50, 0x10 }, //179 +{ 0x00, 0x7f, 0x08, 0x04, 0x00 }, //180 +{ 0x11, 0x12, 0x7c, 0x10, 0x10 }, //181 +{ 0x01, 0x21, 0x21, 0x21, 0x01 }, //182 +{ 0x21, 0x2a, 0x24, 0x2a, 0x30 }, //183 +{ 0x22, 0x24, 0x6f, 0x34, 0x22 }, //184 +{ 0x00, 0x01, 0x02, 0x7c, 0x00 }, //185 +{ 0x0f, 0x00, 0x20, 0x10, 0x0f }, //186 +{ 0x7e, 0x11, 0x11, 0x11, 0x11 }, //187 +{ 0x20, 0x21, 0x21, 0x22, 0x3c }, //188 +{ 0x10, 0x20, 0x10, 0x08, 0x06 }, //189 +{ 0x26, 0x20, 0x7f, 0x20, 0x26 }, //190 +{ 0x20, 0x24, 0x22, 0x25, 0x38 }, //191 +{ 0x00, 0x2a, 0x2a, 0x2a, 0x01 }, //192 +{ 0x0e, 0x12, 0x22, 0x02, 0x07 }, //193 +{ 0x01, 0x0a, 0x04, 0x0a, 0x30 }, //194 +{ 0x28, 0x3e, 0x29, 0x29, 0x29 }, //195 +{ 0x10, 0x7f, 0x10, 0x14, 0x18 }, //196 +{ 0x01, 0x21, 0x21, 0x3f, 0x01 }, //197 +{ 0x29, 0x29, 0x29, 0x29, 0x3f }, //198 +{ 0x10, 0x50, 0x51, 0x52, 0x1c }, //199 +{ 0x78, 0x01, 0x02, 0x7c, 0x00 }, //200 +{ 0x1f, 0x00, 0x3f, 0x01, 0x06 }, //201 +{ 0x3f, 0x01, 0x02, 0x04, 0x08 }, //202 +{ 0x3f, 0x21, 0x21, 0x21, 0x3f }, //203 +{ 0x38, 0x20, 0x21, 0x22, 0x3c }, //204 +{ 0x21, 0x21, 0x01, 0x02, 0x0c }, //205 +{ 0x20, 0x10, 0x40, 0x20, 0x00 }, //206 +{ 0x70, 0x50, 0x70, 0x00, 0x00 }, //207 +{ 0x0e, 0x11, 0x09, 0x06, 0x19 }, //208 +{ 0x02, 0x55, 0x15, 0x55, 0x0f }, //209 +{ 0x1f, 0x2a, 0x2a, 0x2a, 0x14 }, //210 +{ 0x0a, 0x15, 0x15, 0x11, 0x02 }, //211 +{ 0x3f, 0x02, 0x02, 0x04, 0x3e }, //212 +{ 0x0e, 0x11, 0x19, 0x15, 0x12 }, //213 +{ 0x0f, 0x12, 0x22, 0x22, 0x1c }, //214 +{ 0x1c, 0x22, 0x22, 0x22, 0x3f }, //215 +{ 0x02, 0x01, 0x1e, 0x10, 0x10 }, //216 +{ 0x20, 0x20, 0x00, 0x70, 0x00 }, //217 +{ 0x00, 0x00, 0x10, 0x5f, 0x00 }, //218 +{ 0x28, 0x10, 0x28, 0x00, 0x00 }, //219 +{ 0x18, 0x24, 0x7e, 0x24, 0x08 }, //220 +{ 0x14, 0x7f, 0x15, 0x01, 0x01 }, //221 +{ 0x1f, 0x48, 0x50, 0x50, 0x0f }, //222 +{ 0x0e, 0x51, 0x11, 0x51, 0x0e }, //223 +{ 0x3f, 0x12, 0x22, 0x22, 0x1c }, //224 +{ 0x1c, 0x22, 0x22, 0x12, 0x3f }, //225 +{ 0x3c, 0x52, 0x52, 0x52, 0x3c }, //226 +{ 0x03, 0x05, 0x02, 0x05, 0x06 }, //227 +{ 0x1a, 0x26, 0x20, 0x26, 0x1a }, //228 +{ 0x1e, 0x41, 0x01, 0x42, 0x1f }, //229 +{ 0x63, 0x55, 0x49, 0x41, 0x41 }, //230 +{ 0x22, 0x3c, 0x20, 0x3e, 0x22 }, //231 +{ 0x51, 0x4a, 0x44, 0x4a, 0x51 }, //232 +{ 0x3c, 0x02, 0x02, 0x02, 0x3f }, //233 +{ 0x28, 0x28, 0x3e, 0x28, 0x48 }, //234 +{ 0x22, 0x3c, 0x28, 0x28, 0x2e }, //235 +{ 0x3e, 0x28, 0x38, 0x28, 0x3e }, //236 +{ 0x04, 0x04, 0x15, 0x04, 0x04 }, //237 +{ 0x00, 0x00, 0x00, 0x00, 0x00 }, //238 +{ 0x7f, 0x7f, 0x7f, 0x7f, 0x7f }, //239 +{ 0x00, 0x00, 0x00, 0x00, 0x00 }, //240 +{ 0x00, 0x00, 0x00, 0x00, 0x00 }, //241 +{ 0x00, 0x00, 0x00, 0x00, 0x00 }, //242 +{ 0x00, 0x00, 0x00, 0x00, 0x00 }, //243 +{ 0x00, 0x00, 0x00, 0x00, 0x00 }, //244 +{ 0x00, 0x00, 0x00, 0x00, 0x00 }, //245 +{ 0x00, 0x00, 0x00, 0x00, 0x00 }, //246 +{ 0x00, 0x00, 0x00, 0x00, 0x00 }, //247 +{ 0x00, 0x00, 0x00, 0x00, 0x00 }, //248 +{ 0x00, 0x00, 0x00, 0x00, 0x00 }, //249 +{ 0x00, 0x00, 0x00, 0x00, 0x00 }, //250 +{ 0x00, 0x00, 0x00, 0x00, 0x00 }, //251 +{ 0x00, 0x00, 0x00, 0x00, 0x00 }, //252 +{ 0x00, 0x00, 0x00, 0x00, 0x00 }, //253 +{ 0x00, 0x00, 0x00, 0x00, 0x00 }, //254 +{ 0x00, 0x00, 0x00, 0x00, 0x00 }, //255 + }; + + +inline static bool displayBit( unsigned value, unsigned row, unsigned column ) +{ + assert( value < 256 ); + assert( row < 7 ); + assert( column < 5 ); + return characterMap[value][column] & (1 << row); +}; + + +Item* MatrixDisplayDriver::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new MatrixDisplayDriver( (ICNDocument*)itemDocument, newItem, id ); +} + + +LibraryItem * MatrixDisplayDriver::libraryItem() +{ + return new LibraryItem( + "ec/matrix_display_driver", + i18n("Matrix Display Driver"), + i18n("Integrated Circuits"), + "ic2.png", + LibraryItem::lit_component, + MatrixDisplayDriver::construct ); +} + + +MatrixDisplayDriver::MatrixDisplayDriver( ICNDocument *icnDocument, bool newItem, const char *id ) + : Component( icnDocument, newItem, id ? id : "Matrix Display Driver" ) +{ + m_name = i18n("Matrix Display Driver"); + + m_prevCol = 0; + m_nextCol = 0; + m_scanCount = 2; + + createProperty( "diode-configuration", Variant::Type::Select ); + property("diode-configuration")->setCaption( i18n("Configuration") ); + property("diode-configuration")->setAllowed( QStringList::split(',',"Row Cathode,Column Cathode") ); + property("diode-configuration")->setValue("Row Cathode"); + property("diode-configuration")->setAdvanced(true); + + QStringList pins = QStringList::split( ',', "D0,D1,D2,D3,D4,D5,D6,D7,,,,,,C4,C3,C2,C1,C0,,R0,R1,R2,R3,R4,R5,R6", true ); + initDIPSymbol( pins, 64 ); + initDIP(pins); + + m_pValueLogic.resize( 8, 0l ); + for ( unsigned i = 0; i < 8; ++i ) + m_pValueLogic[i] = createLogicIn( ecNodeWithID("D"+QString::number(i)) ); + + m_pRowLogic.resize( 7, 0l ); + for ( unsigned i = 0; i < 7; ++i ) + { + m_pRowLogic[i] = createLogicOut( ecNodeWithID("R"+QString::number(i)), false ); + m_pRowLogic[i]->setOutputLowConductance( 1.0 ); + m_pRowLogic[i]->setOutputHighVoltage(5.0); + } + + m_pColLogic.resize( 5, 0l ); + for ( unsigned i = 0; i < 5; ++i ) + { + m_pColLogic[i] = createLogicOut( ecNodeWithID("C"+QString::number(i)), false ); + m_pColLogic[i]->setOutputHighVoltage(5.0); + } +} + + +MatrixDisplayDriver::~MatrixDisplayDriver() +{ +} + + +void MatrixDisplayDriver::stepNonLogic() +{ + if ( ++m_scanCount < 5 ) + return; + m_scanCount = 0; + + m_pColLogic[m_prevCol]->setHigh(false); + m_pColLogic[m_nextCol]->setHigh(true); + + unsigned value = 0; + for ( unsigned i = 0; i < 8; ++i ) + value |= ( m_pValueLogic[i]->isHigh() ) ? (1 << i) : 0; + + for ( unsigned row = 0; row < 7; row++ ) + { + m_pRowLogic[row]->setHigh( !displayBit( value, row, m_nextCol) ); + } + + m_prevCol = m_nextCol; + + m_nextCol++; + if ( m_nextCol >= 5 ) + m_nextCol = 0; +} + diff --git a/src/electronics/components/matrixdisplaydriver.h b/src/electronics/components/matrixdisplaydriver.h new file mode 100644 index 0000000..e6b01cc --- /dev/null +++ b/src/electronics/components/matrixdisplaydriver.h @@ -0,0 +1,42 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef MATRIXDISPLAYDRIVER_H +#define MATRIXDISPLAYDRIVER_H + +#include "matrixdisplay.h" + +/** +@author David Saxton + */ +class MatrixDisplayDriver : public Component +{ + public: + MatrixDisplayDriver( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~MatrixDisplayDriver(); + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + + virtual bool canFlip() const { return true; } + virtual void stepNonLogic(); + virtual bool doesStepNonLogic() const { return true; } + + protected: + QValueVector<LogicIn*> m_pValueLogic; + QValueVector<LogicOut*> m_pRowLogic; + QValueVector<LogicOut*> m_pColLogic; + + unsigned m_prevCol; + unsigned m_nextCol; + unsigned m_scanCount; +}; + +#endif diff --git a/src/electronics/components/meter.cpp b/src/electronics/components/meter.cpp new file mode 100644 index 0000000..4437794 --- /dev/null +++ b/src/electronics/components/meter.cpp @@ -0,0 +1,265 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "canvasitemparts.h" +#include "ecnode.h" +#include "element.h" +#include "libraryitem.h" +#include "meter.h" +#include "variant.h" +#include "voltagesource.h" +#include "pin.h" +#include "simulator.h" + +#include <cmath> +#include <klocale.h> +#include <qpainter.h> + + +//BEGIN class Meter +Meter::Meter( ICNDocument *icnDocument, bool newItem, const char *id ) + : Component( icnDocument, newItem, id ) +{ + m_bDynamicContent = true; + b_timerStarted = false; + m_timeSinceUpdate = 0.; + m_old_value = 0.; + m_avgValue = 0.; + b_firstRun = true; + setSize( -16, -16, 32, 32 ); + + p_displayText = addDisplayText( "meter", QRect( -16, 16, 32, 16 ), displayText() ); + + createProperty( "0-minValue", Variant::Type::Double ); + property("0-minValue")->setCaption( i18n("Minimum Value") ); + property("0-minValue")->setMinValue(1e-12); + property("0-minValue")->setMaxValue(1e12); + property("0-minValue")->setValue(1e-3); + + createProperty( "1-maxValue", Variant::Type::Double ); + property("1-maxValue")->setCaption( i18n("Maximum Value") ); + property("1-maxValue")->setMinValue(1e-12); + property("1-maxValue")->setMaxValue(1e12); + property("1-maxValue")->setValue(1e3); +} + + +Meter::~Meter() +{ +} + + +void Meter::dataChanged() +{ + m_minValue = dataDouble("0-minValue"); + m_maxValue = dataDouble("1-maxValue"); + setChanged(); +} + +void Meter::stepNonLogic() +{ + if (b_firstRun) + { + p_displayText->setText(displayText()); + updateAttachedPositioning(); + setChanged(); + property("0-minValue")->setUnit(m_unit); + property("1-maxValue")->setUnit(m_unit); + b_firstRun = false; + } + + const double v = meterValue(); + if ( !b_timerStarted && std::abs(((v-m_old_value)/m_old_value)) > 1e-6 ) { + b_timerStarted = true; + } + + if (b_timerStarted) + { + m_timeSinceUpdate += 1./LINEAR_UPDATE_RATE; + m_avgValue += v/LINEAR_UPDATE_RATE; +// setChanged(); + if ( m_timeSinceUpdate > 0.05 ) + { + if ( p_displayText->setText(displayText()) ); + updateAttachedPositioning(); + } + } +} + +void Meter::drawShape( QPainter &p ) +{ + initPainter(p); + p.drawEllipse( (int)x()-16, (int)y()-16, width(), width() ); + p.setPen(QPen(Qt::black,2)); + p.setBrush(Qt::black); + + // The proportion between 0.1mV and 10KV, on a logarithmic scale + double prop; + const double abs_value = std::abs(m_old_value); + if ( abs_value <= m_minValue ) + prop = 0.; + else if ( abs_value >= m_maxValue ) + prop = 1.; + else + prop = std::log10( abs_value/m_minValue ) / std::log10( m_maxValue/m_minValue ); + if ( m_old_value>0 ) + prop *= -1; + double sin_prop = 10*std::sin(prop*1.571); // 1.571 = pi/2 + double cos_prop = 10*std::cos(prop*1.571); // 1.571 = pi/2 + + int cx = int(x()-16+(width()/2)); + int cy = int(y()-16+(height()/2)); + p.drawLine( int(cx-sin_prop), int(cy-cos_prop), int(cx+sin_prop), int(cy+cos_prop) ); + + QPointArray pa(3); + pa[0] = QPoint( int(cx-sin_prop), int(cy-cos_prop) ); // Arrow head + pa[1] = QPoint( int(cx-sin_prop + 8*std::sin(1.571*(-0.3+prop))), int(cy-cos_prop + 8*std::cos(1.571*(-0.3+prop))) ); + pa[2] = QPoint( int(cx-sin_prop + 8*std::sin(1.571*(0.3+prop))), int(cy-cos_prop + 8*std::cos(1.571*(0.3+prop))) ); + p.drawPolygon(pa); + + deinitPainter(p); +} + + +QString Meter::displayText() +{ + double value = m_avgValue/m_timeSinceUpdate; + if ( !std::isfinite(value) ) value = m_old_value; + if ( std::abs((value)) < 1e-9 ) value = 0.; + m_old_value = value; + m_avgValue = 0.; + m_timeSinceUpdate = 0.; + b_timerStarted = false; + return QString::number( value/CNItem::getMultiplier(value), 'g', 3 ) + CNItem::getNumberMag(value) + m_unit; +} +//END class Meter + + +//BEGIN class FrequencyMeter +Item* FrequencyMeter::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new FrequencyMeter( (ICNDocument*)itemDocument, newItem, id ); +} + +LibraryItem* FrequencyMeter::libraryItem() +{ + return new LibraryItem( + QString("ec/frequencymeter"), + i18n("Frequency Meter (TODO)"), + i18n("Outputs"), + "frequencymeter.png", + LibraryItem::lit_component, + FrequencyMeter::construct ); +} + +FrequencyMeter::FrequencyMeter( ICNDocument *icnDocument, bool newItem, const char *id ) + : Meter( icnDocument, newItem, (id) ? id : "frequencymeter" ) +{ + m_name = i18n("Frequency Meter"); + m_desc = i18n("Place this at the point where frequency is to be measured."); + m_unit = "Hz"; + + m_probeNode = createPin( 0, -24, 90, "n1" ); +} + +FrequencyMeter::~FrequencyMeter() +{ +} + +double FrequencyMeter::meterValue() +{ + return 0; +} +//END class FrequencyMeter + + +//BEGIN class ECAmmeter +Item* ECAmmeter::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new ECAmmeter( (ICNDocument*)itemDocument, newItem, id ); +} + +LibraryItem* ECAmmeter::libraryItem() +{ + QStringList ids; + ids << "ec/ammeter" << "ec/ammmeter"; + return new LibraryItem( + ids, + i18n("Ammeter"), + i18n("Outputs"), + "ammeter.png", + LibraryItem::lit_component, + ECAmmeter::construct + ); +} + +ECAmmeter::ECAmmeter( ICNDocument *icnDocument, bool newItem, const char *id ) + : Meter( icnDocument, newItem, (id) ? id : "ammeter" ) +{ + m_name = i18n("Ammeter"); + m_desc = i18n("Place this in series in the circuit to measure the current flowing."); + setSize( -16, -16, 32, 32 ); + m_unit = "A"; + + init1PinLeft(0); + init1PinRight(0); + + m_voltageSource = createVoltageSource( m_pNNode[0], m_pPNode[0], 0. ); +} + +ECAmmeter::~ECAmmeter() +{ +} + +double ECAmmeter::meterValue() +{ + return -m_voltageSource->cbranchCurrent(0); +} +//END class ECAmmeter + + +//BEGIN class ECVoltmeter +Item* ECVoltMeter::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new ECVoltMeter( (ICNDocument*)itemDocument, newItem, id ); +} + +LibraryItem* ECVoltMeter::libraryItem() +{ + return new LibraryItem( + QString("ec/voltmeter"), + i18n("Voltmeter"), + i18n("Outputs"), + "voltmeter.png", + LibraryItem::lit_component, + ECVoltMeter::construct ); +} + +ECVoltMeter::ECVoltMeter( ICNDocument *icnDocument, bool newItem, const char *id ) + : Meter( icnDocument, newItem, (id) ? id : "voltmeter" ) +{ + m_name = i18n("Voltmeter"); + m_desc = i18n("Place this in parallel in the circuit to meaure the voltage between two points."); + m_unit = "V"; + + init1PinLeft(0); + init1PinRight(0); +} + +ECVoltMeter::~ECVoltMeter() +{ +} + +double ECVoltMeter::meterValue() +{ + return m_pNNode[0]->pin()->voltage() - m_pPNode[0]->pin()->voltage(); +} +//END class ECVoltMeter + diff --git a/src/electronics/components/meter.h b/src/electronics/components/meter.h new file mode 100644 index 0000000..fa52e95 --- /dev/null +++ b/src/electronics/components/meter.h @@ -0,0 +1,103 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef METER_H +#define METER_H + +#include <component.h> + +/** +@author David Saxton +*/ +class Meter : public Component +{ +public: + Meter( ICNDocument *icnDocument, bool newItem, const char *id ); + ~Meter(); + + virtual void stepNonLogic(); + virtual bool doesStepNonLogic() const { return true; } + virtual void drawShape( QPainter &p ); + +protected: + QString displayText(); + virtual void dataChanged(); + /** + * Return the value / current, or whatever the meter is measuring + */ + virtual double meterValue() = 0; + + bool b_firstRun; // If true, then update the text dispalyed + bool b_timerStarted; // The timer to change the text is started on change + double m_timeSinceUpdate; + double m_avgValue; + double m_old_value; + double m_minValue; + double m_maxValue; + Text *p_displayText; + QString m_unit; +}; + +/** +@short Measures the frequency at a point in the circuit +@author David Saxton +*/ +class FrequencyMeter : public Meter +{ +public: + FrequencyMeter( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~FrequencyMeter(); + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + +protected: + virtual double meterValue(); + ECNode *m_probeNode; +}; + +/** +@short Simple resistor +@author David Saxton +*/ +class ECAmmeter : public Meter +{ +public: + ECAmmeter( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~ECAmmeter(); + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + +protected: + virtual double meterValue(); + +private: + VoltageSource *m_voltageSource; +}; + +/** +@short Displays voltage across terminals +@author David Saxton +*/ +class ECVoltMeter : public Meter +{ +public: + ECVoltMeter( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~ECVoltMeter(); + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + +protected: + virtual double meterValue(); +}; + +#endif diff --git a/src/electronics/components/multiinputgate.cpp b/src/electronics/components/multiinputgate.cpp new file mode 100644 index 0000000..7453845 --- /dev/null +++ b/src/electronics/components/multiinputgate.cpp @@ -0,0 +1,530 @@ +/*************************************************************************** + * Copyright (C) 2004-2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "ecnode.h" +#include "icndocument.h" +#include "libraryitem.h" +#include "multiinputgate.h" +#include "logic.h" + +#include <cmath> +#include <klocale.h> +#include <qpainter.h> + +//BEGIN class MultiInputGate +MultiInputGate::MultiInputGate( ICNDocument *icnDocument, bool newItem, const char *id, int baseWidth ) + : Component( icnDocument, newItem, id ) +{ + b_doneInit = false; + m_numInputs = 0; + if ( baseWidth == -1 ) { + baseWidth = 32; + } + m_baseWidth = baseWidth; + + for ( int i=0; i<maxGateInput; ++i ) + { + inLogic[i] = 0l; + inNode[i] = 0l; + } + + updateInputs(2); + + init1PinRight(16); + m_pOut = createLogicOut( m_pPNode[0], false ); + + + createProperty( "numInput", Variant::Type::Int ); + property("numInput")->setCaption( i18n("Number Inputs") ); + property("numInput")->setMinValue(2); + property("numInput")->setMaxValue(maxGateInput); + property("numInput")->setValue(2); + + b_doneInit = true; +} + + +MultiInputGate::~MultiInputGate() +{ +} + + +void MultiInputGate::dataChanged() +{ + updateInputs( QMIN( maxGateInput, dataInt("numInput") ) ); +} + + +void MultiInputGate::updateInputs( int newNum ) +{ + if ( newNum == m_numInputs ) { + return; + } + + if ( newNum < 2 ) { + newNum = 2; + } + else if ( newNum > maxGateInput ) { + newNum = maxGateInput; + } + + const int newWidth = m_baseWidth; + + setSize( -newWidth/2, -8*newNum, newWidth, 16*newNum, true ); + + const bool added = ( newNum > m_numInputs ); + if (added) + { + for ( int i = m_numInputs; i<newNum; ++i ) + { + ECNode *node = createPin( 0, 0, 0, "in"+QString::number(i) ); + inNode[i] = node; + inLogic[i] = createLogicIn(node); + inLogic[i]->setCallback( this, (CallbackPtr)(&MultiInputGate::inStateChanged) ); + } + } + else + { + for ( int i=newNum; i<m_numInputs; ++i ) + { + removeNode("in"+QString::number(i)); + removeElement( inLogic[i], false ); + inNode[i] = 0l; + inLogic[i] = 0l; + } + } + + m_numInputs = newNum; + + // We can't call a pure-virtual function if we haven't finished our constructor yet... + if (b_doneInit) + inStateChanged(!added); + + updateAttachedPositioning(); +} + + +void MultiInputGate::updateAttachedPositioning() +{ + // Check that our ndoes have been created before we attempt to use them + if ( !m_nodeMap.contains("p1") || !m_nodeMap.contains("in"+QString::number(m_numInputs-1)) ) + return; + + int _x = offsetX()+8; + int _y = offsetY()+8; + + m_nodeMap["p1"].x = m_baseWidth/2 + 8; + m_nodeMap["p1"].y = 0; + + for ( int i=0; i< m_numInputs; ++i ) + { + m_nodeMap["in"+QString::number(i)].x = _x - 16; + m_nodeMap["in"+QString::number(i)].y = _y + 16*i; + } + + if (b_doneInit) + Component::updateAttachedPositioning(); +} +//END class MultiInputGate + + +//BEGIN class ECXNor +Item* ECXnor::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new ECXnor( (ICNDocument*)itemDocument, newItem, id ); +} + +LibraryItem* ECXnor::libraryItem() +{ + return new LibraryItem( + QString::QString("ec/xnor"), + i18n("XNOR gate"), + i18n("Logic"), + "xnor.png", + LibraryItem::lit_component, + ECXnor::construct ); +} + +ECXnor::ECXnor( ICNDocument *icnDocument, bool newItem, const char *id ) + : MultiInputGate( icnDocument, newItem, (id) ? id : "xnor", 48 ) +{ + m_name = i18n("XNOR gate"); + m_desc = i18n("Exclusive NOR gate. Output is low when exactly one input is high."); + + inStateChanged(false); +} + +ECXnor::~ECXnor() +{ +} + +void ECXnor::inStateChanged(bool) +{ + int highCount = 0; + for ( int i=0; i<m_numInputs; ++i ) + { + if ( inLogic[i]->isHigh() ) + highCount++; + } + + m_pOut->setHigh( highCount != 1 ); +} + +void ECXnor::drawShape( QPainter &p ) +{ + initPainter(p); + int _x = (int)x()+offsetX(); + int _y = (int)y()+offsetY(); + + p.save(); + p.setPen( Qt::NoPen ); + p.drawChord( _x-width()+22, _y, 2*width()-28, height(), -16*81, 16*162 ); + p.restore(); + + p.drawArc( _x-width()+22, _y, 2*width()-28, height(), -16*90, 16*180 ); + p.drawArc( _x-8, _y, 16, height(), -16*90, 16*180 ); + p.drawArc( _x, _y, 16, height(), -16*90, 16*180 ); + + p.drawEllipse( _x+width()-6, _y+(height()/2)-3, 6, 6 ); + + const int n = m_numInputs; + for ( int i=0; i<n; ++i ) + { + p.setPen( inNode[i]->isSelected() ? m_selectedCol : Qt::black ); + int pin_x = (int)std::sqrt((double)(64*n*n - (8*n-8-16*i)*(8*n-8-16*i)))/n; + p.drawLine( _x, _y+16*i+8, _x+pin_x, _y+16*i+8 ); + } + + deinitPainter(p); +} +//END class ECXnor + + +//BEGIN class ECXor +Item* ECXor::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new ECXor( (ICNDocument*)itemDocument, newItem, id ); +} + +LibraryItem* ECXor::libraryItem() +{ + return new LibraryItem( + QString::QString("ec/xor"), + i18n("XOR gate"), + i18n("Logic"), + "xor.png", + LibraryItem::lit_component, + ECXor::construct ); +} + +ECXor::ECXor( ICNDocument *icnDocument, bool newItem, const char *id ) + : MultiInputGate( icnDocument, newItem, (id) ? id : "xor", 48 ) +{ + m_name = i18n("XOR gate"); + m_desc = i18n("Exclusive OR gate. Output is high when exactly one input is high."); + + inStateChanged(false); +} + +ECXor::~ECXor() +{ +} + +void ECXor::inStateChanged(bool) +{ + int highCount = 0; + for ( int i=0; i<m_numInputs; ++i ) + { + if ( inLogic[i]->isHigh() ) + highCount++; + } + + m_pOut->setHigh( highCount == 1 ); +} + +void ECXor::drawShape( QPainter &p ) +{ + initPainter(p); + int _x = (int)x()+offsetX(); + int _y = (int)y()+offsetY(); + + p.save(); + p.setPen( Qt::NoPen ); + p.drawChord( _x-width()+16, _y, 2*width()-16, height(), -16*81, 16*162 ); + p.restore(); + + p.drawArc( _x-width()+16, _y, 2*width()-16, height(), -16*90, 16*180 ); + p.drawArc( _x-8, _y, 16, height(), -16*90, 16*180 ); + p.drawArc( _x, _y, 16, height(), -16*90, 16*180 ); + + const int n = m_numInputs; + for ( int i=0; i<n; ++i ) + { + p.setPen( inNode[i]->isSelected() ? m_selectedCol : Qt::black ); + int pin_x = (int)std::sqrt((double)(64*n*n - (8*n-8-16*i)*(8*n-8-16*i)))/n; + p.drawLine( _x, _y+16*i+8, _x+pin_x, _y+16*i+8 ); + } + + deinitPainter(p); +} +//END class ECXor + + +//BEGIN class EXOr +Item* ECOr::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new ECOr( (ICNDocument*)itemDocument, newItem, id ); +} + +LibraryItem* ECOr::libraryItem() +{ + return new LibraryItem( + QString::QString("ec/or"), + i18n("OR gate"), + i18n("Logic"), + "or.png", + LibraryItem::lit_component, + ECOr::construct ); +} + +ECOr::ECOr( ICNDocument *icnDocument, bool newItem, const char *id ) + : MultiInputGate( icnDocument, newItem, (id) ? id : "or", 48 ) +{ + m_name = i18n("OR gate"); + m_desc = i18n("The output is high when at least one of the inputs is high; or low when all of the inputs are off"); + + inStateChanged(false); +} + +ECOr::~ECOr() +{ +} + +void ECOr::inStateChanged(bool) +{ + bool allLow = true; + for ( int i=0; i<m_numInputs && allLow; ++i ) + { + if ( inLogic[i]->isHigh() ) + allLow = false; + } + + m_pOut->setHigh(!allLow); +} + +void ECOr::drawShape( QPainter &p ) +{ + initPainter(p); + int _x = (int)x()+offsetX(); + int _y = (int)y()+offsetY(); + + p.save(); + p.setPen( Qt::NoPen ); +// p.setBrush( Qt::red ); + p.drawChord( _x-width(), _y, 2*width(), height(), -16*81, 16*162 ); +// p.drawPie( _x-width()+16, _y, 2*width()-16, height(), -16*100, 16*200 ); + p.restore(); + + p.drawArc( _x-width(), _y, 2*width(), height(), -16*90, 16*180 ); + p.drawArc( _x-8, _y, 16, height(), -16*90, 16*180 ); + + const int n = m_numInputs; + for ( int i=0; i<n; ++i ) + { + p.setPen( inNode[i]->isSelected() ? m_selectedCol : Qt::black ); + int pin_x = (int)std::sqrt((double)(64*n*n - (8*n-8-16*i)*(8*n-8-16*i)))/n; + p.drawLine( _x, _y+16*i+8, _x+pin_x, _y+16*i+8 ); + } + + deinitPainter(p); +} +//END class ECOr + + +//BEGIN class ECNor +Item* ECNor::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new ECNor( (ICNDocument*)itemDocument, newItem, id ); +} + +LibraryItem* ECNor::libraryItem() +{ + return new LibraryItem( + QString::QString("ec/nor"), + i18n("NOR gate"), + i18n("Logic"), + "nor.png", + LibraryItem::lit_component, + ECNor::construct ); +} + +ECNor::ECNor( ICNDocument *icnDocument, bool newItem, const char *id ) + : MultiInputGate( icnDocument, newItem, (id) ? id : "nor", 48 ) +{ + m_name = i18n("NOR Gate"); + m_desc = i18n("The output is high when all inputs are low."); + + inStateChanged(false); +} + +ECNor::~ECNor() +{ +} + +void ECNor::inStateChanged(bool) +{ + bool allLow = true; + for ( int i=0; i<m_numInputs && allLow; ++i ) + { + if ( inLogic[i]->isHigh() ) + allLow = false; + } + + m_pOut->setHigh(allLow); +} + +void ECNor::drawShape( QPainter &p ) +{ + initPainter(p); + int _x = (int)x()+offsetX(); + int _y = (int)y()+offsetY(); + + p.save(); + p.setPen( Qt::NoPen ); + p.drawChord( _x-width()+6, _y, 2*width()-12, height(), -16*81, 16*162 ); + p.restore(); + + p.drawArc( _x-width()+6, _y, 2*width()-12, height(), -16*90, 16*180 ); + p.drawArc( _x-8, _y, 16, height(), -16*90, 16*180 ); + + p.drawEllipse( _x+width()-6, _y+(height()/2)-3, 6, 6 ); + + const int n = m_numInputs; + for ( int i=0; i<n; ++i ) + { + p.setPen( inNode[i]->isSelected() ? m_selectedCol : Qt::black ); + int pin_x = (int)std::sqrt((double)(64*n*n - (8*n-8-16*i)*(8*n-8-16*i)))/n; + p.drawLine( _x, _y+16*i+8, _x+pin_x, _y+16*i+8 ); + } + + deinitPainter(p); +} +//END class ECNor + + +//BEGIN class ECNand +Item* ECNand::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new ECNand( (ICNDocument*)itemDocument, newItem, id ); +} + +LibraryItem* ECNand::libraryItem() +{ + return new LibraryItem( + QString::QString("ec/nand"), + i18n("NAND gate"), + i18n("Logic"), + "nand.png", + LibraryItem::lit_component, + ECNand::construct ); +} + +ECNand::ECNand( ICNDocument *icnDocument, bool newItem, const char *id ) + : MultiInputGate( icnDocument, newItem, id ? id : "nand" ) +{ + m_name = i18n("NAND Gate"); + m_desc = i18n("The output is low only when all of the inputs are high."); + + inStateChanged(false); +} + +ECNand::~ECNand() +{ +} + +void ECNand::inStateChanged(bool) +{ + bool allHigh = true; + for ( int i=0; i<m_numInputs && allHigh; ++i ) + { + if ( !inLogic[i]->isHigh() ) + allHigh = false; + } + + m_pOut->setHigh(!allHigh); +} + +void ECNand::drawShape( QPainter &p ) +{ + initPainter(p); + int _x = (int)x()+offsetX(); + int _y = (int)y()+offsetY(); + p.drawChord( _x-width()+6, _y, 2*width()-12, height(), -16*90, 16*180 ); + p.drawEllipse( _x+width()-6, _y+(height()/2)-3, 6, 6 ); + deinitPainter(p); +} +//END class ECNand + + +//BEGIN class ECAnd +Item* ECAnd::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new ECAnd( (ICNDocument*)itemDocument, newItem, id ); +} + +LibraryItem* ECAnd::libraryItem() +{ + QStringList idList; + idList << "ec/and" << "ec/and_2"; + return new LibraryItem( + idList, + i18n("AND gate"), + i18n("Logic"), + "and.png", + LibraryItem::lit_component, + ECAnd::construct ); +} + +ECAnd::ECAnd( ICNDocument *icnDocument, bool newItem, const char *id ) + : MultiInputGate( icnDocument, newItem, id ? id : "and" ) +{ + m_name = i18n("AND Gate"); + m_desc = i18n("The output is high if and only if all of the inputs are high."); + + inStateChanged(false); +} + +ECAnd::~ECAnd() +{ +} + +void ECAnd::inStateChanged(bool) +{ + bool allHigh = true; + for ( int i=0; i<m_numInputs && allHigh; ++i ) + { + if ( !inLogic[i]->isHigh() ) + allHigh = false; + } + + m_pOut->setHigh(allHigh); +} + +void ECAnd::drawShape( QPainter &p ) +{ + initPainter(p); + + int _x = (int)x()+offsetX(); + int _y = (int)y()+offsetY(); + p.drawChord( _x-width(), _y, 2*width(), height(), -16*90, 16*180 ); + + deinitPainter(p); +} +//END class ECAnd diff --git a/src/electronics/components/multiinputgate.h b/src/electronics/components/multiinputgate.h new file mode 100644 index 0000000..1981217 --- /dev/null +++ b/src/electronics/components/multiinputgate.h @@ -0,0 +1,160 @@ +/*************************************************************************** + * Copyright (C) 2004-2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef MULTIINPUTGATE_H +#define MULTIINPUTGATE_H + +#include "component.h" +#include "logic.h" + +const int maxGateInput = 256; + +/** +@author David Saxton +*/ +class MultiInputGate : public CallbackClass, public Component +{ +public: + MultiInputGate( ICNDocument *icnDocument, bool newItem, const char *id, int baseWidth = -1 ); + ~MultiInputGate(); + +protected: + virtual void inStateChanged( bool newState ) = 0; + void dataChanged(); + void updateInputs( int newNum ); + + int m_numInputs; + int m_baseWidth; + + LogicIn *inLogic[maxGateInput]; + ECNode *inNode[maxGateInput]; + + LogicOut * m_pOut; + + virtual void updateAttachedPositioning(); + +private: + bool b_doneInit; +}; + + +/** +@short Boolean XNOR +@author David Saxton +*/ +class ECXnor : public MultiInputGate +{ +public: + ECXnor( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~ECXnor(); + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + +private: + void inStateChanged( bool newState ); + virtual void drawShape( QPainter &p ); +}; + + +/** +@short Boolean XOR +@author David Saxton +*/ +class ECXor : public MultiInputGate +{ +public: + ECXor( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~ECXor(); + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + +protected: + void inStateChanged( bool newState ); + virtual void drawShape( QPainter &p ); +}; + + +/** +@short Boolean OR +@author David Saxton +*/ +class ECOr : public MultiInputGate +{ +public: + ECOr( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~ECOr(); + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + +protected: + void inStateChanged( bool newState ); + virtual void drawShape( QPainter &p ); +}; + +/** +@short Boolean NOR +@author David Saxton +*/ +class ECNor : public MultiInputGate +{ +public: + ECNor( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~ECNor(); + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + +private: + void inStateChanged( bool newState ); + virtual void drawShape( QPainter &p ); +}; + +/** +@short Boolean NAND +@author David Saxton +*/ +class ECNand : public MultiInputGate +{ +public: + ECNand( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~ECNand(); + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + +private: + void inStateChanged( bool newState ); + virtual void drawShape( QPainter &p ); +}; + +/** +@short Boolean AND +@author David Saxton +*/ +class ECAnd : public MultiInputGate +{ +public: + ECAnd( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~ECAnd(); + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + +private: + void inStateChanged( bool newState ); + virtual void drawShape( QPainter &p ); +}; + + + +#endif diff --git a/src/electronics/components/multiplexer.cpp b/src/electronics/components/multiplexer.cpp new file mode 100644 index 0000000..e421fee --- /dev/null +++ b/src/electronics/components/multiplexer.cpp @@ -0,0 +1,176 @@ +/*************************************************************************** + * Copyright (C) 2004-2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "multiplexer.h" + +#include "logic.h" +#include "libraryitem.h" + +#include <kiconloader.h> +#include <klocale.h> + +#include <cmath> + +Item* Multiplexer::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new Multiplexer( (ICNDocument*)itemDocument, newItem, id ); +} + +LibraryItem* Multiplexer::libraryItem() +{ + return new LibraryItem( + QString("ec/multiplexer"), + i18n("Multiplexer"), + i18n("Integrated Circuits"), + "ic1.png", + LibraryItem::lit_component, + Multiplexer::construct + ); +} + +Multiplexer::Multiplexer( ICNDocument *icnDocument, bool newItem, const char *id ) + : Component( icnDocument, newItem, (id) ? id : "multiplexer" ) +{ + m_name = i18n("Multiplexer"); + m_desc = i18n("Combines the input data stream into one single stream. The value of the input selected by the \"A\" inputs is passed to the output."); + + m_output = 0l; + + createProperty( "addressSize", Variant::Type::Int ); + property("addressSize")->setCaption( i18n("Address Size") ); + property("addressSize")->setMinValue(1); + property("addressSize")->setMaxValue(8); + property("addressSize")->setValue(1); + + // For backwards compatibility + createProperty( "numInput", Variant::Type::Int ); + property("numInput")->setMinValue(-1); + property("numInput")->setValue(-1); + property("numInput")->setHidden(true); +} + +Multiplexer::~Multiplexer() +{ +} + + +void Multiplexer::dataChanged() +{ + if ( hasProperty("numInput") && dataInt("numInput") != -1 ) + { + int addressSize = int( std::ceil( std::log( (double)dataInt("numInput") ) / std::log(2.0) ) ); + property("numInput")->setValue(-1); + + if ( addressSize < 1 ) + addressSize = 1; + else if ( addressSize > 8 ) + addressSize = 8; + + // This function will get called again when we set the value of numInput + property("addressSize")->setValue(addressSize); + return; + } + + if ( hasProperty("numInput") ) + { + m_variantData["numInput"]->deleteLater(); + m_variantData.remove("numInput"); + } + + initPins( unsigned(dataInt("addressSize")) ); +} + + +void Multiplexer::inStateChanged( bool /*state*/ ) +{ + unsigned long long pos = 0; + for ( unsigned i = 0; i < m_aLogic.size(); ++i ) + { + if ( m_aLogic[i]->isHigh() ) + pos += 1 << i; + } + m_output->setHigh( m_xLogic[pos]->isHigh() ); +} + + +void Multiplexer::initPins( unsigned newAddressSize ) +{ + unsigned oldAddressSize = m_aLogic.size(); + unsigned long long oldXLogicCount = m_xLogic.size(); + unsigned long long newXLogicCount = 1 << newAddressSize; + + if ( newXLogicCount == oldXLogicCount ) + return; + + QStringList pins; + + const int length = newAddressSize + newXLogicCount; + + for ( unsigned i=0; i<newAddressSize; ++i ) + pins += "A"+QString::number(i); + for ( unsigned i=0; i<newXLogicCount; ++i ) + pins += "X"+QString::number(i); + for ( int i=0; i<(length-(length%2))/2; ++i ) + pins += ""; + pins += "X"; + for ( int i=0; i<((length+(length%2))/2)-1; ++i ) + pins += ""; + + initDIPSymbol( pins, 64 ); + initDIP(pins); + + ECNode *node; + + if (!m_output) + { + node = ecNodeWithID("X"); + m_output = createLogicOut( node, false ); + } + + if ( newXLogicCount > oldXLogicCount ) + { + m_xLogic.resize(newXLogicCount); + for ( unsigned i=oldXLogicCount; i<newXLogicCount; ++i ) + { + node = ecNodeWithID("X"+QString::number(i)); + m_xLogic.insert( i, createLogicIn(node) ); + m_xLogic[i]->setCallback( this, (CallbackPtr)(&Multiplexer::inStateChanged) ); + } + + m_aLogic.resize(newAddressSize); + for ( unsigned i=oldAddressSize; i<newAddressSize; ++i ) + { + node = ecNodeWithID("A"+QString::number(i)); + m_aLogic.insert( i, createLogicIn(node) ); + m_aLogic[i]->setCallback( this, (CallbackPtr)(&Multiplexer::inStateChanged) ); + } + } + else + { + for ( unsigned i = newXLogicCount; i < oldXLogicCount; ++i ) + { + QString id = "X"+QString::number(i); + removeDisplayText(id); + removeElement( m_xLogic[i], false ); + removeNode(id); + } + m_xLogic.resize(newXLogicCount); + + for ( unsigned i = newAddressSize; i < oldAddressSize; ++i ) + { + QString id = "A"+QString::number(i); + removeDisplayText(id); + removeElement( m_aLogic[i], false ); + removeNode(id); + } + m_aLogic.resize(newAddressSize); + } +} + diff --git a/src/electronics/components/multiplexer.h b/src/electronics/components/multiplexer.h new file mode 100644 index 0000000..42bcfc1 --- /dev/null +++ b/src/electronics/components/multiplexer.h @@ -0,0 +1,46 @@ +/*************************************************************************** + * Copyright (C) 2004-2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef MULTIPLEXER_H +#define MULTIPLEXER_H + +#include "component.h" +#include "logic.h" + +#include <qptrvector.h> + +/** +@author David Saxton +*/ +class Multiplexer : public CallbackClass, public Component +{ +public: + Multiplexer( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~Multiplexer(); + + virtual bool canFlip() const { return true; } + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + +protected: + void dataChanged(); + /** + * Add / remove pins according to the number of inputs the user has requested + */ + void initPins( unsigned addressSize ); + + void inStateChanged( bool newState ); + + QPtrVector<LogicIn> m_aLogic; + QPtrVector<LogicIn> m_xLogic; + LogicOut * m_output; +}; + +#endif diff --git a/src/electronics/components/parallelportcomponent.cpp b/src/electronics/components/parallelportcomponent.cpp new file mode 100644 index 0000000..9bc17b4 --- /dev/null +++ b/src/electronics/components/parallelportcomponent.cpp @@ -0,0 +1,241 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "port.h" +#include "parallelportcomponent.h" + +#include "ecnode.h" +#include "itemdocument.h" +#include "libraryitem.h" +#include "pin.h" +#include "resistance.h" + +#include <kdebug.h> +#include <klocale.h> +#include <qpainter.h> + +#include <cmath> +#include <termios.h> + +Item* ParallelPortComponent::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new ParallelPortComponent( (ICNDocument*)itemDocument, newItem, id ); +} + +LibraryItem* ParallelPortComponent::libraryItem() +{ + return new LibraryItem( + "ec/parallel_port", + i18n("Parallel Port"), + i18n("Connections"), + "ic1.png", + LibraryItem::lit_component, + ParallelPortComponent::construct + ); +} + +ParallelPortComponent::ParallelPortComponent( ICNDocument *icnDocument, bool newItem, const char *id ) + : Component( icnDocument, newItem, id ? id : "parallel_port" ) +{ + m_name = i18n("Parallel Port"); + m_desc = i18n("The pins are divided into 3 registers.<br><br>" + "<b>Data Pins</b><br><br>" + "The data pins can be configured as either all input or all output. They are:" + "<ul>" + "<li><b>D<i>[0..7]</i></b></li>" + "</ul><br>" + "<b>Status Pins</b><br><br>" + "The status pins are read-only. They area:" + "<ul>" + "<li><b>ERR</b> - Error</li>" + "<li><b>ON</b> - Online</li>" + "<li><b>PE</b> - Paper End</li>" + "<li><b>ACK</b> - Acknowledge</li>" + "<li><b>BUSY</b> - Busy</li>" + "</ul><br>" + "<b>Control Pins</b>" + "<ul>" + "<li><b>STR</b> - Strobe</li>" + "<li><b>AUT</b> - Auto Feed</li>" + "<li><b>INIT</b> - Init</li>" + "<li><b>SEL</b> - Select</li>" + "</ul><br>" + "The remaining pins are all ground." + ); + + QPointArray pa( 4 ); + pa[0] = QPoint( -32, -112 ); + pa[1] = QPoint( 32, -104 ); + pa[2] = QPoint( 32, 104 ); + pa[3] = QPoint( -32, 112 ); + setItemPoints( pa ); + + m_pParallelPort = new ParallelPort(); + + for ( unsigned i = 0; i < 24; ++i ) + m_pLogic[i] = 0; + + ECNode * pin = 0; + + //BEGIN Data register + for ( int i = 0; i < 8; ++i ) + { + QString id = QString("D%1").arg(i); + QString name = id; + + pin = createPin( -40, -80 + 16*i, 0, id ); + addDisplayText( id, QRect( -28, -88 + 16*i, 28, 16 ), name, true, Qt::AlignLeft | Qt::AlignVCenter ); + + m_pLogic[i] = createLogicOut( pin, false ); + m_pLogic[i]->setCallback( this, (CallbackPtr)(&ParallelPortComponent::dataCallback) ); + } + //END Data register + + + //BEGIN Status register + QString statusNames[] = { "ERR", "ON", "PE", "ACK", "BUSY" }; + + // The statusIDs are referenced in the save file and must not change + QString statusIDs[] = { "ERROR", "ONLINE", "PE", "ACK", "BUSY" }; + + // Bits 0...2 in the Status register are not used + for ( int i = 3; i < 8; ++i ) + { + QString id = statusIDs[i-3]; + QString name = statusNames[i-3]; + + // Bit 3 (pin 15) doesn't not follow the same positioning pattern as + // the other pins in the Status register. + if ( i == 3 ) + { + pin = createPin( 40, -72, 180, id ); + addDisplayText( id, QRect( 0, -80, 28, 16 ), name, true, Qt::AlignRight | Qt::AlignVCenter ); + } + else + { + pin = createPin( -40, -16 + 16*i, 0, id ); + addDisplayText( id, QRect( -28, -24 + 16*i, 28, 16 ), name, true, Qt::AlignLeft | Qt::AlignVCenter ); + } + + m_pLogic[i+8] = createLogicOut( pin, false ); + } + //END Status register + + + //BEGIN Control register + QString controlNames[] = { "STR", "AUT", "INIT", "SEL" }; + + // The controlIDs are referenced in the save file and must not change + QString controlIDs[] = { "STROBE", "AUTO", "INIT", "SELECT" }; + + // Bits 4..7 are not used (well; bit 5 is, but not as a pin) + for ( int i = 0; i < 4; ++i ) + { + QString id = controlIDs[i]; + QString name = controlNames[i]; + + if ( i == 0 ) + { + pin = createPin( -40, -96, 0, id ); + addDisplayText( id, QRect( -28, -104, 28, 16 ), name, true, Qt::AlignLeft | Qt::AlignVCenter ); + } + else if ( i == 1 ) + { + pin = createPin( 40, -88, 180, id ); + addDisplayText( id, QRect( 0, -96, 28, 16 ), name, true, Qt::AlignRight | Qt::AlignVCenter ); + } + else + { + pin = createPin( 40, -88 + i*16, 180, id ); + addDisplayText( id, QRect( 0, -96 + i*16, 28, 16 ), name, true, Qt::AlignRight | Qt::AlignVCenter ); + } + + m_pLogic[i+16] = createLogicOut( pin, false ); + m_pLogic[i+16]->setCallback( this, (CallbackPtr)(&ParallelPortComponent::controlCallback) ); + } + //END Control register + + +#if 0 + // And make the rest of the pins ground + for ( int i = 0; i < 8; ++i ) + { + pin = createPin( 40, -24 + i*16, 180, QString("GND%1").arg( i ) ); + pin->pin()->setGroundType( Pin::gt_always ); + } +#endif + + Variant * v = createProperty( "port", Variant::Type::Combo ); + v->setAllowed( ParallelPort::ports( Port::ExistsAndRW ) ); + v->setCaption( i18n("Port") ); +} + + +ParallelPortComponent::~ParallelPortComponent() +{ + delete m_pParallelPort; +} + + +void ParallelPortComponent::dataChanged() +{ + initPort( dataString("port") ); +} + + +void ParallelPortComponent::initPort( const QString & port ) +{ + if ( port.isEmpty() ) + { + m_pParallelPort->closePort(); + return; + } + + if ( ! m_pParallelPort->openPort( port ) ) + { + p_itemDocument->canvas()->setMessage( i18n("Could not open port %1").arg( port ) ); + return; + } +} + + +void ParallelPortComponent::dataCallback( bool ) +{ + uchar value = 0; + for ( unsigned i = 0; i < 8; ++ i ) + value |= m_pLogic[ i + 0 ]->isHigh() ? 0 : (1 << i); + + m_pParallelPort->writeToData( value ); +} + + +void ParallelPortComponent::controlCallback( bool ) +{ + uchar value = 0; + for ( unsigned i = 0; i < 4; ++ i ) + value |= m_pLogic[ i + 16 ]->isHigh() ? 0 : (1 << i); + + m_pParallelPort->writeToControl( value ); +} + + +void ParallelPortComponent::stepNonLogic() +{ + uchar status = m_pParallelPort->readFromRegister( ParallelPort::Status ); + // Bits 0...2 in the Status register are not used + for ( int i = 3; i < 8; ++i ) + m_pLogic[i + 8]->setHigh( status | (1 << i) ); +} + + +void ParallelPortComponent::drawShape( QPainter & p ) +{ + drawPortShape( p ); +} diff --git a/src/electronics/components/parallelportcomponent.h b/src/electronics/components/parallelportcomponent.h new file mode 100644 index 0000000..89ead80 --- /dev/null +++ b/src/electronics/components/parallelportcomponent.h @@ -0,0 +1,49 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef PARALLELPORTCOMPONENT_H +#define PARALLELPORTCOMPONENT_H + +#include "logic.h" +#include "component.h" + +class ParallelPort; + +/** +@author David Saxton + */ +class ParallelPortComponent : public CallbackClass, public Component +{ + public: + ParallelPortComponent( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~ParallelPortComponent(); + + virtual bool canFlip() const { return true; } + static Item * construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem * libraryItem(); + + virtual void stepNonLogic(); + virtual bool doesStepNonLogic() const { return true; } + + protected: + void initPort( const QString & port ); + virtual void dataChanged(); + virtual void drawShape( QPainter & p ); + + void dataCallback( bool ); + void controlCallback( bool ); + + /// Registers: { Data[0...7], Status[0...5], 0[6...7], Control[0...4], 0[5...7] } + LogicOut * m_pLogic[24]; + + ParallelPort * m_pParallelPort; +}; + +#endif diff --git a/src/electronics/components/piccomponent.cpp b/src/electronics/components/piccomponent.cpp new file mode 100644 index 0000000..47320bb --- /dev/null +++ b/src/electronics/components/piccomponent.cpp @@ -0,0 +1,437 @@ +/*************************************************************************** + * Copyright (C) 2003-2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "config.h" +#ifndef NO_GPSIM + +#include "canvasitemparts.h" +#include "circuitdocument.h" +#include "docmanager.h" +#include "gpsimprocessor.h" +#include "libraryitem.h" +#include "logic.h" +#include "ktechlab.h" +#include "micropackage.h" +#include "picinfo.h" +#include "microlibrary.h" +#include "piccomponent.h" +#include "piccomponentpin.h" +#include "projectmanager.h" + +#include <kdebug.h> +#include <kiconloader.h> +#include <klocale.h> +#include <kmessagebox.h> +#include <qguardedptr.h> +#include <qstringlist.h> + +#include "gpsim/ioports.h" +#include "gpsim/pic-processor.h" + +const QString _def_PICComponent_fileName = i18n("<Enter location of PIC Program>"); + + +Item* PICComponent::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new PICComponent( (ICNDocument*)itemDocument, newItem, id ); +} + + +LibraryItem* PICComponent::libraryItem() +{ + QStringList IDs; + IDs << "ec/pic" << "ec/picitem" << "ec/picitem_18pin"; + + return new LibraryItem( + IDs, + "PIC", + i18n("Integrated Circuits"), + "ic2.png", + LibraryItem::lit_component, + PICComponent::construct ); +} + +PICComponent::PICComponent( ICNDocument *icnDocument, bool newItem, const char *id ) + : Component( icnDocument, newItem, id ? id : "pic" ) +{ + m_name = i18n("PIC Micro"); + m_desc = i18n("The PIC component allows the simulation of a PIC program.<br><br>" + "The loadable PIC program must be one of the following formats:" + "<ul><li>Assembly (.asm)</li>" + "<li>FlowCode (.flowcode)</li>" + "<li>Symbol file (.cod)</li>" + "<li>Microbe (.microbe, .basic)</li>" + "<li>C source (.c)</li></ul>" + "Doubleclick on the PIC component to open up the program source file.<br><br>" + "If the program source file is of type assembly, then the the opened text file will automatically be linked to the simulation. " + "You can control the program from the text document using the debug controls.<br><br>" + "Explanation of buttons:" + "<ul><li>Play - Run the PIC program from the point at which it was paused, or from the start otherwise.</li>" + "<li>Pause - Pause the simulation at the current execution point.</li>" + "<li>Stop - Reset all parts of the simulation.</li>" + "<li>Reload - Reread the PIC program from disk and restart gpsim.</li></ul>"); + + m_bCreatedInitialPackage = false; + m_bLoadingProgram = false; + m_pGpsim = 0L; + + addButton( "run", QRect(), KGlobal::iconLoader()->loadIcon( "player_play", KIcon::Small ) ); + addButton( "pause", QRect(), KGlobal::iconLoader()->loadIcon( "player_pause", KIcon::Small ) ); + addButton( "reset", QRect(), KGlobal::iconLoader()->loadIcon( "stop", KIcon::Small ) ); + addButton( "reload", QRect(), KGlobal::iconLoader()->loadIcon( "reload", KIcon::Small ) ); + + if ( icnDocument->ktechlab() ) + connect( icnDocument->ktechlab(), SIGNAL(recentFileAdded(const KURL &)), this, SLOT(slotUpdateFileList()) ); + + connect( ProjectManager::self(), SIGNAL(projectOpened()), this, SLOT(slotUpdateFileList()) ); + connect( ProjectManager::self(), SIGNAL(projectClosed()), this, SLOT(slotUpdateFileList()) ); + connect( ProjectManager::self(), SIGNAL(projectCreated()), this, SLOT(slotUpdateFileList()) ); + connect( ProjectManager::self(), SIGNAL(subprojectCreated()), this, SLOT(slotUpdateFileList()) ); + connect( ProjectManager::self(), SIGNAL(filesAdded()), this, SLOT(slotUpdateFileList()) ); + connect( ProjectManager::self(), SIGNAL(filesRemoved()), this, SLOT(slotUpdateFileList()) ); + + createProperty( "program", Variant::Type::FileName ); + property("program")->setCaption( i18n("Program") ); + property("program")->setFilter("*.flowcode *.asm *.cod *.basic|All Supported Files\n*.flowcode|FlowCode (*.flowcode)\n*.cod|Symbol File (*.cod)\n*.asm|Assembly Code (*.asm)\n*.basic *.microbe|Microbe (*.basic, *.microbe)\n*.c|C (*.c)*|All Files"); + + // Used for restoring the pins on file loading before we have had a change + // to compile the PIC program + createProperty( "lastPackage", Variant::Type::String ); + property("lastPackage")->setHidden( true ); + +// //HACK This is to enable loading with pre-0.3 files (which didn't set a "lastPackage" +// // property). This will allow a P16F84 PIC to be initialized (which agrees with pre-0.3 +// // behaviour), but it will also load it if + + // This to allow loading of the PIC component from pre-0.3 files (which didn't set a + // "lastPackage" property). + if ( !newItem ) + property("lastPackage")->setValue("P16F84"); + + slotUpdateFileList(); + slotUpdateBtns(); + + initPackage( 0 ); +} + + +PICComponent::~PICComponent() +{ + deletePICComponentPins(); + delete m_pGpsim; +} + + +void PICComponent::dataChanged() +{ + initPIC(false); +} + + +void PICComponent::initPIC( bool forceReload ) +{ + if ( !m_bCreatedInitialPackage ) + { + // We are still being created, so other connectors will be expecting us to + // have grown pins soonish. + MicroInfo * microInfo = MicroLibrary::self()->microInfoWithID( dataString("lastPackage") ); + if ( microInfo ) + initPackage( microInfo ); + } + + QString newProgram = KURL( dataString("program") ).path(); + bool newFile = (m_picFile != newProgram); + if ( !newFile && !forceReload ) + return; + + delete m_pGpsim; + m_pGpsim = 0L; + + switch ( GpsimProcessor::isValidProgramFile(newProgram) ) + { + case GpsimProcessor::DoesntExist: + if ( newProgram == _def_PICComponent_fileName && !newProgram.isEmpty() ) + break; + KMessageBox::sorry( 0l, i18n("The file \"%1\" does not exist.").arg( newProgram ) ); + m_picFile = QString::null; + break; + + case GpsimProcessor::IncorrectType: + if ( newProgram == _def_PICComponent_fileName && !newProgram.isEmpty() ) + break; + KMessageBox::sorry( 0L, i18n("\"%1\" is not a valid PIC program.\nThe file must exist, and the extension should be \".cod\", \".asm\", \".flowcode\", \".basic\", \".microbe\" or \".c\".\n\".hex\" is allowed, provided that there is a corresponding \".cod\" file.").arg(newProgram) ); + m_picFile = QString::null; + break; + + case GpsimProcessor::Valid: + m_picFile = newProgram; + m_symbolFile = createSymbolFile(); + break; + } + + slotUpdateBtns(); +} + + +void PICComponent::deletePICComponentPins() +{ + const PICComponentPinMap::iterator picComponentMapEnd = m_picComponentPinMap.end(); + for ( PICComponentPinMap::iterator it = m_picComponentPinMap.begin(); it != picComponentMapEnd; ++it ) + delete it.data(); + m_picComponentPinMap.clear(); +} + + +void PICComponent::initPackage( MicroInfo * microInfo ) +{ + MicroPackage * microPackage = microInfo ? microInfo->package() : 0l; + + if ( microPackage ) + { + m_bCreatedInitialPackage = true; + + //BEGIN Get pin IDs + QStringList allPinIDs = microPackage->pinIDs(); + QStringList ioPinIDs = microPackage->pinIDs( PicPin::type_bidir | PicPin::type_input | PicPin::type_open ); + + // Now, we make the unwanted pin ids blank, so a pin is not created for them + const QStringList::iterator allPinIDsEnd = allPinIDs.end(); + for ( QStringList::iterator it = allPinIDs.begin(); it != allPinIDsEnd; ++it ) + { + if ( !ioPinIDs.contains(*it) ) + *it = ""; + } + //END Get pin IDs + + + //BEGIN Remove old stuff + // Remove old text + TextMap textMapCopy = m_textMap; + const TextMap::iterator textMapEnd = textMapCopy.end(); + for ( TextMap::iterator it = textMapCopy.begin(); it != textMapEnd; ++it ) + removeDisplayText(it.key()); + + // Remove the old pins + deletePICComponentPins(); + + // Remove old nodes + NodeMap nodeMapCopy = m_nodeMap; + const NodeMap::iterator nodeMapEnd = nodeMapCopy.end(); + for ( NodeMap::iterator it = nodeMapCopy.begin(); it != nodeMapEnd; ++it ) + { + if ( !ioPinIDs.contains(it.key()) ) + removeNode( it.key() ); + } + + removeElements(); + //END Remove old stuff + + + + //BEGIN Create new stuff + initDIPSymbol( allPinIDs, 80 ); + initDIP(allPinIDs); + + PicPinMap picPinMap = microPackage->pins( PicPin::type_bidir | PicPin::type_input | PicPin::type_open ); + const PicPinMap::iterator picPinMapEnd = picPinMap.end(); + for ( PicPinMap::iterator it = picPinMap.begin(); it != picPinMapEnd; ++it ) + m_picComponentPinMap[it.key()] = new PICComponentPin( this, it.data() ); + //END Create new stuff + + + removeDisplayText( "no_file" ); + addDisplayText( "picid", QRect(offsetX(), offsetY()-16, width(), 16), microInfo->id() ); + } + else + { + setSize( -48, -72, 96, 144 ); + removeDisplayText( "picid" ); + addDisplayText( "no_file", sizeRect(), i18n("(No\nprogram\nloaded)") ); + } + + + //BEGIN Update button positions + int leftpos = (width()-88)/2+offsetX(); + button("run")->setOriginalRect( QRect( leftpos, height()+4+offsetY(), 20, 20 ) ); + button("pause")->setOriginalRect( QRect( leftpos+23, height()+4+offsetY(), 20, 20 ) ); + button("reset")->setOriginalRect( QRect( leftpos+46, height()+4+offsetY(), 20, 20 ) ); + button("reload")->setOriginalRect( QRect( leftpos+69, height()+4+offsetY(), 20, 20 ) ); + updateAttachedPositioning(); + //END Update button positions +} + + +void PICComponent::attachPICComponentPins() +{ + if ( !m_pGpsim || !m_pGpsim->picProcessor() ) + return; + + pic_processor * picProcessor = m_pGpsim->picProcessor(); + + const PICComponentPinMap::iterator end = m_picComponentPinMap.end(); + for ( PICComponentPinMap::iterator it = m_picComponentPinMap.begin(); it != end; ++it ) + it.data()->attach( picProcessor->get_pin( it.key() ) ); +} + + +void PICComponent::slotUpdateFileList() +{ + QStringList preFileList; + if ( p_icnDocument && p_icnDocument->ktechlab() ) + preFileList += p_icnDocument->ktechlab()->recentFiles(); + + QStringList fileList; + + if ( ProjectInfo * info = ProjectManager::self()->currentProject() ) + { + const KURL::List urls = info->childOutputURLs( ProjectItem::AllTypes, ProjectItem::ProgramOutput ); + KURL::List::const_iterator urlsEnd = urls.end(); + for ( KURL::List::const_iterator it = urls.begin(); it != urlsEnd; ++it ) + fileList << (*it).path(); + } + + const QStringList::iterator end = preFileList.end(); + for ( QStringList::iterator it = preFileList.begin(); it != end; ++it ) + { + QString file = KURL(*it).path(); + if ( (file.endsWith(".flowcode") || file.endsWith(".asm") || file.endsWith(".cod") || file.endsWith(".basic") || file.endsWith(".microbe") ) && !fileList.contains(file) ) { + fileList.append(file); + } + } + + QString fileName = dataString("program"); + + property("program")->setAllowed(fileList); + property("program")->setValue( fileName.isEmpty() ? _def_PICComponent_fileName : fileName ); +} + + +void PICComponent::buttonStateChanged( const QString &id, bool state ) +{ + if (!state) + return; + + if ( id == "reload" ) + { + programReload(); + return; + } + + if (!m_pGpsim) + return; + + if ( id == "run" ) + m_pGpsim->setRunning(true); + + else if ( id == "pause" ) + m_pGpsim->setRunning(false); + + else if ( id == "reset" ) + { + m_pGpsim->reset(); + + // Set all pin outputs to low + const PICComponentPinMap::iterator end = m_picComponentPinMap.end(); + for ( PICComponentPinMap::iterator it = m_picComponentPinMap.begin(); it != end; ++it ) + it.data()->resetOutput(); + } + + slotUpdateBtns(); +} + + +bool PICComponent::mouseDoubleClickEvent ( const EventInfo &eventInfo ) +{ + Q_UNUSED(eventInfo); + if ( m_picFile.isEmpty() || (m_picFile == _def_PICComponent_fileName) ) + return false; + + (void) DocManager::self()->openURL(m_picFile); + + return true; +} + + +QString PICComponent::createSymbolFile() +{ + m_bLoadingProgram = true; + slotUpdateBtns(); + + return GpsimProcessor::generateSymbolFile( dataString("program"), this, SLOT(slotCODCreationSucceeded()), SLOT(slotCODCreationFailed()) ); +} + + +void PICComponent::slotCODCreationSucceeded() +{ + m_bLoadingProgram = false; + + delete m_pGpsim; + m_pGpsim = new GpsimProcessor(m_symbolFile); + + if ( m_pGpsim->codLoadStatus() == GpsimProcessor::CodSuccess ) + { + MicroInfo * microInfo = m_pGpsim->microInfo(); + property("lastPackage")->setValue( microInfo->id() ); + initPackage( microInfo ); + + connect( m_pGpsim, SIGNAL(runningStatusChanged(bool )), this, SLOT(slotUpdateBtns()) ); + attachPICComponentPins(); + } + + else + { + m_pGpsim->displayCodLoadStatus(); + delete m_pGpsim; + m_pGpsim = 0l; + } + + slotUpdateBtns(); +} + + +void PICComponent::slotCODCreationFailed() +{ + m_bLoadingProgram = false; + slotUpdateBtns(); +} + + +void PICComponent::programReload() +{ + delete m_pGpsim; + m_pGpsim = 0L; + + initPIC(true); + + slotUpdateBtns(); +} + + +void PICComponent::slotUpdateBtns() +{ + // We can get called by the destruction of gpsim after our canvas has been set to NULL + if (!canvas()) + return; + + button("run")->setEnabled( m_pGpsim && !m_pGpsim->isRunning() ); + button("pause")->setEnabled( m_pGpsim && m_pGpsim->isRunning() ); + button("reset")->setEnabled( m_pGpsim ); + button("reload")->setEnabled( !m_bLoadingProgram && (dataString("program") != _def_PICComponent_fileName) ); + + canvas()->setChanged( button("run")->boundingRect() ); + canvas()->setChanged( button("pause")->boundingRect() ); + canvas()->setChanged( button("reset")->boundingRect() ); + canvas()->setChanged( button("reload")->boundingRect() ); +} + + +#include "piccomponent.moc" + +#endif diff --git a/src/electronics/components/piccomponent.h b/src/electronics/components/piccomponent.h new file mode 100644 index 0000000..165fbf6 --- /dev/null +++ b/src/electronics/components/piccomponent.h @@ -0,0 +1,97 @@ +/*************************************************************************** + * Copyright (C) 2003-2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef PICCOMPONENT_H +#define PICCOMPONENT_H + +#include "config.h" +#ifndef NO_GPSIM + +#include "component.h" + +#include <qguardedptr.h> +#include <qmap.h> + +class Document; +class ECNode; +class GpsimProcessor; +class IOPIN; +class KTechlab; +class MicroInfo; +class MicroPackage; +class PIC_IOPORT; +class PICComponent; +class PICComponentPin; +class PicPin; +class TextDocument; + +typedef QMap< int, PICComponentPin * > PICComponentPinMap; + +/** +@short Electronic PIC device +@author David Saxton +*/ +class PICComponent : public Component +{ + Q_OBJECT + public: + PICComponent( ICNDocument * icnDocument, bool newItem, const char *id = 0L ); + ~PICComponent(); + + virtual bool canFlip() const { return true; } + static Item * construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem * libraryItem(); + + virtual void buttonStateChanged( const QString &id, bool state ); + virtual bool mouseDoubleClickEvent( const EventInfo &eventInfo ); + + void programReload(); + /** + * Sets up the pins, text, etc for the given PIC type. If info is null, + * then a generic rectangle is displayed (used when no file has been + * loaded yet). + */ + void initPackage( MicroInfo * info ); + + public slots: + void slotUpdateFileList(); + void slotUpdateBtns(); + + protected slots: + void slotCODCreationSucceeded(); + void slotCODCreationFailed(); + + protected: + /** + * Attaches all PICComponentPins to the current instance of gpsim. + */ + void attachPICComponentPins(); + void deletePICComponentPins(); + /** + * Attempts to compile the program to a symbol file, and connects the assembly + * finish signal to loadGpsim + */ + QString createSymbolFile(); + virtual void dataChanged(); + /** + * Initializes the PIC from the options the user has selected. + */ + void initPIC( bool forceReload ); + + QGuardedPtr<GpsimProcessor> m_pGpsim; + QString m_picFile; ///< The input program that the user selected + QString m_symbolFile; ///< The symbol file that was generated from m_picFile + bool m_bLoadingProgram; ///< True between createSymbolFile being called and the file being created + PICComponentPinMap m_picComponentPinMap; + bool m_bCreatedInitialPackage; ///< Set true once the initial package is loaded; until then, will load a package from the lastPackage data +}; + +#endif +#endif diff --git a/src/electronics/components/piccomponentpin.cpp b/src/electronics/components/piccomponentpin.cpp new file mode 100644 index 0000000..47ef6cb --- /dev/null +++ b/src/electronics/components/piccomponentpin.cpp @@ -0,0 +1,172 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "config.h" +#ifndef NO_GPSIM + +#include "micropackage.h" +#include "piccomponent.h" +#include "piccomponentpin.h" + +#include <kdebug.h> + +PICComponentPin::PICComponentPin( PICComponent * picComponent, PicPin picPin ) + : m_id( picPin.pinID ) +{ + m_gOutHigh = 0.0; + m_gOutLow = 0.0; + m_picPin = picPin; + m_pPICComponent = picComponent; + m_pLogicOut = 0l; + m_pLogicIn = 0l; + m_pIOPIN = 0l; + m_pStimulusNode = 0l; + Zth = 0.0; + Vth = 0.0; + + switch ( picPin.type ) + { + case PicPin::type_input: + { + m_pLogicIn = picComponent->createLogicIn( picComponent->ecNodeWithID(picPin.pinID) ); + break; + } + case PicPin::type_bidir: + { + m_pLogicOut = picComponent->createLogicOut( picComponent->ecNodeWithID(picPin.pinID), false ); + m_gOutHigh = 0.004; + m_gOutLow = 0.004; + break; + } + case PicPin::type_open: + { + m_pLogicOut = picComponent->createLogicOut( picComponent->ecNodeWithID(picPin.pinID), false ); + m_pLogicOut->setOutputHighVoltage(0.0); + m_pLogicOut->setOutputHighConductance(0.0); + m_gOutHigh = 0.0; + m_gOutLow = 0.004; + break; + } + case PicPin::type_vss: + case PicPin::type_vdd: + case PicPin::type_mclr: + case PicPin::type_osc: + default: + break; + } + + if (m_pLogicIn) + m_pLogicIn->setCallback( this, (CallbackPtr)(&PICComponentPin::logicCallback) ); + if (m_pLogicOut) + m_pLogicOut->setCallback( this, (CallbackPtr)(&PICComponentPin::logicCallback) ); +} + + +PICComponentPin::~PICComponentPin() +{ + delete m_pStimulusNode; +} + + +void PICComponentPin::attach( IOPIN * iopin ) +{ + if (!iopin) + { + kdWarning() << k_funcinfo << " iopin is NULL" << endl; + return; + } + + if (m_pStimulusNode) + { + kdWarning() << k_funcinfo << " Already have a node stimulus" << endl; + return; + } + + if (m_pIOPIN) + { + kdWarning() << k_funcinfo << " Already have an iopin" << endl; + return; + } + + m_pIOPIN = iopin; + m_pStimulusNode = new Stimulus_Node(m_id.ascii()); + m_pStimulusNode->attach_stimulus(iopin); + m_pStimulusNode->attach_stimulus(this); + + + // We need to tell the iopin whether or not we are high + if (m_pLogicOut) + logicCallback( m_pLogicOut->isHigh() ); + else if (m_pLogicIn) + logicCallback( m_pLogicIn->isHigh() ); +} + + +double PICComponentPin::get_Vth( ) +{ + if (!m_pIOPIN) + return 0.0; + + if ( m_pIOPIN->get_direction() == IOPIN::DIR_INPUT ) + return Vth; + else + return m_pIOPIN->get_Vth(); +} + + +void PICComponentPin::set_nodeVoltage( double v ) +{ + Q_UNUSED(v); + + if ( !m_pLogicOut || !m_pIOPIN ) + return; + + if ( m_pIOPIN->get_direction() == IOPIN::DIR_INPUT ) + { + m_pLogicOut->setOutputHighConductance(0.0); + m_pLogicOut->setOutputLowConductance(0.0); + return; + } + + m_pLogicOut->setHigh( m_pIOPIN->getDrivingState() ); + m_pLogicOut->setOutputHighConductance(m_gOutHigh); + m_pLogicOut->setOutputLowConductance(m_gOutLow); +} + + +void PICComponentPin::logicCallback( bool state ) +{ + if (!m_pIOPIN) + return; + + Vth = state ? 5e10 : 0; + bDrivingState = state; + + if ( m_pIOPIN->get_direction() == IOPIN::DIR_INPUT ) + { + Zth = 1e5; + + m_pIOPIN->setDrivenState(state); + if (m_pStimulusNode) + m_pStimulusNode->update(); + } + else + Zth = 0; +} + + +void PICComponentPin::resetOutput() +{ + if ( m_pLogicOut ) + m_pLogicOut->setHigh( false ); +} + +#endif + diff --git a/src/electronics/components/piccomponentpin.h b/src/electronics/components/piccomponentpin.h new file mode 100644 index 0000000..0fc433d --- /dev/null +++ b/src/electronics/components/piccomponentpin.h @@ -0,0 +1,68 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef PICCOMPONENTPIN_H +#define PICCOMPONENTPIN_H + +#include "config.h" +#ifndef NO_GPSIM + +#include "logic.h" +#include "gpsim/stimuli.h" + +#include <qstring.h> + +/** +@short Controls a pin on the PIC component +@author David Saxton + */ +class PICComponentPin : public CallbackClass, public stimulus +{ + public: + PICComponentPin( PICComponent * picComponent, PicPin picPin ); + ~PICComponentPin(); + /** + * Attach this to gpsim + */ + void attach( IOPIN * iopin ); + /** + * Called when the IOPIN this class is associated with changes state. + * Updates the associated LogicOut / LogicIn / etc according to what + * type of pin this is. + */ + virtual void set_nodeVoltage( double v ); + /** + * Called from our logic pin when the logic changes state. + */ + void logicCallback( bool state ); + /** + * Sets the output (if has one) to low. Called when the user stops the + * PIC. + */ + void resetOutput(); + + virtual double get_Vth(); + + protected: + // Conductance of pin in different configurations + double m_gOutHigh; + double m_gOutLow; + + PicPin m_picPin; + IOPIN * m_pIOPIN; + LogicOut * m_pLogicOut; + LogicIn * m_pLogicIn; + PICComponent * m_pPICComponent; + Stimulus_Node * m_pStimulusNode; + const QString m_id; +}; + +#endif +#endif diff --git a/src/electronics/components/probe.cpp b/src/electronics/components/probe.cpp new file mode 100644 index 0000000..db6725e --- /dev/null +++ b/src/electronics/components/probe.cpp @@ -0,0 +1,291 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "ecnode.h" +#include "libraryitem.h" +#include "logic.h" +#include "pin.h" +#include "probe.h" //HACK: This has to be included before the oscilloscope headers +#include "oscilloscope.h" +#include "oscilloscopedata.h" +#include "simulator.h" +#include "voltagesource.h" + +#include <klocale.h> +#include <qpainter.h> + +//BEGIN class Probe +Probe::Probe( ICNDocument *icnDocument, bool newItem, const char *id ) + : Component( icnDocument, newItem, id ) +{ + p_probeData = 0l; + setSize( -16, -8, 32, 16 ); + + createProperty( "color", Variant::Type::Color ); + property("color")->setCaption( i18n("Color") ); + property("color")->setValue( Qt::black ); +} + + +Probe::~ Probe() +{ + delete p_probeData; +} + + +void Probe::dataChanged() +{ + m_color = dataColor("color"); + if (p_probeData) + p_probeData->setColor(m_color); + setChanged(); +} +//END class Probe + + + +//BEGIN class FloatingProbe +FloatingProbe::FloatingProbe( ICNDocument *icnDocument, bool newItem, const char *id ) + : Probe( icnDocument, newItem, id ) +{ + p_probeData = m_pFloatingProbeData = static_cast<FloatingProbeData*>(registerProbe(this)); + property("color")->setValue( p_probeData->color() ); + + createProperty( "scaling", Variant::Type::Select ); + property("scaling")->setCaption( i18n("Scaling") ); + property("scaling")->setAllowed( QStringList::split( ',', "Linear,Logarithmic") ); + property("scaling")->setValue("Linear"); + property("scaling")->setAdvanced( true ); + + createProperty( "upper_abs_value", Variant::Type::Double ); + property("upper_abs_value")->setCaption( i18n("Upper Absolute Value") ); + property("upper_abs_value")->setValue(10.0); + property("upper_abs_value")->setMinValue(0.0); + property("upper_abs_value")->setUnit("V"); + property("upper_abs_value")->setAdvanced(true); + + createProperty( "lower_abs_value", Variant::Type::Double ); + property("lower_abs_value")->setCaption( i18n("Lower Absolute Value") ); + property("lower_abs_value")->setValue(0.1); + property("lower_abs_value")->setMinValue(0.0); + property("lower_abs_value")->setUnit("V"); + property("lower_abs_value")->setAdvanced(true); +} + + +FloatingProbe::~FloatingProbe() +{ +} + + +void FloatingProbe::dataChanged() +{ + Probe::dataChanged(); + + if ( dataString("scaling") == "Linear" ) + m_pFloatingProbeData->setScaling( FloatingProbeData::Linear ); + else + m_pFloatingProbeData->setScaling( FloatingProbeData::Logarithmic ); + + m_pFloatingProbeData->setUpperAbsValue( dataDouble("upper_abs_value") ); + m_pFloatingProbeData->setLowerAbsValue( dataDouble("lower_abs_value") ); +} + + +void FloatingProbe::drawShape( QPainter &p ) +{ + initPainter(p); + + int _x = int(x())-16; + int _y = int(y())-8; + + p.drawRect( _x, _y, 32, 16 ); + + QPointArray bezier(4); + + bezier[0] = QPoint( _x+4, _y+10 ); + bezier[1] = QPoint( _x+12, _y-6 ); + bezier[2] = QPoint( _x+20, _y+24 ); + bezier[3] = QPoint( _x+28, _y+4 ); + + p.setPen( QPen( m_color, 2 ) ); + p.drawCubicBezier(bezier); + + deinitPainter(p); +} +//END class FloatingProbe + + + +//BEGIN class VoltageProbe +Item* VoltageProbe::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new VoltageProbe( (ICNDocument*)itemDocument, newItem, id ); +} + +LibraryItem* VoltageProbe::libraryItem() +{ + return new LibraryItem( + "ec/voltageprobe", + i18n("Voltage Probe"), + i18n("Outputs"), + "floatingprobe.png", + LibraryItem::lit_component, + VoltageProbe::construct ); +} + +VoltageProbe::VoltageProbe( ICNDocument *icnDocument, bool newItem, const char *id ) + : FloatingProbe( icnDocument, newItem, id ? id : "voltageprobe" ) +{ + m_name = i18n("Voltage Probe"); + m_desc = i18n("Displays the voltage at the probe point on the oscilloscope."); + + init1PinLeft(); + init1PinRight(); + m_pPin1 = m_pNNode[0]->pin(); + m_pPin2 = m_pPNode[0]->pin(); +} + + +VoltageProbe::~VoltageProbe() +{ +} + + +void VoltageProbe::stepNonLogic() +{ + m_pFloatingProbeData->addDataPoint( m_pPin1->voltage() - m_pPin2->voltage() ); +} +//END class VoltageProbe + + + +//BEGIN class CurrentProbe +Item* CurrentProbe::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new CurrentProbe( (ICNDocument*)itemDocument, newItem, id ); +} + +LibraryItem* CurrentProbe::libraryItem() +{ + return new LibraryItem( + "ec/currentprobe", + i18n("Current Probe"), + i18n("Outputs"), + "floatingprobe.png", + LibraryItem::lit_component, + CurrentProbe::construct ); +} + +CurrentProbe::CurrentProbe( ICNDocument *icnDocument, bool newItem, const char *id ) + : FloatingProbe( icnDocument, newItem, id ? id : "currentprobe" ) +{ + m_name = i18n("Current Probe"); + m_desc = i18n("Displays the current at the probe point on the oscilloscope."); + + + init1PinLeft(0); + init1PinRight(0); + + m_voltageSource = createVoltageSource( m_pNNode[0], m_pPNode[0], 0. ); +} + + +CurrentProbe::~CurrentProbe() +{ +} + + +void CurrentProbe::stepNonLogic() +{ + m_pFloatingProbeData->addDataPoint( -m_voltageSource->cbranchCurrent(0) ); +} +//END class CurrentProbe + + + + +//BEGIN class LogicProbe +Item* LogicProbe::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new LogicProbe( (ICNDocument*)itemDocument, newItem, id ); +} + +LibraryItem* LogicProbe::libraryItem() +{ + QStringList ids; + ids << "ec/probe" << "ec/logicprobe"; + return new LibraryItem( + ids, + i18n("Logic Probe"), + i18n("Outputs"), + "logicprobe.png", + LibraryItem::lit_component, + LogicProbe::construct ); +} + +LogicProbe::LogicProbe( ICNDocument *icnDocument, bool newItem, const char *id ) + : Probe( icnDocument, newItem, id ? id : "probe" ) +{ + m_name = i18n("Logic Probe"); + m_desc = i18n("Connect this probe the the point in the circuit to measure the logic value. The output will be displayed in the Oscilloscope view."); + + init1PinRight(); + m_pIn = createLogicIn( m_pPNode[0] ); + + p_probeData = p_logicProbeData = static_cast<LogicProbeData*>(registerProbe(this)); + property("color")->setValue( p_probeData->color() ); + + m_pSimulator = Simulator::self(); + m_pIn->setCallback( this, (CallbackPtr)(&LogicProbe::logicCallback) ); + logicCallback(false); +} + + +LogicProbe::~LogicProbe() +{ +} + + +void LogicProbe::logicCallback( bool value ) +{ + p_logicProbeData->addDataPoint( LogicDataPoint( value, m_pSimulator->time() ) ); +} + + +void LogicProbe::drawShape( QPainter &p ) +{ + initPainter(p); + + int _x = int(x())-16; + int _y = int(y())-8; + + p.drawRect( _x, _y, 32, 16 ); + + p.setPen( QPen( m_color, 2 ) ); + + p.drawLine( _x+4, _y+11, _x+6, _y+11 ); + p.drawLine( _x+6, _y+11, _x+6, _y+4 ); + p.drawLine( _x+6, _y+4, _x+10, _y+4 ); + p.drawLine( _x+10, _y+4, _x+10, _y+11 ); + p.drawLine( _x+10, _y+11, _x+16, _y+11 ); + p.drawLine( _x+16, _y+11, _x+16, _y+4 ); + p.drawLine( _x+16, _y+4, _x+23, _y+4 ); + p.drawLine( _x+23, _y+4, _x+23, _y+11 ); + p.drawLine( _x+23, _y+11, _x+28, _y+11 ); +// p.drawLine( _x+23, _y+11, _x+26, _y+11 ); +// p.drawLine( _x+26, _y+11, _x+26, _y+4 ); +// p.drawLine( _x+26, _y+4, _x+28, _y+4 ); + + deinitPainter(p); +} +//END class LogicProbe + + diff --git a/src/electronics/components/probe.h b/src/electronics/components/probe.h new file mode 100644 index 0000000..9beb62e --- /dev/null +++ b/src/electronics/components/probe.h @@ -0,0 +1,113 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef PROBE_H +#define PROBE_H + +#include <component.h> + +class LogicProbeData; +class ProbeData; +class FloatingProbeData; + +/** +@author David Saxton +*/ +class Probe : public Component +{ + public: + Probe( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~Probe(); + + protected: + virtual void dataChanged(); + + ProbeData * p_probeData; // As obtained via registering with the oscilloscope + QColor m_color; +}; + +/** +@author David Saxton + */ +class FloatingProbe : public Probe +{ + public: + FloatingProbe( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~FloatingProbe(); + + virtual bool doesStepNonLogic() const { return true; } + + protected: + virtual void dataChanged(); + virtual void drawShape( QPainter &p ); + + FloatingProbeData * m_pFloatingProbeData; +}; + +/** +@author David Saxton + */ +class VoltageProbe : public FloatingProbe +{ + public: + VoltageProbe( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~VoltageProbe(); + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + + virtual void stepNonLogic(); + + protected: + Pin * m_pPin1; + Pin * m_pPin2; +}; + +/** +@author David Saxton + */ +class CurrentProbe : public FloatingProbe +{ + public: + CurrentProbe( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~CurrentProbe(); + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + + virtual void stepNonLogic(); + + protected: + VoltageSource *m_voltageSource; +}; + +/** +@author David Saxton + */ +class LogicProbe : public CallbackClass, public Probe +{ + public: + LogicProbe( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~LogicProbe(); + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + + void logicCallback( bool value ); + + protected: + virtual void drawShape( QPainter &p ); + + LogicProbeData * p_logicProbeData; + LogicIn * m_pIn; + Simulator * m_pSimulator; +}; + +#endif diff --git a/src/electronics/components/pushswitch.cpp b/src/electronics/components/pushswitch.cpp new file mode 100644 index 0000000..b7b38b7 --- /dev/null +++ b/src/electronics/components/pushswitch.cpp @@ -0,0 +1,204 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "canvasitemparts.h" +#include "libraryitem.h" +#include "pushswitch.h" +#include "switch.h" + +#include <klocale.h> +#include <qpainter.h> +#include <qpixmap.h> +#include <qpoint.h> +#include <qpointarray.h> + +//BEGIN class ECPTBSwitch +Item* ECPTBSwitch::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new ECPTBSwitch( (ICNDocument*)itemDocument, newItem, id ); +} + +LibraryItem* ECPTBSwitch::libraryItem() +{ + return new LibraryItem( + QString("ec/ptb_switch"), + i18n("Push-to-Break"), + i18n("Switches"), + "ptb.png", + LibraryItem::lit_component, + ECPTBSwitch::construct ); +} + +ECPTBSwitch::ECPTBSwitch( ICNDocument *icnDocument, bool newItem, const char *id ) + : Component( icnDocument, newItem, (id) ? id : "ptb_switch" ) +{ + m_name = i18n("Push to Break"); + setSize( -16, -16, 32, 24 ); + + addButton( "button", QRect( -16, 8, 32, 20 ), "" ); + + createProperty( "button_text", Variant::Type::String ); + property("button_text")->setCaption( i18n("Button Text") ); + + Variant * v = createProperty( "bounce", Variant::Type::Bool ); + v->setCaption( i18n("Bounce") ); + v->setAdvanced(true); + v->setValue(false); + + v = createProperty( "bounce_period", Variant::Type::Double ); + v->setCaption( i18n("Bounce Period") ); + v->setAdvanced(true); + v->setUnit("s"); + v->setValue(5e-3); + + init1PinLeft(0); + init1PinRight(0); + + m_switch = createSwitch( m_pPNode[0], m_pNNode[0], false ); + pressed = false; +} + +ECPTBSwitch::~ECPTBSwitch() +{ +} + + +void ECPTBSwitch::dataChanged() +{ + button("button")->setText( dataString("button_text") ); + + bool bounce = dataBool("bounce"); + int bouncePeriod_ms = int(dataDouble("bounce_period")*1e3); + m_switch->setBounce( bounce, bouncePeriod_ms ); +} + + +void ECPTBSwitch::drawShape( QPainter &p ) +{ + initPainter(p); + + int _x = (int)x()-16; + int _y = (int)y()-8; + const int radius = 2; + const int _height = height()-8; + + int dy = pressed ? 6 : 4; + + p.drawLine( _x+width()/4, _y+dy, _x+(3*width())/4, _y+dy ); // Top horizontal line + p.drawLine( _x, _y+(_height/2)-radius+dy, _x+width(), _y+(_height/2)-radius+dy ); // Bottom horizontal line + p.drawLine( _x+width()/2, _y+dy, _x+width()/2, _y+(_height/2)-radius+dy ); // Vertical line + + p.drawEllipse( _x, _y+(_height/2)-radius, 2*radius, 2*radius ); // Left circle + p.drawEllipse( _x+width()-2*radius+1, _y+(_height/2)-radius, 2*radius, 2*radius ); // Right circle + + deinitPainter(p); +} + +void ECPTBSwitch::buttonStateChanged( const QString &id, bool state ) +{ + if ( id != "button" ) + return; + m_switch->setState( state ? Switch::Open : Switch::Closed ); + pressed = state; +} +//END class ECPTBSwitch + + +//BEGIN class ECPTMSwitch +Item* ECPTMSwitch::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new ECPTMSwitch( (ICNDocument*)itemDocument, newItem, id ); +} + +LibraryItem* ECPTMSwitch::libraryItem() +{ + return new LibraryItem( + QString("ec/ptm_switch"), + i18n("Push-to-Make"), + i18n("Switches"), + "ptm.png", + LibraryItem::lit_component, + ECPTMSwitch::construct ); +} + +ECPTMSwitch::ECPTMSwitch( ICNDocument *icnDocument, bool newItem, const char *id ) + : Component( icnDocument, newItem, (id) ? id : "ptm_switch" ) +{ + m_name = i18n("Push to Make"); + setSize( -16, -16, 32, 24 ); + + addButton( "button", QRect( -16, 8, 32, 20 ), "" ); + + createProperty( "button_text", Variant::Type::String ); + property("button_text")->setCaption( i18n("Button Text") ); + + Variant * v = createProperty( "bounce", Variant::Type::Bool ); + v->setCaption("Bounce"); + v->setAdvanced(true); + v->setValue(false); + + v = createProperty( "bounce_period", Variant::Type::Double ); + v->setCaption("Bounce Period"); + v->setAdvanced(true); + v->setUnit("s"); + v->setValue(5e-3); + + init1PinLeft(0); + init1PinRight(0); + + m_switch = createSwitch( m_pPNode[0], m_pNNode[0], true ); + pressed = false; +} + + +ECPTMSwitch::~ECPTMSwitch() +{ +} + + +void ECPTMSwitch::dataChanged() +{ + button("button")->setText( dataString("button_text") ); + + bool bounce = dataBool("bounce"); + int bouncePeriod_ms = int(dataDouble("bounce_period")*1e3); + m_switch->setBounce( bounce, bouncePeriod_ms ); +} + + +void ECPTMSwitch::drawShape( QPainter &p ) +{ + initPainter(p); + + int _x = (int)x()-16; + int _y = (int)y()-8; + const int radius = 2; + const int _height = height()-8; + + int dy = pressed ? 1 : 3; + + p.drawLine( _x+width()/4, _y-dy, _x+(3*width())/4, _y-dy ); // Top horizontal line + p.drawLine( _x, _y+(_height/2)-radius-dy, _x+width(), _y+(_height/2)-radius-dy ); // Bottom horizontal line + p.drawLine( _x+width()/2, _y-dy, _x+width()/2, _y+(_height/2)-radius-dy ); // Vertical line + + p.drawEllipse( _x, _y+(_height/2)-radius, 2*radius, 2*radius ); // Left circle + p.drawEllipse( _x+width()-2*radius+1, _y+(_height/2)-radius, 2*radius, 2*radius ); // Right circle + + deinitPainter(p); +} + +void ECPTMSwitch::buttonStateChanged( const QString &id, bool state ) +{ + if ( id != "button" ) return; + m_switch->setState( state ? Switch::Closed : Switch::Open ); + pressed = state; +} +//END class ECPTMSwitch + diff --git a/src/electronics/components/pushswitch.h b/src/electronics/components/pushswitch.h new file mode 100644 index 0000000..c22ea93 --- /dev/null +++ b/src/electronics/components/pushswitch.h @@ -0,0 +1,62 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef PUSHSWITCH_H +#define PUSHSWITCH_H + +#include "component.h" + +/** +@short Push-to-Break switch component +@author David Saxton +*/ +class ECPTBSwitch : public Component +{ +public: + ECPTBSwitch( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~ECPTBSwitch(); + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + + virtual void buttonStateChanged( const QString &id, bool state ); + virtual void dataChanged(); + +private: + virtual void drawShape( QPainter &p ); + Switch *m_switch; + bool pressed; +}; + + +/** +@short Push-to-make switch +@author David Saxton +*/ +class ECPTMSwitch : public Component +{ +public: + ECPTMSwitch( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~ECPTMSwitch(); + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + + virtual void buttonStateChanged( const QString &id, bool state ); + virtual void dataChanged(); + +private: + virtual void drawShape( QPainter &p ); + Switch *m_switch; + bool pressed; +}; + + +#endif diff --git a/src/electronics/components/ram.cpp b/src/electronics/components/ram.cpp new file mode 100644 index 0000000..add745a --- /dev/null +++ b/src/electronics/components/ram.cpp @@ -0,0 +1,232 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "libraryitem.h" +#include "logic.h" +#include "ram.h" +#include "variant.h" + +#include <cmath> +#include <klocale.h> + +Item* RAM::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new RAM( (ICNDocument*)itemDocument, newItem, id ); +} + +LibraryItem* RAM::libraryItem() +{ + return new LibraryItem( + QString("ec/ram"), + i18n("RAM"), + i18n("Integrated Circuits"), + "ic2.png", + LibraryItem::lit_component, + RAM::construct + ); +} + +RAM::RAM( ICNDocument *icnDocument, bool newItem, const char *id ) + : Component( icnDocument, newItem, id ? id : "ram" ) +{ + m_name = i18n("RAM"); + m_desc = i18n("This RAM stores data as a collection of words; each of which contains <i>word size</i> bits of data.<br><br>To read data, set the CS (<i>chip select</i>) and the OE (<i>output enable</i>) pins high, and select the word using the address pins <i>A*</i>. The word is outputted on the data-out pins: <i>DO*</i>.<br><br>To write data, set the CS (<i>chip select</i>) and the WE (<i>write enable</i>) pins high, and select the address to write to with the <i>A*</i> pins. Write to the selected word using the data-in pins: <i>DI*</i>.<br><br>The <i>Address Size</i> is the number of bits that determine an address; so the total number of words stored will be 2^<sup><i>Address Size</i></sup>."); + + m_data = 0l; + m_pCS = 0l; + m_pOE = 0l; + m_pWE = 0l; + m_wordSize = 0; + m_addressSize = 0; + + createProperty( "wordSize", Variant::Type::Int ); + property("wordSize")->setCaption( i18n("Word Size") ); + property("wordSize")->setMinValue(1); + property("wordSize")->setMaxValue(256); + property("wordSize")->setValue(2); + + createProperty( "addressSize", Variant::Type::Int ); + property("addressSize")->setCaption( i18n("Address Size") ); + property("addressSize")->setMinValue(1); + property("addressSize")->setMaxValue(32); + property("addressSize")->setValue(4); + + m_data = createProperty( "data", Variant::Type::Raw )->value().asBitArray(); +} + +RAM::~RAM() +{ +} + + +void RAM::dataChanged() +{ + m_wordSize = dataInt("wordSize"); + m_addressSize = dataInt("addressSize"); + + int newSize = int( m_wordSize * std::pow( 2., m_addressSize ) ); + m_data.resize(newSize); + + initPins(); +} + + +void RAM::inStateChanged( bool newState ) +{ + Q_UNUSED(newState); + + bool cs = m_pCS->isHigh(); + bool oe = m_pOE->isHigh(); + bool we = m_pWE->isHigh(); + + if ( !cs || !oe ) + { + for ( int i = 0; i < m_wordSize; ++i ) + m_dataOut[i]->setHigh(false); + } + + if ( !cs || (!oe && !we) ) + return; + + unsigned address = 0; + for ( int i = 0; i < m_addressSize; ++i ) + address += (m_address[i]->isHigh() ? 1 : 0) << i; + + if (we) + { + for ( int i = 0; i < m_wordSize; ++i ) + m_data[ m_wordSize * address + i ] = m_dataIn[i]->isHigh(); + } + + if (oe) + { + for ( int i = 0; i < m_wordSize; ++i ) + m_dataOut[i]->setHigh( m_data[ m_wordSize * address + i ] ); + } +} + + +void RAM::initPins() +{ + int oldWordSize = m_dataIn.size(); + int oldAddressSize = m_address.size(); + + int newWordSize = dataInt("wordSize"); + int newAddressSize = dataInt("addressSize"); + + if ( newAddressSize == oldAddressSize && + newWordSize == oldWordSize ) + return; + + QStringList leftPins; // Pins on left of IC + leftPins << "CS" << "OE" << "WE"; + for ( int i = 0; i < newAddressSize; ++i ) + leftPins << QString("A%1").arg( QString::number(i) ); + + QStringList rightPins; // Pins on right of IC + for ( unsigned i = newWordSize; i > 0; --i ) + rightPins << QString("DI%1").arg( QString::number(i-1) ); + for ( unsigned i = newWordSize; i > 0; --i ) + rightPins << QString("DO%1").arg( QString::number(i-1) ); + + // Make pin lists of consistent sizes + for ( unsigned i = leftPins.size(); i < rightPins.size(); ++i ) + leftPins.append(""); + for ( unsigned i = rightPins.size(); i < leftPins.size(); ++i ) + rightPins.prepend(""); + + QStringList pins = leftPins + rightPins; + + initDIPSymbol( pins, 72 ); + initDIP(pins); + + ECNode *node; + + if (!m_pCS) + { + node = ecNodeWithID("CS"); + m_pCS = createLogicIn(node); + m_pCS->setCallback( this, (CallbackPtr)(&RAM::inStateChanged) ); + } + + if (!m_pOE) + { + node = ecNodeWithID("OE"); + m_pOE = createLogicIn(node); + m_pOE->setCallback( this, (CallbackPtr)(&RAM::inStateChanged) ); + } + + if (!m_pWE) + { + node = ecNodeWithID("WE"); + m_pWE = createLogicIn(node); + m_pWE->setCallback( this, (CallbackPtr)(&RAM::inStateChanged) ); + } + + if ( newWordSize > oldWordSize ) + { + m_dataIn.resize(newWordSize); + m_dataOut.resize(newWordSize); + + for ( int i = oldWordSize; i < newWordSize; ++i ) + { + node = ecNodeWithID( QString("DI%1").arg( QString::number(i) ) ); + m_dataIn.insert( i, createLogicIn(node) ); + m_dataIn[i]->setCallback( this, (CallbackPtr)(&RAM::inStateChanged) ); + + node = ecNodeWithID( QString("DO%1").arg( QString::number(i) ) ); + m_dataOut.insert( i, createLogicOut(node, false) ); + } + } + else if ( newWordSize < oldWordSize ) + { + for ( int i = newWordSize; i < oldWordSize; ++i ) + { + QString id = QString("DO%1").arg( QString::number(i) ); + removeDisplayText(id); + removeElement( m_dataIn[i], false ); + removeNode(id); + + id = QString("DI%1").arg( QString::number(i) ); + removeDisplayText(id); + removeElement( m_dataOut[i], false ); + removeNode(id); + } + + m_dataIn.resize(newWordSize); + m_dataOut.resize(newWordSize); + } + + if ( newAddressSize > oldAddressSize ) + { + m_address.resize(newAddressSize); + + for ( int i = oldAddressSize; i < newAddressSize; ++i ) + { + node = ecNodeWithID( QString("A%1").arg( QString::number(i) ) ); + m_address.insert( i, createLogicIn(node) ); + m_address[i]->setCallback( this, (CallbackPtr)(&RAM::inStateChanged) ); + } + } + else if ( newAddressSize < oldAddressSize ) + { + for ( int i = newAddressSize; i < oldAddressSize; ++i ) + { + QString id = QString("A%1").arg( QString::number(i) ); + removeDisplayText(id); + removeElement( m_address[i], false ); + removeNode(id); + } + + m_address.resize(newAddressSize); + } +} + + diff --git a/src/electronics/components/ram.h b/src/electronics/components/ram.h new file mode 100644 index 0000000..7b78ee9 --- /dev/null +++ b/src/electronics/components/ram.h @@ -0,0 +1,51 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef RAM_H +#define RAM_H + +#include "component.h" +#include "logic.h" + +#include <qbitarray.h> +#include <qptrvector.h> + +/** +@author David Saxton +*/ +class RAM : public CallbackClass, public Component +{ + public: + RAM( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~RAM(); + + virtual bool canFlip() const { return true; } + static Item * construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem * libraryItem(); + + protected: + void initPins(); + virtual void dataChanged(); + void inStateChanged( bool newState ); + + QBitArray m_data; + LogicIn * m_pCS; // Chip select + LogicIn * m_pOE; // Output enable + LogicIn * m_pWE; // Write enable + + int m_wordSize; + int m_addressSize; + + QPtrVector<LogicIn> m_address; + QPtrVector<LogicIn> m_dataIn; + QPtrVector<LogicOut> m_dataOut; +}; + +#endif diff --git a/src/electronics/components/resistordip.cpp b/src/electronics/components/resistordip.cpp new file mode 100644 index 0000000..d2c5bdb --- /dev/null +++ b/src/electronics/components/resistordip.cpp @@ -0,0 +1,132 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "libraryitem.h" +#include "node.h" +#include "resistance.h" +#include "resistordip.h" + +#include <kiconloader.h> +#include <klocale.h> +#include <qpainter.h> + +Item* ResistorDIP::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new ResistorDIP( (ICNDocument*)itemDocument, newItem, id ); +} + +LibraryItem* ResistorDIP::libraryItem() +{ + return new LibraryItem( + "ec/resistordip", + i18n("Resistor DIP"), + i18n("Discrete"), + "resistordip.png", + LibraryItem::lit_component, + ResistorDIP::construct + ); +} + +ResistorDIP::ResistorDIP( ICNDocument *icnDocument, bool newItem, const char *id ) + : Component( icnDocument, newItem, id ? id : "multiplexer" ) +{ + m_name = i18n("Resistor DIP"); + m_desc = i18n("Set of resistors with identical values in a Dual Inline Package."); + + m_resistorCount = 0; + for ( int i=0; i<maxCount; ++i ) + m_resistance[i] = 0l; + + createProperty( "resistance", Variant::Type::Double ); + property("resistance")->setCaption( i18n("Resistance") ); + property("resistance")->setUnit( QChar(0x3a9) ); + property("resistance")->setValue(1e4); + property("resistance")->setMinValue(1e-6); + + createProperty( "count", Variant::Type::Int ); + property("count")->setCaption( i18n("Count") ); + property("count")->setMinValue(2); + property("count")->setMaxValue(maxCount); + property("count")->setValue(8); +} + +ResistorDIP::~ResistorDIP() +{ +} + + +void ResistorDIP::dataChanged() +{ + initPins(); + const double resistance = dataDouble("resistance"); + for ( int i=0; i<m_resistorCount; ++i ) + m_resistance[i]->setResistance(resistance); + + const QString display = QString::number( resistance / getMultiplier(resistance), 'g', 3 ) + getNumberMag(resistance) + QChar(0x3a9); + addDisplayText( "res", QRect( offsetX(), offsetY()-16, 32, 12 ), display ); +} + + +void ResistorDIP::initPins() +{ + const int count = dataInt("count"); + const double resistance = dataDouble("resistance"); + + if ( count == m_resistorCount ) + return; + + if ( count < m_resistorCount ) + { + for ( int i=count; i<m_resistorCount; ++i ) + { + removeElement( m_resistance[i], false ); + m_resistance[i] = 0l; + removeNode( "n"+QString::number(i) ); + removeNode( "p"+QString::number(i) ); + } + } + else + { + for ( int i=m_resistorCount; i<count; ++i ) + { + const QString nid = "n"+QString::number(i); + const QString pid = "p"+QString::number(i); + m_resistance[i] = createResistance( createPin( -24, 0, 0, nid ), createPin( 24, 0, 180, pid ), resistance ); + } + } + m_resistorCount = count; + + setSize( -16, -count*8, 32, count*16, true ); + updateDIPNodePositions(); +} + + +void ResistorDIP::updateDIPNodePositions() +{ + for ( int i=0; i<m_resistorCount; ++i ) + { + m_nodeMap["n"+QString::number(i)].y = offsetY() + 8 + 16*i; + m_nodeMap["p"+QString::number(i)].y = offsetY() + 8 + 16*i; + } + updateAttachedPositioning(); +} + + +void ResistorDIP::drawShape( QPainter &p ) +{ + int _x = int(x()+offsetX()); + int _y = int(y()+offsetY()); + + initPainter(p); + for ( int i=0; i<m_resistorCount; ++i ) + p.drawRect( _x, _y+16*i+2, 32, 12 ); + deinitPainter(p); +} + diff --git a/src/electronics/components/resistordip.h b/src/electronics/components/resistordip.h new file mode 100644 index 0000000..ce1d03a --- /dev/null +++ b/src/electronics/components/resistordip.h @@ -0,0 +1,43 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef RESISTORDIP_H +#define RESISTORDIP_H + +#include "component.h" + +const int maxCount = 256; + +/** +@author David Saxton +*/ +class ResistorDIP : public Component +{ +public: + ResistorDIP( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~ResistorDIP(); + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + +protected: + virtual void drawShape( QPainter &p ); + void updateDIPNodePositions(); + virtual void dataChanged(); + /** + * Add / remove pins according to the number of inputs the user has requested + */ + void initPins(); + + int m_resistorCount; + Resistance* m_resistance[maxCount]; +}; + +#endif diff --git a/src/electronics/components/rotoswitch.cpp b/src/electronics/components/rotoswitch.cpp new file mode 100644 index 0000000..872714c --- /dev/null +++ b/src/electronics/components/rotoswitch.cpp @@ -0,0 +1,317 @@ +/*************************************************************************** + * Copyright (C) 2005 by John Myers * + * electronerd@electronerdia.net * + * * + * This program 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. * + ***************************************************************************/ + +#include "rotoswitch.h" + +#include "canvasitemparts.h" +#include "ecnode.h" +#include "libraryitem.h" +#include "switch.h" + +#include <klocale.h> +#include <qpainter.h> +#include <cmath> +#include <assert.h> + +#include <kdebug.h> + +//BEGIN class ECRotoSwitch +Item* ECRotoSwitch::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new ECRotoSwitch( (ICNDocument*)itemDocument, newItem, id ); +} + +LibraryItem* ECRotoSwitch::libraryItem() +{ + return new LibraryItem( + QString("ec/roto_switch"), + i18n("Rotary"), + i18n("Switches"), + "rotary.png", + LibraryItem::lit_component, + ECRotoSwitch::construct ); +} + + +ECRotoSwitch::ECRotoSwitch( ICNDocument *icnDocument, bool newItem, const char *id ) +: Component( icnDocument, newItem, id ? id : "roto_switch" ), +m_numPositions(0) +{ +// m_name = i18n("Rotary Switch(WIP)"); + m_name = i18n("Rotary Switch"); + m_desc = i18n("Rotary Switch"); ///< \todo better description for rotoswitch + QPointArray pa; + pa.makeArc( -_pinInnerRadius, -_pinInnerRadius, 2*_pinInnerRadius, 2*_pinInnerRadius , 0, 16*360 ); + setItemPoints( pa ); + //setSize( -64, -64, 128, 128 ); + + //half the side length of the buttons + int buttonRadius = 10; + addButton( "go_left", QRect( -_pinOuterRadius/3-buttonRadius, _pinOuterRadius-3*buttonRadius, 2*buttonRadius, 2*buttonRadius ), "<", false ); + addButton( "go_right", QRect(_pinOuterRadius/3-buttonRadius, _pinOuterRadius-3*buttonRadius, 2*buttonRadius, 2*buttonRadius ), ">", false ); + + /*Variant * v = createProperty( "button_map", Variant::Type::String ); + v->setCaption( i18n("Button Map") ); + v->setAdvanced(true); + const QString defButtonMap("SSSSSSSSSSSM"); + v->setValue(defButtonMap); + */ + Variant * v = createProperty( "num_positions", Variant::Type::Int ); + v->setCaption( i18n("Number of Positions") ); + v->setAdvanced(false); + v->setValue(6); + v->setMinValue(3); + m_inNode = createPin(0,height()/2,270,"in"); + + v = createProperty( "bounce", Variant::Type::Bool ); + v->setCaption("Bounce"); + v->setAdvanced(true); + v->setValue(false); + + + v = createProperty( "bounce_period", Variant::Type::Double ); + v->setCaption("Bounce Period"); + v->setAdvanced(true); + v->setUnit("s"); + v->setValue(5e-3); + + + v = createProperty( "cur_position", Variant::Type::Int ); + v->setHidden( true ); + v->setValue( 0 ); + + //v = createProperty( "left_momentary", Variant::Type::Bool ); + //v->setCaption(i18n("Left Momentary" ) ); + //v->setValue(false); +} + + +ECRotoSwitch::~ECRotoSwitch() +{ +} + + +void ECRotoSwitch::dataChanged() +{ + bool bounce = dataBool("bounce"); + int bouncePeriod_ms = int(dataDouble("bounce_period")*1e3); + m_curPosition = dataInt( "cur_position" ); + setUpSwitches(); + if(m_positions[m_curPosition].posSwitch->state() != Switch::Closed) + { + m_positions[m_curPosition].posSwitch->setState(Switch::Closed); + } + for(int i = 0; i < m_numPositions; i++) + { + m_positions[i].posSwitch->setBounce( bounce, bouncePeriod_ms ); + } +} + +inline int roundTo10(int a){return ((a/10)+(a%10<5?0:1))*10;} +void ECRotoSwitch::drawShape( QPainter &p ) +{ + initPainter(p); + + + int cx = static_cast<int>(x()); + int cy = static_cast<int>(y()); + + const int rotorRadius = 5; + + + //draw the rotor + p.drawEllipse(cx - rotorRadius, cy-rotorRadius, 2*rotorRadius, 2*rotorRadius); + //and its connection + p.drawLine(cx, cy+rotorRadius, cx, cy+_pinInnerRadius); + + //draw the output positions + double angleBetweenPositions = (4*M_PI/3)/(m_numPositions - 1); + //kdDebug() << "drawShape: " << bigRadius << " " << angleBetweenPositions << endl; + + /// \internal \brief Round to the nearest multiple of 8 +#define round_8(a) (((a) > 0) ? int(((a)+4)/8)*8 : int(((a)-4)/8)*8) + for(int i = 0; i < m_numPositions ; i++) + { + double angle = (7*M_PI/6) - (i * angleBetweenPositions); + int contactX = static_cast<int>(_contactRingRadius * cos(angle)); + int contactY = static_cast<int>(_contactRingRadius * sin(angle)); + + p.drawEllipse(cx+contactX-_contactRadius, cy-contactY-_contactRadius, 2*_contactRadius, 2*_contactRadius); + int pinX, pinY; + switch(m_positions[i].pinAngle) + { + case 180: + pinX = _pinInnerRadius; + pinY = round_8(contactY); + break; + case 90: + pinX = round_8(contactX); + pinY = _pinInnerRadius; + break; + case 0: + pinX = -_pinInnerRadius; + pinY = round_8(contactY); + break; + default: + assert(!"Bad pin angle"); + } + p.drawLine(cx+contactX, cy-contactY, cx+pinX, cy-pinY); + //kdDebug() << contactX <<", "<< contactY <<endl; + } +#undef round_8 + //draw the connection to the selected position + double angle = (7*M_PI/6) - (m_curPosition * angleBetweenPositions); + int contactX = static_cast<int>(_contactRingRadius * cos(angle)); + int contactY = static_cast<int>(_contactRingRadius * sin(angle)); + int rotorX = static_cast<int>(rotorRadius * cos(angle)); + int rotorY = static_cast<int>(rotorRadius * sin(angle)); + p.drawLine(cx+rotorX, cy-rotorY, cx+contactX, cy-contactY); + + + deinitPainter(p); +} + +void ECRotoSwitch::buttonStateChanged( const QString & id, bool state ) +{ + SwitchPosition& curSP = m_positions[m_curPosition]; + int nextPos = m_curPosition; + if(m_numPositions < 2) + { + return; + } + if(!state) //release + { + if(!curSP.isMomentary) + { + return; + } + + if(m_curPosition == 0) + { + nextPos = m_curPosition + 1; + } + else if(m_curPosition == m_numPositions - 1) + { + nextPos = m_curPosition - 1; + } + + } + else //press + { + if(id == "go_left" && m_curPosition > 0) + { + nextPos = m_curPosition - 1; + } + else if(id == "go_right" && m_curPosition < m_numPositions - 1) + { + nextPos = m_curPosition + 1; + } + + } + if(nextPos != m_curPosition) + { + SwitchPosition& nextSP = m_positions[nextPos]; + + curSP.posSwitch->setState(Switch::Open); + nextSP.posSwitch->setState(Switch::Closed); + + m_curPosition = nextPos; + + property( "cur_position" )->setValue( m_curPosition ); + } +} + + +/*! + Set up the switches according to the button_map + * + * + */ +void ECRotoSwitch::setUpSwitches() +{ + if( dataInt("num_positions") == m_numPositions ) + { + // number of positions didn't change, so we don't have to do anything. + return; + } + //this uses the _old_ value of m_numPositions! + for(int i=0; i<m_numPositions; i++) + { + SwitchPosition& sp = m_positions[i]; + QString pinName = QString("pin_%1").arg(i); + removeNode(pinName); + removeSwitch(sp.posSwitch); + } + + m_numPositions = dataInt("num_positions"); + if(m_curPosition >= m_numPositions ) + { + setActivePosition( m_numPositions - 1 ); + } + m_positions.clear();///\todo readjust old pins + m_positions.reserve(m_numPositions); + double angleBetweenPositions = (4*M_PI/3)/(m_numPositions - 1); + //kdDebug() << "setUpSwitches: " << bigRadius << " " << angleBetweenPositions << endl; + for( int i = 0; i < m_numPositions; i++) + { + double angle = (7*M_PI/6) - (i * angleBetweenPositions); + int contactX = static_cast<int>(_contactRingRadius * cos(angle)); + int contactY = static_cast<int>(_contactRingRadius * sin(angle)); + + SwitchPosition sp; + if(angle > 3*M_PI/4) + { + sp.pinAngle = 0; + contactX = -_pinOuterRadius; + } + else if(angle > M_PI/4) + { + sp.pinAngle = 90; + contactY=_pinOuterRadius; + } + else + { + sp.pinAngle = 180; + contactX = _pinOuterRadius; + } + // kdDebug() << contactX <<", "<< contactY <<endl; + + + sp.node = createPin(contactX,-contactY,sp.pinAngle,QString("pin_%1").arg(i)); + sp.posSwitch = createSwitch(m_inNode, sp.node, true); + sp.isMomentary = false;//(map[i] == 'M'); + m_positions.push_back(sp); + } + updateAttachedPositioning(); + + // redraw ourself + setChanged(); +} + +/*! + * Set the current position to \c newPosition updating the state of the switch. + * \c m_curPosition must reference a valid position to switch away from + * + * \param newPosition the position to switch to + */ +void ECRotoSwitch::setActivePosition(int newPosition) +{ + SwitchPosition& curSP = m_positions[m_curPosition]; + SwitchPosition& nextSP = m_positions[newPosition]; + + curSP.posSwitch->setState(Switch::Open); + nextSP.posSwitch->setState(Switch::Closed); + + m_curPosition = newPosition; + + property( "cur_position" )->setValue( m_curPosition ); + +} +//END class ECRotoSwitch diff --git a/src/electronics/components/rotoswitch.h b/src/electronics/components/rotoswitch.h new file mode 100644 index 0000000..909c0ea --- /dev/null +++ b/src/electronics/components/rotoswitch.h @@ -0,0 +1,70 @@ +/*************************************************************************** + * Copyright (C) 2005 by John Myers * + * electronerd@electronerdia.net * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef ROTOSWITCH_H +#define ROTOSWITCH_H + +#include "component.h" +#include <qvaluevector.h> + +struct SwitchPosition +{ + ECNode* node; + Switch* posSwitch; + bool isMomentary; + int pinAngle; +}; + +/** + * A rotary switch + * \author John Myers + */ +class ECRotoSwitch : public Component +{ +public: + ECRotoSwitch( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~ECRotoSwitch(); + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + + virtual void buttonStateChanged( const QString &id, bool state ); + virtual void dataChanged(); + +private: + virtual void drawShape( QPainter &p ); + + int m_numPositions; + int m_curPosition; + + ///Half the total width of the component including pins + static const int _pinOuterRadius = 64; + ///The width of the pins + static const int _pinWidth = 8; + ///the radius of the circle centered at the origin and tangent to the inner edge of the rows of pins + static const int _pinInnerRadius = _pinOuterRadius - _pinWidth; + ///gap between pins and contact circles + static const int _wireGap = 7; + ///The radius of the largest circle tangent to the pin circles + static const int _contactOuterRadius = _pinInnerRadius - _wireGap; + ///The radius of the circles used to show positions + static const int _contactRadius = 2; + ///The radius of the ring of positions + static const int _contactRingRadius = _contactOuterRadius - _contactRadius; + + QValueVector<SwitchPosition> m_positions; + ECNode* m_inNode; + +protected: + void setUpSwitches(); +protected: + void setActivePosition(int newPosition); +}; +#endif //ROTOSWITCH_H diff --git a/src/electronics/components/serialportcomponent.cpp b/src/electronics/components/serialportcomponent.cpp new file mode 100644 index 0000000..c8e6a4e --- /dev/null +++ b/src/electronics/components/serialportcomponent.cpp @@ -0,0 +1,242 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "port.h" +#include "serialportcomponent.h" + +#include "ecnode.h" +#include "itemdocument.h" +#include "libraryitem.h" +#include "pin.h" +#include "resistance.h" + +#include <kdebug.h> +#include <klocale.h> +#include <qpainter.h> + +#include <cmath> +#include <termios.h> + +Item* SerialPortComponent::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new SerialPortComponent( (ICNDocument*)itemDocument, newItem, id ); +} + +LibraryItem* SerialPortComponent::libraryItem() +{ + return new LibraryItem( + "ec/serial_port", + i18n("Serial Port"), + i18n("Connections"), + "ic1.png", + LibraryItem::lit_component, + SerialPortComponent::construct + ); +} + +SerialPortComponent::SerialPortComponent( ICNDocument *icnDocument, bool newItem, const char *id ) + : Component( icnDocument, newItem, id ? id : "serial_port" ) +{ + m_name = i18n("Serial Port"); + m_desc = i18n("Interface to a serial port. The pins are:<br>" + "<ul>" + "<li><b>CD</b> - Carrier Detect (control; output)</li>" + "<li><b>RD</b> - Received Data (data; output)</li>" + "<li><b>TD</b> - Transmitted Data (data; input)</li>" + "<li><b>DTR</b> - Data Terminal Ready (control; input)</li>" + "<li><b>GND</b> - Signal Ground (ground)</li>" + "<li><b>DSR</b> - Data Set Ready (control; input)</li>" + "<li><b>RTS</b> - Request to Send (control; input)</li>" + "<li><b>CTS</b> - Clear to Send (control; output)</li>" + "<li><b>RI</b> - Ring Indicator (control; output)</li>" + "</ul>"); + + QPointArray pa( 4 ); + pa[0] = QPoint( -32, -48 ); + pa[1] = QPoint( 32, -40 ); + pa[2] = QPoint( 32, 40 ); + pa[3] = QPoint( -32, 48 ); + + setItemPoints( pa ); + + m_pSerialPort = new SerialPort(); + + ECNode * pin = 0; + + // Works + pin = createPin( -40, 32, 0, "CD" ); + addDisplayText( "CD", QRect( -28, 24, 28, 16 ), "CD", true, Qt::AlignLeft | Qt::AlignVCenter ); + m_pCD = createLogicOut( pin, false ); + + // Doesn't work +// pin = createPin( -40, 16, 0, "RD" ); + addDisplayText( "RD", QRect( -28, 8, 28, 16 ), "RD", true, Qt::AlignLeft | Qt::AlignVCenter ); +// m_pRD = createLogicOut( pin, false ); + + // Works + pin = createPin( -40, 0, 0, "TD" ); + addDisplayText( "TD", QRect( -28, -8, 28, 16 ), "TD", true, Qt::AlignLeft | Qt::AlignVCenter ); + m_pTD = createLogicIn( pin); + m_pTD->setCallback( this, (CallbackPtr)(&SerialPortComponent::tdCallback) ); + + // Works + pin = createPin( -40, -16, 0, "DTR" ); + addDisplayText( "DTR", QRect( -28, -24, 28, 16 ), "DTR", true, Qt::AlignLeft | Qt::AlignVCenter ); + m_pDTR = createLogicIn( pin ); + m_pDTR->setCallback( this, (CallbackPtr)(&SerialPortComponent::dtrCallback) ); + + // N/A + pin = createPin( -40, -32, 0, "GND" ); + addDisplayText( "GND", QRect( -28, -40, 28, 16 ), "GND", true, Qt::AlignLeft | Qt::AlignVCenter ); + pin->pin()->setGroundType( Pin::gt_always ); + + // Doesn't work +// pin = createPin( 40, 24, 180, "DSR" ); + addDisplayText( "DSR", QRect( 0, 16, 28, 16 ), "DSR", true, Qt::AlignRight | Qt::AlignVCenter ); +// m_pDSR = createLogicIn( pin ); +// m_pDSR->setCallback( this, (CallbackPtr)(&SerialPortComponent::dsrCallback) ); + + // Doesn't work +// pin = createPin( 40, 8, 180, "RTS" ); + addDisplayText( "RTS", QRect( 0, 0, 28, 16 ), "RTS", true, Qt::AlignRight | Qt::AlignVCenter ); +// m_pRTS = createLogicIn( pin ); +// m_pRTS->setCallback( this, (CallbackPtr)(&SerialPortComponent::rtsCallback) ); + + // Works + pin = createPin( 40, -8, 180, "CTS" ); + addDisplayText( "CTS", QRect( 0, -16, 28, 16 ), "CTS", true, Qt::AlignRight | Qt::AlignVCenter ); + m_pCTS = createLogicOut( pin, false ); + + // Works + pin = createPin( 40, -24, 180, "RI" ); + addDisplayText( "RI", QRect( 0, -32, 28, 16 ), "RI", true, Qt::AlignRight | Qt::AlignVCenter ); + m_pRI = createLogicOut( pin, false ); + + Variant * v = createProperty( "port", Variant::Type::Combo ); + v->setAllowed( SerialPort::ports( Port::ExistsAndRW ) ); + v->setCaption( i18n("Port") ); + +// v = createProperty( "baudRate", Variant::Type::Select ); +// v->setAllowed( QStringList::split( ",", "B0,B50,B75,B110,B134,B150,B200,B300,B600,B1200,B1800,B2400,B4800,B9600,B19200,B38400" ) ); +// v->setCaption( i18n("Baud rate") ); +// v->setValue("B9600"); +} + + +SerialPortComponent::~SerialPortComponent() +{ + delete m_pSerialPort; +} + + +void SerialPortComponent::dataChanged() +{ +#if 0 + QString baudString = dataString("baudRate"); + unsigned baudRate = 0; + + if ( baudString == "B0" ) + baudRate = B0; + else if ( baudString == "B50" ) + baudRate = B50; + else if ( baudString == "B75" ) + baudRate = B75; + else if ( baudString == "B110" ) + baudRate = B110; + else if ( baudString == "B134" ) + baudRate = B134; + else if ( baudString == "B150" ) + baudRate = B150; + else if ( baudString == "B200" ) + baudRate = B200; + else if ( baudString == "B300" ) + baudRate = B300; + else if ( baudString == "B600" ) + baudRate = B600; + else if ( baudString == "B1200" ) + baudRate = B1200; + else if ( baudString == "B1800" ) + baudRate = B1800; + else if ( baudString == "B2400" ) + baudRate = B2400; + else if ( baudString == "B4800" ) + baudRate = B4800; + else if ( baudString == "B9600" ) + baudRate = B9600; + else if ( baudString == "B19200" ) + baudRate = B19200; + else if ( baudString == "B38400" ) + baudRate = B38400; + else + { + kdError() << k_funcinfo << "Unknown baud rate = \""<<baudString<<"\""<<endl; + return; + } + + initPort( dataString("port"), baudRate ); +#endif + + initPort( dataString("port"), B200 ); +} + + +void SerialPortComponent::initPort( const QString & port, unsigned baudRate ) +{ + if ( port.isEmpty() ) + { + m_pSerialPort->closePort(); + return; + } + + if ( ! m_pSerialPort->openPort( port, baudRate ) ) + { + p_itemDocument->canvas()->setMessage( i18n("Could not open port %1").arg( port ) ); + return; + } +} + + +void SerialPortComponent::stepNonLogic() +{ + m_pCD->setHigh( m_pSerialPort->pinState( SerialPort::CD ) ); +// m_pRD->setHigh( m_pSerialPort->pinState( SerialPort::RD ) ); + m_pCTS->setHigh( m_pSerialPort->pinState( SerialPort::CTS ) ); + m_pRI->setHigh( m_pSerialPort->pinState( SerialPort::RI ) ); +} + + +void SerialPortComponent::tdCallback( bool isHigh ) +{ + m_pSerialPort->setPinState( SerialPort::TD, isHigh ); +} + + +void SerialPortComponent::dtrCallback( bool isHigh ) +{ + m_pSerialPort->setPinState( SerialPort::DTR, isHigh ); +} + + +void SerialPortComponent::dsrCallback( bool isHigh ) +{ + m_pSerialPort->setPinState( SerialPort::DSR, isHigh ); +} + + +void SerialPortComponent::rtsCallback( bool isHigh ) +{ + m_pSerialPort->setPinState( SerialPort::RTS, isHigh ); +} + + +void SerialPortComponent::drawShape( QPainter & p ) +{ + drawPortShape( p ); +} diff --git a/src/electronics/components/serialportcomponent.h b/src/electronics/components/serialportcomponent.h new file mode 100644 index 0000000..4f9bc9a --- /dev/null +++ b/src/electronics/components/serialportcomponent.h @@ -0,0 +1,61 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef SERIALPORTCOMPONENT_H +#define SERIALPORTCOMPONENT_H + +#include "logic.h" +#include "component.h" + +class SerialPort; + +/** +@author David Saxton +*/ +class SerialPortComponent : public CallbackClass, public Component +{ + public: + SerialPortComponent( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~SerialPortComponent(); + + virtual bool canFlip() const { return true; } + static Item * construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem * libraryItem(); + + virtual void stepNonLogic(); + virtual bool doesStepNonLogic() const { return true; } + + protected: + /** + * @param baudRate as defined in <bits/termios.h> + */ + void initPort( const QString & port, unsigned baudRate ); + virtual void dataChanged(); + virtual void drawShape( QPainter & p ); + + void tdCallback( bool isHigh ); + void dtrCallback( bool isHigh ); + void dsrCallback( bool isHigh ); + void rtsCallback( bool isHigh ); + + LogicIn * m_pTD; + LogicIn * m_pDTR; +// LogicIn * m_pDSR; +// LogicIn * m_pRTS; + + LogicOut * m_pCD; +// LogicOut * m_pRD; + LogicOut * m_pCTS; + LogicOut * m_pRI; + + SerialPort * m_pSerialPort; +}; + +#endif diff --git a/src/electronics/components/toggleswitch.cpp b/src/electronics/components/toggleswitch.cpp new file mode 100644 index 0000000..4efe9ab --- /dev/null +++ b/src/electronics/components/toggleswitch.cpp @@ -0,0 +1,407 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "toggleswitch.h" + +#include "canvasitemparts.h" +#include "ecnode.h" +#include "libraryitem.h" +#include "switch.h" + +#include <klocale.h> +#include <qpainter.h> + +//BEGIN class ECDPDT +Item* ECDPDT::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new ECDPDT( (ICNDocument*)itemDocument, newItem, id ); +} + +LibraryItem* ECDPDT::libraryItem() +{ + return new LibraryItem( + QString("ec/dpdt_toggle"), + i18n("DPDT"), + i18n("Switches"), + "dpdt.png", + LibraryItem::lit_component, + ECDPDT::construct ); +} + + +ECDPDT::ECDPDT( ICNDocument *icnDocument, bool newItem, const char *id ) + : Component( icnDocument, newItem, id ? id : "dpdt_toggle" ) +{ + m_name = i18n("DPDT Toggle"); + m_desc = i18n("Double-Pole Double-Throw switch."); + setSize( -16, -32, 32, 64 ); + + addButton( "button", QRect( -16, 32, 32, 20 ), "", true ); + + createProperty( "button_text", Variant::Type::String ); + property("button_text")->setCaption( i18n("Button Text") ); + + Variant * v = createProperty( "bounce", Variant::Type::Bool ); + v->setCaption("Bounce"); + v->setAdvanced(true); + v->setValue(false); + + v = createProperty( "bounce_period", Variant::Type::Double ); + v->setCaption("Bounce Period"); + v->setAdvanced(true); + v->setUnit("s"); + v->setValue(5e-3); + + init4PinRight( -24, -8, 8, 24 ); + + init2PinLeft( -16, 16 ); + + m_switch1 = createSwitch( m_pNNode[0], m_pPNode[0], false ); + m_switch2 = createSwitch( m_pNNode[0], m_pPNode[1], true ); + m_switch3 = createSwitch( m_pNNode[1], m_pPNode[2], false ); + m_switch4 = createSwitch( m_pNNode[1], m_pPNode[3], true ); + pressed = false; +} + + +ECDPDT::~ECDPDT() +{ +} + + +void ECDPDT::dataChanged() +{ + button("button")->setText( dataString("button_text") ); + + bool bounce = dataBool("bounce"); + int bouncePeriod_ms = int(dataDouble("bounce_period")*1e3); + + m_switch1->setBounce( bounce, bouncePeriod_ms ); + m_switch2->setBounce( bounce, bouncePeriod_ms ); + m_switch3->setBounce( bounce, bouncePeriod_ms ); + m_switch4->setBounce( bounce, bouncePeriod_ms ); +} + + +void ECDPDT::drawShape( QPainter &p ) +{ + initPainter(p); + + int _x = (int)x()-16; + int _y = (int)y()-32; + const int radius = 2; + + p.drawEllipse( _x, _y+15, 2*radius, 2*radius ); + p.drawEllipse( _x, _y+47, 2*radius, 2*radius ); + p.drawEllipse( _x+width()-2*radius+1, _y+7, 2*radius, 2*radius ); + p.drawEllipse( _x+width()-2*radius+1, _y+23, 2*radius, 2*radius ); + p.drawEllipse( _x+width()-2*radius+1, _y+39, 2*radius, 2*radius ); + p.drawEllipse( _x+width()-2*radius+1, _y+55, 2*radius, 2*radius ); + + const int dy = pressed ? 6 : -6; + + p.drawLine( _x+2*radius, _y+16, _x+width()-2*radius+2, _y+16+dy ); + p.drawLine( _x+2*radius, _y+48, _x+width()-2*radius+2, _y+48+dy ); + + deinitPainter(p); +} + +void ECDPDT::buttonStateChanged( const QString &, bool state ) +{ + pressed = state; + m_switch1->setState( state ? Switch::Open : Switch::Closed ); + m_switch2->setState( state ? Switch::Closed : Switch::Open ); + m_switch3->setState( state ? Switch::Open : Switch::Closed ); + m_switch4->setState( state ? Switch::Closed : Switch::Open ); +} +//END class ECDPDT + + +//BEGIN class ECDPST +Item* ECDPST::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new ECDPST( (ICNDocument*)itemDocument, newItem, id ); +} + +LibraryItem* ECDPST::libraryItem() +{ + return new LibraryItem( + QString("ec/dpst_toggle"), + i18n("DPST"), + i18n("Switches"), + "dpst.png", + LibraryItem::lit_component, + ECDPST::construct ); +} + +ECDPST::ECDPST( ICNDocument *icnDocument, bool newItem, const char *id ) + : Component( icnDocument, newItem, (id) ? id : "dpst_toggle" ) +{ + m_name = i18n("DPST Toggle"); + m_desc = i18n("Double-Pole Single-Throw switch."); + setSize( -16, -16, 32, 32 ); + + addButton( "button", QRect( -16, 16, 32, 20 ), "", true ); + + createProperty( "button_text", Variant::Type::String ); + property("button_text")->setCaption( i18n("Button Text") ); + + Variant * v = createProperty( "bounce", Variant::Type::Bool ); + v->setCaption("Bounce"); + v->setAdvanced(true); + v->setValue(false); + + v = createProperty( "bounce_period", Variant::Type::Double ); + v->setCaption("Bounce Period"); + v->setAdvanced(true); + v->setUnit("s"); + v->setValue(5e-3); + + init2PinLeft( -8, 8 ); + init2PinRight( -8, 8 ); + + m_switch1 = createSwitch( m_pPNode[0], m_pNNode[0], true ); + m_switch2 = createSwitch( m_pPNode[1], m_pNNode[1], true ); + pressed = false; +} + + +ECDPST::~ECDPST() +{ +} + + +void ECDPST::dataChanged() +{ + button("button")->setText( dataString("button_text") ); + + bool bounce = dataBool("bounce"); + int bouncePeriod_ms = int(dataDouble("bounce_period")*1e3); + + m_switch1->setBounce( bounce, bouncePeriod_ms ); + m_switch2->setBounce( bounce, bouncePeriod_ms ); +} + + +void ECDPST::drawShape( QPainter &p ) +{ + initPainter(p); + + int _x = (int)x()-16; + int _y = (int)y()-16; + const int radius = 2; + + p.drawEllipse( _x, _y+6, 2*radius, 2*radius ); + p.drawEllipse( _x, _y+22, 2*radius, 2*radius ); + p.drawEllipse( _x+width()-2*radius+1, _y+6, 2*radius, 2*radius ); + p.drawEllipse( _x+width()-2*radius+1, _y+22, 2*radius, 2*radius ); + + const int dy = pressed ? 6 : 0; + + p.drawLine( _x+2*radius,_y+7,_x+width()-2*radius,_y+1+dy ); + p.drawLine( _x+2*radius,_y+24,_x+width()-2*radius,_y+18+dy ); + + deinitPainter(p); +} + +void ECDPST::buttonStateChanged( const QString &, bool state ) +{ + m_switch1->setState( state ? Switch::Closed : Switch::Open ); + m_switch2->setState( state ? Switch::Closed : Switch::Open ); + pressed = state; +} +//END class ECDPST + + +//BEGIN class ECSPDT +Item* ECSPDT::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new ECSPDT( (ICNDocument*)itemDocument, newItem, id ); +} + +LibraryItem* ECSPDT::libraryItem() +{ + return new LibraryItem( + QString("ec/spdt_toggle"), + i18n("SPDT"), + i18n("Switches"), + "spdt.png", + LibraryItem::lit_component, + ECSPDT::construct ); +} + + +ECSPDT::ECSPDT( ICNDocument *icnDocument, bool newItem, const char *id ) + : Component( icnDocument, newItem, (id) ? id : "spdt_toggle" ) +{ + m_name = i18n("SPDT Toggle"); + m_desc = i18n("Single-Pole Double-Throw switch."); + setSize( -16, -16, 32, 32 ); + + addButton( "button", QRect( -16, 16, width(), 20 ), "", true ); + + createProperty( "button_text", Variant::Type::String ); + property("button_text")->setCaption( i18n("Button Text") ); + + Variant * v = createProperty( "bounce", Variant::Type::Bool ); + v->setCaption("Bounce"); + v->setAdvanced(true); + v->setValue(false); + + v = createProperty( "bounce_period", Variant::Type::Double ); + v->setCaption("Bounce Period"); + v->setAdvanced(true); + v->setUnit("s"); + v->setValue(5e-3); + + init1PinLeft( 0 ); + init2PinRight( -8, 8 ); + + m_switch1 = createSwitch( m_pNNode[0], m_pPNode[0], false ); + m_switch2 = createSwitch( m_pNNode[0], m_pPNode[1], true ); + + pressed = false; +} + + +ECSPDT::~ECSPDT() +{ +} + + +void ECSPDT::dataChanged() +{ + button("button")->setText( dataString("button_text") ); + + bool bounce = dataBool("bounce"); + int bouncePeriod_ms = int(dataDouble("bounce_period")*1e3); + + m_switch1->setBounce( bounce, bouncePeriod_ms ); + m_switch2->setBounce( bounce, bouncePeriod_ms ); +} + + +void ECSPDT::drawShape( QPainter &p ) +{ + initPainter(p); + + int _x = (int)x()-16; + int _y = (int)y()-16; + const int radius = 2; + + p.drawEllipse( _x, _y+15, 2*radius, 2*radius ); + p.drawEllipse( _x+width()-2*radius+1, _y+6, 2*radius, 2*radius ); + p.drawEllipse( _x+width()-2*radius+1, _y+22, 2*radius, 2*radius ); + + const int dy = pressed ? 21 : 10; + p.drawLine( _x+2*radius, _y+16, _x+width()-2*radius+2, _y+dy ); + + deinitPainter(p); +} + +void ECSPDT::buttonStateChanged( const QString &, bool state ) +{ + pressed = state; + m_switch1->setState( state ? Switch::Open : Switch::Closed ); + m_switch2->setState( state ? Switch::Closed : Switch::Open ); +} +//END class ECSPDT + + +//BEGIN class ECSPST +Item* ECSPST::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new ECSPST( (ICNDocument*)itemDocument, newItem, id ); +} + +LibraryItem* ECSPST::libraryItem() +{ + return new LibraryItem( + QString("ec/spst_toggle"), + i18n("SPST"), + i18n("Switches"), + "spst.png", + LibraryItem::lit_component, + ECSPST::construct + ); +} + + +ECSPST::ECSPST( ICNDocument *icnDocument, bool newItem, const char *id ) + : Component( icnDocument, newItem, (id) ? id : "spst_toggle" ) +{ + m_name = i18n("SPST Toggle"); + m_desc = i18n("Single-Pole Single-Throw switch."); + setSize( -16, -8, 32, 16 ); + pressed = false; + + addButton( "button", QRect( -16, 8, width(), 20 ), "", true ); + + createProperty( "button_text", Variant::Type::String ); + property("button_text")->setCaption( i18n("Button Text") ); + + Variant * v = createProperty( "bounce", Variant::Type::Bool ); + v->setCaption("Bounce"); + v->setAdvanced(true); + v->setValue(false); + + v = createProperty( "bounce_period", Variant::Type::Double ); + v->setCaption("Bounce Period"); + v->setAdvanced(true); + v->setUnit("s"); + v->setValue(5e-3); + + button("button")->setState(pressed); + + init1PinLeft(); + init1PinRight(); + + m_switch = createSwitch( m_pNNode[0], m_pPNode[0], !pressed ); +} + + +ECSPST::~ECSPST() +{ +} + + +void ECSPST::dataChanged() +{ + button("button")->setText( dataString("button_text") ); + + bool bounce = dataBool("bounce"); + int bouncePeriod_ms = int(dataDouble("bounce_period")*1e3); + m_switch->setBounce( bounce, bouncePeriod_ms ); +} + + +void ECSPST::drawShape( QPainter &p ) +{ + initPainter(p); + + int _x = (int)x()-16; + int _y = (int)y()-8; + const int radius = 2; + + p.drawEllipse( _x, _y+7, 2*radius, 2*radius ); + p.drawEllipse( _x+width()-2*radius+1, _y+7, 2*radius, 2*radius ); + const int dy = pressed ? 0 : -6; + p.drawLine( _x+2*radius, _y+8, _x+width()-2*radius, _y+8+dy ); + + deinitPainter(p); +} + +void ECSPST::buttonStateChanged( const QString &, bool state ) +{ + pressed = state; + m_switch->setState( state ? Switch::Closed : Switch::Open ); +} +//END class ECSPST + diff --git a/src/electronics/components/toggleswitch.h b/src/electronics/components/toggleswitch.h new file mode 100644 index 0000000..80fd064 --- /dev/null +++ b/src/electronics/components/toggleswitch.h @@ -0,0 +1,116 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef TOGGLESWITCH_H +#define TOGGLESWITCH_H + +#include "component.h" + +/** +@short Double Pole Double Throw +@author David Saxton +*/ +class ECDPDT : public Component +{ +public: + ECDPDT( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~ECDPDT(); + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + + virtual void buttonStateChanged( const QString &id, bool state ); + virtual void dataChanged(); + virtual bool canFlip() const { return true; } + +private: + virtual void drawShape( QPainter &p ); + Switch *m_switch1; + Switch *m_switch2; + Switch *m_switch3; + Switch *m_switch4; + bool pressed; +}; + + +/** +@short Double Pole Single Throw +@author David Saxton +*/ +class ECDPST : public Component +{ +public: + ECDPST( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~ECDPST(); + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + + virtual void buttonStateChanged( const QString &id, bool state ); + virtual void dataChanged(); + virtual bool canFlip() const { return true; } + +private: + virtual void drawShape( QPainter &p ); + Switch *m_switch1; + Switch *m_switch2; + bool pressed; +}; + + +/** +@short Single Pole Double Throw +@author David Saxton +*/ +class ECSPDT : public Component +{ +public: + ECSPDT( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~ECSPDT(); + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + + virtual void buttonStateChanged( const QString &id, bool state ); + virtual void dataChanged(); + virtual bool canFlip() const { return true; } + +private: + virtual void drawShape( QPainter &p ); + Switch *m_switch1; + Switch *m_switch2; + bool pressed; +}; + + +/** +@short Single-Pole Single-Throw Switch +@author David Saxton +*/ +class ECSPST : public Component +{ +public: + ECSPST( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~ECSPST(); + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + + virtual void buttonStateChanged( const QString &id, bool state ); + virtual void dataChanged(); + virtual bool canFlip() const { return true; } + +private: + virtual void drawShape( QPainter &p ); + Switch *m_switch; + bool pressed; +}; + +#endif diff --git a/src/electronics/ecnode.cpp b/src/electronics/ecnode.cpp new file mode 100644 index 0000000..0fb2801 --- /dev/null +++ b/src/electronics/ecnode.cpp @@ -0,0 +1,239 @@ +/*************************************************************************** + * Copyright (C) 2003-2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "circuitdocument.h" +#include "src/core/ktlconfig.h" +#include "component.h" +#include "connector.h" +#include "ecnode.h" +#include "pin.h" + +#include <kdebug.h> +#include <qpainter.h> + +#include <cmath> + +// The voltage at the middle of the voltage indicator +const double vMidPoint = 5.; + +// The maximum length of the voltage indiactor +const int vLength = 8; + +// The current at the middle of the current indicator +const double iMidPoint = 0.03; + +// The maximum thicnkess of the current indicator +const int iLength = 6; + +inline double calcVProp( const double v ) +{ + return 1 - vMidPoint/(vMidPoint+std::abs(v)); +} + +inline double calcIProp( const double i ) +{ + return 1 - iMidPoint/(iMidPoint+std::abs(i)); +} + +inline int calcThickness( const double prop ) +{ + return (int)((iLength-2)*prop+2); +} + +inline int calcLength( const double prop, const double v ) +{ + return (v>0) ? -int(vLength*prop) : int(vLength*prop); +} + +ECNode::ECNode( ICNDocument *icnDocument, Node::node_type _type, node_dir dir, const QPoint &pos, QString *_id ) + : Node( icnDocument, _type, dir, pos, _id ) +{ + m_prevV = 0; + m_prevI = 0; + m_pinPoint = 0l; + m_bShowVoltageBars = KTLConfig::showVoltageBars(); + + icnDocument->registerItem(this); + + if ( type() == ec_pin ) + { + m_pinPoint = new QCanvasRectangle( 0, 0, 3, 3, canvas() ); + m_pinPoint->setBrush(Qt::black); + m_pinPoint->setPen(Qt::black); + } + + m_pins.resize(1); + m_pins[0] = new Pin(this); +} + + +ECNode::~ECNode() +{ + if (m_pinPoint) + m_pinPoint->setCanvas(0l); + delete m_pinPoint; + m_pinPoint = 0l; + + for ( unsigned i = 0; i < m_pins.size(); i++ ) + delete m_pins[i]; + m_pins.resize(0); +} + + +void ECNode::setNumPins( unsigned num ) +{ + unsigned oldNum = m_pins.size(); + + if ( num == oldNum ) + return; + + if ( num > oldNum ) + { + m_pins.resize(num); + for ( unsigned i = oldNum; i < num; i++ ) + m_pins[i] = new Pin(this); + } + else + { + for ( unsigned i = num; i < oldNum; i++ ) + delete m_pins[i]; + m_pins.resize(num); + } + + emit numPinsChanged(num); +} + + +void ECNode::setNodeChanged() +{ + if ( !canvas() || numPins() != 1 ) + return; + + Pin * pin = m_pins[0]; + + double v = pin->voltage(); + double i = pin->current(); + + if ( v != m_prevV || i != m_prevI ) + { + QRect r = boundingRect(); + r.setCoords( r.left()+(r.width()/2)-1, r.top()+(r.height()/2)-1, r.right()-(r.width()/2)+1, r.bottom()-(r.height()/2)+1 ); + canvas()->setChanged(r); + m_prevV = v; + m_prevI = i; + } +} + + +void ECNode::setParentItem( CNItem * parentItem ) +{ + Node::setParentItem(parentItem); + + if ( Component * component = dynamic_cast<Component*>(parentItem) ) + { + connect( component, SIGNAL(elementDestroyed(Element* )), this, SLOT(removeElement(Element* )) ); + connect( component, SIGNAL(switchDestroyed( Switch* )), this, SLOT(removeSwitch( Switch* )) ); + } +} + + +void ECNode::removeElement( Element * e ) +{ + for ( unsigned i = 0; i < m_pins.size(); i++ ) + m_pins[i]->removeElement(e); +} + + +void ECNode::removeSwitch( Switch * sw ) +{ + for ( unsigned i = 0; i < m_pins.size(); i++ ) + m_pins[i]->removeSwitch( sw ); +} + + +void ECNode::drawShape( QPainter &p ) +{ + const int _x = int(x()); + const int _y = int(y()); + + if ( type() == ec_junction ) + { +// p.drawRect( _x-2, _y-1, 5, 3 ); +// p.drawRect( _x-1, _y-2, 3, 5 ); + p.drawRect( _x-1, _y-1, 3, 3 ); + return; + } + + if (m_pinPoint) + { + bool drawDivPoint; + QPoint divPoint = findConnectorDivergePoint(&drawDivPoint); + m_pinPoint->setVisible(drawDivPoint); + m_pinPoint->move( divPoint.x()-1, divPoint.y()-1 ); + } + + // Now to draw on our current/voltage bar indicators + + if ( numPins() == 1 ) + { + double v = pin()->voltage(); + double vProp = calcVProp(v); + int length = calcLength( vProp, v ); + + if ( m_bShowVoltageBars && length != 0 ) + { + // we can assume that v != 0 as length != 0 + + QPen oldPen = p.pen(); + + double i = pin()->current(); + double iProp = calcIProp(i); + int thickness = calcThickness(iProp); + + if ( v > 0 ) + p.setPen( QPen( QColor( 255, 166, 0 ), thickness ) ); + + else + p.setPen( QPen( QColor( 0, 136, 255 ), thickness ) ); + + // The node line (drawn at the end of this function) will overdraw + // some of the voltage bar, so we need to adapt the length + if ( v > 0 && (m_dir == Node::dir_up || m_dir == Node::dir_down) ) + length--; + else if ( v < 0 && (m_dir == Node::dir_left || m_dir == Node::dir_right) ) + length++; + + if ( m_dir == Node::dir_right ) + p.drawLine( _x+3, _y, _x+3, _y+length ); + + else if ( m_dir == Node::dir_down ) + p.drawLine( _x, _y+3, _x-length, _y+3 ); + + else if ( m_dir == Node::dir_left ) + p.drawLine( _x-3, _y, _x-3, _y+length ); + + else if ( m_dir == Node::dir_up ) + p.drawLine( _x, _y-3, _x-length, _y-3 ); + + p.setPen(oldPen); + } + } + + QPen pen( p.pen() ); + pen.setWidth( (numPins() > 1) ? 2 : 1 ); + p.setPen(pen); + + if ( m_dir == Node::dir_right ) p.drawLine( _x, _y, _x+8, _y ); + else if ( m_dir == Node::dir_down ) p.drawLine( _x, _y, _x, _y+8 ); + else if ( m_dir == Node::dir_left ) p.drawLine( _x, _y, _x-8, _y ); + else if ( m_dir == Node::dir_up ) p.drawLine( _x, _y, _x, _y-8 ); +} + +#include "ecnode.moc" diff --git a/src/electronics/ecnode.h b/src/electronics/ecnode.h new file mode 100644 index 0000000..6f8f36e --- /dev/null +++ b/src/electronics/ecnode.h @@ -0,0 +1,74 @@ +/*************************************************************************** + * Copyright (C) 2003-2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef ECNODE_H +#define ECNODE_H + +#include "node.h" + +#include <qvaluevector.h> + +class ECNode; +class Element; +class Pin; +class Switch; +class QTimer; + +typedef QValueList<ECNode*> ECNodeList; +typedef QValueList<Element*> ElementList; +typedef QValueVector<Pin*> PinVector; + +/** +@short Electrical node with voltage / current / etc properties +@author David Saxton +*/ +class ECNode : public Node +{ + Q_OBJECT + public: + ECNode( ICNDocument *icnDocument, Node::node_type type, node_dir dir, const QPoint &pos, QString *id = 0L ); + ~ECNode(); + + virtual void setParentItem( CNItem *parentItem ); + virtual void drawShape( QPainter &p ); + /** + * Set the number of pins "contained" in this node. + */ + void setNumPins( unsigned num ); + /** + * @return the number of pins in this node. + * @see setNumPins + */ + unsigned numPins() const { return m_pins.size(); } + PinVector pins() const { return m_pins; } + Pin * pin( unsigned num = 0 ) const { return (num < m_pins.size()) ? m_pins[num] : 0l; } + bool showVoltageBars() const { return m_bShowVoltageBars; } + void setShowVoltageBars( bool show ) { m_bShowVoltageBars = show; } + void setNodeChanged(); + + signals: + void numPinsChanged( unsigned newNum ); + + protected slots: + void removeElement( Element * e ); + void removeSwitch( Switch * sw ); + + protected: + bool m_bShowVoltageBars; + double m_prevV; + double m_prevI; + QCanvasRectangle * m_pinPoint; + PinVector m_pins; +}; + +#endif + + + diff --git a/src/electronics/gpsimprocessor.cpp b/src/electronics/gpsimprocessor.cpp new file mode 100644 index 0000000..1a3b862 --- /dev/null +++ b/src/electronics/gpsimprocessor.cpp @@ -0,0 +1,880 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "config.h" +#ifndef NO_GPSIM + +#include "asmparser.h" +#include "debugmanager.h" +#include "flowcodedocument.h" +#include "gpsimprocessor.h" +#include "language.h" +#include "languagemanager.h" +#include "microlibrary.h" +#include "processchain.h" +#include "simulator.h" + +#include <assert.h> + +#include <kdebug.h> +#include <klocale.h> +#include <kmessagebox.h> +#include <ktempfile.h> +#include <kstandarddirs.h> +#include <qfile.h> +#include <qtextstream.h> +#include <qtimer.h> + +#include "gpsim/cod.h" +#include "gpsim/interface.h" +#include "gpsim/gpsim_classes.h" +#include "gpsim/pic-processor.h" +#include "gpsim/registers.h" +#include "gpsim/14bit-registers.h" +#include "gpsim/symbol.h" +#include "gpsim/sim_context.h" + +bool bDoneGpsimInit = false; +bool bUseGUI = true; +// extern "C" void initialize_gpsim(); +// void initialize_gpsim(void); +extern void initialize_commands(); +void initialize_ConsoleUI(); +extern void initialize_readline(); +extern void gui_main(void); +extern void cli_main(); +void gpsim_version() {}; +void quit_gui() {}; + + +//BEGIN class GpsimProcessor +/** +Work around a bug in gpsim: the directory in a filename is recorded twice, e.g. +"/home/david/afile.asm" is recorded as "/home/david//home/david/afile.asm". This +function will remove the duplicated directory path (by searching for a "//"). +*/ +QString sanitizeGpsimFile( QString file ) +{ + int pos = file.find("//"); + if ( pos != -1 ) + { + file.remove( 0, pos + 1 ); + } + return file; +} + + +GpsimProcessor::GpsimProcessor( QString symbolFile, QObject *parent ) + : QObject(parent), + m_symbolFile(symbolFile) +{ + if (!bDoneGpsimInit) + { +#ifndef GPSIM_0_21_4 + initialize_ConsoleUI(); +#endif + initialize_gpsim_core(); + initialization_is_complete(); + + bDoneGpsimInit = true; + } + + m_bCanExecuteNextCycle = true; + m_bIsRunning = false; + m_pPicProcessor = 0l; + m_codLoadStatus = CodUnknown; + m_pRegisterMemory = 0l; + m_debugMode = GpsimDebugger::AsmDebugger; + m_pDebugger[0] = m_pDebugger[1] = 0l; + + Processor * tempProcessor = 0l; + const char * fileName = symbolFile.ascii(); + +#ifdef GPSIM_0_21_4 + switch ( (cod_errors)load_symbol_file( &tempProcessor, fileName ) ) + { + case COD_SUCCESS: + m_codLoadStatus = CodSuccess; + break; + case COD_FILE_NOT_FOUND: + m_codLoadStatus = CodFileNotFound; + break; + case COD_UNRECOGNIZED_PROCESSOR: + m_codLoadStatus = CodUnrecognizedProcessor; + break; + case COD_FILE_NAME_TOO_LONG: + m_codLoadStatus = CodFileNameTooLong; + break; + case COD_LST_NOT_FOUND: + m_codLoadStatus = CodLstNotFound; + break; + case COD_BAD_FILE: + m_codLoadStatus = CodBadFile; + break; + default: + m_codLoadStatus = CodUnknown; + } +#else // GPSIM_0_21_11+ + FILE * pFile = fopen( fileName, "r" ); + if ( !pFile ) + m_codLoadStatus = CodFileUnreadable; + else + m_codLoadStatus = ( ProgramFileTypeList::GetList().LoadProgramFile( & tempProcessor, fileName, pFile ) ) ? CodSuccess : CodFailure; +#endif + + m_pPicProcessor = dynamic_cast<pic_processor*>(tempProcessor); + + if ( codLoadStatus() == CodSuccess ) + { + m_pRegisterMemory = new RegisterSet( m_pPicProcessor ); + m_pDebugger[0] = new GpsimDebugger( GpsimDebugger::AsmDebugger, this ); + m_pDebugger[1] = new GpsimDebugger( GpsimDebugger::HLLDebugger, this ); + Simulator::self()->attachGpsimProcessor(this); + DebugManager::self()->registerGpsim(this); + } +} + + +GpsimProcessor::~GpsimProcessor() +{ + Simulator::self()->detachGpsimProcessor(this); + delete m_pRegisterMemory; + + if ( m_pDebugger[0] ) + m_pDebugger[0]->deleteLater(); + if ( m_pDebugger[1] ) + m_pDebugger[1]->deleteLater(); +} + + +void GpsimProcessor::displayCodLoadStatus( ) +{ + switch (m_codLoadStatus) + { + case CodSuccess: + break; + case CodFileNotFound: + KMessageBox::sorry( 0l, i18n("The cod file \"%1\" was not found.").arg(m_symbolFile), i18n("File Not Found") ); + break; + case CodUnrecognizedProcessor: + KMessageBox::sorry( 0l, i18n("The processor for cod file \"%1\" is unrecognized.").arg(m_symbolFile), i18n("Unrecognized Processor") ); + break; + case CodFileNameTooLong: + KMessageBox::sorry( 0l, i18n("The file name \"%1\" is too long.").arg(m_symbolFile), i18n("Filename Too Long") ); + break; + case CodLstNotFound: + KMessageBox::sorry( 0l, i18n("The lst file associated with the cod file \"%1\" was not found.").arg(m_symbolFile), i18n("LST File Not Found") ); + break; + case CodBadFile: + KMessageBox::sorry( 0l, i18n("The cod file \"%1\" is bad.").arg(m_symbolFile), i18n("Bad File") ); + break; + case CodFileUnreadable: + KMessageBox::sorry( 0l, i18n("The cod file \"%1\" could not be read from.").arg(m_symbolFile), i18n("Unreadable File") ); + break; + case CodFailure: + case CodUnknown: + KMessageBox::sorry( 0l, i18n("An error occured with the cod file \"%1\".").arg(m_symbolFile), i18n("Error") ); + break; + } +} + + +unsigned GpsimProcessor::programMemorySize() const +{ + return m_pPicProcessor->program_memory_size(); +} + + +QStringList GpsimProcessor::sourceFileList() +{ + QStringList files; + + // Work around nasty bug in gpsim 0.21.4 where nsrc_files value might be used uninitiazed + int max = m_pPicProcessor->files.nsrc_files(); +#ifdef GPSIM_0_21_4 + if ( max > 10 ) + max = 10; +#endif + + for ( int i = 0; i < max; ++i ) + { + if ( !m_pPicProcessor->files[i] ) + continue; + + files << sanitizeGpsimFile( m_pPicProcessor->files[i]->name().c_str() ); + } + + return files; +} + + +void GpsimProcessor::emitLineReached() +{ + m_pDebugger[0]->emitLineReached(); + m_pDebugger[1]->emitLineReached(); +} + + +void GpsimProcessor::setRunning( bool run ) +{ + if ( m_bIsRunning == run ) + return; + + m_bIsRunning = run; + emit runningStatusChanged(run); +} + + +void GpsimProcessor::executeNext() +{ + if ( !m_bIsRunning ) + return; + + if ( !m_bCanExecuteNextCycle ) + { + m_bCanExecuteNextCycle = true; + return; + } + + unsigned long long beforeExecuteCount = get_cycles().get(); + + m_pPicProcessor->step_one(false); // Don't know what the false is for; gpsim ignores its value anyway + + // Some instructions take more than one cycle to execute, so ignore next cycle if this was the case + if ( (get_cycles().get() - beforeExecuteCount) > 1 ) + m_bCanExecuteNextCycle = false; + + currentDebugger()->checkForBreak(); + + // Let's also update the values of RegisterInfo every 50 milliseconds + if ( (beforeExecuteCount % 20000) == 0 ) + registerMemory()->update(); +} + + +void GpsimProcessor::reset() +{ + bool wasRunning = isRunning(); + m_pPicProcessor->reset(SIM_RESET); + setRunning(false); + if (!wasRunning) + { + // If we weren't running before, then the next signal won't have been emitted + emitLineReached(); + } +} + + +MicroInfo * GpsimProcessor::microInfo( ) const +{ + if ( !m_pPicProcessor ) + return 0l; + + return MicroLibrary::self()->microInfoWithID( m_pPicProcessor->name().c_str() ); +} + + +int GpsimProcessor::operandRegister( unsigned address ) +{ + instruction * ins = m_pPicProcessor->program_memory[ address ]; + if ( Register_op * reg = dynamic_cast<Register_op*>(ins) ) + return reg->register_address; + return -1; +} + + +int GpsimProcessor::operandLiteral( unsigned address ) +{ + instruction * ins = m_pPicProcessor->program_memory[ address ]; + if ( Literal_op * lit = dynamic_cast<Literal_op*>(ins) ) + return lit->L; + return -1; +} + + +GpsimProcessor::ProgramFileValidity GpsimProcessor::isValidProgramFile( const QString & programFile ) +{ + if ( !KStandardDirs::exists(programFile) ) + return DoesntExist; + + QString extension = programFile.right( programFile.length() - programFile.findRev('.') - 1 ).lower(); + + if ( extension == "flowcode" || + extension == "asm" || + extension == "cod" || + extension == "basic" || extension == "microbe" || + extension == "c" ) + return Valid; + + if ( extension == "hex" && QFile::exists( QString(programFile).replace(".hex",".cod") ) ) + return Valid; + + return IncorrectType; +} + + +QString GpsimProcessor::generateSymbolFile( const QString &fileName, QObject *receiver, const char *successMember, const char * failMember ) +{ + if ( !isValidProgramFile(fileName) ) + return QString::null; + + QString extension = fileName.right( fileName.length() - fileName.findRev('.') - 1 ).lower(); + + if ( extension == "cod" ) + { + QTimer::singleShot( 0, receiver, successMember ); + return fileName; + } + if ( extension == "hex" ) + { + QTimer::singleShot( 0, receiver, successMember ); + // We've already checked for the existance of the ".cod" file in GpsimProcessor::isValidProgramFile + return QString(fileName).replace(".hex",".cod"); + } + + else if ( extension == "basic" || extension == "microbe" ) + { + compileMicrobe( fileName, receiver, successMember, failMember ); + return QString(fileName).replace( "."+extension, ".cod" ); + } + else if ( extension == "flowcode" ) + { + const QString hexFile = KTempFile( QString::null, ".hex" ).name(); + + ProcessOptions o; + o.b_addToProject = false; + o.setTargetFile( hexFile ); + o.setInputFiles( fileName ); + o.setMethod( ProcessOptions::Method::Forget ); + o.setProcessPath( ProcessOptions::ProcessPath::FlowCode_Program ); + + ProcessChain * pc = LanguageManager::self()->compile(o); + if (receiver) + { + if (successMember) + connect( pc, SIGNAL(successful()), receiver, successMember ); + if (failMember) + connect( pc, SIGNAL(failed()), receiver, failMember ); + } + + return QString(hexFile).replace( ".hex", ".cod" ); + } + else if ( extension == "asm" ) + { + ProcessOptions o; + o.b_addToProject = false; + o.setTargetFile( QString(fileName).replace(".asm",".hex")); + o.setInputFiles(fileName); + o.setMethod( ProcessOptions::Method::Forget ); + o.setProcessPath( ProcessOptions::ProcessPath::path( ProcessOptions::guessMediaType(fileName), ProcessOptions::ProcessPath::Program ) ); + + ProcessChain *pc = LanguageManager::self()->compile(o); + if (receiver) + { + if (successMember) + connect( pc, SIGNAL(successful()), receiver, successMember ); + if (failMember) + connect( pc, SIGNAL(failed()), receiver, failMember ); + } + + return QString(fileName).replace(".asm",".cod"); + } + else if ( extension == "c" ) + { + ProcessOptions o; + o.b_addToProject = false; + o.setTargetFile( QString(fileName).replace(".c",".hex")); + o.setInputFiles(fileName); + o.setMethod( ProcessOptions::Method::Forget ); + o.setProcessPath( ProcessOptions::ProcessPath::C_Program ); + + ProcessChain *pc = LanguageManager::self()->compile(o); + if (receiver) + { + if (successMember) + connect( pc, SIGNAL(successful()), receiver, successMember ); + if (failMember) + connect( pc, SIGNAL(failed()), receiver, failMember ); + } + + return QString(fileName).replace(".c",".cod"); + } + + if ( failMember ) + QTimer::singleShot( 0, receiver, failMember ); + return QString::null; +} + + +void GpsimProcessor::compileMicrobe( const QString &filename, QObject *receiver, const char * successMember, const char * failMember ) +{ + ProcessOptions o; + o.b_addToProject = false; + o.setTargetFile( QString(filename).replace(".microbe",".hex") ); + o.setInputFiles(filename); + o.setMethod( ProcessOptions::Method::Forget ); + o.setProcessPath( ProcessOptions::ProcessPath::Microbe_Program ); + ProcessChain * pc = LanguageManager::self()->compile(o); + if (receiver) + { + if (successMember) + connect( pc, SIGNAL(successful()), receiver, successMember ); + if (failMember) + connect( pc, SIGNAL(failed()), receiver, failMember ); + } +} +//END class GpsimProcessor + + + +//BEGIN class GpsimDebugger +GpsimDebugger::GpsimDebugger( Type type, GpsimProcessor * gpsim ) + : QObject() +{ + m_pGpsim = gpsim; + m_type = type; + m_pBreakFromOldLine = 0l; + m_addressToLineMap = 0l; + m_stackLevelLowerBreak = -1; + m_addressSize = 0; + + connect( m_pGpsim, SIGNAL(runningStatusChanged(bool )), this, SLOT(gpsimRunningStatusChanged(bool )) ); + + if ( type == HLLDebugger ) + { + const QStringList sourceFileList = m_pGpsim->sourceFileList(); + QStringList::const_iterator sflEnd = sourceFileList.end(); + for ( QStringList::const_iterator it = sourceFileList.begin(); it != sflEnd; ++it ) + { + AsmParser p(*it); + p.parse(this); + } + } + + initAddressToLineMap(); +} + + +GpsimDebugger::~GpsimDebugger() +{ + QValueList<DebugLine*> debugLinesToDelete; + + for ( unsigned i = 0; i < m_addressSize; ++i ) + { + DebugLine * dl = m_addressToLineMap[i]; + if ( !dl || dl->markedAsDeleted() ) + continue; + + dl->markAsDeleted(); + debugLinesToDelete += dl; + } + + const QValueList<DebugLine*>::iterator end = debugLinesToDelete.end(); + for ( QValueList<DebugLine*>::iterator it = debugLinesToDelete.begin(); it != end; ++it ) + delete *it; + + delete [] m_addressToLineMap; +} + + +void GpsimDebugger::gpsimRunningStatusChanged( bool isRunning ) +{ + if (!isRunning) + { + m_stackLevelLowerBreak = -1; + m_pBreakFromOldLine = 0l; + emitLineReached(); + } +} + + +void GpsimDebugger::associateLine( const QString & sourceFile, int sourceLine, const QString & assemblyFile, int assemblyLine ) +{ + if ( assemblyLine < 0 || sourceLine < 0 ) + { + kdWarning() << k_funcinfo << "Invalid lines: assemblyLine="<<assemblyLine<<" sourceLine="<<sourceLine<<endl; + return; + } + + SourceLine hllSource = SourceLine( sourceFile, sourceLine ); + SourceLine asmSource = SourceLine( assemblyFile, assemblyLine ); + + if ( m_sourceLineMap.contains(asmSource) ) + { + kdWarning() << k_funcinfo << "Already have an association for assembly (\""<<assemblyFile<<"\","<<assemblyLine<<")"<<endl; + return; + } + + m_sourceLineMap[asmSource] = hllSource; +} + + +void GpsimDebugger::initAddressToLineMap() +{ + m_addressSize = m_pGpsim->programMemorySize(); + + delete [] m_addressToLineMap; + m_addressToLineMap = new DebugLine*[m_addressSize]; + memset( m_addressToLineMap, 0, m_addressSize * sizeof(DebugLine*) ); + + if ( m_type == AsmDebugger ) + { + for ( unsigned i = 0; i < m_addressSize; ++i ) + { + int line = m_pGpsim->picProcessor()->pma->get_src_line(i) - 1; + int fileID = m_pGpsim->picProcessor()->pma->get_file_id(i); + FileContext * fileContext = m_pGpsim->picProcessor()->files[fileID]; + + if (fileContext) + m_addressToLineMap[i] = new DebugLine( sanitizeGpsimFile( fileContext->name().c_str() ), line ); + } + } + else + { + SourceLineMap::const_iterator slmEnd = m_sourceLineMap.end(); + for ( SourceLineMap::const_iterator it = m_sourceLineMap.begin(); it != slmEnd; ++it ) + { + SourceLineMap::const_iterator next = it; + ++next; + + int asmToLine = ((next == slmEnd) || (next.key().fileName() != it.key().fileName())) ? -1 : next.key().line() - 1; + + QString asmFile = it.key().fileName(); + int asmFromLine = it.key().line(); + SourceLine sourceLine = it.data(); + + + std::string stdAsmFile( asmFile.ascii() ); + int fileID = m_pGpsim->picProcessor()->files.Find( stdAsmFile ); + if ( fileID == -1 ) + { + kdWarning() << k_funcinfo << "Could not find FileContext (asmFile=\""<<asmFile<<"\")"<<endl; + continue; + } + + if ( asmToLine == -1 ) + asmToLine = m_pGpsim->picProcessor()->files[fileID]->max_line() - 2; + + if ( (asmFromLine < 0) || (asmToLine < asmFromLine) ) + { + kdWarning() << k_funcinfo << "Invalid lines: asmFromLine="<<asmFromLine<<" asmToLine="<<asmToLine<<endl; + continue; + } + + DebugLine * debugLine = new DebugLine( sourceLine.fileName(), sourceLine.line() ); + bool used = false; + + for ( int i = asmFromLine; i <= asmToLine; ++i ) + { +#ifdef GPSIM_0_21_4 + int address = m_pGpsim->picProcessor()->pma->find_address_from_line( fileID, i+1 ); +#else // GPSIM_0_21_11 + int address = m_pGpsim->picProcessor()->pma->find_address_from_line( m_pGpsim->picProcessor()->files[fileID], i+1 ); +#endif + if ( address != -1 ) + { + used = true; + m_addressToLineMap[address] = debugLine; + } + } + + if (!used) + delete debugLine; + } + } +} + + +void GpsimDebugger::setBreakpoints( const QString & path, const IntList & lines ) +{ + for ( unsigned i = 0; i < m_addressSize; i++ ) + { + DebugLine * dl = m_addressToLineMap[i]; + if ( !dl || dl->fileName() != path ) + continue; + + dl->setBreakpoint( lines.contains( dl->line() ) ); + } +} + + +void GpsimDebugger::setBreakpoint( const QString & path, int line, bool isBreakpoint ) +{ + for ( unsigned i = 0; i < m_addressSize; i++ ) + { + if ( !m_addressToLineMap[i] ) + continue; + + if ( (m_addressToLineMap[i]->fileName() == path) && + ( line == m_addressToLineMap[i]->line() ) ) + m_addressToLineMap[i]->setBreakpoint(isBreakpoint); + } +} + + +DebugLine * GpsimDebugger::currentDebugLine() +{ + return m_addressToLineMap[ m_pGpsim->picProcessor()->pc->get_value() ]; +} + + +SourceLine GpsimDebugger::currentLine() +{ + DebugLine * dl = currentDebugLine(); + return dl ? *dl : SourceLine(); +} + + +void GpsimDebugger::emitLineReached() +{ + SourceLine currentAt = currentLine(); + + if ( currentAt == m_previousAtLineEmit ) + return; + + m_previousAtLineEmit = currentAt; + m_pGpsim->registerMemory()->update(); + emit lineReached(currentAt); +} + + +void GpsimDebugger::checkForBreak() +{ + DebugLine * currentLine = m_addressToLineMap[ m_pGpsim->picProcessor()->pc->get_value() ]; + int currentStackLevel = int( m_pGpsim->picProcessor()->stack->pointer & m_pGpsim->picProcessor()->stack->stack_mask ); + + bool ontoNextLine = m_pBreakFromOldLine != currentLine; + bool lineBreakpoint = currentLine ? currentLine->isBreakpoint() : false; + bool stackBreakpoint = m_stackLevelLowerBreak >= currentStackLevel; + + if ( ontoNextLine && (lineBreakpoint || stackBreakpoint) ) + m_pGpsim->setRunning(false); +} + + +int GpsimDebugger::programAddress( const QString & path, int line ) +{ + for ( unsigned i = 0; i < m_addressSize; ++i ) + { + DebugLine * dl = m_addressToLineMap[i]; + if ( !dl || (dl->line() != line) || (dl->fileName() != path) ) + continue; + + return i; + } + + return -1; +} + + +void GpsimDebugger::stepInto() +{ + // I'm not aware of the stack being able to increase in size by more than + // one at a time, so "1" should suffice here...but to be on the safe side, + // make it a nice large number + stackStep( 1 << 16 ); +} +void GpsimDebugger::stepOver() +{ + stackStep(0); +} +void GpsimDebugger::stepOut() +{ + stackStep(-1); +} +void GpsimDebugger::stackStep( int dl ) +{ + if ( m_pGpsim->isRunning() ) + return; + + int initialStack = (m_pGpsim->picProcessor()->stack->pointer & m_pGpsim->picProcessor()->stack->stack_mask) + dl; + DebugLine * initialLine = currentDebugLine(); + + if ( initialStack < 0 ) + initialStack = 0; + + // Reset any previous stackStep, and step + m_pBreakFromOldLine = 0l; + m_stackLevelLowerBreak = -1; + m_pGpsim->picProcessor()->step_one(false); + + int currentStack = m_pGpsim->picProcessor()->stack->pointer & m_pGpsim->picProcessor()->stack->stack_mask; + DebugLine * currentLine = currentDebugLine(); + + if ( (initialStack >= currentStack) && (initialLine != currentLine) ) + emitLineReached(); + + else + { + // Looks like we stepped into something or haven't gone onto the next + // instruction, wait until we step back out.... + m_stackLevelLowerBreak = initialStack; + m_pBreakFromOldLine = initialLine; + m_pGpsim->setRunning(true); + } +} +//END class Debugger + + + +//BEGIN class RegisterSet +RegisterSet::RegisterSet( pic_processor * picProcessor ) +{ + unsigned numRegisters = picProcessor->rma.get_size(); + m_registers.resize( numRegisters, 0l ); + for ( unsigned i = 0; i < numRegisters; ++i ) + { + RegisterInfo * info = new RegisterInfo( & picProcessor->rma[i] ); + m_registers[i] = info; + m_nameToRegisterMap[ info->name() ] = info; + } + + RegisterInfo * info = new RegisterInfo( picProcessor->W ); + m_registers.append( info ); + m_nameToRegisterMap[ info->name() ] = info; +} + + +RegisterSet::~RegisterSet() +{ + for ( unsigned i = 0; i < m_registers.size(); ++i ) + delete m_registers[i]; +} + + +RegisterInfo * RegisterSet::fromAddress( unsigned address ) +{ + return (address < m_registers.size()) ? m_registers[address] : 0l; +} + + +RegisterInfo * RegisterSet::fromName( const QString & name ) +{ + // First try the name as case sensitive, then as case insensitive. + if ( m_nameToRegisterMap.contains( name ) ) + return m_nameToRegisterMap[ name ]; + + QString nameLower = name.lower(); + + RegisterInfoMap::iterator end = m_nameToRegisterMap.end(); + for ( RegisterInfoMap::iterator it = m_nameToRegisterMap.begin(); it != end; ++ it ) + { + if ( it.key().lower() == nameLower ) + return it.data(); + } + + return 0l; +} + + +void RegisterSet::update() +{ + for ( unsigned i = 0; i < m_registers.size(); ++i ) + m_registers[i]->update(); +} +//END class RegisterSet + + + +//BEGIN class RegisterInfo +RegisterInfo::RegisterInfo( Register * reg ) +{ + assert(reg); + m_pRegister = reg; + m_type = Invalid; + m_prevEmitValue = 0; + + switch ( m_pRegister->isa() ) + { + case Register::GENERIC_REGISTER: + m_type = Generic; + break; + case Register::FILE_REGISTER: + m_type = File; + break; + case Register::SFR_REGISTER: + m_type = SFR; + break; + case Register::BP_REGISTER: + m_type = Breakpoint; + break; + case Register::INVALID_REGISTER: + m_type = Invalid; + break; + } + + m_name = m_pRegister->baseName(); +} + + +unsigned RegisterInfo::value() const +{ + return m_pRegister->value.data; +} + + +void RegisterInfo::update() +{ + unsigned newValue = value(); + if ( newValue != m_prevEmitValue ) + { + m_prevEmitValue = newValue; + emit valueChanged(newValue); + } +} + + +QString RegisterInfo::toString( RegisterType type ) +{ + switch ( type ) + { + case Generic: + return i18n("Generic"); + + case File: + return i18n("File"); + + case SFR: + return i18n("SFR"); + + case Breakpoint: + return i18n("Breakpoint"); + + case Invalid: + return i18n("Invalid"); + } + + return i18n("Unknown"); +} +//END class RegisterInfo + + + +//BEGIN class DebugLine +DebugLine::DebugLine( const QString & fileName, int line ) + : SourceLine( fileName, line ) +{ + m_bIsBreakpoint = false; + m_bMarkedAsDeleted = false; +} + + +DebugLine::DebugLine() + : SourceLine() +{ + m_bIsBreakpoint = false; + m_bMarkedAsDeleted = false; +} +//END class DebugLine + + +#include "gpsimprocessor.moc" + +#endif diff --git a/src/electronics/gpsimprocessor.h b/src/electronics/gpsimprocessor.h new file mode 100644 index 0000000..0650fd9 --- /dev/null +++ b/src/electronics/gpsimprocessor.h @@ -0,0 +1,393 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "config.h" +#ifndef NO_GPSIM + +#ifndef GPSIMPROCESSOR_H +#define GPSIMPROCESSOR_H + +#include "sourceline.h" + +#include <qmap.h> +#include <qvaluevector.h> +#include <qobject.h> +#include <qvaluelist.h> + +class DebugLine; +class GpsimProcessor; +class MicroInfo; +class pic_processor; // from gpsim +class Register; +class RegisterMemoryAccess; + +typedef QMap<SourceLine, SourceLine> SourceLineMap; +typedef QMap<int, QString> QStringMap; +typedef QValueList<int> IntList; + + +class DebugLine : public SourceLine +{ + public: + DebugLine(); + DebugLine( const QString & fileName, int line ); + /** + * Whether or not to break when we reach this line. + */ + bool isBreakpoint() const { return m_bIsBreakpoint; } + /** + * Set whether or not to break when we reach this line. + */ + void setBreakpoint( bool breakpoint ) { m_bIsBreakpoint = breakpoint; } + /** + * Used for efficiency purposes by GpsimProcessor. Sets a flag. + */ + void markAsDeleted() { m_bMarkedAsDeleted = true; } + /** + * Used for efficiency purposes by GpsimProcessor. + */ + bool markedAsDeleted() const { return m_bMarkedAsDeleted; } + + protected: + bool m_bIsBreakpoint; + bool m_bMarkedAsDeleted; + + private: + DebugLine( const DebugLine & dl ); + DebugLine & operator = ( const DebugLine & dl ); +}; + + +/** +@short Stores info from gpsim register, used to hide gpsim interface +@author David Saxton +*/ +class RegisterInfo : public QObject +{ + Q_OBJECT + public: + RegisterInfo( Register * reg ); + + enum RegisterType + { + Invalid, + Generic, + File, + SFR, + Breakpoint + }; + + RegisterType type() const { return m_type; } + QString name() const { return m_name; } + unsigned value() const; + static QString toString( RegisterType type ); + + /** + * Checks to see if the value has changed; if so, emit new value. + */ + void update(); + + signals: + void valueChanged( unsigned newValue ); + + protected: + QString m_name; + RegisterType m_type; + Register * m_pRegister; + unsigned m_prevEmitValue; +}; + + +/** +@short Stores information about a set of registers, used to hide gpsim interface. +@author David Saxton +*/ +class RegisterSet +{ + public: + RegisterSet( pic_processor * picProcessor ); + ~RegisterSet(); + + /** + * Calls update for each RegisterInfo in this set. + */ + void update(); + /** + * Returns the number of registers. + */ + unsigned size() const { return m_registers.size(); } + + RegisterInfo * fromAddress( unsigned address ); + RegisterInfo * fromName( const QString & name ); + + protected: + typedef QMap< QString, RegisterInfo * > RegisterInfoMap; + RegisterInfoMap m_nameToRegisterMap; + QValueVector< RegisterInfo * > m_registers; +}; + + +/** +@author David Saxton +*/ +class GpsimDebugger : public QObject +{ + friend class GpsimProcessor; + Q_OBJECT + + public: + enum Type + { + AsmDebugger = 0, + HLLDebugger = 1 + }; + + GpsimDebugger( Type type, GpsimProcessor * gpsim ); + ~GpsimDebugger(); + + GpsimProcessor * gpsim() const { return m_pGpsim; } + + /** + * When an assembly file was generated by a high level language compiler + * like SDCC, it will insert markers like ";#CSRC" that show which line + * of source-code generated the given set of assembly instructions. This + * matches up the assembly file lines with the associated source file + * lines. + */ + void associateLine( const QString & sourceFile, int sourceLine, const QString & assemblyFile, int assemblyLine ); + /** + * Check to see if we've hit a breakpoint or similar; if so, this + * function will stop the execution of the PIC program. + */ + void checkForBreak(); + /** + * Sets the breakpoints used for the given file to exactly those that + * are contained in this list. Breakpoints for other files are not + * affected. + * @param path the location of the file (which gpsim must recognise). + */ + void setBreakpoints( const QString & path, const IntList & lines ); + /** + * Sets / removes the breakpoint at the given line + */ + void setBreakpoint( const QString & path, int line, bool isBreakpoint ); + /** + * Returns the current source line that gpsim is at. By default, this + * will be the corresponding assembly line. That can be overwritten + * using mapAddressBlockToLine. + */ + SourceLine currentLine(); + /** + * Returns a pointer to the debug info for the current line. + */ + DebugLine * currentDebugLine(); + /** + * @return the program address for the given line (or -1 if no such + * line). + */ + int programAddress( const QString & path, int line ); + /** + * Step into the next program line. + */ + void stepInto(); + /** + * Step over the next program instruction. If we are currently running, + * this function will do nothing. Otherwise, it will record the current + * stack level, step, and if the new stack level is <= the initial level + * then return - otherwise, this processor will set a breakpoint for + * stack levels <= initial, and go to running mode. + */ + void stepOver(); + /** + * Similar to stepOver, except we break when the stack level becomes < + * the initial stack level (instead of <= initial). + */ + void stepOut(); + + signals: + /** + * Emitted when a line is reached. By default, this is the line of the + * input assembly file; however, the line associated with an address in + * the PIC memory can be changed with mapAddressBlockToLine. + */ + void lineReached( const SourceLine & sourceLine ); + + protected slots: + void gpsimRunningStatusChanged( bool isRunning ); + + protected: + void initAddressToLineMap(); + void stackStep( int dl ); + void emitLineReached(); + + int m_stackLevelLowerBreak; // Set by step-over, for when the stack level decreases to the one given + SourceLine m_previousAtLineEmit; // Used for working out whether we should emit a new line reached signal + DebugLine ** m_addressToLineMap; + DebugLine * m_pBreakFromOldLine; + GpsimProcessor * m_pGpsim; + Type m_type; + unsigned m_addressSize; + SourceLineMap m_sourceLineMap; // assembly <--> High level language +}; + + +/** +@author David Saxton +*/ +class GpsimProcessor : public QObject +{ + friend class GpsimDebugger; + Q_OBJECT + + public: + /** + * Create a new gpsim processor. After calling this constructor, you + * should always call codLoadStatus() to ensure that the cod file was + * loaded successfully. + */ + GpsimProcessor( QString symbolFile, QObject *parent = 0 ); + ~GpsimProcessor(); + + void setDebugMode( GpsimDebugger::Type mode ) { m_debugMode = mode; } + GpsimDebugger * currentDebugger() const { return m_pDebugger[m_debugMode]; } + + enum CodLoadStatus + { + CodSuccess, + CodFileNotFound, + CodUnrecognizedProcessor, + CodFileNameTooLong, + CodLstNotFound, + CodBadFile, + CodFileUnreadable, + CodFailure, + CodUnknown // Should never be this, but just in case load_symbol_file returns something funny + }; + + enum InstructionType + { + LiteralOp, + BitOp, + RegisterOp, + UnknownOp, + }; + + /** + * @return status of opening the COD file + * @see displayCodLoadStatus + */ + CodLoadStatus codLoadStatus() const { return m_codLoadStatus; } + /** + * Popups a messagebox to the user according to the CodLoadStatus. Will + * only popup a messagebox if the CodLoadStatus wasn't CodSuccess. + */ + void displayCodLoadStatus(); + /** + * Returns a list of source files for the currently running program. + */ + QStringList sourceFileList(); + /** + * Set whether or not to run gpsim. (i.e. whether or not the step + * function should do anything when called with force=false). + */ + void setRunning( bool run ); + /** + * Returns true if running (currently simulating), else gpsim is paused. + */ + bool isRunning() const { return m_bIsRunning; } + /** + * Execute the next program instruction. If we are not in a running + * mode, then this function will do nothing. + */ + void executeNext(); + /** + * Reset all parts of the simulation. Gpsim will not run until + * setRunning(true) is called. Breakpoints are not affected. + */ + void reset(); + /** + * Returns the microinfo describing this processor. + */ + MicroInfo * microInfo() const; + + pic_processor * picProcessor() const { return m_pPicProcessor; } + unsigned programMemorySize() const; + RegisterSet * registerMemory() const { return m_pRegisterMemory; } + /** + * @return the instruction type at the given address. + */ + InstructionType instructionType( unsigned address ); + /** + * @return the address of the operand's register at address if the + * instruction at address is a register operation, and -1 otherwise. + */ + int operandRegister( unsigned address ); + /** + * @return the literal if the instruction at address is a literal + * operation, and -1 otherwise. + */ + int operandLiteral( unsigned address ); + + //BEGIN Convenience functions for PIC files + enum ProgramFileValidity { DoesntExist, IncorrectType, Valid }; + /** + * @return information on the validity of the given program file (either + * DoesntExist, IncorrectType, or Valid). + * @see static QString generateSymbolFile + */ + static ProgramFileValidity isValidProgramFile( const QString & programFile ); + /** + * Converts the file at programFile to a Symbol file for emulation, + * and returns that symbol file's path + * @param programFile The full url to the file + * @param assembled The slot to connect the assembled signal to + * @see static bool isValidProgramFile( const QString &programFile ) + */ + static QString generateSymbolFile( const QString &fileName, QObject *receiver, const char *successMember, const char * failMember = 0l ); + /** + *Compile microbe to output to the given filename + */ + static void compileMicrobe( const QString &filename, QObject *receiver, const char * successMember, const char * failMember = 0l ); + //END convenience functions for PIC files + + signals: + /** + * Emitted when the running status of gpsim changes. + */ + void runningStatusChanged( bool isRunning ); + + protected: + /** + * Calls emitLineReached for each debugger. + */ + void emitLineReached(); + + pic_processor * m_pPicProcessor; + CodLoadStatus m_codLoadStatus; + const QString m_symbolFile; + RegisterSet * m_pRegisterMemory; + GpsimDebugger::Type m_debugMode; + GpsimDebugger * m_pDebugger[2]; // Asm, HLL + + /** + * We are called effectively for each cycle of the cycle of the + * processor. This value is used as some instructions (e.g. goto) take + * two cycles to execute, and so we must ignore one cycle to ensure + * realtime simulation. + */ + bool m_bCanExecuteNextCycle; + + private: + bool m_bIsRunning; +}; + +#endif + +#endif // !NO_GPSIM diff --git a/src/electronics/pin.cpp b/src/electronics/pin.cpp new file mode 100644 index 0000000..56848fc --- /dev/null +++ b/src/electronics/pin.cpp @@ -0,0 +1,175 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "pin.h" + +#include <assert.h> +#include <kdebug.h> + +Pin::Pin( ECNode * parent ) +{ + assert(parent); + m_pECNode = parent; + m_voltage = 0.; + m_current = 0.; + m_eqId = -2; + m_bCurrentIsKnown = false; + m_groundType = Pin::gt_never; +} + + +Pin::~Pin() +{ + WireList::iterator end = m_inputWireList.end(); + for ( WireList::iterator it = m_inputWireList.begin(); it != end; ++it ) + delete *it; + + end = m_outputWireList.end(); + for ( WireList::iterator it = m_outputWireList.begin(); it != end; ++it ) + delete *it; +} + + +PinList Pin::localConnectedPins( ) const +{ +// kdDebug() << k_funcinfo << "Input wires: "<<m_inputWireList.size()<<" Output wires: " << m_outputWireList.size() << " Switch connected: " << m_switchConnectedPins.size() << endl; + + PinList pins; + + WireList::const_iterator end = m_inputWireList.end(); + for ( WireList::const_iterator it = m_inputWireList.begin(); it != end; ++it ) + { + if (*it) + pins << (*it)->startPin(); + } + + end = m_outputWireList.end(); + for ( WireList::const_iterator it = m_outputWireList.begin(); it != end; ++it ) + { + if (*it) + pins << (*it)->endPin(); + } + + pins += m_switchConnectedPins; + + return pins; +} + + +void Pin::setSwitchConnected( Pin * pin, bool isConnected ) +{ + if (!pin) + return; + + if (isConnected) + { + if ( !m_switchConnectedPins.contains(pin) ) + m_switchConnectedPins.append(pin); + } + else + m_switchConnectedPins.remove(pin); +} + + +void Pin::addCircuitDependentPin( Pin * pin ) +{ + if ( pin && !m_circuitDependentPins.contains(pin) ) + m_circuitDependentPins.append(pin); +} + + +void Pin::addGroundDependentPin( Pin * pin ) +{ + if ( pin && !m_groundDependentPins.contains(pin) ) + m_groundDependentPins.append(pin); +} + + +void Pin::removeDependentPins() +{ + m_circuitDependentPins.clear(); + m_groundDependentPins.clear(); +} + + +void Pin::addElement( Element * e ) +{ + if ( !e || m_elementList.contains(e) ) + return; + m_elementList.append(e); +} + + +void Pin::removeElement( Element * e ) +{ + m_elementList.remove(e); +} + + +void Pin::addSwitch( Switch * sw ) +{ + if ( !sw || m_switchList.contains( sw ) ) + return; + m_switchList << sw; +} + + +void Pin::removeSwitch( Switch * sw ) +{ + m_switchList.remove( sw ); +} + + +void Pin::addInputWire( Wire * wire ) +{ + if ( wire && !m_inputWireList.contains(wire) ) + m_inputWireList << wire; +} + + +void Pin::addOutputWire( Wire * wire ) +{ + if ( wire && !m_outputWireList.contains(wire) ) + m_outputWireList << wire; +} + + +bool Pin::calculateCurrentFromWires() +{ + m_inputWireList.remove( (Wire*)0l ); + m_outputWireList.remove( (Wire*)0l ); + + const WireList inputs = m_inputWireList; + const WireList outputs = m_outputWireList; + + m_current = 0.0; + + WireList::const_iterator end = inputs.end(); + for ( WireList::const_iterator it = inputs.begin(); it != end; ++it ) + { + if ( !(*it)->currentIsKnown() ) + return false; + + m_current -= (*it)->current(); + } + + end = outputs.end(); + for ( WireList::const_iterator it = outputs.begin(); it != end; ++it ) + { + if ( !(*it)->currentIsKnown() ) + return false; + + m_current += (*it)->current(); + } + + m_bCurrentIsKnown = true; + return true; +} + diff --git a/src/electronics/pin.h b/src/electronics/pin.h new file mode 100644 index 0000000..1e10663 --- /dev/null +++ b/src/electronics/pin.h @@ -0,0 +1,211 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef PIN_H +#define PIN_H + +#include "wire.h" + +#include <qguardedptr.h> +#include <qobject.h> +#include <qvaluelist.h> + +class ECNode; +class Element; +class Pin; +class Switch; +class Wire; + +typedef QValueList<Element*> ElementList; +typedef QValueList<QGuardedPtr<Pin> > PinList; +typedef QValueList<Switch*> SwitchList; +typedef QValueList<QGuardedPtr<Wire> > WireList; + + +/** +@author David Saxton +*/ +class Pin : public QObject +{ + public: + /** + * Priorities for ground pin. gt_always will (as expected) always assign + * the given pin as ground, gt_never will never do. If no gt_always pins + * exist, then the pin with the highest priority will be set as ground - + * if there is at least one pin that is not of ground type gt_never. These + * are only predefined recommended values, so if you choose not to use one + * of these, please respect the priorities with respect to the examples, and + * always specify a priority between 0 and 20. + * @see groundLevel + */ + enum GroundType + { + gt_always = 0, // ground + gt_high = 5, // voltage points + gt_medium = 10, // voltage sources + gt_low = 15, // current sources + gt_never = 20 // everything else + }; + Pin( ECNode * parent ); + ~Pin(); + + ECNode * parentECNode() const { return m_pECNode; } + /** + * This function returns the pins that are directly connected to this pins: + * either at the ends of connected wires, or via switches. + */ + PinList localConnectedPins() const; + /** + * Adds/removes the given pin to the list of ones that this pin is/isn't + * connected to via a switch. + */ + void setSwitchConnected( Pin * pin, bool isConnected ); + /** + * After calculating the nodal voltages in the circuit, this function should + * be called to tell the pin what its voltage is. + */ + void setVoltage( double v ) { m_voltage = v; } + /** + * Returns the voltage as set by setVoltage. + */ + double voltage() const { return m_voltage; } + /** + * After calculating nodal voltages, each component will be called to tell + * its pins what the current flowing *into* the component is. This sets it + * to zero in preparation to merging the current. + */ + void resetCurrent() { m_current = 0.0; } + /** + * Adds the given current to that already flowing into the pin. + * @see setCurrent + */ + void mergeCurrent( double i ) { m_current += i; } + /** + * Returns the current as set by mergeCurrent. + */ + double current() const { return m_current; } + /** + * In many cases (such as if this pin is a ground pin), the current + * flowing into the pin has not been calculated, and so the value + * returned by current() cannot be trusted. + */ + void setCurrentKnown( bool isKnown ) { m_bCurrentIsKnown = isKnown; } + /** + * Tell thie Pin that none of the currents from the switches have yet + * been merged. + */ + void setSwitchCurrentsUnknown() { m_switchList.remove( 0l ); m_unknownSwitchCurrents = m_switchList; } + /** + * This returns the value given by setCurrentKnown AND'd with whether + * we know the current from each switch attached to this pin. + * @see setCurrentKnown + */ + bool currentIsKnown() const { return m_bCurrentIsKnown && m_unknownSwitchCurrents.isEmpty(); } + /** + * Tells the Pin that the current from the given switch has been merged. + */ + void setSwitchCurrentKnown( Switch * sw ) { m_unknownSwitchCurrents.remove( sw ); } + /** + * Tries to calculate the Pin current from the input / output wires. + * @return whether was successful. + */ + bool calculateCurrentFromWires(); + /** + * Sets the "ground type" - i.e. the priority that this pin has for being + * ground over other pins in the circuit. Lower gt = higher priority. It's + * recommended to use Pin::GroundType. + */ + void setGroundType( int gt ) { m_groundType = gt; } + /** + * Returns the priority for ground. + */ + int groundType() const { return m_groundType; } + /** + * Adds a dependent pin - one whose voltages will (or might) affect the + * voltage of this pin. This is set by Component. + */ + void addCircuitDependentPin( Pin * pin ); + /** + * Adds a dependent pin - one whose voltages will (or might) affect the + * voltage of this pin. This is set by Component. + */ + void addGroundDependentPin( Pin * pin ); + /** + * Removes all Circuit and Ground dependent pins. + */ + void removeDependentPins(); + /** + * Returns the ids of the pins whose voltages will affect this pin. + * @see void setDependentPins( QStringList ids ) + */ + PinList circuitDependentPins() const { return m_circuitDependentPins; } + /** + * Returns the ids of the pins whose voltages will affect this pin. + * @see void setDependentPins( QStringList ids ) + */ + PinList groundDependentPins() const { return m_groundDependentPins; } + /** + * Use this function to set the pin identifier for equations, + * which should be done every time new pins are registered. + */ + void setEqId( int id ) { m_eqId = id; } + /** + * The equation identifier. + * @see setEqId + */ + int eqId() const { return m_eqId; } + /** + * Returns a list of elements that will affect this pin (e.g. if this + * pin is part of a resistor, then that list will contain a pointer to a + * Resistance element) + */ + ElementList elements() const { return m_elementList; } + /** + * Adds an element to the list of those that will affect this pin. + */ + void addElement( Element *e ); + /** + * Removes an element from the list of those that will affect this pin. + */ + void removeElement( Element *e ); + /** + * Adds an switch to the list of those that will affect this pin. + */ + void addSwitch( Switch *e ); + /** + * Removes an switch from the list of those that will affect this pin. + */ + void removeSwitch( Switch *e ); + + void addInputWire( Wire * wire ); + void addOutputWire( Wire * wire ); + void removeWire( Wire * wire ); + WireList inputWireList() const { return m_inputWireList; } + WireList outputWireList() const { return m_outputWireList; } + int numWires() const { return m_inputWireList.size() + m_outputWireList.size(); } + + protected: + double m_voltage; + double m_current; + int m_eqId; + bool m_bCurrentIsKnown; + PinList m_circuitDependentPins; + PinList m_groundDependentPins; + ElementList m_elementList; + SwitchList m_switchList; + int m_groundType; + PinList m_switchConnectedPins; + WireList m_inputWireList; + WireList m_outputWireList; + ECNode * m_pECNode; + SwitchList m_unknownSwitchCurrents; +}; + +#endif diff --git a/src/electronics/port.cpp b/src/electronics/port.cpp new file mode 100644 index 0000000..541195b --- /dev/null +++ b/src/electronics/port.cpp @@ -0,0 +1,514 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "port.h" + +#include <kdebug.h> + +#include <errno.h> +#include <fcntl.h> +#include <linux/ppdev.h> +#include <sys/ioctl.h> +#include <unistd.h> + +//BEGIN class Port +Port::Port() +{ +} + + +Port::~Port() +{ +} + + +QStringList Port::ports( unsigned probeResult ) +{ + return SerialPort::ports(probeResult) + ParallelPort::ports(probeResult); +} +//END class Port + + + +//BEGIN class SerialPort +SerialPort::SerialPort() +{ + m_file = -1; +} + + +SerialPort::~SerialPort() +{ + closePort(); +} + + +void SerialPort::setPinState( Pin pin, bool state ) +{ + if ( m_file == -1 ) + return; + + int flags = -1; + + switch ( pin ) + { + case TD: + ioctl( m_file, state ? TIOCSBRK : TIOCCBRK, 0 ); + return; + + case DTR: + flags = TIOCM_DTR; + break; + + case DSR: + flags = TIOCM_DSR; + break; + + case RTS: + flags = TIOCM_RTS; + break; + + case CD: + case RD: + case GND: + case CTS: + case RI: + break; + }; + + if ( flags == -1 ) + { + kdError() << k_funcinfo << "Bad pin " << pin << endl; + return; + } + + if ( ioctl( m_file, state ? TIOCMBIS : TIOCMBIC, & flags ) == -1 ) + kdError() << k_funcinfo << "Could not set pin " << pin << " errno = " << errno << endl; +} + + +bool SerialPort::pinState( Pin pin ) +{ + if ( m_file == -1 ) + return false; + + int mask = 0; + + switch ( pin ) + { + case CD: + mask = TIOCM_CD; + break; + + case RD: + mask = TIOCM_SR; + break; + + case CTS: + mask = TIOCM_CTS; + break; + + case RI: + mask = TIOCM_RI; + break; + + case TD: + case DTR: + case GND: + case DSR: + case RTS: + break; + } + + if ( mask == 0 ) + { + kdError() << k_funcinfo << "Bad pin " << pin << endl; + return false; + } + + int bits = 0; + if ( ioctl( m_file, TIOCMGET, & bits ) == -1 ) + { + kdError() << k_funcinfo << "Could not read pin" << pin << " errno = " << errno << endl; + return false; + } + + return bits & mask; +} + + +Port::ProbeResult SerialPort::probe( const QString & port ) +{ + int file = open( port.ascii(), O_NOCTTY | O_NONBLOCK | O_RDONLY ); + if ( file == -1 ) + return Port::DoesntExist; + + close(file); + + file = open( port.ascii(), O_NOCTTY | O_NONBLOCK | O_RDWR ); + if ( file == -1 ) + return Port::ExistsButNotRW; + close(file); + + return Port::ExistsAndRW; +} + + +bool SerialPort::openPort( const QString & port, speed_t baudRate ) +{ + closePort(); + + m_file = open( port.ascii(), O_NOCTTY | O_NONBLOCK | O_RDWR ); + if ( m_file == -1 ) + { + kdError() << k_funcinfo << "Could not open port " << port << endl; + return false; + } + + termios state; + tcgetattr( m_file, & state ); + + // Save the previous state for restoration in close. + m_previousState = state; + + state.c_iflag = IGNBRK | IGNPAR; + state.c_oflag = 0; + state.c_cflag = baudRate | CS8 | CREAD | CLOCAL; + state.c_lflag = 0; + tcsetattr( m_file, TCSANOW, & state ); + + return true; +} + + +void SerialPort::closePort() +{ + if ( m_file == -1 ) + return; + + ioctl( m_file, TIOCCBRK, 0 ); + usleep(1); + tcsetattr( m_file, TCSANOW, & m_previousState ); + close( m_file ); + m_file = -1; +} + + +QStringList SerialPort::ports( unsigned probeResult ) +{ + QStringList list; + + for ( int i = 0; i < 8; ++i ) + { + QString dev = QString("/dev/ttyS%1").arg(i); + if ( probe(dev) & probeResult ) + list << dev; + } + + for ( unsigned i = 0; i < 8; ++i ) + { + QString dev = QString("/dev/tts/%1").arg(i); + if ( probe(dev) & probeResult ) + list << dev; + } + + for ( unsigned i = 0; i < 8; ++i ) + { + QString dev = QString("/dev/ttyUSB%1").arg(i); + if ( probe(dev) & probeResult ) + list << dev; + } + + for ( unsigned i = 0; i < 8; ++i ) + { + QString dev = QString("/dev/usb/tts/%1").arg(i); + if ( probe(dev) & probeResult ) + list << dev; + } + + return list; +} +//END class SerialPort + + + +//BEGIN class ParallelPort +const int IRQ_MODE_BIT = 1 << 20; // Controls if pin 10 (Ack) causes interrupts +const int INPUT_MODE_BIT = 1 << 21; // Controls if the data pins are input or output +const int ALWAYS_INPUT_PINS = ParallelPort::STATUS_PINS; + +const int IOCTL_REG_READ[3] = { + PPRDATA, + PPRSTATUS, + PPRCONTROL, +}; + +const int IOCTL_REG_WRITE[3] = { + PPWDATA, + 0, + PPWCONTROL, +}; + +const int INVERT_MASK[3] = { + 0x0, + 0x80, // 10000000 + 0x0b, // 00001011 +}; + +ParallelPort::ParallelPort() +{ + reset(); +} + + +ParallelPort::~ParallelPort() +{ +} + + +void ParallelPort::reset() +{ + m_file = -1; + m_reg[Data] = 0; + m_reg[Status] = 0; + m_reg[Control] = 0; + m_outputPins = INPUT_MODE_BIT | IRQ_MODE_BIT; + m_inputPins = ALWAYS_INPUT_PINS | INPUT_MODE_BIT | IRQ_MODE_BIT; +} + + +//BEGIN Pin-oriented operations +void ParallelPort::setPinState( int pins, bool state ) +{ + // only allow writing to output pins + pins &= m_outputPins; + + if ( pins & DATA_PINS ) + setDataState( (pins & DATA_PINS) >> 0, state ); + + if ( pins & CONTROL_PINS ) + setControlState( (pins & CONTROL_PINS) >> 16, state ); +} + + +int ParallelPort::pinState( int pins ) +{ + int value = 0; + + // only allow reading from input pins + pins &= m_inputPins; + + if ( pins & DATA_PINS ) + value |= ((readFromRegister( Data ) & ((pins & DATA_PINS) >> 0)) << 0); + + if ( pins & STATUS_PINS ) + value |= ((readFromRegister( Status ) & ((pins & STATUS_PINS) >> 8)) << 8); + + if ( pins & CONTROL_PINS ) + value |= ((readFromRegister( Control ) & ((pins & CONTROL_PINS) >> 16)) << 16); + + return value; +} + + +void ParallelPort::setDataState( uchar pins, bool state ) +{ + uchar value = readFromRegister( Data ); + + if ( state ) + value |= pins; + else + value &= ~pins; + + writeToData( value ); +} + + +void ParallelPort::setControlState( uchar pins, bool state ) +{ + uchar value = readFromRegister( Control ); + + if ( state ) + value |= pins; + else + value &= ~pins; + + writeToControl( value ); +} +//END Pin-oriented operations + + + +//BEGIN Register-oriented operations +uchar ParallelPort::readFromRegister( Register reg ) +{ + if ( m_file == -1 ) + return 0; + +// uchar value = inb( m_lpBase + reg ) ^ INVERT_MASK[reg]; + uchar value = 0; + if ( ioctl( m_file, IOCTL_REG_READ[reg], &value ) ) + kdError() << k_funcinfo << "errno=" << errno << endl; + else + m_reg[reg] = value; + return value; +} + + +void ParallelPort::writeToRegister( Register reg, uchar value ) +{ + if ( m_file == -1 ) + return; + +// outb( value ^ INVERT_MASK[reg], m_lpBase + reg ); + if ( ioctl( m_file, IOCTL_REG_WRITE[reg], & value ) ) + kdError() << k_funcinfo << "errno=" << errno << endl; + else + m_reg[reg] = value; +} + + +void ParallelPort::writeToData( uchar value ) +{ + writeToRegister( Data, value ); +} + + +void ParallelPort::writeToControl( uchar value ) +{ + // Set all inputs to ones + value |= ((m_inputPins & CONTROL_PINS) >> 16); + + writeToRegister( Control, value ); +} +//END Register-oriented operations + + +//BEGIN Changing pin directions +void ParallelPort::setDataDirection( Direction dir ) +{ + if ( dir == Input ) + { + m_inputPins |= DATA_PINS; + m_outputPins &= ~DATA_PINS; + } + else + { + m_inputPins &= DATA_PINS; + m_outputPins |= ~DATA_PINS; + } + + setPinState( INPUT_MODE_BIT, dir == Input ); +} + + +void ParallelPort::setControlDirection( int pins, Direction dir ) +{ + pins &= CONTROL_PINS; + + if ( dir == Input ) + { + m_inputPins |= pins; + m_outputPins &= ~pins; + } + else + { + m_inputPins &= pins; + m_outputPins |= ~pins; + } + + setControlState( 0, true ); +} +//END Changing pin directions + + +Port::ProbeResult ParallelPort::probe( const QString & port ) +{ + int file = open( port.ascii(), O_RDWR ); + if ( file == -1 ) + return Port::DoesntExist; + + if ( ioctl( file, PPCLAIM ) != 0 ) + { + close(file); + return Port::ExistsButNotRW; + } + + ioctl( file, PPRELEASE ); + close(file); + return Port::ExistsAndRW; +} + + +QStringList ParallelPort::ports( unsigned probeResult ) +{ + QStringList list; + + for ( unsigned i = 0; i < 8; ++i ) + { + QString dev = QString("/dev/parport%1").arg(i); + if ( probe(dev) & probeResult ) + list << dev; + } + + for ( unsigned i = 0; i < 8; ++i ) + { + QString dev = QString("/dev/parports/%1").arg(i); + if ( probe(dev) & probeResult ) + list << dev; + } + + return list; +} + + +bool ParallelPort::openPort( const QString & port ) +{ + if ( m_file != -1 ) + { + kdWarning() << k_funcinfo << "Port already open" << endl; + return false; + } + + m_file = open( port.ascii(), O_RDWR ); + + if ( m_file == -1 ) + { + kdError() << k_funcinfo << "Could not open port \"" << port << "\": errno="<<errno<<endl; + return false; + } + + if ( ioctl( m_file, PPCLAIM ) ) + { + kdError() << k_funcinfo << "Port " << port << " must be RW" << endl; + close( m_file ); + m_file = -1; + return false; + } + + return true; +} + + +void ParallelPort::closePort() +{ + if ( m_file == -1 ) + return; + + int res = ioctl( m_file, PPRELEASE ); + close( m_file ); + + if ( res ) + kdError() << k_funcinfo << "res="<<res<<endl; + + m_file = -1; +} +//END class ParallelPort + diff --git a/src/electronics/port.h b/src/electronics/port.h new file mode 100644 index 0000000..53182bc --- /dev/null +++ b/src/electronics/port.h @@ -0,0 +1,248 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef PORT_H +#define PORT_H + +#include <qstringlist.h> + +#include <termios.h> + +/** +@author David Saxton + */ +class Port +{ + public: + enum ProbeResult + { + ExistsAndRW = 1 << 0, + ExistsButNotRW = 1 << 1, + DoesntExist = 1 << 2 + }; + + Port(); + virtual ~Port(); + + /** + * Returns a list of ports, whose probe status AND's with probeResult. + * This function just returns the combination of the lists for + * SerialPort::ports and ParallelPort::ports. + */ + static QStringList ports( unsigned probeResult ); +}; + + +/** +Abstraction for a serial port, allowing control over individual pins. + +@author David Saxton + */ +class SerialPort : public Port +{ + public: + enum Pin + { + CD = 1, // Carrier detect + RD = 2, // Recieved data + TD = 3, // Transmitted data + DTR = 4, // Data terminal ready + GND = 5, // Signal ground + DSR = 6, // Data set ready + RTS = 7, // Request to send + CTS = 8, // Clear to send + RI = 9, // Ring indicator + }; + + SerialPort(); + ~SerialPort(); + + /** + * Writes state (high or low) to the given pin. + */ + void setPinState( Pin pin, bool state ); + bool pinState( Pin pin ); + + static ProbeResult probe( const QString & port ); + /** + * @see Port::ports + */ + static QStringList ports( unsigned probeResult ); + /** + * Opens the given port. + * @return if the port could be opened. + * @param baudRate The baud rate as defined in bits/termios.h + */ + bool openPort( const QString & port, speed_t baudRate ); + /** + * Closes any currently open port. + */ + void closePort(); + + protected: + /// Read in on port open; restored on port close + termios m_previousState; + + /// File descriptor for the port. + int m_file; +}; + + +/** +Abstraction for a parallel port, allowing control over individual pins. +Based loosely on code in the parapin project; see http://parapin.sf.net + +@author David Saxton +*/ +class ParallelPort : public Port +{ + public: + enum Pin + { + // Data Register + // Offset: Base + 0 + // Readable / writable + PIN02 = 1 << 0, // Data 0 + PIN03 = 1 << 1, // Data 1 + PIN04 = 1 << 2, // Data 2 + PIN05 = 1 << 3, // Data 3 + PIN06 = 1 << 4, // Data 4 + PIN07 = 1 << 5, // Data 5 + PIN08 = 1 << 6, // Data 6 + PIN09 = 1 << 7, // Data 7 + DATA_PINS = PIN02 | PIN03 | PIN04 | PIN05 | PIN06 + | PIN07 | PIN08 | PIN09, + + // Status Register + // Offset: Base + 1 + // Read only + PIN15 = 1 << 11, // Error + PIN13 = 1 << 12, // Online + PIN12 = 1 << 13, // Paper End + PIN10 = 1 << 14, // Ack + PIN11 = 1 << 15, // Busy + STATUS_PINS = PIN15 | PIN13 | PIN12 | PIN10 | PIN11, + + // Control Register + // Offset: Base + 2 + // Readable / writable + PIN01 = 1 << 16, // Strobe + PIN14 = 1 << 17, // Auto Feed + PIN16 = 1 << 18, // Init + PIN17 = 1 << 19, // Select + CONTROL_PINS = PIN01 | PIN14 | PIN16 | PIN17, + + + // Pins 18 to 25 are ground + }; + + enum Register + { + Data = 0, + Status = 1, + Control = 2, + }; + + /** + * For setting the direction of the Data register or the Control pins. + */ + enum Direction + { + Input = 0, + Output = 1, + }; + + ParallelPort(); + ~ParallelPort(); + + /** + * Opens the given port. + * @return if the port could be opened. + */ + bool openPort( const QString & port ); + /** + * Closes any currently open port. + */ + void closePort(); + + //BEGIN Pin-oriented operations + /** + * @param pins A list of ParallelPort::Pin OR'd together. + */ + void setPinState( int pins, bool state ); + /** + * @return the pin states for the given list of pins. + */ + int pinState( int pins ); + /** + * Sets the given pins to the given state in the Data register. + */ + void setDataState( uchar pins, bool state ); + /** + * Sets the given pins to the given state in the Control register. + */ + void setControlState( uchar pins, bool state ); + //END Pin-oriented operations + + + //BEGIN Register-oriented operations + /** + * Reads and stores the value in the given reigsters, and returns it. + */ + uchar readFromRegister( Register reg ); + /** + * Write the given value to the Data register. + */ + void writeToData( uchar value ); + /** + * Write the given value to the Control register (any input pins will be + * set to one). + */ + void writeToControl( uchar value ); + //END Register-oriented operations + + + //BEGIN Changing pin directions + /** + * Sets the (input / ouput) direction of the data pins. + */ + void setDataDirection( Direction dir ); + /** + * Sets the given pins to the given direction. + */ + void setControlDirection( int pins, Direction dir ); + //END Changing pin directions + + static ProbeResult probe( const QString & port ); + /** + * @see Port::ports + */ + static QStringList ports( unsigned probeResult ); + + protected: + /** + * Writes the value to the given register. + */ + void writeToRegister( Register reg, uchar value ); + void reset(); + + uchar m_reg[3]; + + /// Mask of the pins that are currently set as input + int m_inputPins; + + /// Mask of the pins that are currently set as output + int m_outputPins; + + /// File descriptor for the port. + int m_file; +}; + +#endif diff --git a/src/electronics/simulation/Makefile.am b/src/electronics/simulation/Makefile.am new file mode 100644 index 0000000..c45c6a0 --- /dev/null +++ b/src/electronics/simulation/Makefile.am @@ -0,0 +1,11 @@ +INCLUDES = -I$(top_srcdir)/src -I$(top_srcdir)/src/electronics $(all_includes) +METASOURCES = AUTO +noinst_LTLIBRARIES = libelements.la +libelements_la_SOURCES = cccs.cpp ccvs.cpp circuit.cpp currentsource.cpp \ + diode.cpp element.cpp elementset.cpp logic.cpp matrix.cpp vccs.cpp vcvs.cpp \ + voltagesource.cpp capacitance.cpp resistance.cpp currentsignal.cpp voltagepoint.cpp \ + voltagesignal.cpp elementsignal.cpp nonlinear.cpp reactive.cpp vec.cpp bjt.cpp opamp.cpp \ + inductance.cpp +noinst_HEADERS = cccs.h ccvs.h circuit.h currentsource.h diode.h element.h \ + elementset.h logic.h matrix.h vccs.h vcvs.h voltagesource.h capacitance.h \ + resistance.h elementsignal.h nonlinear.h reactive.h vec.h bjt.h opamp.h inductance.h diff --git a/src/electronics/simulation/bjt.cpp b/src/electronics/simulation/bjt.cpp new file mode 100644 index 0000000..8daab40 --- /dev/null +++ b/src/electronics/simulation/bjt.cpp @@ -0,0 +1,257 @@ +/*************************************************************************** + * Copyright (C) 2003-2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "bjt.h" +#include "diode.h" +#include "elementset.h" +#include "matrix.h" + +#include <cmath> +using namespace std; + + +//BEGIN class BJTSettings +BJTSettings::BJTSettings() +{ + I_S = 1e-16; + N_F = 1.0; + N_R = 1.0; + B_F = 100.0; + B_R = 1.0; +} +//END class BJTSettings + + + +//BEGIN class BJTState +BJTState::BJTState() +{ + reset(); +} + + +void BJTState::reset() +{ + for ( unsigned i = 0; i < 3; ++i ) + { + for ( unsigned j = 0; j < 3; ++j ) + A[i][j] = 0.0; + + I[i] = 0.0; + } +} + + +BJTState BJTState::operator-( const BJTState & s ) const +{ + BJTState newState( *this ); + + for ( unsigned i = 0; i < 3; ++i ) + { + for ( unsigned j = 0; j < 3; ++j ) + newState.A[i][j] -= s.A[i][j]; + + newState.I[i] -= s.I[i]; + } + + return newState; +} +//END class BJTState + + + +//BEGIN class BJT +BJT::BJT( const bool isNPN ) + : NonLinear() +{ + V_BE_prev = 0.0; + V_BC_prev = 0.0; + m_pol = isNPN ? 1 : -1; + m_numCNodes = 3; +} + + +BJT::~BJT() +{ +} + + +void BJT::add_map() +{ + if (!b_status) return; + + if ( !p_cnode[0]->isGround ) + { + p_A->setUse( p_cnode[0]->n(), p_cnode[0]->n(), Map::et_unstable, false ); + } + if ( !p_cnode[1]->isGround ) + { + p_A->setUse( p_cnode[1]->n(), p_cnode[1]->n(), Map::et_unstable, false ); + } + if ( !p_cnode[2]->isGround ) + { + p_A->setUse( p_cnode[2]->n(), p_cnode[2]->n(), Map::et_unstable, false ); + } + + if ( !p_cnode[0]->isGround && !p_cnode[2]->isGround ) + { + p_A->setUse( p_cnode[0]->n(), p_cnode[2]->n(), Map::et_unstable, false ); + p_A->setUse( p_cnode[2]->n(), p_cnode[0]->n(), Map::et_unstable, false ); + } + if ( !p_cnode[1]->isGround && !p_cnode[2]->isGround ) + { + p_A->setUse( p_cnode[2]->n(), p_cnode[1]->n(), Map::et_unstable, false ); + p_A->setUse( p_cnode[1]->n(), p_cnode[2]->n(), Map::et_unstable, false ); + } +} + + +void BJT::add_initial_dc() +{ + V_BE_prev = 0.0; + V_BC_prev = 0.0; + m_os.reset(); + update_dc(); +} + + +void BJT::updateCurrents() +{ + if (!b_status) + return; + + double V_B = p_cnode[0]->v; + double V_C = p_cnode[1]->v; + double V_E = p_cnode[2]->v; + + double V_BE = (V_B - V_E) * m_pol; + double V_BC = (V_B - V_C) * m_pol; + + double I_BE, I_BC, I_T, g_BE, g_BC, g_IF, g_IR; + calcIg( V_BE, V_BC, & I_BE, & I_BC, & I_T, & g_BE, & g_BC, & g_IF, & g_IR ); + + m_cnodeI[1] = I_BC - I_T; + m_cnodeI[2] = I_BE + I_T; + m_cnodeI[0] = -(m_cnodeI[1] + m_cnodeI[2]); +} + + +void BJT::update_dc() +{ + if (!b_status) + return; + + calc_eq(); + + BJTState diff = m_ns - m_os; + for ( unsigned i = 0; i < 3; ++i ) + { + for ( unsigned j = 0 ; j < 3; ++j ) + A_g( i, j ) += diff.A[i][j]; + + b_i( i ) += diff.I[i]; + } + + m_os = m_ns; +} + + +void BJT::calc_eq() +{ + double V_B = p_cnode[0]->v; + double V_C = p_cnode[1]->v; + double V_E = p_cnode[2]->v; + + double V_BE = (V_B - V_E) * m_pol; + double V_BC = (V_B - V_C) * m_pol; + + double I_S = m_bjtSettings.I_S; + double N_F = m_bjtSettings.N_F; + double N_R = m_bjtSettings.N_R; + + // adjust voltage to help convergence + double V_BEcrit = diodeCriticalVoltage( I_S, N_F * V_T ); + double V_BCcrit = diodeCriticalVoltage( I_S, N_R * V_T ); + V_BE_prev = V_BE = diodeVoltage( V_BE, V_BE_prev, V_T * N_F, V_BEcrit ); + V_BC_prev = V_BC = diodeVoltage( V_BC, V_BC_prev, V_T * N_R, V_BCcrit ); + + double I_BE, I_BC, I_T, g_BE, g_BC, g_IF, g_IR; + calcIg( V_BE, V_BC, & I_BE, & I_BC, & I_T, & g_BE, & g_BC, & g_IF, & g_IR ); + + double I_eq_B = I_BE - V_BE * g_BE; + double I_eq_C = I_BC - V_BC * g_BC; + double I_eq_E = I_T - V_BE * g_IF + V_BC * g_IR; + + m_ns.A[0][0] = g_BC + g_BE; + m_ns.A[0][1] = -g_BC; + m_ns.A[0][2] = -g_BE; + + m_ns.A[1][0] = -g_BC + (g_IF - g_IR); + m_ns.A[1][1] = g_IR + g_BC; + m_ns.A[1][2] = -g_IF; + + m_ns.A[2][0] = -g_BE - (g_IF - g_IR); + m_ns.A[2][1] = -g_IR; + m_ns.A[2][2] = g_BE + g_IF; + + m_ns.I[0] = (-I_eq_B - I_eq_C) * m_pol; + m_ns.I[1] = (+I_eq_C - I_eq_E) * m_pol; + m_ns.I[2] = (+I_eq_B + I_eq_E) * m_pol; +} + + +void BJT::calcIg( double V_BE, double V_BC, double * I_BE, double * I_BC, double * I_T, double * g_BE, double * g_BC, double * g_IF, double * g_IR ) +{ + double I_S = m_bjtSettings.I_S; + double N_F = m_bjtSettings.N_F; + double N_R = m_bjtSettings.N_R; + double B_F = m_bjtSettings.B_F; + double B_R = m_bjtSettings.B_R; + + // base-emitter diodes + double g_tiny = (V_BE < (-10 * V_T * N_F)) ? I_S : 0; + + double I_F; + diodeJunction( V_BE, I_S, V_T * N_F, & I_F, g_IF ); + + double I_BEI = I_F / B_F; + double g_BEI = *g_IF / B_F; + double I_BEN = g_tiny * V_BE; + double g_BEN = g_tiny; + *I_BE = I_BEI + I_BEN; + *g_BE = g_BEI + g_BEN; + + // base-collector diodes + g_tiny = (V_BC < (-10 * V_T * N_R)) ? I_S : 0; + + double I_R; + diodeJunction( V_BC, I_S, V_T * N_R, & I_R, g_IR ); + + double I_BCI = I_R / B_R; + double g_BCI = *g_IR / B_R; + double I_BCN = g_tiny * V_BC; + double g_BCN = g_tiny; + *I_BC = I_BCI + I_BCN; + *g_BC = g_BCI + g_BCN; + + *I_T = I_F - I_R; +} + + +void BJT::setBJTSettings( const BJTSettings & settings ) +{ + m_bjtSettings = settings; + + if (p_eSet) + p_eSet->setCacheInvalidated(); +} +//END class BJT + + diff --git a/src/electronics/simulation/bjt.h b/src/electronics/simulation/bjt.h new file mode 100644 index 0000000..52022aa --- /dev/null +++ b/src/electronics/simulation/bjt.h @@ -0,0 +1,75 @@ +/*************************************************************************** + * Copyright (C) 2003-2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef BJT_H +#define BJT_H + +#include "matrix.h" +#include "nonlinear.h" + +class BJTState +{ + public: + BJTState(); + void reset(); + + BJTState operator-( const BJTState & s ) const; + + double A[3][3]; + double I[3]; +}; + + +class BJTSettings +{ + public: + BJTSettings(); + + double I_S; ///< saturation current + double N_F; ///< forward emission coefficient + double N_R; ///< reverse emission coefficient + double B_F; ///< forward beta + double B_R; ///< reverse beta +}; + + +/** +@author David Saxton +*/ +class BJT : public NonLinear +{ + public: + BJT( bool isNPN ); + virtual ~BJT(); + + virtual Type type() const { return Element_BJT; } + virtual void update_dc(); + virtual void add_initial_dc(); + virtual void add_map(); + BJTSettings settings() const { return m_bjtSettings; } + void setBJTSettings( const BJTSettings & settings ); + + protected: + virtual void updateCurrents(); + /** + * Calculates the new BJTState from the voltages on the nodes. + */ + void calc_eq(); + + void calcIg( double V_BE, double V_BC, double * I_BE, double * I_BC, double * I_T, double * g_BE, double * g_BC, double * g_IF, double * g_IR ); + + BJTState m_os; + BJTState m_ns; + int m_pol; + double V_BE_prev, V_BC_prev; + BJTSettings m_bjtSettings; +}; + +#endif diff --git a/src/electronics/simulation/capacitance.cpp b/src/electronics/simulation/capacitance.cpp new file mode 100644 index 0000000..9087c7f --- /dev/null +++ b/src/electronics/simulation/capacitance.cpp @@ -0,0 +1,117 @@ +/*************************************************************************** + * Copyright (C) 2003-2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "capacitance.h" +#include "matrix.h" + +Capacitance::Capacitance( const double capacitance, const double delta ) + : Reactive(delta) +{ + m_cap = capacitance; + g_eq_old = i_eq_old = 0.; + m_numCNodes = 2; + setMethod( Capacitance::m_euler ); +} + +Capacitance::~Capacitance() +{ +} + +void Capacitance::setCapacitance( const double c ) +{ + m_cap = c; +} + +void Capacitance::add_initial_dc() +{ + // We don't need to do anything here, as time_step() will do that for us, + // apart from to make sure our old values are 0 + g_eq_old = i_eq_old = 0.; +} + +void Capacitance::updateCurrents() +{ + if (!b_status) return; + const double r_i = (p_cnode[0]->v-p_cnode[1]->v)*g_eq_old; + m_cnodeI[0] = -i_eq_old-r_i; + m_cnodeI[1] = -m_cnodeI[0]; +} + + +void Capacitance::add_map() +{ + if (!b_status) return; + + if ( !p_cnode[0]->isGround ) + { + p_A->setUse( p_cnode[0]->n(), p_cnode[0]->n(), Map::et_unstable, false ); + } + if ( !p_cnode[1]->isGround ) + { + p_A->setUse( p_cnode[1]->n(), p_cnode[1]->n(), Map::et_unstable, false ); + } + + if ( !p_cnode[0]->isGround && !p_cnode[1]->isGround ) + { + p_A->setUse( p_cnode[0]->n(), p_cnode[1]->n(), Map::et_unstable, false ); + p_A->setUse( p_cnode[1]->n(), p_cnode[0]->n(), Map::et_unstable, false ); + } +} + + +void Capacitance::time_step() +{ + if (!b_status) return; + + double v = p_cnode[0]->v-p_cnode[1]->v; + double i_eq_new = 0.0, g_eq_new = 0.0; + + if ( m_method == Capacitance::m_euler ) + { + g_eq_new = m_cap/m_delta; + i_eq_new = -v*g_eq_new; + } + else if ( m_method == Capacitance::m_trap ) { + // TODO Implement + test trapezoidal method + g_eq_new = 2.*m_cap/m_delta; + } + + if ( g_eq_old != g_eq_new ) + { + A_g( 0, 0 ) += g_eq_new-g_eq_old; + A_g( 1, 1 ) += g_eq_new-g_eq_old; + A_g( 0, 1 ) -= g_eq_new-g_eq_old; + A_g( 1, 0 ) -= g_eq_new-g_eq_old; + } + + if ( i_eq_new != i_eq_old ) + { + b_i( 0 ) -= i_eq_new-i_eq_old; + b_i( 1 ) += i_eq_new-i_eq_old; + } + + g_eq_old = g_eq_new; + i_eq_old = i_eq_new; +} + +bool Capacitance::updateStatus() +{ + b_status = Reactive::updateStatus(); + if ( m_method == Capacitance::m_none ) b_status = false; + return b_status; +} + +void Capacitance::setMethod( Method m ) +{ + m_method = m; + updateStatus(); +} + + diff --git a/src/electronics/simulation/capacitance.h b/src/electronics/simulation/capacitance.h new file mode 100644 index 0000000..ccc083d --- /dev/null +++ b/src/electronics/simulation/capacitance.h @@ -0,0 +1,55 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef CAPACITANCE_H +#define CAPACITANCE_H + +#include "reactive.h" + +/** +@author David Saxton +@short Capacitance +*/ +class Capacitance : public Reactive +{ +public: + enum Method + { + m_none, // None + m_euler, // Backward Euler + m_trap // Trapezoidal (currently unimplemented) + }; + Capacitance( const double capacitance, const double delta ); + virtual ~Capacitance(); + + virtual Type type() const { return Element_Capacitance; } + /** + * Set the stepping use for numerical integration of capacitance, + * and the interval between successive updates + */ + void setMethod( Method m ); + virtual void time_step(); + virtual void add_initial_dc(); + void setCapacitance( const double c ); + virtual void add_map(); + +protected: + virtual void updateCurrents(); + virtual bool updateStatus(); + +private: + double m_cap; // Capacitance + Method m_method; // Method of integration + + double g_eq_old; + double i_eq_old; +}; + +#endif diff --git a/src/electronics/simulation/cccs.cpp b/src/electronics/simulation/cccs.cpp new file mode 100644 index 0000000..9725735 --- /dev/null +++ b/src/electronics/simulation/cccs.cpp @@ -0,0 +1,89 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "elementset.h" +#include "matrix.h" +#include "cccs.h" + +CCCS::CCCS( const double gain ) + : Element::Element() +{ + m_g = gain; + m_numCBranches = 2; + m_numCNodes = 4; +} + +CCCS::~CCCS() +{ +} + +void CCCS::setGain( const double g ) +{ + if ( m_g == g ) + return; + + if (p_eSet) + p_eSet->setCacheInvalidated(); + + m_g = g; + add_initial_dc(); +} + + +void CCCS::add_map() +{ + if (!b_status) return; + + if ( !p_cnode[0]->isGround ) + { + p_A->setUse_b( p_cnode[0]->n(), p_cbranch[0]->n(), Map::et_constant, true ); + p_A->setUse_c( p_cbranch[0]->n(), p_cnode[0]->n(), Map::et_constant, true ); + } + if ( !p_cnode[1]->isGround ) + { + p_A->setUse_b( p_cnode[1]->n(), p_cbranch[0]->n(), Map::et_constant, true ); + p_A->setUse_c( p_cbranch[0]->n(), p_cnode[1]->n(), Map::et_constant, true ); + } + if ( !p_cnode[2]->isGround ) + { + p_A->setUse_b( p_cnode[2]->n(), p_cbranch[1]->n(), Map::et_constant, true ); + } + if ( !p_cnode[3]->isGround ) + { + p_A->setUse_b( p_cnode[3]->n(), p_cbranch[1]->n(), Map::et_constant, true ); + } + p_A->setUse_d( p_cbranch[1]->n(), p_cbranch[0]->n(), Map::et_stable, true ); + p_A->setUse_d( p_cbranch[1]->n(), p_cbranch[1]->n(), Map::et_constant, true ); +} + +void CCCS::add_initial_dc() +{ + if (!b_status) + return; + + A_b( 0, 0 ) = 1; + A_c( 0, 0 ) = 1; + A_b( 1, 0 ) = -1; + A_c( 0, 1 ) = -1; + A_b( 2, 1 ) = 1; + A_b( 3, 1 ) = -1; + A_d( 1, 0 ) = -m_g; + A_d( 1, 1 ) = 1; +} + +void CCCS::updateCurrents() +{ + if (!b_status) return; + m_cnodeI[1] = p_cbranch[0]->i; + m_cnodeI[0] = -m_cnodeI[1]; + m_cnodeI[3] = p_cbranch[1]->i; + m_cnodeI[2] = -m_cnodeI[3]; +} + diff --git a/src/electronics/simulation/cccs.h b/src/electronics/simulation/cccs.h new file mode 100644 index 0000000..ba86e9e --- /dev/null +++ b/src/electronics/simulation/cccs.h @@ -0,0 +1,41 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef CCCS_H +#define CCCS_H + +#include "element.h" + +/** +CNodes n0 and n1 are used for the current control. +CNodes n2 and n3 are used for the current output. +Branches b0 and b1 are for control and output +@short Current Controlled Current Source +@author David Saxton +*/ +class CCCS : public Element +{ +public: + CCCS( const double gain ); + virtual ~CCCS(); + + virtual Type type() const { return Element_CCCS; } + void setGain( const double g ); + virtual void add_map(); + +protected: + virtual void updateCurrents(); + virtual void add_initial_dc(); + +private: + double m_g; // Conductance +}; + +#endif diff --git a/src/electronics/simulation/ccvs.cpp b/src/electronics/simulation/ccvs.cpp new file mode 100644 index 0000000..fc12bf5 --- /dev/null +++ b/src/electronics/simulation/ccvs.cpp @@ -0,0 +1,91 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "elementset.h" +#include "matrix.h" +#include "ccvs.h" + +CCVS::CCVS( const double gain ) + : Element::Element() +{ + m_g = gain; + m_numCBranches = 2; + m_numCNodes = 4; +} + +CCVS::~CCVS() +{ +} + +void CCVS::setGain( const double g ) +{ + if ( m_g == g ) + return; + + if (p_eSet) + p_eSet->setCacheInvalidated(); + + m_g = g; + add_initial_dc(); +} + + +void CCVS::add_map() +{ + if (!b_status) return; + + if ( !p_cnode[0]->isGround ) + { + p_A->setUse_b( p_cnode[0]->n(), p_cbranch[0]->n(), Map::et_constant, true ); + p_A->setUse_c( p_cbranch[0]->n(), p_cnode[0]->n(), Map::et_constant, true ); + } + if ( !p_cnode[1]->isGround ) + { + p_A->setUse_b( p_cnode[1]->n(), p_cbranch[0]->n(), Map::et_constant, true ); + p_A->setUse_c( p_cbranch[0]->n(), p_cnode[1]->n(), Map::et_constant, true ); + } + if ( !p_cnode[2]->isGround ) + { + p_A->setUse_b( p_cnode[2]->n(), p_cbranch[1]->n(), Map::et_constant, true ); + p_A->setUse_c( p_cbranch[1]->n(), p_cnode[2]->n(), Map::et_constant, true ); + } + if ( !p_cnode[3]->isGround ) + { + p_A->setUse_b( p_cnode[3]->n(), p_cbranch[1]->n(), Map::et_constant, true ); + p_A->setUse_c( p_cbranch[1]->n(), p_cnode[3]->n(), Map::et_constant, true ); + } + p_A->setUse_d( p_cbranch[1]->n(), p_cbranch[0]->n(), Map::et_stable, true ); +} + + +void CCVS::add_initial_dc() +{ + if (!b_status) return; + + A_b( 0, 0 ) = 1; + A_c( 0, 0 ) = 1; + A_b( 1, 0 ) = -1; + A_c( 0, 1 ) = -1; + A_b( 2, 1 ) = 1; + A_c( 1, 2 ) = 1; + A_b( 3, 1 ) = -1; + A_c( 1, 3 ) = -1; + A_d( 1, 0 ) = -m_g; +} + +void CCVS::updateCurrents() +{ + if (!b_status) return; + m_cnodeI[1] = p_cbranch[0]->i; + m_cnodeI[0] = -m_cnodeI[1]; + m_cnodeI[3] = p_cbranch[0]->i; + m_cnodeI[2] = -m_cnodeI[3]; +} + diff --git a/src/electronics/simulation/ccvs.h b/src/electronics/simulation/ccvs.h new file mode 100644 index 0000000..bcb1ac0 --- /dev/null +++ b/src/electronics/simulation/ccvs.h @@ -0,0 +1,41 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef CCVS_H +#define CCVS_H + +#include "element.h" + +/** +CNodes n0 and n1 are used for the current control. +CNodes n2 and n3 are used for the voltage output. +Branches b0 and b1 are for control and output +@short Current Controlled Voltage Source +@author David Saxton +*/ +class CCVS : public Element +{ +public: + CCVS( const double gain ); + virtual ~CCVS(); + + virtual Type type() const { return Element_CCVS; } + void setGain( const double g ); + virtual void add_map(); + +protected: + virtual void updateCurrents(); + virtual void add_initial_dc(); + +private: + double m_g; // Conductance +}; + +#endif diff --git a/src/electronics/simulation/circuit.cpp b/src/electronics/simulation/circuit.cpp new file mode 100644 index 0000000..c152756 --- /dev/null +++ b/src/electronics/simulation/circuit.cpp @@ -0,0 +1,550 @@ +/*************************************************************************** + * Copyright (C) 2003-2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include <vector> +#include "circuit.h" +#include "circuitdocument.h" +#include "element.h" +#include "elementset.h" +#include "logic.h" +#include "matrix.h" +#include "nonlinear.h" +#include "pin.h" +#include "reactive.h" +#include "wire.h" + + +#include <cmath> +#include <map> + +typedef std::multimap<int, PinList> PinListMap; + +//BEGIN class Circuit +Circuit::Circuit() +{ + m_bCanAddChanged = true; + m_pNextChanged[0] = m_pNextChanged[1] = 0l; + m_logicOutCount = 0; + m_bCanCache = false; + m_pLogicOut = 0l; + m_elementSet = new ElementSet( this, 0, 0 ); + m_cnodeCount = m_branchCount = -1; + m_prepNLCount = 0; + m_pLogicCacheBase = new LogicCacheNode; +} + +Circuit::~Circuit() +{ + delete m_elementSet; + delete m_pLogicCacheBase; + delete[] m_pLogicOut; +} + + +void Circuit::addPin( Pin *node ) +{ + if ( m_pinList.contains(node) ) return; + m_pinList.append(node); +} + +void Circuit::addElement( Element *element ) +{ + if ( m_elementList.contains(element) ) return; + m_elementList.append(element); +} + +bool Circuit::contains( Pin *node ) +{ + return m_pinList.contains(node); +} + + +// static function +int Circuit::identifyGround( PinList nodeList, int *highest ) +{ + // What this function does: + // We are given a list of pins. First, we divide them into groups of pins + // that are directly connected to each other (e.g. through wires or + // switches). Then, each group of connected pins is looked at to find the + // pin with the highest "ground priority", and this is taken to be + // the priority of the group. The highest ground priority from all the + // groups is recorded. If the highest ground priority found is the maximum, + // then all the pins in groups with this priority are marked as ground + // (their eq-id is set to -1). Otherwise, the first group of pins with the + // highest ground priority found is marked as ground, and all others are + // marked as non ground (their eq-id is set to 0). + + int temp_highest; + if (!highest) + highest = &temp_highest; + + // Now to give all the Pins ids + PinListMap eqs; + while ( !nodeList.isEmpty() ) + { + PinList associated; + PinList nodes; + Pin *node = *nodeList.begin(); + recursivePinAdd( node, &nodeList, &associated, &nodes ); + if ( nodes.size() > 0 ) + { + eqs.insert( std::make_pair( associated.size(), nodes ) ); + } + } + + + // Now, we want to look through the associated Pins, + // to find the ones with the highest "Ground Priority". Anything with a lower + // priority than Pin::gt_never will not be considered + *highest = Pin::gt_never; // The highest priority found so far + int numGround = 0; // The number of node groups found with that priority + const PinListMap::iterator eqsEnd = eqs.end(); + for ( PinListMap::iterator it = eqs.begin(); it != eqsEnd; ++it ) + { + int highPri = Pin::gt_never; // The highest priority found in these group of nodes + const PinList::iterator send = it->second.end(); + for ( PinList::iterator sit = it->second.begin(); sit != send; ++sit ) + { + if ( (*sit)->groundType() < highPri ) + highPri = (*sit)->groundType(); + } + + if ( highPri == *highest ) + numGround++; + + else if ( highPri < *highest ) + { + numGround = 1; + *highest = highPri; + } + } + + if ( *highest == Pin::gt_never ) + { + (*highest)--; + numGround=0; + } + // If there are no Always Ground nodes, then we only want to set one of the nodes as ground + else if ( *highest > Pin::gt_always ) + numGround = 1; + + + // Now, we can give the nodes their cnode ids, or tell them they are ground + bool foundGround = false; // This is only used when we don't have a Always ground node + for ( PinListMap::iterator it = eqs.begin(); it != eqsEnd; ++it ) + { + bool ground = false; + const PinList::iterator send = it->second.end(); + for ( PinList::iterator sit = it->second.begin(); sit != send; ++sit ) + { + ground |= (*sit)->groundType() <= (*highest); + } + if ( ground && (!foundGround || *highest == Pin::gt_always ) ) + { + for ( PinList::iterator sit = it->second.begin(); sit != send; ++sit ) + { + (*sit)->setEqId(-1); + } + foundGround = true; + } + else + { + for ( PinList::iterator sit = it->second.begin(); sit != send; ++sit ) + { + (*sit)->setEqId(0); + } + } + } + + return numGround; +} + + +void Circuit::init() +{ + m_branchCount = 0; + + const ElementList::iterator listEnd = m_elementList.end(); + for ( ElementList::iterator it = m_elementList.begin(); it != listEnd; ++it ) + { + m_branchCount += (*it)->numCBranches(); + } + + // Now to give all the Pins ids + int groundCount = 0; + PinListMap eqs; + PinList unassignedNodes = m_pinList; + while ( !unassignedNodes.isEmpty() ) + { + PinList associated; + PinList nodes; + Pin *node = *unassignedNodes.begin(); + if ( recursivePinAdd( node, &unassignedNodes, &associated, &nodes ) ) { + groundCount++; + } + if ( nodes.size() > 0 ) { + eqs.insert( std::make_pair( associated.size(), nodes ) ); + } + } + + m_cnodeCount = eqs.size() - groundCount; + + delete m_pLogicCacheBase; + m_pLogicCacheBase = 0l; + + delete m_elementSet; + m_elementSet = new ElementSet( this, m_cnodeCount, m_branchCount ); + + // Now, we can give the nodes their cnode ids, or tell them they are ground + Vector *x = m_elementSet->x(); + int i=0; + const PinListMap::iterator eqsEnd = eqs.end(); + for ( PinListMap::iterator it = eqs.begin(); it != eqsEnd; ++it ) + { + bool foundGround = false; + + const PinList::iterator sEnd = it->second.end(); + for ( PinList::iterator sit = it->second.begin(); sit != sEnd; ++sit ) + foundGround |= (*sit)->eqId() == -1; + + if ( foundGround ) + continue; + + bool foundEnergyStoragePin = false; + + for ( PinList::iterator sit = it->second.begin(); sit != sEnd; ++sit ) + { + (*sit)->setEqId(i); + + bool energyStorage = false; + const ElementList elements = (*sit)->elements(); + ElementList::const_iterator elementsEnd = elements.end(); + for ( ElementList::const_iterator it = elements.begin(); it != elementsEnd; ++it ) + { + if ( !*it ) + continue; + + if ( ((*it)->type() == Element::Element_Capacitance) + || ((*it)->type() == Element::Element_Inductance) ) + { + energyStorage = true; + break; + } + } + + // A pin attached to an energy storage pin overrides one that doesn't. + // If the two pins have equal status with in this regard, we pick the + // one with the highest absolute voltage on it. + + if ( foundEnergyStoragePin && !energyStorage ) + continue; + + double v = (*sit)->voltage(); + + if ( energyStorage && !foundEnergyStoragePin ) + { + foundEnergyStoragePin = true; + (*x)[i] = v; + continue; + } + + if ( std::abs(v) > std::abs( (*x)[i] ) ) + (*x)[i] = v; + } + i++; + } + + + // And add the elements to the elementSet + for ( ElementList::iterator it = m_elementList.begin(); it != listEnd; ++it ) + { + // We don't want the element to prematurely try to do anything, + // as it doesn't know its actual cnode ids yet + (*it)->setCNodes(); + (*it)->setCBranches(); + m_elementSet->addElement(*it); + } + // And give the branch ids to the elements + i=0; + for ( ElementList::iterator it = m_elementList.begin(); it != listEnd; ++it ) + { + switch ( (*it)->numCBranches() ) + { + case 0: + break; + case 1: + (*it)->setCBranches( i ); + i += 1; + break; + case 2: + (*it)->setCBranches( i, i+1 ); + i += 2; + break; + case 3: + (*it)->setCBranches( i, i+1, i+2 ); + i += 3; + break; + default: + // What the?! + break; + } + } +} + + +void Circuit::initCache() +{ + m_elementSet->updateInfo(); + + m_bCanCache = true; + m_logicOutCount = 0; + + delete[] m_pLogicOut; + m_pLogicOut = 0l; + + delete m_pLogicCacheBase; + m_pLogicCacheBase = 0l; + + const ElementList::iterator end = m_elementList.end(); + for ( ElementList::iterator it = m_elementList.begin(); it != end && m_bCanCache; ++it ) + { + switch ( (*it)->type() ) + { + case Element::Element_BJT: + case Element::Element_CCCS: + case Element::Element_CCVS: + case Element::Element_CurrentSource: + case Element::Element_Diode: + case Element::Element_LogicIn: + case Element::Element_OpAmp: + case Element::Element_Resistance: + case Element::Element_VCCS: + case Element::Element_VCVS: + case Element::Element_VoltagePoint: + case Element::Element_VoltageSource: + { + break; + } + + case Element::Element_LogicOut: + { + m_logicOutCount++; + break; + } + + case Element::Element_CurrentSignal: + case Element::Element_VoltageSignal: + case Element::Element_Capacitance: + case Element::Element_Inductance: + { + m_bCanCache = false; + break; + } + } + } + + if ( !m_bCanCache ) + return; + + m_pLogicOut = new LogicOut*[m_logicOutCount]; + unsigned i = 0; + for ( ElementList::iterator it = m_elementList.begin(); it != end && m_bCanCache; ++it ) + { + if ( (*it)->type() == Element::Element_LogicOut ) + m_pLogicOut[i++] = static_cast<LogicOut*>(*it); + } + + m_pLogicCacheBase = new LogicCacheNode; +} + + +void Circuit::setCacheInvalidated() +{ + if (m_pLogicCacheBase) + { + delete m_pLogicCacheBase->high; + m_pLogicCacheBase->high = 0l; + + delete m_pLogicCacheBase->low; + m_pLogicCacheBase->low = 0l; + + delete m_pLogicCacheBase->data; + m_pLogicCacheBase->data = 0l; + } +} + + +void Circuit::cacheAndUpdate() +{ + LogicCacheNode * node = m_pLogicCacheBase; + for ( unsigned i = 0; i < m_logicOutCount; i++ ) + { + if ( m_pLogicOut[i]->outputState() ) + { + if (!node->high) + node->high = new LogicCacheNode; + + node = node->high; + } + else + { + if (!node->low) + node->low = new LogicCacheNode; + + node = node->low; + } + } + + if ( node->data ) + { + (*m_elementSet->x()) = *node->data; + m_elementSet->updateInfo(); + return; + } + + if ( m_elementSet->containsNonLinear() ) + m_elementSet->doNonLinear( 150, 1e-10, 1e-13 ); + else + m_elementSet->doLinear(true); + + node->data = new Vector( m_elementSet->x()->size() ); + *node->data = *m_elementSet->x(); +} + + +void Circuit::createMatrixMap() +{ + m_elementSet->createMatrixMap(); +} + + +bool Circuit::recursivePinAdd( Pin *node, PinList *unassignedNodes, PinList *associated, PinList *nodes ) +{ + if ( !unassignedNodes->contains(node) ) + return false; + unassignedNodes->remove(node); + + bool foundGround = node->eqId() == -1; + + const PinList circuitDependentPins = node->circuitDependentPins(); + const PinList::const_iterator dEnd = circuitDependentPins.end(); + for ( PinList::const_iterator it = circuitDependentPins.begin(); it != dEnd; ++it ) + { + if ( !associated->contains(*it) ) + associated->append(*it); + } + + nodes->append(node); + + const PinList localConnectedPins = node->localConnectedPins(); + const PinList::const_iterator end = localConnectedPins.end(); + for ( PinList::const_iterator it = localConnectedPins.begin(); it != end; ++it ) + foundGround |= recursivePinAdd( *it, unassignedNodes, associated, nodes ); + + return foundGround; +} + + +void Circuit::doNonLogic() +{ + if ( !m_elementSet || m_cnodeCount+m_branchCount <= 0 ) + return; + + if (m_bCanCache) + { + if ( !m_elementSet->b()->isChanged() && !m_elementSet->matrix()->isChanged() ) + return; + cacheAndUpdate(); + updateNodalVoltages(); + m_elementSet->b()->setUnchanged(); + return; + } + + stepReactive(); + if ( m_elementSet->containsNonLinear() ) + { + m_elementSet->doNonLinear( 10, 1e-9, 1e-12 ); + updateNodalVoltages(); + } + else + { + if ( m_elementSet->doLinear(true) ) + updateNodalVoltages(); + } +} + + +void Circuit::stepReactive() +{ + ElementList::iterator listEnd = m_elementList.end(); + for ( ElementList::iterator it = m_elementList.begin(); it != listEnd; ++it ) + { + Element * const e = *it; + if ( e && e->isReactive() ) + (static_cast<Reactive*>(e))->time_step(); + } +} + + +void Circuit::updateNodalVoltages() +{ + CNode **_cnodes = m_elementSet->cnodes(); + + const PinList::iterator endIt = m_pinList.end(); + for ( PinList::iterator it = m_pinList.begin(); it != endIt; ++it ) + { + Pin * const node = *it; + int i = node->eqId(); + if ( i == -1 ) + node->setVoltage(0.); + else + { + const double v = _cnodes[i]->v; + node->setVoltage( std::isfinite(v)?v:0. ); + } + } +} + + +void Circuit::updateCurrents() +{ + ElementList::iterator listEnd = m_elementList.end(); + for ( ElementList::iterator it = m_elementList.begin(); it != listEnd; ++it ) + { + (*it)->updateCurrents(); + } +} + +void Circuit::displayEquations() +{ + m_elementSet->displayEquations(); +} +//END class Circuit + + + +//BEGIN class LogicCacheNode +LogicCacheNode::LogicCacheNode() +{ + low = 0l; + high = 0l; + data = 0l; +} + + +LogicCacheNode::~LogicCacheNode() +{ + delete low; + delete high; + delete data; +} +//END class LogicCacheNode + + diff --git a/src/electronics/simulation/circuit.h b/src/electronics/simulation/circuit.h new file mode 100644 index 0000000..2455edc --- /dev/null +++ b/src/electronics/simulation/circuit.h @@ -0,0 +1,137 @@ +/*************************************************************************** + * Copyright (C) 2003-2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef CIRCUIT_H +#define CIRCUIT_H + +#include <qguardedptr.h> +#include "qstringlist.h" +#include "qvaluelist.h" + +#include "elementset.h" + +class CircuitDocument; +class Wire; +class Pin; +class Element; +class LogicOut; + +typedef QValueList<QGuardedPtr<Pin> > PinList; +typedef QValueList<Element*> ElementList; + + +class LogicCacheNode +{ + public: + LogicCacheNode(); + ~LogicCacheNode(); + + LogicCacheNode * high; + LogicCacheNode * low; + Vector * data; +}; + + +/** +Usage of this class (usually invoked from CircuitDocument): +(1) Add Wires, Pins and Elements to the class as appropriate +(2) Call init to initialize the simulation +(3) Control the simulation with step() + +This class can be considered a bridge between the gui-tainted CircuitDocument - specific +to this implementation, and the pure untainted ElementSet. Please keep it that way. + +@short Simulates a collection of components +@author David Saxton +*/ +class Circuit +{ + public: + Circuit(); + ~Circuit(); + + void addPin( Pin *node ); + void addElement( Element *element ); + + bool contains( Pin *node ); + bool containsNonLinear() const { return m_elementSet->containsNonLinear(); } + + void init(); + /** + * Called after everything else has been setup - before doNonLogic or + * doLogic are called for the first time. Preps the circuit. + */ + void initCache(); + /** + * Marks all cached results as invalidated and removes them. + */ + void setCacheInvalidated(); + /** + * Solves for non-logic elements + */ + void doNonLogic(); + /** + * Solves for logic elements (i.e just does fbSub) + */ + void doLogic() { m_elementSet->doLinear(false); } + + void displayEquations(); + void updateCurrents(); + + void createMatrixMap(); + /** + * This will identify the ground node and non-ground nodes in the given set. + * Ground will be given the eqId -1, non-ground of 0. + * @param highest The highest ground type of the groundnodes found. If no + ground nodes were found, this will be (gt_never-1). + * @returns the number of ground nodes. If all nodes are at or below the + * gt_never threshold, then this will be zero. + */ + static int identifyGround( PinList nodeList, int *highest = 0l ); + + void setNextChanged( Circuit * circuit, unsigned char chain ) { m_pNextChanged[chain] = circuit; } + Circuit * nextChanged( unsigned char chain ) const { return m_pNextChanged[chain]; } + void setCanAddChanged( bool canAdd ) { m_bCanAddChanged = canAdd; } + bool canAddChanged() const { return m_bCanAddChanged; } + + protected: + void cacheAndUpdate(); + /** + * Update the nodal voltages from those calculated in ElementSet + */ + void updateNodalVoltages(); + /** + * Step the reactive elements. + */ + void stepReactive(); + /** + * Returns true if any of the nodes are ground + */ + static bool recursivePinAdd( Pin *node, PinList *unassignedNodes, PinList *associated, PinList *nodes ); + + int m_cnodeCount; + int m_branchCount; + int m_prepNLCount; // Count until next m_elementSet->prepareNonLinear() is called + + PinList m_pinList; + ElementList m_elementList; + ElementSet *m_elementSet; + + //Stuff for caching + bool m_bCanCache; + LogicCacheNode * m_pLogicCacheBase; + unsigned m_logicOutCount; + LogicOut ** m_pLogicOut; + + bool m_bCanAddChanged; + Circuit * m_pNextChanged[2]; +}; + +#endif diff --git a/src/electronics/simulation/currentsignal.cpp b/src/electronics/simulation/currentsignal.cpp new file mode 100644 index 0000000..5e5388d --- /dev/null +++ b/src/electronics/simulation/currentsignal.cpp @@ -0,0 +1,67 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "currentsignal.h" +#include "element.h" +#include "matrix.h" + +CurrentSignal::CurrentSignal( const double delta, const double current ) + : Reactive::Reactive(delta) +{ + m_current = current; + m_oldCurrent = m_newCurrent = 0.; + m_numCNodes = 2; +} + +CurrentSignal::~CurrentSignal() +{ +} + +void CurrentSignal::setCurrent( const double i ) +{ + if ( m_oldCurrent != m_newCurrent ) add_initial_dc(); + m_newCurrent *= i/m_current; // Instead of calling step again, we can just "adjust" what the current should be + m_current = i; + add_initial_dc(); +} + + +void CurrentSignal::add_map() +{ + // We don't need a map for current signal :-) +} + + +void CurrentSignal::add_initial_dc() +{ + if ( !b_status ) + return; + + if ( m_newCurrent == m_oldCurrent ) + return; + + b_i( 0 ) -= m_newCurrent-m_oldCurrent; + b_i( 1 ) += m_newCurrent-m_oldCurrent; + + m_oldCurrent = m_newCurrent; +} + +void CurrentSignal::updateCurrents() +{ + m_cnodeI[1] = m_newCurrent; + m_cnodeI[0] = -m_newCurrent; +} + +void CurrentSignal::time_step() +{ + add_initial_dc(); // Make sure our old and new are synced + m_newCurrent = m_current*advance(); + add_initial_dc(); +} diff --git a/src/electronics/simulation/currentsignal.h b/src/electronics/simulation/currentsignal.h new file mode 100644 index 0000000..b217b79 --- /dev/null +++ b/src/electronics/simulation/currentsignal.h @@ -0,0 +1,43 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef CURRENTSIGNAL_H +#define CURRENTSIGNAL_H + +#include "reactive.h" +#include "elementsignal.h" + +/** +@short CurrentSignal +@author David saxton +*/ +class CurrentSignal : public Reactive, public ElementSignal +{ +public: + CurrentSignal( const double delta, const double current ); + virtual ~CurrentSignal(); + + virtual Element::Type type() const { return Element_CurrentSignal; } + void setCurrent( const double current ); + double current() { return m_current; } + virtual void time_step(); + virtual void add_map(); + +protected: + virtual void updateCurrents(); + virtual void add_initial_dc(); + +private: + double m_current; // Current + double m_oldCurrent; // Old calculated current + double m_newCurrent; // New calculated current +}; + +#endif diff --git a/src/electronics/simulation/currentsource.cpp b/src/electronics/simulation/currentsource.cpp new file mode 100644 index 0000000..675b0b7 --- /dev/null +++ b/src/electronics/simulation/currentsource.cpp @@ -0,0 +1,64 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "currentsource.h" +#include "elementset.h" +#include "matrix.h" + +CurrentSource::CurrentSource( const double current ) + : Element::Element() +{ + m_i = current; + m_numCNodes = 2; +} + + +CurrentSource::~CurrentSource() +{ +} + + +void CurrentSource::setCurrent( const double i ) +{ + if ( i == m_i ) return; + + if (p_eSet) + p_eSet->setCacheInvalidated(); + + // Remove the old current + m_i = -m_i; + add_initial_dc(); + + m_i = i; + add_initial_dc(); +} + + +void CurrentSource::add_map() +{ + // We don't need a map for current source :-) +} + + +void CurrentSource::add_initial_dc() +{ + if (!b_status) + return; + + b_i( 0 ) -= m_i; + b_i( 1 ) += m_i; +} + + +void CurrentSource::updateCurrents() +{ + m_cnodeI[0] = -m_i; + m_cnodeI[1] = m_i; +} diff --git a/src/electronics/simulation/currentsource.h b/src/electronics/simulation/currentsource.h new file mode 100644 index 0000000..9b72e0d --- /dev/null +++ b/src/electronics/simulation/currentsource.h @@ -0,0 +1,39 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef CURRENTSOURCE_H +#define CURRENTSOURCE_H + +#include "element.h" + +/** +cnode n0 has current flowing otu of it, cnode n1 has current flowing into it +@author David Saxton +@short Current Source +*/ +class CurrentSource : public Element +{ +public: + CurrentSource( const double current ); + virtual ~CurrentSource(); + + virtual Type type() const { return Element_CurrentSource; } + void setCurrent( const double i ); + virtual void add_map(); + +protected: + virtual void updateCurrents(); + virtual void add_initial_dc(); + +private: + double m_i; // Current +}; + +#endif diff --git a/src/electronics/simulation/diode.cpp b/src/electronics/simulation/diode.cpp new file mode 100644 index 0000000..e13d478 --- /dev/null +++ b/src/electronics/simulation/diode.cpp @@ -0,0 +1,198 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include <vector> +#include "diode.h" +#include "elementset.h" +#include "matrix.h" + +#include <cmath> + + +//BEGIN class Diode Settings +DiodeSettings::DiodeSettings() +{ + reset(); +} + + +void DiodeSettings::reset() +{ + I_S = 1e-15; + N = 1.0; + V_B = 4.7; +// R = 0.001; +} +//END class Diode Settings + + + +//BEGIN class Diode +Diode::Diode() + : NonLinear() +{ + m_numCNodes = 2; + g_new = g_old = I_new = I_old = V_prev = 0.0; +} + + +Diode::~Diode() +{ +} + + +void Diode::add_map() +{ + if (!b_status) + return; + + if ( !p_cnode[0]->isGround ) + { + p_A->setUse( p_cnode[0]->n(), p_cnode[0]->n(), Map::et_unstable, false ); + } + if ( !p_cnode[1]->isGround ) + { + p_A->setUse( p_cnode[1]->n(), p_cnode[1]->n(), Map::et_unstable, false ); + } + + if ( !p_cnode[0]->isGround && !p_cnode[1]->isGround ) + { + p_A->setUse( p_cnode[0]->n(), p_cnode[1]->n(), Map::et_unstable, false ); + p_A->setUse( p_cnode[1]->n(), p_cnode[0]->n(), Map::et_unstable, false ); + } +} + + +void Diode::add_initial_dc() +{ + g_new = g_old = I_new = I_old = V_prev = 0.0; + update_dc(); +} + + +double Diode::current() const +{ + if (!b_status) + return 0.0; + + double I; + calcIg( p_cnode[0]->v - p_cnode[1]->v, & I, 0 ); + + return I; +} + + +void Diode::updateCurrents() +{ + if (!b_status) + return; + + m_cnodeI[1] = current(); + m_cnodeI[0] = -m_cnodeI[1]; +} + + +void Diode::update_dc() +{ + if (!b_status) + return; + + calc_eq(); + + A_g( 0, 0 ) += g_new - g_old; + A_g( 1, 1 ) += g_new - g_old; + A_g( 0, 1 ) -= g_new - g_old; + A_g( 1, 0 ) -= g_new - g_old; + + b_i( 0 ) -= I_new - I_old; + b_i( 1 ) += I_new - I_old; + + g_old = g_new; + I_old = I_new; +} + + + +#ifndef MIN +# define MIN(x,y) (((x) < (y)) ? (x) : (y)) +#endif + + + +void Diode::calc_eq() +{ + double I_S = m_diodeSettings.I_S; + double N = m_diodeSettings.N; + double V_B = m_diodeSettings.V_B; +// double R = m_diodeSettings.R; + + double v = p_cnode[0]->v - p_cnode[1]->v; + + // adjust voltage to help convergence + double V_crit = diodeCriticalVoltage( I_S, N * V_T ); + if (V_B != 0 && v < MIN (0, -V_B + 10 * N * V_T)) + { + double V = -(v + V_B); + V = diodeVoltage( V, -(V_prev + V_B), V_T * N, V_crit ); + v = -(V + V_B); + } + else + v = diodeVoltage( v, V_prev, V_T * N, V_crit ); + + V_prev = v; + + double I_D; + calcIg( v, & I_D, & g_new ); + + I_new = I_D - (v * g_new); +} + + +void Diode::calcIg( double V, double * I_D, double * g ) const +{ + double I_S = m_diodeSettings.I_S; + double N = m_diodeSettings.N; + double V_B = m_diodeSettings.V_B; +// double R = m_diodeSettings.R; + + double gtiny = (V < - 10 * V_T * N && V_B != 0) ? I_S : 0; + + if ( V >= (-3 * N * V_T) ) + { + if ( g ) + *g = diodeConductance( V, I_S, V_T * N ) + gtiny; + *I_D = diodeCurrent( V, I_S, V_T * N ) + (gtiny * V); + } + else if ( V_B == 0 || V >= -V_B ) + { + double a = (3 * N * V_T) / (V * M_E); + a = a * a * a; + *I_D = (-I_S * (1 + a)) + (gtiny * V); + if ( g ) + *g = ((I_S * 3 * a) / V) + gtiny; + } + else + { + double a = exp( -(V_B + V) / N / V_T ); + *I_D = (-I_S * a) + (gtiny * V); + if ( g ) + *g = I_S * a / V_T / N + gtiny; + } +} + + +void Diode::setDiodeSettings( const DiodeSettings & settings ) +{ + m_diodeSettings = settings; + if (p_eSet) + p_eSet->setCacheInvalidated(); +} +//END class Diode + diff --git a/src/electronics/simulation/diode.h b/src/electronics/simulation/diode.h new file mode 100644 index 0000000..0b13946 --- /dev/null +++ b/src/electronics/simulation/diode.h @@ -0,0 +1,73 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef DIODE_H +#define DIODE_H + +#include "nonlinear.h" + +class DiodeSettings +{ + public: + DiodeSettings(); + void reset(); + + double I_S; ///< Diode saturation current + double N; ///< Emission coefficient + double V_B; ///< Reverse breakdown +// double R; ///< Series resistance +}; + + +/** +This simulates a diode. The simulated diode characteristics are: + +@li I_s: Diode saturation current +@li V_T: Thermal voltage = kT/4 = 25mV at 20 C +@li n: Emission coefficient, typically between 1 and 2 +@li V_RB: Reverse breakdown (large negative voltage) +@li G_RB: Reverse breakdown conductance +@li R_D: Base resistance of diode + +@short Simulates the electrical property of diode-ness +@author David Saxton +*/ +class Diode : public NonLinear +{ + public: + Diode(); + virtual ~Diode(); + + virtual void update_dc(); + virtual void add_initial_dc(); + virtual void add_map(); + virtual Element::Type type() const { return Element_Diode; } + DiodeSettings settings() const { return m_diodeSettings; } + void setDiodeSettings( const DiodeSettings & settings ); + /** + * Returns the current flowing through the diode + */ + double current() const; + + protected: + virtual void updateCurrents(); + void calc_eq(); + + void calcIg( double V, double * I, double * g ) const; + + double g_new, g_old; + double I_new, I_old; + double V_prev; + + DiodeSettings m_diodeSettings; +}; + +#endif + diff --git a/src/electronics/simulation/element.cpp b/src/electronics/simulation/element.cpp new file mode 100644 index 0000000..2411897 --- /dev/null +++ b/src/electronics/simulation/element.cpp @@ -0,0 +1,193 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "element.h" +#include "elementset.h" + +#include <assert.h> + +#include <kdebug.h> + +Element::Element() +{ + b_status = false; + p_A = 0l; + p_eSet = 0l; + p_b = 0l; + b_componentDeleted = false; + b_eSetDeleted = true; + + for ( int i=0; i<8; i++ ) + p_cnode[i] = 0l; + + resetCurrents(); + + for ( int i=0; i<4; i++ ) + p_cbranch[i] = 0l; + + m_numCBranches = 0; + m_numCNodes = 0; +} + +Element::~ Element() +{ +} + +void Element::resetCurrents() +{ + for ( int i=0; i<8; i++ ) + m_cnodeI[i] = 0.0; +} + +void Element::setElementSet( ElementSet *c ) +{ + assert(!b_componentDeleted); + assert(!p_eSet); + if (!c) return elementSetDeleted(); + b_eSetDeleted = false; + p_eSet = c; + p_A = p_eSet->matrix(); + p_b = p_eSet->b(); + updateStatus(); +} + +void Element::componentDeleted() +{ +// assert(!b_componentDeleted); + if (b_componentDeleted) + { + // Something strange happened here.... + } + if (b_eSetDeleted) return delete this; + b_componentDeleted = true; + b_status = false; +// kdDebug() << "Element::componentDeleted(): Setting b_status to false, this="<<this<<endl; + + p_eSet = 0l; + p_A = 0l; + p_b = 0l; + setCNodes(); + setCBranches(); +} + +void Element::elementSetDeleted() +{ +// assert(!b_eSetDeleted); + if (b_eSetDeleted) + { + // Something strange happened here.... + } + if (b_componentDeleted) return delete this; + b_eSetDeleted = true; + b_status = false; +// kdDebug() << "Element::elementSetDeleted(): Setting b_status to false, this="<<this<<endl; + + p_eSet = 0l; + p_A = 0l; + p_b = 0l; + setCNodes(); + setCBranches(); +} + + +void Element::setCNodes( const int n0, const int n1, const int n2, const int n3 ) +{ + if ( !p_eSet ) + { +// cerr << "Element::setCNodes: can't set nodes without circuit!"<<endl; + for ( int i=0; i<8; i++ ) + p_cnode[i] = 0l; + return; + } + + // MAX_CNODES-1 should match the last array index below. + assert( MAX_CNODES == 4 ); + p_cnode[0] = (n0>-1)?p_eSet->cnodes()[n0]:(n0==-1?p_eSet->ground():0l); + p_cnode[1] = (n1>-1)?p_eSet->cnodes()[n1]:(n1==-1?p_eSet->ground():0l); + p_cnode[2] = (n2>-1)?p_eSet->cnodes()[n2]:(n2==-1?p_eSet->ground():0l); + p_cnode[3] = (n3>-1)?p_eSet->cnodes()[n3]:(n3==-1?p_eSet->ground():0l); + updateStatus(); +} + +void Element::setCBranches( const int b0, const int b1, const int b2, const int b3 ) +{ + if ( !p_eSet ) + { +// cerr << "Element::setCBranches: can't set branches without circuit!"<<endl; + for ( int i=0; i<4; i++ ) p_cbranch[i] = 0l; + return; + } + p_cbranch[0] = (b0>-1)?p_eSet->cbranches()[b0]:0l; + p_cbranch[1] = (b1>-1)?p_eSet->cbranches()[b1]:0l; + p_cbranch[2] = (b2>-1)?p_eSet->cbranches()[b2]:0l; + p_cbranch[3] = (b3>-1)?p_eSet->cbranches()[b3]:0l; + updateStatus(); +} + +bool Element::updateStatus() +{ + // First, set status to false if all nodes in use are ground + b_status = false; + for ( int i=0; i<m_numCNodes; i++ ) + { + b_status |= p_cnode[i]?!p_cnode[i]->isGround:false; + } + + // Set status to false if any of the nodes are not set + for ( int i=0; i<m_numCNodes; i++ ) + { + if (!p_cnode[i]) b_status = false; + } + + // Finally, set status to false if not all the required branches are set + for ( int i=0; i<m_numCBranches; i++ ) + { + if (!p_cbranch[i]) b_status = false; + } + + // Finally, check for various pointers + if ( !p_eSet || !p_A || !p_b ) b_status = false; + + if (!b_status) + { + resetCurrents(); + } + // And return the status :-) +// kdDebug() << "Element::updateStatus(): Setting b_status to "<<(b_status?"true":"false")<<" this="<<this<<endl; + return b_status; +} + +double Element::cbranchCurrent( const int branch ) +{ + if ( !b_status || branch<0 || branch>=m_numCBranches ) return 0.; + return (*p_cbranch)[branch].i; +} + +double Element::cnodeVoltage( const int node ) +{ + if ( !b_status || node<0 || node>=m_numCNodes ) return 0.; + return (*p_cnode)[node].v; +} + + +CNode::CNode() +{ + m_n = 0; + v = 0.0; + isGround = false; +} + +CBranch::CBranch() +{ + m_n = 0; + i = 0.0; +} + + diff --git a/src/electronics/simulation/element.h b/src/electronics/simulation/element.h new file mode 100644 index 0000000..e05de46 --- /dev/null +++ b/src/electronics/simulation/element.h @@ -0,0 +1,255 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef ELEMENT_H +#define ELEMENT_H + +#include "elementset.h" +#include "matrix.h" + +class ElementSet; +class Vector; +typedef unsigned int uint; + +const double T = 300.; // Temperature in Kelvin +const double K = 1.38e-23; // Boltzmann's constant +const double q = 1.602e-19; // Charge on an electron +const double V_T = K*T/q; // Thermal voltage +const double gmin = 1e-12; // Minimum parallel conductance used in dc domain + +class CNode +{ +public: + CNode(); + void set_n( const uint n ) { m_n=n; } + uint n() { return m_n; } + double v; // Voltage on node. This is set from the last calculated voltage. + bool isGround; // True for ground nodes. Obviously, you should ignore n and v if this is true +private: + uint m_n; // CNode number +}; + +class CBranch +{ +public: + CBranch(); + void set_n( const uint n ) { m_n=n; } + uint n() { return m_n; } + double i; // Current flowing through branch. This is set from the last calculated current. +private: + uint m_n; // CBranch number +}; + +const int MAX_CNODES = 4; + +// Default node number that represents no node (remember that +// Ground node is -1, and the rest are numbered from 0 to n-1 +const int noCNode = -2; +// Likewise for branch (although there is no "ground" branch; +// it is merely -2 for likeness with noCNode) +const int noBranch = -2; + +/** +@short Represents a circuit element (such as resistance) +@author David Saxton +*/ +class Element +{ +public: + enum Type + { + Element_BJT, + Element_Capacitance, + Element_CCCS, + Element_CCVS, + Element_CurrentSignal, + Element_CurrentSource, + Element_Diode, + Element_Inductance, + Element_LogicIn, + Element_LogicOut, + Element_OpAmp, + Element_Resistance, + Element_VCCS, + Element_VCVS, + Element_VoltagePoint, + Element_VoltageSignal, + Element_VoltageSource + }; + + Element(); + virtual ~Element(); + /** + * This must be called when the circuit is changed. The function will get + * all the required pointers from ElementSet + */ + virtual void setElementSet( ElementSet *c ); + /** + * Returns a pointer to the current element set + */ + ElementSet *elementSet() { return p_eSet; } + /** + * Tells the element which nodes to use. Remember that -1 is ground. You + * should refer to the individual elements for which nodes are used for what. + */ + void setCNodes( const int n0 = noCNode, const int n1 = noCNode, const int n2 = noCNode, const int n3 = noCNode ); + /** + * Tells the element it's branch numbers (if it should have one). Not + * all elements use this. + */ + void setCBranches( const int b0 = noBranch, const int b1 = noBranch, const int b2 = noBranch, const int b3 = noBranch ); + /** + * Returns a pointer to the given CNode + */ + CNode *cnode( const uint num ) { return p_cnode[num]; } + /** + * Returns a pointer to the given CNode + */ + CBranch *cbranch( const uint num ) { return p_cbranch[num]; } + /** + * Returns the number of branches used by the element + */ + int numCBranches() { return m_numCBranches; } + /** + * Returns the number of circuit nodes used by the element + */ + int numCNodes() { return m_numCNodes; } + /** + * Call this function to tell the element to calculate the + * current flowing *into* it's cnodes *from* the element. You + * can get the currents with m_cnodeI. Child class must implement this function. + */ + virtual void updateCurrents() = 0; + /** + * Returns true for reactive elements that need stepping for numerical-integration + * (such as capacitors) + */ + virtual bool isReactive() { return false; } + /** + * Returns true for NonLinear elements that need iteration to converge to a solution + * as the matrix A is a function of x. + */ + virtual bool isNonLinear() { return false; } + /** + * Returns the type of element + */ + virtual Type type() const = 0; + /** + * Call this function to tell the element to add its map to the matrix in use + */ + virtual void add_map() {}; + /** + * Does the required MNA stuff. This should be called from ElementSet when necessary. + */ + virtual void add_initial_dc() = 0; + /** + * This is called from the Component destructor. When elementSetDeleted has + * also been called, this class will delete itself. + */ + void componentDeleted(); + void elementSetDeleted(); + + double m_cnodeI[8]; ///< Current flowing into the cnodes from the element + double cbranchCurrent( const int branch ); + double cnodeVoltage( const int node ); + +protected: + /** + * Resets all calculated currents in the nodes to 0 + */ + void resetCurrents(); + + inline double & A_g( uint i, uint j ); + inline double & A_b( uint i, uint j ); + inline double & A_c( uint i, uint j ); + inline double & A_d( uint i, uint j ); + + inline double & b_i( uint i ); + inline double & b_v( uint i ); + + ElementSet *p_eSet; + Matrix *p_A; + Vector *p_b; + CNode *p_cnode[MAX_CNODES]; + CBranch *p_cbranch[4]; + + /** + * True when the element can do add_initial_dc(), i.e. when it has + * pointers to the circuit, and at least one of its nodes is not ground. + */ + bool b_status; + /** + * Update the status, returning b_status + */ + virtual bool updateStatus(); + /** + * Set by child class - the number of branches that the element uses + * Typically, this is 0, but could be 1 (e.g. independent voltage source) + * or 2 (e.g. cccs) + */ + int m_numCBranches; + /** + * Set by child class - the number of circuit nodes that the element uses + */ + int m_numCNodes; + +private: + bool b_componentDeleted; + bool b_eSetDeleted; + double m_temp; +}; + + +double & Element::A_g( uint i, uint j ) +{ + if ( p_cnode[i]->isGround || p_cnode[j]->isGround ) + return m_temp; + return p_A->g( p_cnode[i]->n(), p_cnode[j]->n() ); +} + + +double & Element::A_b( uint i, uint j ) +{ + if ( p_cnode[i]->isGround ) + return m_temp; + return p_A->b( p_cnode[i]->n(), p_cbranch[j]->n() ); +} + + +double & Element::A_c( uint i, uint j ) +{ + if ( p_cnode[j]->isGround ) + return m_temp; + return p_A->c( p_cbranch[i]->n(), p_cnode[j]->n() ); +} + + +double & Element::A_d( uint i, uint j ) +{ + return p_A->d( p_cbranch[i]->n(), p_cbranch[j]->n() ); +} + + + +double & Element::b_i( uint i ) +{ + if ( p_cnode[i]->isGround ) + return m_temp; + + return (*p_b)[ p_cnode[i]->n() ]; +} + + +double & Element::b_v( uint i ) +{ + return (*p_b)[ p_eSet->cnodeCount() + p_cbranch[i]->n() ]; +} + +#endif diff --git a/src/electronics/simulation/elementset.cpp b/src/electronics/simulation/elementset.cpp new file mode 100644 index 0000000..25057c2 --- /dev/null +++ b/src/electronics/simulation/elementset.cpp @@ -0,0 +1,253 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "bjt.h" +#include "circuit.h" +#include "elementset.h" +#include "element.h" +#include "logic.h" +#include "matrix.h" +#include "nonlinear.h" +#include "vec.h" + +#include <kdebug.h> + +#include <cmath> +#include <iostream> +#include <assert.h> + +ElementSet::ElementSet( Circuit * circuit, const int n, const int m ) +{ + m_pCircuit = circuit; + m_cn = n; + m_cb = m; + p_logicIn = 0l; + p_A = new Matrix( m_cn, m_cb ); + p_b = new Vector(m_cn+m_cb); + p_x = new Vector(m_cn+m_cb); + p_x_prev = new Vector(m_cn+m_cb); + m_cbranches = new CBranch*[m_cb]; + m_cnodes = new CNode*[m_cn]; + for ( uint i=0; i<m_cn; i++ ) + { + m_cnodes[i] = new CNode(); + m_cnodes[i]->set_n(i); + } + for ( uint i=0; i<m_cb; i++ ) + { + m_cbranches[i] = new CBranch(); + m_cbranches[i]->set_n(i); + } + m_ground = new CNode(); + m_ground->isGround = true; + b_containsNonLinear = false; +} + +ElementSet::~ElementSet() +{ + const ElementList::iterator end = m_elementList.end(); + for ( ElementList::iterator it = m_elementList.begin(); it != end; ++it ) + { + // Note: By calling setElementSet(0l), we might have deleted it (the Element will commit + // suicide when both the the ElementSet and Component to which it belongs have deleted + // themselves). So be very careful it you plan to do anything with the (*it) pointer + if (*it) (*it)->elementSetDeleted(); + } + for ( uint i=0; i<m_cn; i++ ) + { + delete m_cnodes[i]; + } + for ( uint i=0; i<m_cb; i++ ) + { + delete m_cbranches[i]; + } + delete[] m_cbranches; + delete[] m_cnodes; + delete[] p_logicIn; + delete m_ground; + delete p_A; + delete p_b; + delete p_x; + delete p_x_prev; +} + + +void ElementSet::setCacheInvalidated() +{ + m_pCircuit->setCacheInvalidated(); +} + + +void ElementSet::addElement( Element *e ) +{ + if ( !e || m_elementList.contains(e) ) return; + e->setElementSet(this); + m_elementList.append(e); + if ( e->isNonLinear() ) + { + b_containsNonLinear = true; + m_cnonLinearList.append( static_cast<NonLinear*>(e) ); + } +} + + +void ElementSet::createMatrixMap() +{ + p_A->createMap(); + + + // And do our logic as well... + + m_clogic = 0; + ElementList::iterator end = m_elementList.end(); + for ( ElementList::iterator it = m_elementList.begin(); it != end; ++it ) + { + if ( dynamic_cast<LogicIn*>(*it) ) + m_clogic++; + } + + p_logicIn = new LogicIn*[m_clogic]; + int i=0; + for ( ElementList::iterator it = m_elementList.begin(); it != end; ++it ) + { + if ( LogicIn * in = dynamic_cast<LogicIn*>(*it) ) + p_logicIn[i++] = in; + } +} + + +void ElementSet::doNonLinear( int maxIterations, double maxErrorV, double maxErrorI ) +{ +// p_x_prev->reset(); + + // And now tell the cnodes and cbranches about their new voltages & currents + updateInfo(); + + const NonLinearList::iterator end = m_cnonLinearList.end(); + + int k = 0; + do + { + // Tell the nonlinear elements to update its J, A and b from the newly calculated x + for ( NonLinearList::iterator it = m_cnonLinearList.begin(); it != end; ++it ) + (*it)->update_dc(); + + *p_x = *p_b; + p_A->performLU(); + p_A->fbSub(p_x); + updateInfo(); + + // Now, check for convergence + bool converged = true; + for ( unsigned i = 0; i < m_cn; ++i ) + { + double diff = std::abs( (*p_x_prev)[i] - (*p_x)[i] ); + if ( diff > maxErrorI ) + { + converged = false; + break; + } + } + if ( converged ) + { + for ( unsigned i = m_cn; i < m_cn+m_cb; ++i ) + { + double diff = std::abs( (*p_x_prev)[i] - (*p_x)[i] ); + if ( diff > maxErrorV ) + { + converged = false; + break; + } + } + } + + *p_x_prev = *p_x; + + if ( converged ) + break; + } + while ( ++k < maxIterations ); +} + + +bool ElementSet::doLinear( bool performLU ) +{ + if ( b_containsNonLinear || (!p_b->isChanged() && ((performLU && !p_A->isChanged()) || !performLU)) ) + return false; + + if (performLU) + p_A->performLU(); + + *p_x = *p_b; + p_A->fbSub(p_x); + updateInfo(); + p_b->setUnchanged(); + + return true; +} + + +void ElementSet::updateInfo() +{ + for ( uint i=0; i<m_cn; i++ ) + { + const double v = (*p_x)[i]; + if (std::isfinite(v)) { + m_cnodes[i]->v = v; + } else { + (*p_x)[i] = 0.; + m_cnodes[i]->v = 0.; + } + } + for ( uint i=0; i<m_cb; i++ ) + { + // NOTE: I've used lowercase and uppercase "I" here, so be careful! + const double I = (*p_x)[i+m_cn]; + if (std::isfinite(I)) { + m_cbranches[i]->i = I; + } else { + (*p_x)[i+m_cn] = 0.; + m_cbranches[i]->i = 0.; + } + } + + // Tell logic to check themselves + for ( uint i=0; i<m_clogic; ++i ) + { + p_logicIn[i]->check(); + } +} + + +void ElementSet::displayEquations() +{ + std::cout.setf(std::ios_base::fixed); + std::cout.precision(5); + std::cout.setf(std::ios_base::showpoint); + std::cout << "A x = b :"<<std::endl; + for ( uint i=0; i<m_cn+m_cb; i++ ) + { + std::cout << "( "; + for ( uint j=0; j<m_cn+m_cb; j++ ) + { + const double value = p_A->g(i,j); +// if ( value > 0 ) cout <<"+"; +// else if ( value == 0 ) cout <<" "; + std::cout.width(10); + std::cout << value<<" "; + } + std::cout << ") ( "<<(*p_x)[i]<<" ) = ( "; + std::cout<<(*p_b)[i]<<" )"<<std::endl; + } + std::cout << "A_LU:"<<std::endl; + p_A->displayLU(); +} + + diff --git a/src/electronics/simulation/elementset.h b/src/electronics/simulation/elementset.h new file mode 100644 index 0000000..c7ef7ca --- /dev/null +++ b/src/electronics/simulation/elementset.h @@ -0,0 +1,131 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef ELEMENTSET_H +#define ELEMENTSET_H + +#include <vector> + +#include <qvaluelist.h> + +class CBranch; +class Circuit; +class CNode; +class Element; +class ElementSet; +class LogicIn; +class Matrix; +class NonLinear; +class Vector; + +typedef QValueList<Element*> ElementList; +typedef QValueList<NonLinear*> NonLinearList; + +/** +Steps in simulation of a set of elements: +(1) Create this class with given number of nodes "n" and voltage sources "m" +(2) Add the various elements with addElement. +(3) Call performDC() +(4) Get the nodal voltages and voltage currents with x() +(5) Repeat steps 3 and 4 if necessary for further transient analysis. + +This class shouldn't be confused with the Circuit class, but considered a helper class to Circuit. +Circuit will handle the simulation of a set of components over time. This just finds the DC-operating +point of the circuit for a given set of elements. + +@short Handles a set of circuit elements +@author David Saxton +*/ +class ElementSet +{ +public: + /** + * Create a new circuit, with "n" nodes and "m" voltage sources. + * After creating the circuit, you must call setGround to specify + * the ground nodes, before adding any elements. + */ + ElementSet( Circuit * circuit, const int n, const int m ); + /** + * Destructor. Note that only the matrix and supporting data is deleted. + * i.e. Any elements added to the circuit will not be deleted. + */ + ~ElementSet(); + Circuit * circuit() const { return m_pCircuit; } + void addElement( Element *e ); + void setCacheInvalidated(); + /** + * Returns the matrix in use. This is created once on the creation of the ElementSet + * class, and deleted in the destructor, so the pointer returned will never change. + */ + Matrix *matrix() const { return p_A; } + /** + * Returns the vector for b (i.e. the independent currents & voltages) + */ + Vector *b() const { return p_b; } + /** + * Returns the vector for x (i.e. the currents & voltages at the branches and nodes) + */ + Vector *x() const { return p_x; } + /** + * @return if we have any nonlinear elements (e.g. diodes, tranaistors). + */ + bool containsNonLinear() const { return b_containsNonLinear; } + /** + * Solves for nonlinear elements, or just does linear if it doesn't contain + * any nonlinear. + */ + void doNonLinear( int maxIterations, double maxErrorV = 1e-9, double maxErrorI = 1e-12 ); + /** + * Solves for linear and logic elements. + * @returns true if anything changed + */ + bool doLinear( bool performLU ); + CBranch **cbranches() const { return m_cbranches; } + CNode **cnodes() const { return m_cnodes; } + CNode *ground() const { return m_ground; } + /** + * Returns the number of nodes in the circuit (excluding ground 'nodes') + */ + int cnodeCount() const { return m_cn; } + /** + * Returns the number of voltage sources in the circuit + */ + int cbranchCount() const { return m_cb; } + + void createMatrixMap(); + /** + * Displays the matrix equations Ax=b and J(dx)=-r + */ + void displayEquations(); + /** + * Update the nodal voltages and branch currents from the x vector + */ + void updateInfo(); + +private: + Matrix *p_A; + Vector *p_x; + Vector *p_x_prev; + Vector *p_b; + uint m_cn; + uint m_cb; + ElementList m_elementList; + NonLinearList m_cnonLinearList; + CBranch **m_cbranches; // Pointer to an array of cbranches + CNode **m_cnodes; // Pointer to an array of cnodes + CNode *m_ground; + uint m_clogic; + LogicIn **p_logicIn; + bool b_containsNonLinear; + Circuit * m_pCircuit; +}; + +#endif + diff --git a/src/electronics/simulation/elementsignal.cpp b/src/electronics/simulation/elementsignal.cpp new file mode 100644 index 0000000..31d7d78 --- /dev/null +++ b/src/electronics/simulation/elementsignal.cpp @@ -0,0 +1,64 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "elementsignal.h" +#include <cmath> + +ElementSignal::ElementSignal() +{ + m_type = ElementSignal::st_sinusoidal; + m_time = 0.; + m_frequency = 0.; +} + +ElementSignal::~ElementSignal() +{ +} + +void ElementSignal::setStep( double delta, Type type, double frequency ) +{ + m_type = type; + m_delta = delta; + m_frequency = frequency; + m_omega = 6.283185307179586*m_frequency; + m_time = 1./(4.*m_frequency); +} + +double ElementSignal::advance() +{ + m_time += m_delta; + if ( m_time >= 1./m_frequency ) m_time -= 1./m_frequency; + + switch (m_type) + { + case ElementSignal::st_sawtooth: + { + // TODO Sawtooth signal + return 0.; + } + case ElementSignal::st_square: + { + return (sin(m_time*m_omega)>=0)?1:-1; + } + case ElementSignal::st_triangular: + { + // TODO Triangular signal + return 0.; + } + case ElementSignal::st_sinusoidal: + default: + { + return sin(m_time*m_omega); + } + } +} + + + diff --git a/src/electronics/simulation/elementsignal.h b/src/electronics/simulation/elementsignal.h new file mode 100644 index 0000000..c26bea1 --- /dev/null +++ b/src/electronics/simulation/elementsignal.h @@ -0,0 +1,45 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef ELEMENTSIGNAL_H +#define ELEMENTSIGNAL_H + +/** +@short Provides different signals +@author David Saxton +*/ +class ElementSignal +{ +public: + enum Type + { + st_sinusoidal, + st_square, + st_sawtooth, + st_triangular + }; + ElementSignal(); + ~ElementSignal(); + + void setStep( double delta, Type type, double frequency ); + /** + * Advances the timer, returns amplitude (between -1 and 1) + */ + double advance(); + +protected: + Type m_type; + double m_time; + double m_frequency; + double m_delta; + double m_omega; // Used for sinusoidal signal +}; + +#endif diff --git a/src/electronics/simulation/inductance.cpp b/src/electronics/simulation/inductance.cpp new file mode 100644 index 0000000..22c5f9e --- /dev/null +++ b/src/electronics/simulation/inductance.cpp @@ -0,0 +1,126 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "elementset.h" +#include "inductance.h" +#include "matrix.h" + +Inductance::Inductance( double inductance, double delta ) + : Reactive(delta) +{ + m_inductance = inductance; + r_eq_old = v_eq_old = 0.0; + m_numCNodes = 2; + m_numCBranches = 1; + setMethod( Inductance::m_euler ); +} + + +Inductance::~Inductance() +{ +} + + +void Inductance::setInductance( double i ) +{ + m_inductance = i; +} + + +void Inductance::add_initial_dc() +{ + A_c( 0, 0 ) = 1; + A_b( 0, 0 ) = 1; + A_c( 0, 1 ) = -1; + A_b( 1, 0 ) = -1; + + // The adding of r_eg and v_eq will be done for us by time_step. + // So for now, just reset the constants used. + r_eq_old = v_eq_old = 0.0; +} + + +void Inductance::updateCurrents() +{ + if (!b_status) + return; + m_cnodeI[0] = p_cbranch[0]->i; + m_cnodeI[1] = -m_cnodeI[0]; +} + + +void Inductance::add_map() +{ + if (!b_status) + return; + + if ( !p_cnode[0]->isGround ) + { + p_A->setUse_c( p_cbranch[0]->n(), p_cnode[0]->n(), Map::et_constant, true ); + p_A->setUse_b( p_cnode[0]->n(), p_cbranch[0]->n(), Map::et_constant, true ); + } + + if ( !p_cnode[1]->isGround ) + { + p_A->setUse_c( p_cbranch[0]->n(), p_cnode[1]->n(), Map::et_constant, true ); + p_A->setUse_b( p_cnode[1]->n(), p_cbranch[0]->n(), Map::et_constant, true ); + } + + p_A->setUse_d( p_cbranch[0]->n(), p_cbranch[0]->n(), Map::et_unstable, false ); +} + + +void Inductance::time_step() +{ + if (!b_status) return; + + double i = p_cbranch[0]->i; + double v_eq_new = 0.0, r_eq_new = 0.0; + + if ( m_method == Inductance::m_euler ) + { + r_eq_new = m_inductance/m_delta; + v_eq_new = -i*r_eq_new; + } + else if ( m_method == Inductance::m_trap ) { + // TODO Implement + test trapezoidal method + r_eq_new = 2.0*m_inductance/m_delta; + } + + if ( r_eq_old != r_eq_new ) + { + A_d( 0, 0 ) -= r_eq_new - r_eq_old; + } + + if ( v_eq_new != v_eq_old ) + { + b_v( 0 ) += v_eq_new - v_eq_old; + } + + r_eq_old = r_eq_new; + v_eq_old = v_eq_new; +} + + +bool Inductance::updateStatus() +{ + b_status = Reactive::updateStatus(); + if ( m_method == Inductance::m_none ) + b_status = false; + return b_status; +} + + +void Inductance::setMethod( Method m ) +{ + m_method = m; + updateStatus(); +} + diff --git a/src/electronics/simulation/inductance.h b/src/electronics/simulation/inductance.h new file mode 100644 index 0000000..46ccb09 --- /dev/null +++ b/src/electronics/simulation/inductance.h @@ -0,0 +1,55 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef INDUCTANCE_H +#define INDUCTANCE_H + +#include "reactive.h" + +/** + +@author David Saxton +*/ +class Inductance : public Reactive +{ + public: + enum Method + { + m_none, // None + m_euler, // Backward Euler + m_trap // Trapezoidal (currently unimplemented) + }; + Inductance( double capacitance, double delta ); + virtual ~Inductance(); + + virtual Type type() const { return Element_Inductance; } + /** + * Set the stepping use for numerical integration of inductance, and the + * interval between successive updates. + */ + void setMethod( Method m ); + virtual void time_step(); + virtual void add_initial_dc(); + void setInductance( double i ); + virtual void add_map(); + + protected: + virtual void updateCurrents(); + virtual bool updateStatus(); + + private: + double m_inductance; // Inductance + Method m_method; // Method of integration + + double r_eq_old; + double v_eq_old; +}; + +#endif diff --git a/src/electronics/simulation/logic.cpp b/src/electronics/simulation/logic.cpp new file mode 100644 index 0000000..031dd2e --- /dev/null +++ b/src/electronics/simulation/logic.cpp @@ -0,0 +1,319 @@ +/*************************************************************************** + * Copyright (C) 2003-2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include <vector> +#include "circuit.h" +#include "elementset.h" +#include "logic.h" +#include "matrix.h" +#include "simulator.h" +#include "src/core/ktlconfig.h" + +LogicIn::LogicIn( LogicConfig config ) + : Element::Element() +{ + m_config = config; + m_pCallbackFunction = 0l; + m_numCNodes = 1; + m_bLastState = false; + m_pNextLogic = 0l; + setLogic(getConfig()); +} + +LogicIn::~LogicIn() +{ + Simulator::self()->removeLogicInReferences(this); +} + + +void LogicIn::setCallback( CallbackClass * object, CallbackPtr func ) +{ + m_pCallbackFunction = func; + m_pCallbackObject = object; +} + + +void LogicIn::check() +{ + if (!b_status) + return; + + bool newState; + if (m_bLastState) + { + // Was high, will still be high unless voltage is less than falling trigger + newState = p_cnode[0]->v > m_config.fallingTrigger; + } + else + { + // Was low, will still be low unless voltage is more than rising trigger + newState = p_cnode[0]->v > m_config.risingTrigger; + } + + if ( m_pCallbackFunction && (newState != m_bLastState) ) + { + m_bLastState = newState; + (m_pCallbackObject->*m_pCallbackFunction)(newState); + } + m_bLastState = newState; +} + + +void LogicIn::setLogic( LogicConfig config ) +{ + m_config = config; + check(); +} + + +void LogicIn::setElementSet( ElementSet *c ) +{ + if (c) + m_pNextLogic = 0l; + else + m_cnodeI[0] = 0.; + + Element::setElementSet(c); +} + + +void LogicIn::add_initial_dc() +{ +} + + +void LogicIn::updateCurrents() +{ +} + + +LogicConfig LogicIn::getConfig() +{ + LogicConfig c; + c.risingTrigger = KTLConfig::logicRisingTrigger(); + c.fallingTrigger = KTLConfig::logicFallingTrigger(); + c.output = KTLConfig::logicOutputHigh(); + c.highImpedance = KTLConfig::logicOutputHighImpedance(); + c.lowImpedance = KTLConfig::logicOutputLowImpedance(); + return c; +} + + +LogicOut::LogicOut( LogicConfig config, bool _high ) + : LogicIn(config) +{ + m_bCanAddChanged = true; + m_bOutputHighConductanceConst = false; + m_bOutputLowConductanceConst = false; + m_bOutputHighVoltageConst = false; + m_pNextChanged[0] = m_pNextChanged[1] = 0l; + m_pSimulator = 0l; + m_bUseLogicChain = false; + b_state = false; + m_numCNodes = 1; + m_vHigh = m_gHigh = m_gLow = 0.0; + m_old_g_out = m_g_out = 0.0; + m_old_v_out = m_v_out = 0.0; + setHigh(_high); + + // Although we already call this function in LogicIn's constructor, our + // virtual function will not have got called, so we have to call it again. + setLogic(getConfig()); +} + +LogicOut::~LogicOut() +{ + if (!m_pSimulator) + m_pSimulator = Simulator::self(); + + // Note that although this function will get called in the destructor of + // LogicIn, we must call it here as well as it needs to be called before + // removeLogicOutReferences(this) is called. + m_pSimulator->removeLogicInReferences(this); + + m_pSimulator->removeLogicOutReferences(this); +} + + +void LogicOut::setUseLogicChain( bool use ) +{ + if (!m_pSimulator) + m_pSimulator = Simulator::self(); + + m_bUseLogicChain = use; + if (use) + setElementSet(0l); +} + + +void LogicOut::setElementSet( ElementSet *c ) +{ + if (!m_pSimulator) + m_pSimulator = Simulator::self(); + + if (c) + { + m_bUseLogicChain = false; + m_pNextChanged[0] = m_pNextChanged[1] = 0l; + } + + // NOTE Make sure that the next two lines are the same as those in setHigh and setLogic + m_g_out = b_state ? m_gHigh : m_gLow; + m_v_out = b_state ? m_vHigh : 0.0; + + LogicIn::setElementSet(c); +} + + +void LogicOut::setOutputHighConductance( double g ) +{ + m_bOutputHighConductanceConst = true; + if ( g == m_gHigh ) + return; + m_gHigh = g; + configChanged(); +} + + +void LogicOut::setOutputLowConductance( double g ) +{ + m_bOutputLowConductanceConst = true; + if ( g == m_gLow ) + return; + m_gLow = g; + configChanged(); +} + + +void LogicOut::setOutputHighVoltage( double v ) +{ + m_bOutputHighVoltageConst = true; + if ( v == m_vHigh ) + return; + m_vHigh = v; + configChanged(); +} + + +void LogicOut::setLogic( LogicConfig config ) +{ + m_config = config; + + if (!m_bOutputHighConductanceConst) + m_gHigh = 1.0/config.highImpedance; + + if (!m_bOutputLowConductanceConst) + m_gLow = (config.lowImpedance == 0.0) ? 0.0 : 1.0/config.lowImpedance; + + if (!m_bOutputHighVoltageConst) + m_vHigh = config.output; + + configChanged(); +} + + +void LogicOut::configChanged() +{ + if (m_bUseLogicChain) + return; + + if (p_eSet) + p_eSet->setCacheInvalidated(); + + // Re-add the DC stuff using the new values + + m_old_g_out = m_g_out; + m_old_v_out = m_v_out; + + // NOTE Make sure that the next two lines are the same as those in setElementSet and setHigh + m_g_out = b_state ? m_gHigh : m_gLow; + m_v_out = b_state ? m_vHigh : 0.0; + + add_initial_dc(); + + m_old_g_out = 0.; + m_old_v_out = 0.; + + check(); +} + + +void LogicOut::add_map() +{ + if (!b_status) return; + + p_A->setUse( p_cnode[0]->n(), p_cnode[0]->n(), Map::et_variable, false ); +} + + +void LogicOut::add_initial_dc() +{ + if (!b_status) + return; + + A_g( 0, 0 ) += m_g_out-m_old_g_out; + b_i( 0 ) += m_g_out*m_v_out-m_old_g_out*m_old_v_out; +} + +void LogicOut::updateCurrents() +{ + if (m_bUseLogicChain) + { + m_cnodeI[0] = 0.; + return; + } + if (!b_status) { + return; + } + m_cnodeI[0] = (p_cnode[0]->v-m_v_out)*m_g_out; +} + +void LogicOut::setHigh( bool high ) +{ + if ( high == b_state ) + return; + + if (m_bUseLogicChain) + { + b_state = high; + + for ( LogicIn * logic = this; logic; logic = logic->nextLogic() ) + logic->setLastState(high); + + if (m_bCanAddChanged) + { + m_pSimulator->addChangedLogic(this); + m_bCanAddChanged = false; + } + + return; + } + + m_old_g_out = m_g_out; + m_old_v_out = m_v_out; + + // NOTE Make sure that the next two lines are the same as those in setElementSet and setLogic + m_g_out = high ? m_gHigh : m_gLow; + m_v_out = high ? m_vHigh : 0.0; + + add_initial_dc(); + + m_old_g_out = 0.; + m_old_v_out = 0.; + + b_state = high; + + if ( p_eSet && p_eSet->circuit()->canAddChanged() ) + { + m_pSimulator->addChangedCircuit( p_eSet->circuit() ); + p_eSet->circuit()->setCanAddChanged(false); + } +} + diff --git a/src/electronics/simulation/logic.h b/src/electronics/simulation/logic.h new file mode 100644 index 0000000..be8374f --- /dev/null +++ b/src/electronics/simulation/logic.h @@ -0,0 +1,211 @@ +/*************************************************************************** + * Copyright (C) 2003-2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef LOGIC_H +#define LOGIC_H + +#include "element.h" + +#include <qguardedptr.h> +#include <qvaluelist.h> + +class Component; +class Pin; +class Simulator; + +typedef QValueList<QGuardedPtr<Pin> > PinList; + +typedef struct +{ + float risingTrigger; // Trigger on rising edge + float fallingTrigger; // Trigger on falling edge + float output; // Output voltage + float highImpedance; // Output impedance when high + float lowImpedance; // Output impedance when low +} LogicConfig; + +class CallbackClass {}; +typedef void(CallbackClass::*CallbackPtr)( bool isHigh ); + +/** +Use this class for Logic Inputs - this will have infinite impedance. +Use isHigh() will return whether the voltage level at the pin +is high than the predetermined voltage threshold, and setHigh() will make the +output high/low, also according to the predetermined logic type / voltages. + +@short Boolean Logic input +*/ +class LogicIn : public Element +{ + public: + LogicIn( LogicConfig config ); + virtual ~LogicIn(); + + virtual Type type() const { return Element_LogicIn; } + virtual void setElementSet( ElementSet *c ); + + /** + * Set logic values from the LogicConfig. + */ + virtual void setLogic( LogicConfig config ); + /** + * Check if the input state has changed, to see if we need to callback. + */ + void check(); + /** + * Returns whether the pin is 'high', as defined for the logic type + * Note: this is defined as the voltage on the pin, as opposed to what the + * state was set to (the two are not necessarily the same). + */ + bool isHigh() const { return m_bLastState; } + /** + * When the logic state on this LogicIn changes, the function passed in this + * function will be called. At most one Callback can be added per LogicIn. + */ + void setCallback( CallbackClass * object, CallbackPtr func ); + /** + * Reads the LogicConfig values in from KTLConfig, and returns them in a + * nice object form. + */ + static LogicConfig getConfig(); + /** + * If this belongs to a logic chain, then this will be called from the chain. + */ + void setLastState( bool state ) { m_bLastState = state; } + /** + * Returns a pointer to the next LogicIn in the chain. + */ + LogicIn * nextLogic() const { return m_pNextLogic; } + /** + * Sets the next LogicIn in the chain. + */ + void setNextLogic( LogicIn * next ) { m_pNextLogic = next; } + /** + * Calls the callback function, if there is one. + */ + void callCallback() + { + if (m_pCallbackFunction) + (m_pCallbackObject->*m_pCallbackFunction)(m_bLastState); + } + + protected: + virtual void updateCurrents(); + virtual void add_initial_dc(); + + CallbackPtr m_pCallbackFunction; + CallbackClass * m_pCallbackObject; + bool m_bLastState; + LogicIn * m_pNextLogic; + LogicConfig m_config; +}; + + +/** +@short Logic output/input +*/ +class LogicOut : public LogicIn +{ + public: + LogicOut( LogicConfig config, bool _high ); + virtual ~LogicOut(); + + virtual void setLogic( LogicConfig config ); + virtual void setElementSet( ElementSet *c ); + virtual void add_map(); + virtual Type type() const { return Element_LogicOut; } + + /** + * Call this function to override the logic-high output impedance as set by + * the user. Once set, the impedance will not be changed by the user + * updating the config; only by subsequent calls to this function. + */ + void setOutputHighConductance( double g ); + /** + * Call this function to override the logic-low output impedance as set by + * the user. Once set, the impedance will not be changed by the user + * updating the config; only by subsequent calls to this function. + */ + void setOutputLowConductance( double g ); + /** + * Call this function to override the logic out voltage as set by the + * user. Once set, the impedance will not be changed by the user + * updating the config; only by subsequent calls to this function. + */ + void setOutputHighVoltage( double v ); + /** + * Returns the voltage that this will output when high. + */ + double outputHighVoltage() const { return m_vHigh; } + /** + * Sets the pin to be high/low + */ + void setHigh( bool high ); + /** + * @returns the state that this is outputting (regardless of voltage level on logic) + */ + bool outputState() const { return b_state; } + /** + * Set whether or not this LogicOut is the head of a LogicChain (controls + * itself and a bunch of LogicIns). + */ + void setUseLogicChain( bool use ); + /** + * When a LogicOut configured as the start of a LogicChain changes start, it + * appends a pointer to itself to the list of change LogicOut, starting from + * the Simulator. This functions enables appending the next changed LogicOut + * to this one. + */ + void setNextChanged( LogicOut * logicOut, unsigned char chain ) { m_pNextChanged[chain] = logicOut; } + /** + * To avoid a pointer to this LogicOut being added twice in one + * iteration due to the state changing twice, this LogicOut sets an + * added flag to true after adding it to the list of changed. The flag + * must be reset to false with this function (done by Simulator). + */ + void setCanAddChanged( bool canAdd ) { m_bCanAddChanged = canAdd; } + /** + * Returns the next LogicOut that has changed, when configured as the start + * of a LogicChain. + * @see setNextChanged + */ + LogicOut * nextChanged( unsigned char chain ) const { return m_pNextChanged[chain]; } + PinList pinList; + PinList::iterator pinListBegin; + PinList::iterator pinListEnd; + + protected: + void configChanged(); + virtual void updateCurrents(); + virtual void add_initial_dc(); + + // Pre-initalized levels from config + double m_gHigh; + double m_gLow; + double m_vHigh; + + // Whether to use the user-defined logic values + bool m_bOutputHighConductanceConst; + bool m_bOutputLowConductanceConst; + bool m_bOutputHighVoltageConst; + + double m_g_out; + double m_v_out; + double m_old_g_out; + double m_old_v_out; + bool b_state; + bool m_bCanAddChanged; + LogicOut * m_pNextChanged[2]; + Simulator * m_pSimulator; + bool m_bUseLogicChain; +}; + +#endif + diff --git a/src/electronics/simulation/matrix.cpp b/src/electronics/simulation/matrix.cpp new file mode 100644 index 0000000..fb1248f --- /dev/null +++ b/src/electronics/simulation/matrix.cpp @@ -0,0 +1,546 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "matrix.h" + +#include <kdebug.h> + +#include <assert.h> + +#include <cmath> +#include <iostream> +#include <vector> + +/// Minimum value before an entry is deemed "zero" +const double epsilon = 1e-50; + +Matrix::Matrix( uint n, uint m ) +{ + m_n = n; + m_m = m; + m_size = m_n+m_m; + + m_mat = new matrix(m_size); + m_lu = new matrix(m_size); + m_y = new double[m_size]; + m_inMap = new int[m_size]; +// m_outMap = new int[m_size]; + m_map = new Map(m_size); + zero(); +} + + +Matrix::~Matrix() +{ + delete m_map; + delete m_mat; + delete m_lu; + delete [] m_y; + delete [] m_inMap; +// delete [] m_outMap; +} + + +void Matrix::zero() +{ + for ( uint i=0; i<m_size; i++ ) + { + for ( uint j=0; j<m_size; j++ ) + { + (*m_mat)[i][j] = 0.; + (*m_lu)[i][j] = 0.; + } + m_inMap[i] = i; +// m_outMap[i] = i; + } + + max_k = 0; +} + + +void Matrix::setUse( const uint i, const uint j, Map::e_type type, bool big ) +{ + m_map->setUse( i, j, type, big ); +} + + +void Matrix::createMap() +{ + int newMap[m_size]; + m_map->createMap(newMap); + for ( uint i=0; i<m_size; i++ ) + { + const int nu = newMap[i]; + if ( nu != m_inMap[i] ) + { + int old = -1; + for ( uint j=0; j<m_size && old == -1; j++ ) + { + if ( m_inMap[j] == nu ) { + old = j; + } + } + assert( old != -1 ); + swapRows( old, i ); + } + } +} + + +void Matrix::swapRows( const uint a, const uint b ) +{ + if ( a == b ) return; + m_mat->swapRows( a, b ); + + const int old = m_inMap[a]; + m_inMap[a] = m_inMap[b]; + m_inMap[b] = old; + + max_k = 0; +} + + +/*void Matrix::genOutMap() +{ + for ( uint i=0; i<m_size; i++ ) + { + m_outMap[ m_inMap[i] ] = i; + } +}*/ + + +void Matrix::operator=( Matrix *const m ) +{ + for ( uint _i=0; _i<m_size; _i++ ) + { + uint i = m_inMap[_i]; + for ( uint j=0; j<m_size; j++ ) + { + (*m_mat)[i][j] = m->m(i,j); + } + } + + max_k = 0; +} + +void Matrix::operator+=( Matrix *const m ) +{ + for ( uint _i=0; _i<m_size; _i++ ) + { + uint i = m_inMap[_i]; + for ( uint j=0; j<m_size; j++ ) + { + (*m_mat)[i][j] += m->m(i,j); + } + } + + max_k = 0; +} + +void Matrix::performLU() +{ +// max_k = 0; + uint n = m_size; + if ( n == 0 ) return; + + // Copy the affected segment to LU + for ( uint i=max_k; i<n; i++ ) + { + for ( uint j=max_k; j<n; j++ ) + { + (*m_lu)[i][j] = (*m_mat)[i][j]; + } + } + + // LU decompose the matrix, and store result back in matrix + for ( uint k=0; k<n-1; k++ ) + { + double * const lu_K_K = &(*m_lu)[k][k]; + if ( std::abs(*lu_K_K) < 1e-10 ) + { + if ( *lu_K_K < 0. ) *lu_K_K = -1e-10; + else *lu_K_K = 1e-10; + } + for ( uint i=std::max(k,max_k)+1; i<n; i++ ) + { + (*m_lu)[i][k] /= *lu_K_K; + } + for ( uint i=std::max(k,max_k)+1; i<n; i++ ) + { + const double lu_I_K = (*m_lu)[i][k]; + if ( std::abs(lu_I_K) > 1e-12 ) + { + for ( uint j=std::max(k,max_k)+1; j<n; j++ ) + { + (*m_lu)[i][j] -= lu_I_K*(*m_lu)[k][j]; + } + } + } + } + + max_k = n; +} + +void Matrix::fbSub( Vector* b ) +{ + if ( m_size == 0 ) return; + + for ( uint i=0; i<m_size; i++ ) + { + m_y[m_inMap[i]] = (*b)[i]; + } + + // Forward substitution + for ( uint i=1; i<m_size; i++ ) + { + double sum = 0; + for ( uint j=0; j<i; j++ ) + { + sum += (*m_lu)[i][j]*m_y[j]; + } + m_y[i] -= sum; + } + + // Back substitution + m_y[m_size-1] /= (*m_lu)[m_size-1][m_size-1]; + for ( int i=m_size-2; i>=0; i-- ) + { + double sum = 0; + for ( uint j=i+1; j<m_size; j++ ) + { + sum += (*m_lu)[i][j]*m_y[j]; + } + m_y[i] -= sum; + m_y[i] /= (*m_lu)[i][i]; + } + + for ( uint i=0; i<m_size; i++ ) + (*b)[i] = m_y[i]; +} + + +void Matrix::multiply( Vector *rhs, Vector *result ) +{ + if ( !rhs || !result ) return; + result->reset(); + for ( uint _i=0; _i<m_size; _i++ ) + { + uint i = m_inMap[_i]; + for ( uint j=0; j<m_size; j++ ) + { + (*result)[_i] += (*m_mat)[i][j] * (*rhs)[j]; + } + } +} + + +void Matrix::displayMatrix() +{ + uint n = m_size; + for ( uint _i=0; _i<n; _i++ ) + { + uint i = m_inMap[_i]; + for ( uint j=0; j<n; j++ ) + { + if ( j > 0 && (*m_mat)[i][j] >= 0 ) kdDebug() << "+"; + kdDebug() << (*m_mat)[i][j] << "("<<j<<")"; + } + kdDebug() << endl; + } +} + +void Matrix::displayLU() +{ + uint n = m_size; + for ( uint _i=0; _i<n; _i++ ) + { + uint i = m_inMap[_i]; +// uint i = _i; + for ( uint j=0; j<n; j++ ) + { + if ( j > 0 && (*m_lu)[i][j] >= 0 ) std::cout << "+"; + std::cout << (*m_lu)[i][j] << "("<<j<<")"; + } + std::cout << std::endl; + } + std::cout << "m_inMap: "; + for ( uint i=0; i<n; i++ ) + { + std::cout << i<<"->"<<m_inMap[i]<<" "; + } + std::cout << std::endl; + /*cout << "m_outMap: "; + for ( uint i=0; i<n; i++ ) + { + cout << i<<"->"<<m_outMap[i]<<" "; + } + cout << endl;*/ +} + + +Map::Map( const uint size ) +{ + m_size = size; + m_map = new ETMap( m_size ); + reset(); +} + + +Map::~Map() +{ + delete m_map; +} + + +void Map::reset() +{ + for ( uint i=0; i<m_size; i++ ) + { + for ( uint j=0; j<m_size; j++ ) + { + (*m_map)[i][j] = 0; + } + } +} + + +void Map::setUse( const uint i, const uint j, Map::e_type type, bool big ) +{ + if ( type == Map::et_none ) { + (*m_map)[i][j] = Map::et_none; + } else { + (*m_map)[i][j] = type | (big)?Map::et_big:0; + } +} + + +void Map::createMap( int *map ) +{ + assert(map); + + // In this function, the passes through that we make want to be done from + // top left to bottom right, to minimise fill-in + + // available[i] is true if an external-row can be mapped to internal-row "i" + // map[i] gives the internal-row for external-row i + bool available[m_size]; + for ( uint i=0; i<m_size; i++ ) + { + available[i] = true; + map[i] = -1; + } + + // This loop looks through columns and rows to find any swaps that are necessary + // (e.g. only one matrix-element in that row/column), and if no necessary swaps + // were found, then it will swap two rows according to criteria given below + bool badMap = false; + bool changed; + do + { + changed = false; + + // Pass through columns + int E,N; + uint highest = 0; + for ( uint j=0; j<m_size; j++ ) + { + if ( map[j] == -1 ) // If we haven't mapped this column yet + { + int count = 0; // Number of "spare" elements + int element; // Last element that is "spare", only applicable if count=1 + for ( uint i=0; i<m_size; i++ ) + { + if ( available[i] && (*m_map)[i][j] ) + { + count++; + element = i; + } + } + if ( count == 0 ) { + badMap = true; + } + else if ( count == 1 ) + { + const uint newType = (*m_map)[element][j]; + if ( typeCmp( newType, highest) ) + { + E=element; + N=j; + highest=newType; + } + } + } + } + // Pass through rows + for ( uint i=0; i<m_size; i++ ) + { + if ( map[i] == -1 ) // If we haven't mapped this row yet + { + int count = 0; // Number of "spare" elements + int element; // Last element that is "spare", only applicable if count=1 + for ( uint j=0; j<m_size; j++ ) + { + if ( available[j] && (*m_map)[i][j] ) + { + count++; + element = j; + } + } + if ( count == 0 ) { + badMap = true; + } + else if ( count == 1 ) + { + const uint newType = (*m_map)[i][element]; + if ( typeCmp( newType, highest) ) + { + E=element; + N=i; + highest=newType; + } + } + } + } + if (highest) + { + available[E] = false; + map[N] = E; + changed = true; + } + if (!changed) + { + int next = -1; // next is the row to mapped to (interally) + uint j=0; + + /// TODO We want to change this search to one that finds a swap, taking into acocunt the priorities given below + while ( next == -1 && j<m_size ) + { + if ( available[j] ) next=j; + j++; + } + uint i=0; + while ( i<m_size && map[i] != -1 ) i++; + if ( next != -1 && i < m_size ) + { + available[next] = false; + map[i] = next; + changed = true; + } + } + } while (changed); + + if (badMap) + { +// cerr << "Map::createMap: unable to create decent mapping; do not trust the matrix, Neo!"<<endl; + } + + for ( int i = 0; i < int(m_size); ++i ) + { + assert( map[i] >= 0 && map[i] < int(m_size) ); + } + + // Ignore this, for now: + + // Now, we want to order the matrix, with the following priorities: + // (1) How often values change + // (2) How few values there are + // (3) How large the values are + // For each value in the column, +} + + +bool Map::typeCmp( const uint t1, const uint t2 ) +{ + if (!t2) return true; + if (!t1) return false; + + int t1_score = 1; + if ( t1 | Map::et_constant ) t1_score += 64; + else if ( t1 | Map::et_stable ) t1_score += 16; + else if ( t1 | Map::et_variable ) t1_score += 4; + + int t2_score = 1; + if ( t2 | Map::et_constant ) t2_score += 64; + else if ( t2 | Map::et_stable ) t2_score += 16; + else if ( t2 | Map::et_variable ) t2_score += 4; + + if ( t1 | Map::et_big ) t1_score *= 2; + if ( t2 | Map::et_big ) t2_score *= 2; + + return ( t1_score >= t2_score ); +} + + +Matrix22::Matrix22() +{ + reset(); +} + +bool Matrix22::solve() +{ + const double old_x1 = m_x1; + const double old_x2 = m_x2; + + const bool e11 = std::abs((m_a11))<epsilon; + const bool e12 = std::abs((m_a12))<epsilon; + const bool e21 = std::abs((m_a21))<epsilon; + const bool e22 = std::abs((m_a22))<epsilon; + + if (e11) + { + if ( e12||e21 ) + return false; + m_x2 = m_b1/m_a12; + m_x1 = (m_b2-(m_a22*m_x2))/m_a21; + } + else if (e12) + { + if ( e11||e22 ) + return false; + m_x1 = m_b1/m_a11; + m_x2 = (m_b2-(m_a21*m_x1))/m_a22; + } + else if (e21) + { + if ( e11||e22 ) + return false; + m_x2 = m_b2/m_a22; + m_x1 = (m_b1-(m_a12*m_x2))/m_a11; + } + else if (e22) + { + if ( e12||e21 ) + return false; + m_x1 = m_b2/m_a21; + m_x2 = (m_b1-(m_a11*m_x1))/m_a12; + } + else + { + m_x2 = (m_b2-(m_a21*m_b1/m_a11))/(m_a22-(m_a21*m_a12/m_a11)); + m_x1 = (m_b1-(m_a12*m_x2))/m_a11; + } + if ( !std::isfinite(m_x1) || !std::isfinite(m_x2) ) + { + m_x1 = old_x1; + m_x2 = old_x2; + return false; + } + return true; +} + +void Matrix22::reset() +{ + m_a11=m_a12=m_a21=m_a22=0.; + m_b1=m_b2=0.; + m_x1=m_x2=0.; +} + + + diff --git a/src/electronics/simulation/matrix.h b/src/electronics/simulation/matrix.h new file mode 100644 index 0000000..4c3e518 --- /dev/null +++ b/src/electronics/simulation/matrix.h @@ -0,0 +1,248 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef MATRIX_H +#define MATRIX_H + +#include "vec.h" + +#include <vector> + +class Map; + +typedef std::vector<std::vector<uint> > ETMap; + +/** +@short Handles row-wise permutation of matrixes +*/ +class Map +{ +public: + enum e_type + { + et_none = 0x0, // Default value, none + et_constant = 0x1, // Never changes value in lifetime of matrix + et_stable = 0x2, // Value changes occasionally, e.g. user changing resistance value + et_variable = 0x3, // Rate of changing is unknown, probably average - e.g. logic + et_unstable = 0x4, // Value changes practically every call of performLU + et_big = 0x8 // Ignore this :-) It is used to OR with one of the others, but it should only ever be accessed by the class + }; + Map( const uint size ); + ~Map(); + + void setUse( const uint i, const uint j, Map::e_type type, bool big ); + /** + * Generates an optimal permutation, returned in the given array + */ + void createMap( int *map ); + /** + * Resets the info to a blank pattern + */ + void reset(); + +protected: + /** + * Compares two "types", returning true if t1 >= t2, else false + */ + bool typeCmp( const uint t1, const uint t2 ); + +private: + ETMap *m_map; + uint m_size; +}; + +/** +This class performs matrix storage, lu decomposition, forward and backward +substitution, and a few other useful operations. Steps in using class: +(1) Create an instance of this class with the correct size +(2) Define the matrix pattern as neccessary: + (1) Call zero (unnecessary after initial ceration) to reset the pattern + & matrix + (2) Call setUse to set the use of each element in the matrix + (3) Call createMap to generate the row-wise permutation mapping for use + in partial pivoting +(3) Add the values to the matrix +(4) Call performLU, and get the results with fbSub +(5) Repeat 2, 3, 4 or 5 as necessary. +@todo We need to allow createMap to work while the matrix has already been initalised +@short Matrix manipulation class tailored for circuit equations +@author David Saxton +*/ +class Matrix +{ +public: + /** + * Creates a size x size square matrix m, with all values zero, + * and a right side vector x of size m+n + */ + Matrix( uint n, uint m ); + ~Matrix(); + /** + * Sets all elements to zero + */ + void zero(); + /** + * Sets the type of (matrix-) element at the position i, j. + * @param type Describes how often the value changes + * @param big Set this true if the value is likely to be >= 1, else false + */ + void setUse( const uint i, const uint j, Map::e_type type, bool big ); + void setUse_b( const uint i, const uint j, Map::e_type type, bool big ) + { + setUse( i, j+m_n, type, big ); + } + void setUse_c( const uint i, const uint j, Map::e_type type, bool big ) + { + setUse( i+m_n, j, type, big ); + } + void setUse_d( const uint i, const uint j, Map::e_type type, bool big ) + { + setUse( i+m_n, j+m_n, type, big ); + } + /** + * Generates the row-wise permutation mapping from the values set by setUse + */ + void createMap(); + /** + * Returns true if the matrix is changed since last calling performLU() + * - i.e. if we do need to call performLU again. + */ + inline bool isChanged() const { return max_k < m_size; } + /** + * Performs LU decomposition. Going along the rows, + * the value of the decomposed LU matrix depends only on + * the previous values. + */ + void performLU(); + /** + * Applies the right side vector (x) to the decomposed matrix, + * with the solution returned in x. + */ + void fbSub( Vector* x ); + /** + * Prints the matrix to stdout + */ + void displayMatrix(); + /** + * Prints the LU-decomposed matrix to stdout + */ + void displayLU(); + /** + * Sets the element matrix at row i, col j to value x + */ + double& g( uint i, uint j ) + { + i = m_inMap[i]; + if ( i<max_k ) max_k=i; + if ( j<max_k ) max_k=j; + + // I think I need the next line... + if ( max_k>0 ) max_k--; + + return (*m_mat)[i][j]; + } + double& b( uint i, uint j ) { return g( i, j+m_n ); } + double& c( uint i, uint j ) { return g( i+m_n, j ); } + double& d( uint i, uint j ) { return g( i+m_n, j+m_n ); } + const double g( uint i, uint j ) const { return (*m_mat)[m_inMap[i]][j]; } + const double b( uint i, uint j ) const { return g( i, j+m_n ); } + const double c( uint i, uint j ) const { return g( i+m_n, j ); } + const double d( uint i, uint j ) const { return g( i+m_n, j+m_n ); } + /** + * Returns the value of matrix at row i, col j. + */ + const double m( uint i, uint j ) const + { + return (*m_mat)[m_inMap[i]][j]; + } + /** + * Multiplies this matrix by the Vector rhs, and places the result + * in the vector pointed to by result. Will fail if wrong size. + */ + void multiply( Vector *rhs, Vector *result ); + /** + * Sets the values of this matrix to that of the given matrix + */ + void operator=( Matrix *const m ); + /** + * Adds the values of the given matrix to this matrix + */ + void operator+=( Matrix *const m ); + +private: + /** + * Swaps around the rows in the (a) the matrix; and (b) the mappings + */ + void swapRows( const uint a, const uint b ); + +// // Generates m_outMap from m_inMap +// void genOutMap(); + + uint m_n; + uint m_m; + uint m_size; + uint max_k; + + int *m_inMap; // Rowwise permutation mapping from external reference to internal storage +// int *m_outMap; // Opposite of m_inMap + + matrix *m_mat; + matrix *m_lu; + double *m_y; // Avoids recreating it lots of times + Map *m_map; +}; + + +/** +This class provides a very simple, lightweight, 2x2 matrix solver. +It's fast and reliable. Set the values for the entries of A and b: + +A x = b + +call solve() (which returns true if successful - i.e. exactly one solution to the +matrix), and get the values of x with the appropriate functions. + +@short 2x2 Matrix +@author David Saxton +*/ +class Matrix22 +{ +public: + Matrix22(); + + double &a11() { return m_a11; } + double &a12() { return m_a12; } + double &a21() { return m_a21; } + double &a22() { return m_a22; } + + double &b1() { return m_b1; } + double &b2() { return m_b2; } + + /** + * Solve the matrix. Returns true if successful (i.e. non-singular), else + * false. Get the solution with x1() and x2(). + */ + bool solve(); + /** + * Resets all entries to zero + */ + void reset(); + + double x1() const { return m_x1; } + double x2() const { return m_x2; } + +private: + double m_a11, m_a12, m_a21, m_a22; + double m_b1, m_b2; + double m_x1, m_x2; +}; + + +#endif diff --git a/src/electronics/simulation/nonlinear.cpp b/src/electronics/simulation/nonlinear.cpp new file mode 100644 index 0000000..c975f16 --- /dev/null +++ b/src/electronics/simulation/nonlinear.cpp @@ -0,0 +1,97 @@ +/*************************************************************************** + * Copyright (C) 2003-2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "matrix.h" +#include "nonlinear.h" + +#include <cmath> +using namespace std; + +const double KTL_MAX_DOUBLE = 1.7976931348623157e+308; ///< 7fefffff ffffffff +const int KTL_MAX_EXPONENT = int( log( KTL_MAX_DOUBLE ) ); + + +NonLinear::NonLinear() + : Element() +{ +} + + +#ifndef MIN +# define MIN(x,y) (((x) < (y)) ? (x) : (y)) +#endif + + +// The function computes the exponential pn-junction current. +double NonLinear::diodeCurrent( double v, double I_S, double Vte ) const +{ + return I_S * (exp( MIN( v / Vte, KTL_MAX_EXPONENT ) ) - 1); +} + + + +double NonLinear::diodeConductance( double v, double I_S, double Vte ) const +{ + return I_S * exp( MIN( v / Vte, KTL_MAX_EXPONENT ) ) / Vte; +} + + + +double NonLinear::diodeVoltage( double V, double V_prev, double V_T, double Vcrit ) const +{ + if ( V > Vcrit && fabs( V - V_prev ) > 2 * V_T ) + { + if ( V_prev > 0 ) + { + double arg = (V - V_prev) / V_T; + if (arg > 0) + V = V_prev + V_T * (2 + log( arg - 2 )); + else + V = V_prev - V_T * (2 + log( 2 - arg )); + } + else + V = (V_prev < 0) ? (V_T * log (V / V_T)) : Vcrit; + } + else + { + if ( V < 0 ) + { + double arg = (V_prev > 0) ? (-1 - V_prev) : (2 * V_prev - 1); + if (V < arg) + V = arg; + } + } + return V; +} + + +double NonLinear::diodeCriticalVoltage( double I_S, double V_Te ) const +{ + return V_Te * log( V_Te / M_SQRT2 / I_S ); +} + + +void NonLinear::diodeJunction( double V, double I_S, double V_Te, double * I, double * g ) const +{ + if (V < -3 * V_Te) + { + double a = 3 * V_Te / (V * M_E); + a = a * a * a; + *I = -I_S * (1 + a); + *g = +I_S * 3 * a / V; + } + else + { + double e = exp( MIN( V / V_Te, KTL_MAX_EXPONENT ) ); + *I = I_S * (e - 1); + *g = I_S * e / V_Te; + } +} + diff --git a/src/electronics/simulation/nonlinear.h b/src/electronics/simulation/nonlinear.h new file mode 100644 index 0000000..422f3a4 --- /dev/null +++ b/src/electronics/simulation/nonlinear.h @@ -0,0 +1,48 @@ +/*************************************************************************** + * Copyright (C) 2003-2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef NONLINEAR_H +#define NONLINEAR_H + +#include "element.h" + +/** +@short Represents a non-linear circuit element (such as a diode) +@author David Saxton +*/ +class NonLinear : public Element +{ + public: + NonLinear(); + + virtual bool isNonLinear() { return true; } + /** + * Newton-Raphson iteration: Update equation system. + */ + virtual void update_dc() = 0; + + protected: + double diodeCurrent( double v, double I_S, double Vte ) const; + /** + * Conductance of the diode - the derivative of Schockley's + * approximation. + */ + double diodeConductance( double v, double I_S, double Vte ) const; + /** + * Limits the diode voltage to prevent divergence in the nonlinear + * iterations. + */ + double diodeVoltage( double v, double V_prev, double Vt, double V_crit ) const; + void diodeJunction( double v, double I_S, double Vte, double * I, double * g ) const; + + double diodeCriticalVoltage( double I_S, double Vte ) const; +}; + +#endif diff --git a/src/electronics/simulation/opamp.cpp b/src/electronics/simulation/opamp.cpp new file mode 100644 index 0000000..45fbf02 --- /dev/null +++ b/src/electronics/simulation/opamp.cpp @@ -0,0 +1,77 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "elementset.h" +#include "matrix.h" +#include "opamp.h" + +OpAmp::OpAmp() + : Element::Element() +{ + m_numCBranches = 1; + m_numCNodes = 3; +} + + +OpAmp::~OpAmp() +{ +} + + +void OpAmp::add_map() +{ + if (!b_status) + return; + + if ( !p_cnode[0]->isGround ) + { + // Non-inverting input + p_A->setUse_c( p_cbranch[0]->n(), p_cnode[0]->n(), Map::et_constant, true ); + } + + if ( !p_cnode[2]->isGround ) + { + // Inverting input + p_A->setUse_c( p_cbranch[0]->n(), p_cnode[2]->n(), Map::et_constant, true ); + } + + if ( !p_cnode[1]->isGround ) + { + // Output + p_A->setUse_b( p_cnode[1]->n(), p_cbranch[0]->n(), Map::et_constant, true ); + } +} + + +void OpAmp::add_initial_dc() +{ + if (!b_status) + return; + + // Non-inverting input + A_c( 0, 0 ) = 1; + + // Inverting input + A_c( 0, 2 ) = -1; + + // Output + A_b( 1, 0 ) = 1; +} + + +void OpAmp::updateCurrents() +{ + if (!b_status) return; + m_cnodeI[0] = m_cnodeI[2] = 0.0; + m_cnodeI[1] = p_cbranch[0]->i; +} + + + diff --git a/src/electronics/simulation/opamp.h b/src/electronics/simulation/opamp.h new file mode 100644 index 0000000..22fe843 --- /dev/null +++ b/src/electronics/simulation/opamp.h @@ -0,0 +1,36 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef OPAMP_H +#define OPAMP_H + +#include <element.h> + +/** +node 0: non-inverting input +node 1: output +node 2: inverting input +@author David Saxton +*/ +class OpAmp : public Element +{ + public: + OpAmp(); + virtual ~OpAmp(); + + virtual Type type() const { return Element_OpAmp; } + virtual void add_map(); + + protected: + virtual void updateCurrents(); + virtual void add_initial_dc(); +}; + +#endif diff --git a/src/electronics/simulation/reactive.cpp b/src/electronics/simulation/reactive.cpp new file mode 100644 index 0000000..83fcfd4 --- /dev/null +++ b/src/electronics/simulation/reactive.cpp @@ -0,0 +1,36 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + + +#include "reactive.h" + +Reactive::Reactive( const double delta ) + : Element() +{ + m_delta = delta; +} + + +Reactive::~Reactive() +{ +} + + +void Reactive::setDelta( double delta ) +{ + m_delta = delta; + updateStatus(); +} + + +bool Reactive::updateStatus() +{ + return Element::updateStatus(); +} diff --git a/src/electronics/simulation/reactive.h b/src/electronics/simulation/reactive.h new file mode 100644 index 0000000..1142b34 --- /dev/null +++ b/src/electronics/simulation/reactive.h @@ -0,0 +1,42 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef REACTIVE_H +#define REACTIVE_H + +#include "element.h" + +/** +@short Represents a reactive element (such as a capacitor) +@author David Saxton +*/ +class Reactive : public Element +{ +public: + Reactive( const double delta ); + virtual ~Reactive(); + + virtual bool isReactive() { return true; } + /** + * Call this function to set the time period (in seconds) + */ + void setDelta( double delta ); + /** + * Called on every time step for the element to update itself + */ + virtual void time_step() = 0; + +protected: + virtual bool updateStatus(); + + double m_delta; // Delta time interval +}; + +#endif diff --git a/src/electronics/simulation/resistance.cpp b/src/electronics/simulation/resistance.cpp new file mode 100644 index 0000000..9c2d25d --- /dev/null +++ b/src/electronics/simulation/resistance.cpp @@ -0,0 +1,95 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "elementset.h" +#include "matrix.h" +#include "resistance.h" + +// #include <kdebug.h> + +Resistance::Resistance( const double resistance ) + : Element::Element() +{ + m_g = resistance < 1e-9 ? 1e9 : 1./resistance; + m_numCNodes = 2; +// kdDebug() << k_funcinfo << endl; +} + +Resistance::~Resistance() +{ +// kdDebug() << k_funcinfo << endl; +} + +void Resistance::setConductance( const double g ) +{ + if ( g == m_g ) + return; + + if (p_eSet) + p_eSet->setCacheInvalidated(); + + // Remove old resistance + m_g = -m_g; + add_initial_dc(); + + m_g = g; + add_initial_dc(); +} + + +void Resistance::setResistance( const double r ) +{ + setConductance( r < 1e-9 ? 1e9 : 1./r ); +} + + +void Resistance::add_map() +{ + if (!b_status) + return; + + if ( !p_cnode[0]->isGround ) + { + p_A->setUse( p_cnode[0]->n(), p_cnode[0]->n(), Map::et_stable, false ); + } + if ( !p_cnode[1]->isGround ) { + p_A->setUse( p_cnode[1]->n(), p_cnode[1]->n(), Map::et_stable, false ); + } + + if ( !p_cnode[0]->isGround && !p_cnode[1]->isGround ) + { + p_A->setUse( p_cnode[0]->n(), p_cnode[1]->n(), Map::et_stable, false ); + p_A->setUse( p_cnode[1]->n(), p_cnode[0]->n(), Map::et_stable, false ); + } +} + + +void Resistance::add_initial_dc() +{ + if (!b_status) return; + + A_g( 0, 0 ) += m_g; + A_g( 1, 1 ) += m_g; + A_g( 0, 1 ) -= m_g; + A_g( 1, 0 ) -= m_g; +} + + +void Resistance::updateCurrents() +{ + if (!b_status) return; + const double v=p_cnode[0]->v-p_cnode[1]->v; + m_cnodeI[1] = v*m_g; + m_cnodeI[0] = -m_cnodeI[1]; +} + + + + diff --git a/src/electronics/simulation/resistance.h b/src/electronics/simulation/resistance.h new file mode 100644 index 0000000..7e5a62e --- /dev/null +++ b/src/electronics/simulation/resistance.h @@ -0,0 +1,43 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef RESISTANCE_H +#define RESISTANCE_H + +#include "element.h" + +/** +@short Resistance +@author David saxton +*/ +class Resistance : public Element +{ +public: + Resistance( const double resistance ); + virtual ~Resistance(); + + virtual Type type() const { return Element_Resistance; } + + void setConductance( const double g ); + void setResistance( const double r ); + + double resistance() { return 1/m_g; } + double conductance() { return m_g; } + virtual void add_map(); + +protected: + virtual void updateCurrents(); + virtual void add_initial_dc(); + +private: + double m_g; // Conductance +}; + +#endif diff --git a/src/electronics/simulation/vccs.cpp b/src/electronics/simulation/vccs.cpp new file mode 100644 index 0000000..7ff1bc1 --- /dev/null +++ b/src/electronics/simulation/vccs.cpp @@ -0,0 +1,93 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "elementset.h" +#include "matrix.h" +#include "vccs.h" + +VCCS::VCCS( const double gain ) + : Element::Element() +{ + m_g = gain; + m_numCBranches = 1; + m_numCNodes = 4; +} + + +VCCS::~VCCS() +{ +} + + +void VCCS::setGain( const double g ) +{ + if ( g == m_g ) return; + + if (p_eSet) + p_eSet->setCacheInvalidated(); + + // Remove old values + m_g = -m_g; + add_initial_dc(); + + // Add new values + m_g = g; + add_initial_dc(); +} + + +void VCCS::add_map() +{ + if (!b_status) return; + + if ( !p_cnode[0]->isGround ) + { + p_A->setUse_c( p_cbranch[0]->n(), p_cnode[0]->n(), Map::et_stable, false ); + } + if ( !p_cnode[1]->isGround ) + { + p_A->setUse_c( p_cbranch[0]->n(), p_cnode[1]->n(), Map::et_stable, false ); + } + if ( !p_cnode[2]->isGround ) + { + p_A->setUse_b( p_cnode[2]->n(), p_cbranch[0]->n(), Map::et_constant, true ); + p_A->setUse_c( p_cbranch[0]->n(), p_cnode[2]->n(), Map::et_constant, true ); + } + if ( !p_cnode[3]->isGround ) + { + p_A->setUse_b( p_cnode[3]->n(), p_cbranch[0]->n(), Map::et_constant, true ); + p_A->setUse_c( p_cbranch[0]->n(), p_cnode[3]->n(), Map::et_constant, true ); + } +} + + +void VCCS::add_initial_dc() +{ + if (!b_status) + return; + + A_g( 2, 0 ) += m_g; + A_g( 3, 0 ) -= m_g; + A_g( 2, 1 ) -= m_g; + A_g( 3, 1 ) += m_g; +} + + +void VCCS::updateCurrents() +{ + if (!b_status) + return; + + m_cnodeI[0] = m_cnodeI[1] = 0.; + m_cnodeI[3] = (p_cnode[0]->v-p_cnode[1]->v)*m_g; + m_cnodeI[2] = -m_cnodeI[3]; +} + + diff --git a/src/electronics/simulation/vccs.h b/src/electronics/simulation/vccs.h new file mode 100644 index 0000000..26f1101 --- /dev/null +++ b/src/electronics/simulation/vccs.h @@ -0,0 +1,40 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef VCCS_H +#define VCCS_H + +#include "element.h" + +/** +CNodes n0 and n1 are used for the voltage control. +CNodes n2 and n3 are used for the current output. +@short Voltage Controlled Current Source +@author David Saxton +*/ +class VCCS : public Element +{ +public: + VCCS( const double gain ); + virtual ~VCCS(); + + virtual Type type() const { return Element_VCCS; } + void setGain( const double g ); + virtual void add_map(); + +protected: + virtual void updateCurrents(); + virtual void add_initial_dc(); + +private: + double m_g; // Conductance +}; + +#endif diff --git a/src/electronics/simulation/vcvs.cpp b/src/electronics/simulation/vcvs.cpp new file mode 100644 index 0000000..68604df --- /dev/null +++ b/src/electronics/simulation/vcvs.cpp @@ -0,0 +1,93 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "elementset.h" +#include "matrix.h" +#include "vcvs.h" + +VCVS::VCVS( const double gain ) + : Element::Element() +{ + m_g = gain; + m_numCBranches = 1; + m_numCNodes = 4; +} + + +VCVS::~VCVS() +{ +} + + +void VCVS::setGain( const double g ) +{ + if ( g == m_g ) + return; + + if (p_eSet) + p_eSet->setCacheInvalidated(); + + m_g = -m_g; + add_initial_dc(); + + m_g = g; + add_initial_dc(); +} + + +void VCVS::add_map() +{ + if (!b_status) + return; + + if ( !p_cnode[0]->isGround ) + { + p_A->setUse_c( p_cbranch[0]->n(), p_cnode[0]->n(), Map::et_stable, false ); + } + if ( !p_cnode[1]->isGround ) + { + p_A->setUse_c( p_cbranch[0]->n(), p_cnode[1]->n(), Map::et_stable, false ); + } + if ( !p_cnode[2]->isGround ) + { + p_A->setUse_b( p_cnode[2]->n(), p_cbranch[0]->n(), Map::et_constant, true ); + p_A->setUse_c( p_cbranch[0]->n(), p_cnode[2]->n(), Map::et_constant, true ); + } + if ( !p_cnode[3]->isGround ) + { + p_A->setUse_b( p_cnode[3]->n(), p_cbranch[0]->n(), Map::et_constant, true ); + p_A->setUse_c( p_cbranch[0]->n(), p_cnode[3]->n(), Map::et_constant, true ); + } +} + + +void VCVS::add_initial_dc() +{ + if (!b_status) + return; + + A_c( 0, 0 ) -= m_g; + A_c( 0, 1 ) += m_g; + A_b( 2, 0 ) = 1; + A_c( 0, 2 ) = 1; + A_b( 3, 0 ) = -1; + A_c( 0, 3 ) = -1; +} + + +void VCVS::updateCurrents() +{ + if (!b_status) return; + m_cnodeI[0] = m_cnodeI[1] = 0.; + m_cnodeI[3] = p_cbranch[0]->i; + m_cnodeI[2] = -m_cnodeI[3]; +} + + diff --git a/src/electronics/simulation/vcvs.h b/src/electronics/simulation/vcvs.h new file mode 100644 index 0000000..862dea9 --- /dev/null +++ b/src/electronics/simulation/vcvs.h @@ -0,0 +1,40 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef VCVS_H +#define VCVS_H + +#include "element.h" + +/** +Voltage source between nodes c2 and c3 +Controlling voltage between nodes c0 and c1 +@short Voltage Controlled Voltage Source +@author David Saxton +*/ +class VCVS : public Element +{ +public: + VCVS( const double gain ); + virtual ~VCVS(); + + virtual Type type() const { return Element_VCVS; } + void setGain( const double g ); + virtual void add_map(); + +protected: + virtual void updateCurrents(); + virtual void add_initial_dc(); + +private: + double m_g; // Conductance +}; + +#endif diff --git a/src/electronics/simulation/vec.cpp b/src/electronics/simulation/vec.cpp new file mode 100644 index 0000000..d45f505 --- /dev/null +++ b/src/electronics/simulation/vec.cpp @@ -0,0 +1,166 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "vec.h" + +#include <assert.h> +#include <cmath> +#include <string.h> +using namespace std; + +Vector::Vector( const int size ) +{ + m_size = size; + m_vec = new double[m_size]; + reset(); + b_changed = true; +} + + +Vector::~Vector() +{ + // Hmm...this looks like it's the correct format, although valgrind complains + // about improper memory use. Interesting. "delete m_vec" definitely leaks + // memory, so this seems like the lesser of two evils. I miss C memory allocation + // somtimes, with a nice simple free :p + delete [] m_vec; +} + + +void Vector::reset() +{ + for ( int i=0; i<m_size; i++ ) + { + m_vec[i] = 0.; + } + b_changed = true; +} + + +void Vector::limitTo( double scaleMax, Vector * limitVector ) +{ + assert( limitVector ); + assert( limitVector->size() == size() ); + + for ( int i = 0; i < m_size; ++i ) + { + double limitAbs = std::abs( limitVector->m_vec[i] ); + if ( limitAbs < 1e-6 ) + limitAbs = 1e-6; + + double thisAbs = std::abs( m_vec[i] ); + if ( thisAbs < 1e-6 ) + thisAbs = 1e-6; + + if ( (thisAbs / limitAbs) > scaleMax ) + m_vec[i] /= (thisAbs / limitAbs); + + else if ( (limitAbs / thisAbs) > scaleMax ) + m_vec[i] /= (limitAbs / thisAbs); + } + b_changed = true; +} + + +void Vector::operator += ( Vector *rhs ) +{ + if (!rhs) return; + for ( int i=0; i<m_size; i++ ) + { + m_vec[i] += (*rhs)[i]; + } + b_changed = true; +} + + +void Vector::operator -= ( Vector *rhs ) +{ + if (!rhs) return; + for ( int i=0; i<m_size; i++ ) + { + m_vec[i] -= (*rhs)[i]; + } + b_changed = true; +} + + +void Vector::operator *=( double s ) +{ + for ( int i=0; i<m_size; i++ ) + { + m_vec[i] *= s; + } + b_changed = true; +} + + +void Vector::operator = ( Vector& v ) +{ + assert( size() == v.size() ); + memcpy( m_vec, v.m_vec, m_size * sizeof( double ) ); + b_changed = true; +} + + +void Vector::negative( Vector *rhs ) +{ + if (!rhs) return; + for ( int i=0; i<m_size; i++ ) + { + m_vec[i] = -(*rhs)[i]; + } + b_changed = true; +} + + +double Vector::abs() const +{ + double s=0; + for ( int i=0; i<m_size; i++ ) + { + s += m_vec[i]*m_vec[i]; + } + return sqrt(s); +} + + + +// matrix stuff... + +matrix::matrix( const uint size ) +{ + m_size = size; + m_mat = new Vector*[m_size]; + for ( uint i=0; i<m_size; ++i ) + { + m_mat[i] = new Vector(m_size); + } +} + +matrix::~matrix() +{ + for ( uint i=0; i<m_size; ++i ) + { + delete m_mat[i]; + } + delete [] m_mat; +} + + +void matrix::swapRows( const uint a, const uint b ) +{ + if ( a == b ) return; + Vector *v = m_mat[a]; + m_mat[a] = m_mat[b]; + m_mat[b] = v; +} + + + diff --git a/src/electronics/simulation/vec.h b/src/electronics/simulation/vec.h new file mode 100644 index 0000000..7192bb3 --- /dev/null +++ b/src/electronics/simulation/vec.h @@ -0,0 +1,109 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef VEC_H +#define VEC_H + +typedef unsigned uint; + +/** +@short Vector of doubles, faster than STL vector +@author David Saxton +*/ +class Vector +{ +public: + Vector( const int size ); + ~Vector(); + + double & operator[]( const int i ) { b_changed=true; return m_vec[i]; } + const double operator[]( const int i ) const { return m_vec[i]; } + int size() const { return m_size; } + /** + * Resets all values to 0 + */ + void reset(); + /** + * Limits the absolute value of each component of this vector to scaleMax + * times bigger or smaller than the components of limitVector. + */ + void limitTo( double scaleMax, Vector * limitVector ); + /** + * Adds the Vector rhs to this + */ + void operator+=( Vector *rhs ); + /** + * Subtracts the Vector rhs from this + */ + void operator-=( Vector *rhs ); + /** + * Multiplies this Vector by the given scaler constant + */ + void operator*=( double s ); + /** + * Sets this vector equal to the given vector + */ + void operator=( Vector& v ); + /** + * Copies the negative values of the given vector to this vector. + * (i.e. sets this = -rhs ) + */ + void negative( Vector *rhs ); + /** + * Returns the absolute value of this vector, defined as the squareroot + * of the sum of the square of each element of the vector + */ + double abs() const; + /** + * Returns true if the vector has changed since setUnchanged was last called + * Note that this will return true if the vector has just been read, due to + * limitations with the [] operator. + */ + inline bool isChanged() const { return b_changed; } + /** + * Sets the changed status to false. + */ + inline void setUnchanged() { b_changed=false; } + +private: + Vector( const Vector & ); + Vector & operator= ( const Vector & ); + + bool b_changed; + double *m_vec; + int m_size; +}; + +/** +@short Container for Vector of Vectors +@author David Saxton +*/ +class matrix +{ +public: + matrix( const uint size ); + ~matrix(); + + Vector & operator[]( const uint i ) { return *(m_mat[i]); } + const Vector & operator[]( const uint i ) const { return *(m_mat[i]); } + /** + * Swaps the pointers to the given rows + */ + void swapRows( const uint a, const uint b ); + +private: + matrix( const matrix & ); + matrix & operator= ( const matrix & ); + + Vector **m_mat; + uint m_size; +}; + +#endif diff --git a/src/electronics/simulation/voltagepoint.cpp b/src/electronics/simulation/voltagepoint.cpp new file mode 100644 index 0000000..44d3b01 --- /dev/null +++ b/src/electronics/simulation/voltagepoint.cpp @@ -0,0 +1,73 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "matrix.h" +#include "voltagepoint.h" +#include "elementset.h" + +VoltagePoint::VoltagePoint( const double voltage ) + : Element::Element() +{ + m_voltage = -voltage; + m_numCBranches = 1; + m_numCNodes = 1; +} + + +VoltagePoint::~VoltagePoint() +{ +} + + +void VoltagePoint::setVoltage( const double v ) +{ + if ( -v == m_voltage ) return; + + if (p_eSet) + p_eSet->setCacheInvalidated(); + + m_voltage = -v; + add_initial_dc(); +} + + +void VoltagePoint::add_map() +{ + if (!b_status) return; + + if ( !p_cnode[0]->isGround ) + { + p_A->setUse_b( p_cnode[0]->n(), p_cbranch[0]->n(), Map::et_constant, true ); + p_A->setUse_c( p_cbranch[0]->n(), p_cnode[0]->n(), Map::et_constant, true ); + } +} + + +void VoltagePoint::add_initial_dc() +{ + if (!b_status) return; + + A_b( 0, 0 ) = -1; + A_c( 0, 0 ) = -1; + + b_v( 0 ) = m_voltage; +} + + +void VoltagePoint::updateCurrents() +{ + if (!b_status) return; + m_cnodeI[0] = p_cbranch[0]->i; +} + + + + + diff --git a/src/electronics/simulation/voltagepoint.h b/src/electronics/simulation/voltagepoint.h new file mode 100644 index 0000000..924b0af --- /dev/null +++ b/src/electronics/simulation/voltagepoint.h @@ -0,0 +1,39 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef VOLTAGEPOINT_H +#define VOLTAGEPOINT_H + +#include "element.h" + +/** +@short VoltagePoint +@author David saxton +*/ +class VoltagePoint : public Element +{ +public: + VoltagePoint( const double voltage ); + virtual ~VoltagePoint(); + + virtual Type type() const { return Element_VoltagePoint; } + void setVoltage( const double voltage ); + double voltage() { return m_voltage; } + virtual void add_map(); + +protected: + virtual void updateCurrents(); + virtual void add_initial_dc(); + +private: + double m_voltage; // Conductance +}; + +#endif diff --git a/src/electronics/simulation/voltagesignal.cpp b/src/electronics/simulation/voltagesignal.cpp new file mode 100644 index 0000000..d7a5839 --- /dev/null +++ b/src/electronics/simulation/voltagesignal.cpp @@ -0,0 +1,79 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "matrix.h" +#include "voltagesignal.h" +#include "elementset.h" + +VoltageSignal::VoltageSignal( const double delta, const double voltage ) + : Reactive::Reactive(delta) +{ + m_voltage = voltage; + m_numCNodes = 2; + m_numCBranches = 1; +} + + +VoltageSignal::~VoltageSignal() +{ +} + + +void VoltageSignal::setVoltage( const double v ) +{ + m_voltage = v; +} + + +void VoltageSignal::add_map() +{ + if (!b_status) return; + + if ( !p_cnode[0]->isGround ) + { + p_A->setUse_b( p_cnode[0]->n(), p_cbranch[0]->n(), Map::et_constant, true ); + p_A->setUse_c( p_cbranch[0]->n(), p_cnode[0]->n(), Map::et_constant, true ); + } + + if ( !p_cnode[1]->isGround ) + { + p_A->setUse_b( p_cnode[1]->n(), p_cbranch[0]->n(), Map::et_constant, true ); + p_A->setUse_c( p_cbranch[0]->n(), p_cnode[1]->n(), Map::et_constant, true ); + } +} + + +void VoltageSignal::add_initial_dc() +{ + if (!b_status) + return; + + A_b( 0, 0 ) = -1; + A_c( 0, 0 ) = -1; + A_b( 1, 0 ) = 1; + A_c( 0, 1 ) = 1; +} + + +void VoltageSignal::time_step() +{ + if (!b_status) return; + b_v( 0 ) = m_voltage*advance(); +} + + +void VoltageSignal::updateCurrents() +{ + if (!b_status) return; + m_cnodeI[1] = p_cbranch[0]->i; + m_cnodeI[0] = -m_cnodeI[1]; +} + + diff --git a/src/electronics/simulation/voltagesignal.h b/src/electronics/simulation/voltagesignal.h new file mode 100644 index 0000000..5e35e72 --- /dev/null +++ b/src/electronics/simulation/voltagesignal.h @@ -0,0 +1,41 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef VOLTAGESIGNAL_H +#define VOLTAGESIGNAL_H + +#include "reactive.h" +#include "elementsignal.h" + +/** +@short VoltageSignal +@author David saxton +*/ +class VoltageSignal : public Reactive, public ElementSignal +{ +public: + VoltageSignal( const double delta, const double voltage ); + virtual ~VoltageSignal(); + + virtual Element::Type type() const { return Element_VoltageSignal; } + void setVoltage( const double voltage ); + double voltage() { return m_voltage; } + virtual void time_step(); + virtual void add_map(); + +protected: + virtual void updateCurrents(); + virtual void add_initial_dc(); + +private: + double m_voltage; // Voltage +}; + +#endif diff --git a/src/electronics/simulation/voltagesource.cpp b/src/electronics/simulation/voltagesource.cpp new file mode 100644 index 0000000..b4fca64 --- /dev/null +++ b/src/electronics/simulation/voltagesource.cpp @@ -0,0 +1,77 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "matrix.h" +#include "voltagesource.h" +#include "elementset.h" + +VoltageSource::VoltageSource( const double voltage ) + : Element::Element() +{ + m_v = voltage; + m_numCBranches = 1; + m_numCNodes = 2; +} + +VoltageSource::~VoltageSource() +{ +} + +void VoltageSource::setVoltage( const double v ) +{ + if ( m_v == v ) return; + + if (p_eSet) + p_eSet->setCacheInvalidated(); + + m_v = v; + add_initial_dc(); +} + + +void VoltageSource::add_map() +{ + if (!b_status) return; + + if ( !p_cnode[0]->isGround ) + { + p_A->setUse_b( p_cnode[0]->n(), p_cbranch[0]->n(), Map::et_constant, true ); + p_A->setUse_c( p_cbranch[0]->n(), p_cnode[0]->n(), Map::et_constant, true ); + } + + if ( !p_cnode[1]->isGround ) + { + p_A->setUse_b( p_cnode[1]->n(), p_cbranch[0]->n(), Map::et_constant, true ); + p_A->setUse_c( p_cbranch[0]->n(), p_cnode[1]->n(), Map::et_constant, true ); + } +} + + +void VoltageSource::add_initial_dc() +{ + if (!b_status) + return; + + A_b( 0, 0 ) = -1; + A_c( 0, 0 ) = -1; + A_b( 1, 0 ) = 1; + A_c( 0, 1 ) = 1; + + b_v( 0 ) = m_v; +} + +void VoltageSource::updateCurrents() +{ + if (!b_status) return; + m_cnodeI[0] = p_cbranch[0]->i; + m_cnodeI[1] = -m_cnodeI[0]; +} + + diff --git a/src/electronics/simulation/voltagesource.h b/src/electronics/simulation/voltagesource.h new file mode 100644 index 0000000..9b27b0d --- /dev/null +++ b/src/electronics/simulation/voltagesource.h @@ -0,0 +1,38 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef VOLTAGESOURCE_H +#define VOLTAGESOURCE_H + +#include "element.h" + +/** +CNode n0 is the negative terminal, CNode n1 is the positive terminal +@short Voltage Source +*/ +class VoltageSource : public Element +{ +public: + VoltageSource( const double voltage ); + virtual ~VoltageSource(); + + virtual Type type() const { return Element_VoltageSource; } + void setVoltage( const double v ); + virtual void add_map(); + +protected: + virtual void updateCurrents(); + virtual void add_initial_dc(); + +private: + double m_v; // Voltage +}; + +#endif diff --git a/src/electronics/subcircuits.cpp b/src/electronics/subcircuits.cpp new file mode 100644 index 0000000..65ae8e3 --- /dev/null +++ b/src/electronics/subcircuits.cpp @@ -0,0 +1,175 @@ +/*************************************************************************** + * Copyright (C) 2004-2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "circuitdocument.h" +#include "ecsubcircuit.h" +#include "itemdocumentdata.h" +#include "itemlibrary.h" +#include "itemselector.h" +#include "subcircuits.h" + +#include <kapplication.h> +#include <kconfig.h> +#include <kdebug.h> +#include <kiconloader.h> +#include <kstandarddirs.h> +#include <qfile.h> +#include <qtextstream.h> + +Subcircuits::Subcircuits() + : QObject() +{ + connect( ComponentSelector::self(), SIGNAL(itemRemoved(const QString& )), this, SLOT(slotItemRemoved(const QString& )) ); +} + + +Subcircuits::~Subcircuits() +{ +} + + +void Subcircuits::initECSubcircuit( int subcircuitId, ECSubcircuit *ecSubcircuit ) +{ + const QString fileName = genFileName(subcircuitId); + if ( !QFile::exists(fileName) ) + { + kdDebug() << "Subcircuits::createSubcircuit: Subcircuit \""<<fileName<<"\" was not found."<<endl; + return; + } + + SubcircuitData subcircuit; + if (!subcircuit.loadData( genFileName(subcircuitId) ) ) + return; + + subcircuit.initECSubcircuit(ecSubcircuit); +} + + +ECSubcircuit* Subcircuits::createSubcircuit( int id, CircuitDocument *circuitDocument, bool newItem, const char *newId ) +{ + //I pass finishCreation = false here because the subcircuit was getting + //finished twice, causing a segfault in CircuitDocument::assignCircuits() + //--electronerd + ECSubcircuit *ecSubcircuit = static_cast<ECSubcircuit*>(itemLibrary()->createItem( "ec/subcircuit", circuitDocument, newItem, newId, false )); + ecSubcircuit->property("id")->setValue(id); + return ecSubcircuit; +} + + +void Subcircuits::loadSubcircuits() +{ + KConfig *config = kapp->config(); + config->setGroup("Subcircuits"); + + QValueList<int> idList = config->readIntListEntry("Ids"); + const QValueList<int>::iterator idListEnd = idList.end(); + for ( QValueList<int>::iterator it = idList.begin(); it != idListEnd; ++it ) + { + QFile file( genFileName(*it) ); + if ( file.open(IO_ReadOnly) == false ) + { + // File has mysteriously disappeared.... + *it = -1; + } + else + { + config->setGroup("Subcircuit_"+QString::number(*it)); + updateComponentSelector( *it, config->readEntry("Name") ); + } + file.close(); + } + idList.remove(-1); + + // Update the config file if any ids have been removed + config->setGroup("Subcircuits"); + config->writeEntry( "Ids", idList ); +} + + +QString Subcircuits::genFileName( const int nextId ) +{ + return locateLocal( "appdata", "subcircuit_"+QString::number(nextId)+".circuit" ); +} + + +void Subcircuits::updateComponentSelector( int id, const QString &name ) +{ + if ( name.isEmpty() ) + return; + + ComponentSelector::self()->addItem( name, "sc/"+QString::number(id), "Subcircuits", KGlobal::iconLoader()->loadIcon( "ktechlab_circuit", KIcon::Small ), true ); +} + + +void Subcircuits::addSubcircuit( const QString &name, const QString &subcircuitXml ) +{ + KConfig *config = kapp->config(); + config->setGroup("Subcircuits"); + + int nextId = config->readNumEntry( "NextId", 0 ); + + while ( QFile::exists( genFileName(nextId) ) ) { + nextId++; + } + + const int id = nextId; + + const QString fileName = genFileName(id); + QFile file(fileName); + + if ( file.open(IO_WriteOnly) == false ) + { + kdError() << "Subcircuits::addSubcircuit: couldn't open subcircuit save file: "<<fileName<<endl; + return; + } + + QTextStream stream(&file); + stream << subcircuitXml; + file.close(); + + QValueList<int> idList = config->readIntListEntry("Ids"); + idList += id; + config->writeEntry( "Ids", idList ); + config->writeEntry( "NextId", ++nextId ); + + config->setGroup("Subcircuit_"+QString::number(id)); + config->writeEntry( "Name", name ); + + // It's important that we write the configuration *now*, lest the subcircuits be lost + config->sync(); + + updateComponentSelector( id, name ); +} + + +void Subcircuits::slotItemRemoved( const QString &id ) +{ + if ( !id.startsWith("sc/") ) { + return; + } + + QString temp = id; + temp.remove("sc/"); + const int id_num = temp.toInt(); + const QString fileName = genFileName(id_num); + QFile file(fileName); + file.remove(); + + KConfig *config = kapp->config(); + config->setGroup("Subcircuits"); + QValueList<int> idList = config->readIntListEntry("Ids"); + idList.remove(id_num); + config->writeEntry( "Ids", idList ); +} + + +#include "subcircuits.moc" + + diff --git a/src/electronics/subcircuits.h b/src/electronics/subcircuits.h new file mode 100644 index 0000000..3f08dd9 --- /dev/null +++ b/src/electronics/subcircuits.h @@ -0,0 +1,76 @@ +/*************************************************************************** + * Copyright (C) 2004 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef SUBCIRCUITS_H +#define SUBCIRCUITS_H + +#include <qobject.h> + +class CircuitDocument; +class ECSubcircuit; +class Subcircuits; +inline Subcircuits *subcircuits(); + +/** +Interface for dealing with loading / saving / etc of subcircuits +@author David Saxton +*/ +class Subcircuits : public QObject +{ +Q_OBJECT +public: + ~Subcircuits(); + /** + * Handles subcircuit creation when the user selects the subcircuit to be + * created. + * @param id Id of subcircuit; e.g. "sc/10" + */ + static ECSubcircuit* createSubcircuit( int id, CircuitDocument *circuitDocument, bool newItem, const char *newId ); + /** + * Loads a subcircuit into a subcircuit component + */ + static void initECSubcircuit( int subcircuitId, ECSubcircuit *ecSubcircuit ); + /** + * Reads in the config entries and adds the subcircuits found to the + * component selector + */ + static void loadSubcircuits(); + /** + * Saves the given subcircuit to the appdata dir, updates the appropriate + * config entries, and adds the subcircuit to the component selector. + */ + static void addSubcircuit( const QString &name, const QString &subcircuitXml ); + /** + * returns a path to the appdata dir, e.g. genFileName(2) might return + * ~/.kde/share/apps/ktechlab/subcircuit_2.circuit + */ + static QString genFileName( const int nextId ); + /** + * Adds the given entry to the component selector + */ + static void updateComponentSelector( int id, const QString &name ); + +protected slots: + void slotItemRemoved( const QString &id ); + +private: + Subcircuits(); + + friend Subcircuits* subcircuits(); +}; + + +inline Subcircuits* subcircuits() +{ + static Subcircuits *_subcircuits = new Subcircuits(); + return _subcircuits; +} + +#endif diff --git a/src/electronics/switch.cpp b/src/electronics/switch.cpp new file mode 100644 index 0000000..7cbda70 --- /dev/null +++ b/src/electronics/switch.cpp @@ -0,0 +1,221 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "circuitdocument.h" +#include "component.h" +#include "ecnode.h" +#include "pin.h" +#include "resistance.h" +#include "simulator.h" +#include "switch.h" + +#include <kdebug.h> +#include <qtimer.h> + +#include <cmath> +#include <stdlib.h> // for rand +#include <time.h> + +Switch::Switch( Component * parent, Pin * p1, Pin * p2, State state ) +{ + m_bouncePeriod_ms = 5; + m_bBounce = false; + m_bounceStart = 0; + m_pBounceResistance = 0l; + m_pP1 = p1; + m_pP2 = p2; + m_pComponent = parent; + m_pStopBouncingTimer = new QTimer( this ); + connect( m_pStopBouncingTimer, SIGNAL(timeout()), this, SLOT(stopBouncing()) ); + + // Force update + m_state = (state == Open) ? Closed : Open; + setState(state); +} + + +Switch::~ Switch( ) +{ + if (m_pP1) + m_pP1->setSwitchConnected( m_pP2, false ); + + if (m_pP2) + m_pP2->setSwitchConnected( m_pP1, false ); +} + + +void Switch::setState( State state ) +{ + if ( m_state == state ) + return; + + m_state = state; + + if ( m_bBounce ) + startBouncing(); + else + { + // I'm being lazy...calling stopBouncing will connect the stuff + stopBouncing(); + } +} + + +void Switch::setBounce( bool bounce, int msec ) +{ + m_bBounce = bounce; + m_bouncePeriod_ms = msec; +} + + +void Switch::startBouncing() +{ + if ( m_pBounceResistance ) + { + // Already active? + return; + } + + CircuitDocument * cd = m_pComponent->circuitDocument(); + if ( !cd ) + return; + +// kdDebug() << k_funcinfo << endl; + + m_pBounceResistance = m_pComponent->createResistance( m_pP1, m_pP2, 10000 ); + m_bounceStart = Simulator::self()->time(); + Simulator::self()->attachSwitch( this ); +// kdDebug() << "m_bounceStart="<<m_bounceStart<<" m_bouncePeriod_ms="<<m_bouncePeriod_ms<<endl; + + // initialize random generator + srand ( time(NULL) ); + + // Give our bounce resistor an initial value + bounce(); +} + + +void Switch::bounce() +{ + int bounced_ms = (( Simulator::self()->time() - m_bounceStart ) * 1000) / LOGIC_UPDATE_RATE; + if ( bounced_ms >= m_bouncePeriod_ms ) + { + if ( !m_pStopBouncingTimer->isActive() ) + m_pStopBouncingTimer->start( 0, true ); + return; + } + + double g = double(rand())/double(RAND_MAX); + + // 4th power of the conductance seems to give a nice distribution + g = g * g * g * g; + + m_pBounceResistance->setConductance( g ); +} + + +void Switch::stopBouncing() +{ + Simulator::self()->detachSwitch( this ); + m_pComponent->removeElement( m_pBounceResistance, true ); + m_pBounceResistance = 0l; + + bool connected = (m_state == Closed); + + if ( m_pP1 && m_pP2 ) + { + m_pP1->setSwitchConnected( m_pP2, connected ); + m_pP2->setSwitchConnected( m_pP1, connected ); + } + + if ( CircuitDocument * cd = m_pComponent->circuitDocument() ) + cd->requestAssignCircuits(); +} + + +bool Switch::calculateCurrent() +{ + if ( !m_pP1 || !m_pP2 ) + return false; + + if ( state() == Open ) + { + m_pP1->setSwitchCurrentKnown( this ); + m_pP2->setSwitchCurrentKnown( this ); + return true; + } + + Pin * pins[2] = { m_pP1, m_pP2 }; + + double current = 0.0; + bool currentKnown = false; + + int pol; + for ( unsigned i = 0; i < 2; ++i ) + { + pol = (i == 0) ? 1 : -1; + + const WireList inputs = pins[i]->inputWireList(); + const WireList outputs = pins[i]->outputWireList(); + + currentKnown = true; + current = 0.0; + + WireList::const_iterator end = inputs.end(); + for ( WireList::const_iterator it = inputs.begin(); it != end; ++it ) + { + if ( !(*it) ) + continue; + + if ( !(*it)->currentIsKnown() ) + { + currentKnown = false; + break; + } + + current += (*it)->current(); + } + + if ( !currentKnown ) + continue; + + end = outputs.end(); + for ( WireList::const_iterator it = outputs.begin(); it != end; ++it ) + { + if ( !(*it) ) + continue; + + if ( !(*it)->currentIsKnown() ) + { + currentKnown = false; + break; + } + + current -= (*it)->current(); + } + + if ( currentKnown ) + break; + } + + if ( !currentKnown ) + return false; + + m_pP1->setSwitchCurrentKnown( this ); + m_pP2->setSwitchCurrentKnown( this ); + m_pP1->mergeCurrent( -current * pol ); + m_pP2->mergeCurrent( current * pol ); + + return true; +} + +#include "switch.moc" + + diff --git a/src/electronics/switch.h b/src/electronics/switch.h new file mode 100644 index 0000000..40247c9 --- /dev/null +++ b/src/electronics/switch.h @@ -0,0 +1,86 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef SWITCH_H +#define SWITCH_H + +#include <qguardedptr.h> +#include <qobject.h> + +class CircuitDocument; +class Component; +class Pin; +class Resistance; +class QTimer; + +/** +@author David Saxton +*/ +class Switch : public QObject +{ + Q_OBJECT + + public: + enum State + { + Open, + Closed, + }; + + Switch( Component * parent, Pin * p1, Pin * p2, State state ); + ~Switch(); + /** + * If bouncing has been set to true, then the state will not switch + * immediately to that given. + */ + void setState( State state ); + State state() const { return m_state; } + /** + * Tell the switch whether to bounce or not, for the given duration, + * when the state is changed. + */ + void setBounce( bool bounce, int msec = 5 ); + /** + * Tell the switch to continue bouncing (updates the resistance value). + * Called from the simulator. + */ + void bounce(); + /** + * Attempts to calculate the current that is flowing through the switch. + * (If all the connectors at one of the ends know their currents, then + * this switch will give the current to the pins at either end). + * @return whether it was successful. + * @see CircuitDocument::calculateConnectorCurrents + */ + bool calculateCurrent(); + + protected slots: + /** + * Called from a QTimer timeout - our bouncing period has come to an + * end. This will then fully disconnect or connect the pins depending + * on the current state. + */ + void stopBouncing(); + + protected: + void startBouncing(); + + bool m_bBounce; + int m_bouncePeriod_ms; + unsigned long long m_bounceStart; // Simulator time that bouncing started + Resistance * m_pBounceResistance; + State m_state; + Component * m_pComponent; + QGuardedPtr<Pin> m_pP1; + QGuardedPtr<Pin> m_pP2; + QTimer * m_pStopBouncingTimer; +}; + +#endif diff --git a/src/electronics/wire.cpp b/src/electronics/wire.cpp new file mode 100644 index 0000000..619295d --- /dev/null +++ b/src/electronics/wire.cpp @@ -0,0 +1,146 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "pin.h" +#include "wire.h" +#include <assert.h> + +Wire::Wire( Pin * startPin, Pin * endPin ) +{ + assert(startPin); + assert(endPin); + + m_pStartPin = startPin; + m_pEndPin = endPin; + m_current = 0.; + m_bCurrentIsKnown = false; + + m_pStartPin->addOutputWire(this); + m_pEndPin->addInputWire(this); +} + + +Wire::~Wire() +{ +} + + +bool Wire::calculateCurrent() +{ + if ( m_pStartPin->currentIsKnown() && m_pStartPin->numWires() < 2 ) + { + m_current = m_pStartPin->current(); + m_bCurrentIsKnown = true; + return true; + } + + if ( m_pEndPin->currentIsKnown() && m_pEndPin->numWires() < 2 ) + { + m_current = -m_pEndPin->current(); + m_bCurrentIsKnown = true; + return true; + } + + if ( m_pStartPin->currentIsKnown() ) + { + double i = m_pStartPin->current(); + bool ok = true; + const WireList outlist = m_pStartPin->outputWireList(); + WireList::const_iterator end = outlist.end(); + for ( WireList::const_iterator it = outlist.begin(); it != end && ok; ++it ) + { + if ( *it && (Wire*)*it != this ) + { + if ( (*it)->currentIsKnown() ) + i -= (*it)->current(); + + else + ok = false; + } + } + const WireList inlist = m_pStartPin->inputWireList(); + end = inlist.end(); + for ( WireList::const_iterator it = inlist.begin(); it != end && ok; ++it ) + { + if ( *it && (Wire*)*it != this ) + { + if ( (*it)->currentIsKnown() ) + i += (*it)->current(); + + else + ok = false; + } + } + + if (ok) + { + m_current = i; + m_bCurrentIsKnown = true; + return true; + } + } + + if ( m_pEndPin->currentIsKnown() ) + { + double i = -m_pEndPin->current(); + bool ok = true; + const WireList outlist = m_pEndPin->outputWireList(); + WireList::const_iterator end = outlist.end(); + for ( WireList::const_iterator it = outlist.begin(); it != end && ok; ++it ) + { + if ( *it && (Wire*)*it != this ) + { + if ( (*it)->currentIsKnown() ) + i += (*it)->current(); + + else + ok = false; + } + } + const WireList inlist = m_pEndPin->inputWireList(); + end = inlist.end(); + for ( WireList::const_iterator it = inlist.begin(); it != end && ok; ++it ) + { + if ( *it && (Wire*)*it != this ) + { + if ( (*it)->currentIsKnown() ) + i -= (*it)->current(); + + else + ok = false; + } + } + + if (ok) + { + m_current = i; + m_bCurrentIsKnown = true; + return true; + } + } + + m_bCurrentIsKnown = false; + return false; +} + + +double Wire::voltage() const +{ + return ( m_pStartPin->voltage() + m_pEndPin->voltage() )/2; +} + + +void Wire::setCurrentKnown( bool known ) +{ + m_bCurrentIsKnown = known; + if (!known) + m_current = 0.; +} + diff --git a/src/electronics/wire.h b/src/electronics/wire.h new file mode 100644 index 0000000..368004c --- /dev/null +++ b/src/electronics/wire.h @@ -0,0 +1,64 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef WIRE_H +#define WIRE_H + +#include <qguardedptr.h> +#include <qobject.h> + +class Pin; + +/** +@author David Saxton +*/ +class Wire : public QObject +{ + public: + Wire( Pin * startPin, Pin * endPin ); + ~Wire(); + + /** + * Attempts to calculate the current that is flowing through + * the connector. Returns true if successfuly, otherwise returns false + */ + bool calculateCurrent(); + /** + * Returns true if the current flowing through the connector is known + */ + bool currentIsKnown() const { return m_bCurrentIsKnown; } + /** + * Set whether the actual current flowing into this node is known (in some + * cases - such as this node being ground - it is not known, and so the + * value returned by current() cannot be relied on. + */ + void setCurrentKnown( bool known ); + /** + * Returns the current flowing through the connector. + * This only applies for electronic connectors + */ + double current() const { return m_current; } + /** + * Returns the voltage at the connector. This is an average of the + * voltages at either end. + */ + double voltage() const; + + Pin * startPin() const { return m_pStartPin; } + Pin * endPin() const { return m_pEndPin; } + + protected: + double m_current; + bool m_bCurrentIsKnown; + QGuardedPtr<Pin> m_pStartPin; + QGuardedPtr<Pin> m_pEndPin; +}; + +#endif diff --git a/src/error_messages_en_gb b/src/error_messages_en_gb new file mode 100644 index 0000000..2a208c2 --- /dev/null +++ b/src/error_messages_en_gb @@ -0,0 +1,49 @@ +0#Unknown statement#You have started a line with an unrecognised word. Check the statement is not misspelt, or consult the documentation for a list of available statements. + +1#Missing <code>until</code> statement after <code>repeat</code> statement.#After the closing brace of a section of repeated code, there should be <code>until <expression></code> on the next line + +2#Port not supported by target PIC#The port referred to is not recognised for the target PIC. Most port names will be of the form <code>PORTX</code>. Check which ports are supported by your PIC.<example>PORTA</example> + +3#Pin identifier was not followed by '='#The pin can accept values of high or low.<example><code>porta.3 = high</code><br><code>portb.6 = low</code></example> + +4#Pin state can only be set to 'high' or 'low#A pin cannot be set to a number. Use <code>high</code> or <code>low</code> instead of 1 or 0 respectively.<example><code>porta.3 = high</code><br><code>portb.6 = low</code></example> + +5#Port identifier can only be followed by ' = '#A port can be assigned a value between 0 and 255. <example><code>"PORTA = 6"</code> would output the number 6 on <code>PORTA</code> of the PIC.</example> + +6#Unexpected statement before '{'#There is a mistake in the syntax. + +7#Mismatched brackets#Make sure that every opening bracket <code>(</code> has a corresponding closing bracket <code>)</code>. + +8#'=' in expression, did you mean '=='?#Use the operator <code>==</code> to test for the equality of two variables. Use the operator <code>=</code> for assignment. <example><i>Incorrect: </i><code>if x = 2 then ...</code><br><i>Correct: </i><code>if x == 2 then ...</code></example> + +9#Reserved keywords must not be used as a variable name#The word used is a Microbe statement and therefore cannot be used for variable names. + +10#Nothing between operators, did you miss an operand out?#There are two operators in a row, without anything inbetween them. Remember that negative numbers can not be used in this version. <example><i>Incorrect: </i><code>3 + + 5</code></example> + +11#Missing operator or space in operand#You may have accidentally placed a space in a variable name, or missed an operator such as plus from between two expressions. + +12#Unknown variable#Make sure that the variable is initialized before first use. <example><i>Initialising a: </i> <code>a = 0</code></example> + +13#Missing 'then' in 'if' statement#The correct syntax for an 'if' statement is:<code><br>if <expression> then<br>{<br>...<br>}<br></code> + +14#Missing argument(s) from 'alias' statement#The alias statement takes 2 arguments: the targetted expression, and the alias name, respectively. <example><code><p>alias PORTA led</p></code>creates the alias <code>led</code> for <code>PORTA</code></example> + +15#Too many argument(s) in 'alias' statement#The alias statement takes 2 arguments: the targetted expression, and the alias name, respectively. <example><code><p>alias PORTA led</p></code>creates the alias <code>led</code> for <code>PORTA</code></example> + +16#Could not open included file#Check that the file exists at the path name given in the <code>include</code> statement. + +17#Number too big#Numbers must be integers in the range 0 to 255, this is because Microbe only supports 8-bit numbers. + +18#Unexpected statement after '}'#There is a mistake in the syntax. + +19#A constant expression must follow step#In a <code>for</code> statement, if a step is specified, it must be a constant expression. <example><code>for i = 0 to 10 step 3</code><br><code>for i = 30 to 0 step -(10 * 7)</code></example> + +20#Delay argument must be a constant expression#<code>delay</code> takes a single argument, the length of delay required in milliseconds. The length of delay must be a constant expression.<example><code>delay 7 * 3000</code><br><code>delay 1</code></example> + +21#High or low expected after pin expression#An expression involving a pic pin should be of the form PORTX.n is high|low.<example>PORTA.3 is high</example> + +22#Comparison operator was not recognised, perhaps you meant <=, >=, or != ?#Be careful to write comparison operators correctly, they should be <code><=</code>, <code>>=</code> and <code>!=</code>, <i>not</i> <code>=<</code>, <code>=></code> or <code>=!</code>.<example>if a >= 3 then call mysub</example> + +23#Subrountine definition before end of program#Subroutines must be defined after the <code>end</code> statement, and called from the program with <code>call subname</code>. + +255#Internal compiler error#The Microbe compiler has encountered an internal problem. Microbe is still very much in development, and it is likely that this is a genuine bug. Please report this to the authors, as detailed in the Help->About Dialog, so that it can be fixed. diff --git a/src/eventinfo.cpp b/src/eventinfo.cpp new file mode 100644 index 0000000..f6c0746 --- /dev/null +++ b/src/eventinfo.cpp @@ -0,0 +1,132 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "eventinfo.h" +#include "itemdocument.h" +#include "itemview.h" + + +EventInfo::EventInfo() +{ + reset(); +} + + +EventInfo::EventInfo( ItemView *itemView, QEvent *e ) +{ + Q_UNUSED(itemView); + Q_UNUSED(e); + reset(); +} + +EventInfo::EventInfo( ItemView *itemView, QMouseEvent *e ) +{ + pos = e->pos()/itemView->zoomLevel(); + globalPos = e->globalPos(); + isRightClick = e->button() == Qt::RightButton; + ctrlPressed = e->state() & QMouseEvent::ControlButton; + shiftPressed = e->state() & QMouseEvent::ShiftButton; + altPressed = e->state() & QMouseEvent::AltButton; + if ( ItemDocument * id = dynamic_cast<ItemDocument*>(itemView->document()) ) + qcanvasItemClickedOn = id->itemAtTop(pos); + itemRtti = qcanvasItemClickedOn ? qcanvasItemClickedOn->rtti() : ItemDocument::RTTI::None; + scrollDelta = 0; + scrollOrientation = Qt::Vertical; +} + + +EventInfo::EventInfo( ItemView *itemView, QWheelEvent *e ) +{ + pos = e->pos()/itemView->zoomLevel(); + globalPos = e->globalPos(); + isRightClick = false; + ctrlPressed = e->state() & QMouseEvent::ControlButton; + shiftPressed = e->state() & QMouseEvent::ShiftButton; + altPressed = e->state() & QMouseEvent::AltButton; + if ( ItemDocument * id = dynamic_cast<ItemDocument*>(itemView->document()) ) + qcanvasItemClickedOn = id->itemAtTop(pos); + itemRtti = qcanvasItemClickedOn ? qcanvasItemClickedOn->rtti() : ItemDocument::RTTI::None; + scrollDelta = e->delta(); + scrollOrientation = e->orientation(); + +// kdDebug() << "scrollOrientation="<<scrollOrientation<<endl; +} + + +void EventInfo::reset() +{ + isRightClick = false; + ctrlPressed = false; + shiftPressed = false; + altPressed = false; + qcanvasItemClickedOn = 0l; + itemRtti = ItemDocument::RTTI::None; + scrollDelta = 0; + scrollOrientation = Qt::Vertical; +} + + +QMouseEvent *EventInfo::mousePressEvent( int dx, int dy ) const +{ + return new QMouseEvent( QEvent::MouseButtonPress, + pos + QPoint( dx, dy ), + (isRightClick ? Qt::RightButton : Qt::LeftButton), + (isRightClick ? Qt::RightButton : Qt::LeftButton) | + (ctrlPressed ? Qt::ControlButton : 0 ) | + (shiftPressed ? Qt::ShiftButton : 0 ) | + (altPressed ? Qt::AltButton : 0 ) ); +} + + +QMouseEvent *EventInfo::mouseReleaseEvent( int dx, int dy ) const +{ + return new QMouseEvent( QEvent::MouseButtonRelease, + pos + QPoint( dx, dy ), + (isRightClick ? Qt::RightButton : Qt::LeftButton), + (isRightClick ? Qt::RightButton : Qt::LeftButton) | + (ctrlPressed ? Qt::ControlButton : 0 ) | + (shiftPressed ? Qt::ShiftButton : 0 ) | + (altPressed ? Qt::AltButton : 0 ) ); +} + + +QMouseEvent *EventInfo::mouseDoubleClickEvent( int dx, int dy ) const +{ + return new QMouseEvent( QEvent::MouseButtonDblClick, + pos + QPoint( dx, dy ), + (isRightClick ? Qt::RightButton : Qt::LeftButton), + (isRightClick ? Qt::RightButton : Qt::LeftButton) | + (ctrlPressed ? Qt::ControlButton : 0 ) | + (shiftPressed ? Qt::ShiftButton : 0 ) | + (altPressed ? Qt::AltButton : 0 ) ); +} + + +QMouseEvent *EventInfo::mouseMoveEvent( int dx, int dy ) const +{ + return new QMouseEvent( QEvent::MouseMove, + pos + QPoint( dx, dy ), + Qt::NoButton, + (ctrlPressed ? Qt::ControlButton : 0 ) | + (shiftPressed ? Qt::ShiftButton : 0 ) | + (altPressed ? Qt::AltButton : 0 ) ); +} + + +QWheelEvent *EventInfo::wheelEvent( int dx, int dy ) const +{ + return new QWheelEvent( pos + QPoint( dx, dy ), + scrollDelta, + (ctrlPressed ? Qt::ControlButton : 0 ) | + (shiftPressed ? Qt::ShiftButton : 0 ) | + (altPressed ? Qt::AltButton : 0 ), + scrollOrientation ); +} + diff --git a/src/eventinfo.h b/src/eventinfo.h new file mode 100644 index 0000000..ddea10c --- /dev/null +++ b/src/eventinfo.h @@ -0,0 +1,58 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef EVENTINFO_H +#define EVENTINFO_H + +#include <qpoint.h> + +class ItemView; + +class QCanvasItem; +class QEvent; +class QMouseEvent; +class QWheelEvent; + +/** +Contains information from for a mouse event that occured on a canvas. Like a +QMouseEvent / QEvent / QWheelEvent, but abstracted to the canvas coordinate +system, as well as holding lots of useful information. +@author David Saxton +*/ +class EventInfo +{ +public: + EventInfo(); + EventInfo( ItemView *itemView, QMouseEvent *e ); + EventInfo( ItemView *itemView, QWheelEvent *e ); + EventInfo( ItemView *itemView, QEvent *e ); + + QMouseEvent *mousePressEvent( int dx, int dy ) const; + QMouseEvent *mouseReleaseEvent( int dx, int dy ) const; + QMouseEvent *mouseDoubleClickEvent( int dx, int dy ) const; + QMouseEvent *mouseMoveEvent( int dx, int dy ) const; + QWheelEvent *wheelEvent( int dx, int dy ) const; + + QPoint pos; + QPoint globalPos; + QCanvasItem *qcanvasItemClickedOn; + int itemRtti; + short scrollDelta; + Qt::Orientation scrollOrientation; + bool isRightClick:1; + bool ctrlPressed:1; + bool shiftPressed:1; + bool altPressed:1; + +protected: + void reset(); +}; + +#endif diff --git a/src/filemetainfo.cpp b/src/filemetainfo.cpp new file mode 100644 index 0000000..34baccd --- /dev/null +++ b/src/filemetainfo.cpp @@ -0,0 +1,198 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "filemetainfo.h" +#include "textdocument.h" +#include "textview.h" + +#include <kconfig.h> + + +//BEGIN class MetaInfo +MetaInfo::MetaInfo() +{ + m_cursorLine = 0; + m_cursorColumn = 0; +} + + +bool MetaInfo::hasDefaultData() const +{ + return bookmarks().isEmpty() && + breakpoints().isEmpty() && + (m_outputMethodInfo.method() == OutputMethodInfo::Method::Direct ) && + (m_cursorLine == 0) && + (m_cursorColumn == 0); +} + + +void MetaInfo::save( KConfig * conf ) +{ + conf->writeEntry( "Bookmarks", bookmarks() ); + conf->writeEntry( "Breakpoints", breakpoints() ); + conf->writeEntry( "OutputMethod", toID(outputMethodInfo().method()) ); + conf->writePathEntry( "OutputPath", outputMethodInfo().outputFile().prettyURL() ); + conf->writeEntry( "OutputPicID", outputMethodInfo().picID() ); + conf->writeEntry( "CursorLine", cursorLine() ); + conf->writeEntry( "CursorColumn", cursorColumn() ); +} + + +void MetaInfo::load( KConfig * conf ) +{ + setBookmarks( conf->readIntListEntry("Bookmarks") ); + setBreakpoints( conf->readIntListEntry("Breakpoints") ); + m_outputMethodInfo.setMethod( toMethod( conf->readEntry("OutputMethod") ) ); + m_outputMethodInfo.setOutputFile( conf->readPathEntry("OutputPath") ); + m_outputMethodInfo.setPicID( conf->readEntry("OutputPicID") ); + setCursorLine( conf->readNumEntry( "CursorLine", 0 ) ); + setCursorColumn( conf->readNumEntry( "CursorColumn", 0 ) ); +} + + +OutputMethodInfo::Method::Type MetaInfo::toMethod( const QString & id ) +{ + if ( id == "SaveAndLoad" ) + return OutputMethodInfo::Method::SaveAndLoad; + + else if ( id == "SaveAndForget" ) + return OutputMethodInfo::Method::SaveAndForget; + + return OutputMethodInfo::Method::Direct; +} + + +QString MetaInfo::toID( OutputMethodInfo::Method::Type method ) +{ + switch (method) + { + case OutputMethodInfo::Method::SaveAndLoad: + return "SaveAndLoad"; + + case OutputMethodInfo::Method::SaveAndForget: + return "SaveAndForget"; + + case OutputMethodInfo::Method::Direct: + default: + return "Direct"; + } +} +//END class MetaInfo + + +//BEGIN class FileMetaInfo +FileMetaInfo::FileMetaInfo() + : QObject() +{ + m_metaInfoConfig = new KConfig( "metainfo", false, false, "appdata" ); + loadAllMetaInfo(); +} + + +FileMetaInfo::~FileMetaInfo() +{ + saveAllMetaInfo(); + delete m_metaInfoConfig; +} + + +void FileMetaInfo::grabMetaInfo( const KURL & url, TextDocument * textDocument ) +{ + if (!textDocument) + return; + + m_metaInfoMap[url].setBookmarks( textDocument->bookmarkList() ); + m_metaInfoMap[url].setBreakpoints( textDocument->breakpointList() ); +} + + +void FileMetaInfo::initializeFromMetaInfo( const KURL & url, TextDocument * textDocument ) +{ + if (!textDocument) + return; + + textDocument->setBookmarks(m_metaInfoMap[url].bookmarks()); + textDocument->setBreakpoints(m_metaInfoMap[url].breakpoints()); +} + + +void FileMetaInfo::grabMetaInfo( const KURL & url, TextView * textView ) +{ + if (!textView) + return; + + m_metaInfoMap[url].setCursorLine( textView->currentLine() ); + m_metaInfoMap[url].setCursorColumn( textView->currentColumn() ); +} + + +void FileMetaInfo::initializeFromMetaInfo( const KURL & url, TextView * textView ) +{ + if (!textView) + return; + + textView->setCursorPosition( m_metaInfoMap[url].cursorLine(), m_metaInfoMap[url].cursorColumn() ); +} + + +void FileMetaInfo::grabMetaInfo( const KURL & url, OutputMethodDlg * dlg ) +{ + if (!dlg) + return; + + m_metaInfoMap[url].setOutputMethodInfo( dlg->info() ); +} + + +void FileMetaInfo::initializeFromMetaInfo( const KURL & url, OutputMethodDlg * dlg ) +{ + if ( !dlg || url.isEmpty() || !m_metaInfoMap.contains(url) ) + return; + + OutputMethodInfo::Method::Type method = m_metaInfoMap[url].outputMethodInfo().method(); + dlg->setMethod(method); + + if ( method != OutputMethodInfo::Method::Direct ) + dlg->setOutputFile( m_metaInfoMap[url].outputMethodInfo().outputFile() ); + + dlg->setPicID( m_metaInfoMap[url].outputMethodInfo().picID() ); +} + + +void FileMetaInfo::saveAllMetaInfo() +{ + const MetaInfoMap::iterator end = m_metaInfoMap.end(); + for ( MetaInfoMap::iterator it = m_metaInfoMap.begin(); it != end; ++it ) + { + if ( it.data().hasDefaultData() ) + m_metaInfoConfig->deleteGroup(it.key().prettyURL()); + + else + { + m_metaInfoConfig->setGroup( it.key().prettyURL() ); + it.data().save( m_metaInfoConfig ); + } + } +} + + +void FileMetaInfo::loadAllMetaInfo() +{ + QStringList urlList = m_metaInfoConfig->groupList(); + const QStringList::iterator end = urlList.end(); + for ( QStringList::iterator it = urlList.begin(); it != end; ++it ) + { + m_metaInfoConfig->setGroup(*it); + m_metaInfoMap[*it].load(m_metaInfoConfig); + } +} +//END class FileMetaInfo + +#include "filemetainfo.moc" diff --git a/src/filemetainfo.h b/src/filemetainfo.h new file mode 100644 index 0000000..9004217 --- /dev/null +++ b/src/filemetainfo.h @@ -0,0 +1,143 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef FILEMETAINFO_H +#define FILEMETAINFO_H + +#include "outputmethoddlg.h" + + +class TextDocument; +class TextView; +class KConfig; +typedef QValueList<int> IntList; + +class MetaInfo +{ + public: + MetaInfo(); + + /** + * Returns true if all the data stored is default; and therefore does + * not need saving. + */ + bool hasDefaultData() const; + /** + * Writes to the given config the data stored in here. Does not set the + * group. + */ + void save( KConfig * conf ); + /** + * Reads in the data from the config. Does not set the group. + */ + void load( KConfig * conf ); + + IntList bookmarks() const { return m_bookmarks; } + void setBookmarks( IntList bookmarks ) { m_bookmarks = bookmarks; } + + IntList breakpoints() const { return m_breakpoints; } + void setBreakpoints( IntList breakpoints ) { m_breakpoints = breakpoints; } + + OutputMethodInfo & outputMethodInfo() { return m_outputMethodInfo; } + void setOutputMethodInfo( OutputMethodInfo info ) { m_outputMethodInfo = info; } + + unsigned cursorLine() const { return m_cursorLine; } + void setCursorLine( unsigned line ) { m_cursorLine = line; } + + unsigned cursorColumn() const { return m_cursorColumn; } + void setCursorColumn( unsigned column ) { m_cursorColumn = column; } + + protected: + /** + * Convert the id (e.g. "Direct") to a method, used when reading in the + * config file. + */ + OutputMethodInfo::Method::Type toMethod( const QString & id ); + /** + * Conver the method (e.g. OutputMethodInfo::Method::Direct) to an id + * that can be saved in the config file. + */ + QString toID( OutputMethodInfo::Method::Type method ); + + IntList m_bookmarks; + IntList m_breakpoints; + OutputMethodInfo m_outputMethodInfo; + unsigned m_cursorLine; + unsigned m_cursorColumn; +}; +typedef QMap<KURL,MetaInfo> MetaInfoMap; + +/** +Looks after per-file metainfo; e.g. bookmarks, breakpoints, compiling options, etc + +@author David Saxton +*/ +class FileMetaInfo : public QObject +{ + Q_OBJECT + public: + ~FileMetaInfo(); + + /** + * Initialize the TextDocument with the appropriate stored metainfo - e.g. + * setting the appopriate bookmarks, etc + */ + void initializeFromMetaInfo( const KURL & url, TextDocument * textDocument ); + /** + * Initialize the TextView with the appropriate stored metainfo - e.g. + * setting the appopriate cursor position, etc. + */ + void initializeFromMetaInfo( const KURL & url, TextView * textView ); + /** + * Initialize the OutputMethodDlg with the options the user had selected + * for the last time it was used for the given url. + */ + void initializeFromMetaInfo( const KURL & url, OutputMethodDlg * outputMethodDlg ); + /** + * Get the bookmarks, etc from the given TextDocument, and save them + */ + void grabMetaInfo( const KURL & url, TextDocument * textDocument ); + /** + * Get the cursor position, etc from the given TextView, and save them. + */ + void grabMetaInfo( const KURL & url, TextView * textView ); + /** + * Get the output method et al from the given OutputMethodDlg, and save + * them. + */ + void grabMetaInfo( const KURL & url, OutputMethodDlg * outputMethodDlg ); + /** + * Save all metainfo to disk. + */ + void saveAllMetaInfo(); + + protected: + /** + * Load all metainfo from disk (combining that read in with those already + * loaded) + */ + void loadAllMetaInfo(); + + KConfig *m_metaInfoConfig; + + private: + FileMetaInfo(); + friend inline FileMetaInfo* fileMetaInfo(); + + MetaInfoMap m_metaInfoMap; +}; + +inline FileMetaInfo* fileMetaInfo() +{ + static FileMetaInfo *fmi = new FileMetaInfo(); + return fmi; +} + +#endif diff --git a/src/flowcodedocument.cpp b/src/flowcodedocument.cpp new file mode 100644 index 0000000..ad2c346 --- /dev/null +++ b/src/flowcodedocument.cpp @@ -0,0 +1,308 @@ +/*************************************************************************** + * Copyright (C) 2003-2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "canvasmanipulator.h" +#include "documentiface.h" +#include "drawpart.h" +#include "flowcode.h" +#include "flowcodedocument.h" +#include "flowcodeview.h" +#include "flowpart.h" +#include "itemdocumentdata.h" +#include "languagemanager.h" +#include "microinfo.h" +#include "microlibrary.h" +#include "outputmethoddlg.h" +#include "picitem.h" +#include "programmerdlg.h" + +#include <kdebug.h> +#include <klocale.h> +#include <kmessagebox.h> + +FlowCodeDocument::FlowCodeDocument( const QString &caption, KTechlab *ktechlab, const char *name ) + : ICNDocument( caption, ktechlab, name ) +{ + m_pDocumentIface = new FlowCodeDocumentIface(this); + m_type = Document::dt_flowcode; + m_microInfo = 0L; + m_microSettings = 0L; + m_picItem = 0L; + m_pLastTextOutputTarget = 0l; + + m_cmManager->addManipulatorInfo( CMSelect::manipulatorInfo() ); + m_cmManager->addManipulatorInfo( CMDraw::manipulatorInfo() ); + m_cmManager->addManipulatorInfo( CMRightClick::manipulatorInfo() ); + m_cmManager->addManipulatorInfo( CMRepeatedItemAdd::manipulatorInfo() ); + m_cmManager->addManipulatorInfo( CMItemResize::manipulatorInfo() ); + m_cmManager->addManipulatorInfo( CMItemDrag::manipulatorInfo() ); + + m_fileExtensionInfo = i18n("*.flowcode|FlowCode (*.flowcode)\n*|All Files"); + requestStateSave(); +} + + +FlowCodeDocument::~FlowCodeDocument() +{ + m_bDeleted = true; + if (m_picItem) + m_picItem->removeItem(); + + delete m_microSettings; + m_microSettings = 0l; + + delete m_pDocumentIface; + m_pDocumentIface = 0l; +} + + +View *FlowCodeDocument::createView( ViewContainer *viewContainer, uint viewAreaId, const char *name ) +{ + View* view = new FlowCodeView( this, viewContainer, viewAreaId, name ); + handleNewView(view); + return view; +} + + +void FlowCodeDocument::setPicType( const QString &id ) +{ + if ( m_microSettings && m_microSettings->microInfo() && m_microSettings->microInfo()->id() == id ) + return; + + MicroInfo *microInfo = MicroLibrary::self()->microInfoWithID(id); + + if ( !microInfo ) + { + kdWarning() << "FlowCodeDocument::setPicType: Could not set the pic type to PIC \""<<id<<"\"\n"; + return; + } + + m_microInfo = microInfo; + + if (m_microSettings) + { + //TODO write the pic settings to somewhere temporary and then restore them + delete m_microSettings; + } + + m_microSettings = new MicroSettings(m_microInfo); + connect( m_microSettings, SIGNAL(pinMappingsChanged()), this, SIGNAL(pinMappingsChanged()) ); + //TODO restore pic settings from temporary location if appropriate + + delete m_picItem; + m_picItem = new PicItem( this, true, "picItem", m_microSettings ); + m_picItem->show(); + + emit picTypeChanged(); +} + + +bool FlowCodeDocument::isValidItem( const QString &itemId ) +{ + return itemId.startsWith("flow/") || itemId.startsWith("dp/"); +} + + +bool FlowCodeDocument::isValidItem( Item *item ) +{ + if ( !dynamic_cast<FlowPart*>(item) && !dynamic_cast<DrawPart*>(item) ) + return false; + + if ( !item->id().startsWith("START") && !item->id().startsWith("PPEND") ) + return true; + + const ItemList::iterator ciEnd = m_itemList.end(); + + if ( item->id().startsWith("START") ) + { + int count = 0; + + for ( ItemList::iterator it = m_itemList.begin(); it != ciEnd; ++it ) + { + if ( (*it)->id().startsWith("START") ) + count++; + } + if ( count > 1 ) + return false; + } + + else if ( item->id().startsWith("PPEND") ) + { + int count = 0; + for ( ItemList::iterator it = m_itemList.begin(); it != ciEnd; ++it ) + { + if ( (*it)->id().startsWith("PPEND") ) + count++; + } + if ( count > 1 ) + return false; + } + + return true; +} + + +void FlowCodeDocument::setLastTextOutputTarget( TextDocument * target ) +{ + m_pLastTextOutputTarget = target; +} + + +void FlowCodeDocument::slotConvertTo( int target ) +{ + switch ( (ConvertToTarget)target ) + { + case FlowCodeDocument::MicrobeOutput: + convertToMicrobe(); + break; + + case FlowCodeDocument::AssemblyOutput: + convertToAssembly(); + break; + + case FlowCodeDocument::HexOutput: + convertToHex(); + break; + + case FlowCodeDocument::PICOutput: + convertToPIC(); + break; + } +} + + +void FlowCodeDocument::convertToMicrobe() +{ + OutputMethodDlg *dlg = new OutputMethodDlg( i18n("Microbe Code Output"), url(), false, activeView() ); + dlg->setOutputExtension(".microbe"); + dlg->setFilter("*.microbe|Microbe (*.microbe)\n*|All Files"); + dlg->exec(); + if (!dlg->isAccepted()) + { + delete dlg; + return; + } + + ProcessOptions o( dlg->info() ); + o.setTextOutputTarget( m_pLastTextOutputTarget, this, SLOT(setLastTextOutputTarget( TextDocument* )) ); + o.p_flowCodeDocument = this; + o.setProcessPath( ProcessOptions::ProcessPath::FlowCode_Microbe ); + LanguageManager::self()->compile(o); + + delete dlg; +} + + +void FlowCodeDocument::convertToAssembly() +{ + OutputMethodDlg *dlg = new OutputMethodDlg( i18n("Assembly Code Output"), url(), false, activeView() ); + dlg->setOutputExtension(".asm"); + dlg->setFilter("*.asm *.src *.inc|Assembly Code (*.asm, *.src, *.inc)\n*|All Files"); + dlg->exec(); + if (!dlg->isAccepted()) + { + delete dlg; + return; + } + + ProcessOptions o( dlg->info() ); + o.setTextOutputTarget( m_pLastTextOutputTarget, this, SLOT(setLastTextOutputTarget( TextDocument* )) ); + o.p_flowCodeDocument = this; + o.setProcessPath( ProcessOptions::ProcessPath::FlowCode_AssemblyAbsolute ); + LanguageManager::self()->compile(o); + + delete dlg; +} + + +void FlowCodeDocument::convertToHex() +{ + OutputMethodDlg *dlg = new OutputMethodDlg( i18n("Hex Code Output"), url(), false, (QWidget*)p_ktechlab ); + dlg->setOutputExtension(".hex"); + dlg->setFilter("*.hex|Hex (*.hex)\n*|All Files"); + dlg->exec(); + if (!dlg->isAccepted()) + { + delete dlg; + return; + } + + ProcessOptions o( dlg->info() ); + o.setTextOutputTarget( m_pLastTextOutputTarget, this, SLOT(setLastTextOutputTarget( TextDocument* )) ); + o.p_flowCodeDocument = this; + o.setProcessPath( ProcessOptions::ProcessPath::FlowCode_Program ); + LanguageManager::self()->compile(o); + + delete dlg; +} + + +void FlowCodeDocument::convertToPIC() +{ + ProgrammerDlg * dlg = new ProgrammerDlg( microSettings()->microInfo()->id(), (QWidget*)p_ktechlab, "Programmer Dlg" ); + dlg->exec(); + if ( !dlg->isAccepted() ) + { + dlg->deleteLater(); + return; + } + + ProcessOptions o; + dlg->initOptions( & o ); + o.p_flowCodeDocument = this; + o.setProcessPath( ProcessOptions::ProcessPath::FlowCode_PIC ); + LanguageManager::self()->compile( o ); + + dlg->deleteLater(); +} + + +void FlowCodeDocument::varNameChanged( const QString &newValue, const QString &oldValue ) +{ + if (m_bDeleted) + return; + + // Decrease the old variable count + // If none are left after, remove it from microsettings + StringIntMap::iterator it = m_varNames.find(oldValue); + if ( it != m_varNames.end() ) + { + --(it.data()); + if ( it.data() <= 0 ) + { + VariableInfo *info = microSettings()->variableInfo(it.key()); + if ( info && !info->permanent ) microSettings()->deleteVariable(oldValue); + m_varNames.erase(it); + } + } + + // Add the new variable to a count, tell microsettings about it if it is new + if ( !newValue.isEmpty() ) + { + it = m_varNames.find(newValue); + if ( it != m_varNames.end() ) { + ++it.data(); + } else { + m_varNames[newValue] = 1; + microSettings()->setVariable( newValue, QVariant(), false ); + } + } + + // Tell all FlowParts to update their variable lists + const ItemList::iterator end = m_itemList.end(); + for ( ItemList::iterator it = m_itemList.begin(); it != end; ++it ) + { + if ( FlowPart *flowPart = dynamic_cast<FlowPart*>((Item*)(*it)) ) + flowPart->updateVarNames(); + } +} + + +#include "flowcodedocument.moc" diff --git a/src/flowcodedocument.h b/src/flowcodedocument.h new file mode 100644 index 0000000..bb28ae4 --- /dev/null +++ b/src/flowcodedocument.h @@ -0,0 +1,100 @@ +/*************************************************************************** + * Copyright (C) 2003-2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef FLOWCODEDOCUMENT_H +#define FLOWCODEDOCUMENT_H + +#include "icndocument.h" + +#include <qguardedptr.h> + +class KTechlab; +class FlowCode; +class MicroInfo; +class PicItem; +class FlowPart; +class MicroSettings; +class TextDocument; +class QString; + +typedef QValueList<FlowPart*> FlowPartList; +typedef QMap<QString, int > StringIntMap; + +/** +@short View for editing FlowCode +@author David Saxton +*/ +class FlowCodeDocument : public ICNDocument +{ + Q_OBJECT + public: + FlowCodeDocument( const QString &caption, KTechlab *ktechlab, const char *name = 0L); + ~FlowCodeDocument(); + + virtual View *createView( ViewContainer *viewContainer, uint viewAreaId, const char *name = 0l ); + + /** + * Returns a pointer used for the MicroSettings in this FlowCode document + */ + MicroSettings *microSettings() const { return m_microSettings; } + /** + * Sets the type of PIC to be used. FlowCodeDocument se + virtual void convertToMicrobe();ts the internal MicroInfo pointer to that + * returned by MicroLibrary for the given id. The pic type must be set before anything useful + * (such as compilage) can be done. + */ + void setPicType( const QString &id ); + + enum ConvertToTarget + { + MicrobeOutput, + AssemblyOutput, + HexOutput, + PICOutput + }; + +#define protected public + signals: + void picTypeChanged(); +#undef protected + + signals: + void pinMappingsChanged(); + + public slots: + /** + * @param target as ConvertToTarget + */ + void slotConvertTo( int target ); + void convertToMicrobe(); + void convertToAssembly(); + void convertToHex(); + void convertToPIC(); + /** + * Called when a variable name has changed (from an entry box) + */ + void varNameChanged( const QString &newValue, const QString &oldValue ); + + protected: + virtual bool isValidItem( Item *item ); + virtual bool isValidItem( const QString &itemId ); + + private slots: + void setLastTextOutputTarget( TextDocument * target ); + + private: + QGuardedPtr<TextDocument> m_pLastTextOutputTarget; + MicroInfo *m_microInfo; // Stores information about the PIC + MicroSettings *m_microSettings; // Stores initial settings of the PIC + PicItem *m_picItem; // Allows the user to change the PIC settings + StringIntMap m_varNames; +}; + +#endif diff --git a/src/flowcodeview.cpp b/src/flowcodeview.cpp new file mode 100644 index 0000000..8f3cfd1 --- /dev/null +++ b/src/flowcodeview.cpp @@ -0,0 +1,82 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "flowcodedocument.h" +#include "flowcodeview.h" +#include "ktechlab.h" +#include "viewiface.h" + +#include <kiconloader.h> +#include <klocale.h> +#include <kpopupmenu.h> +#include <qwhatsthis.h> + +FlowCodeView::FlowCodeView( FlowCodeDocument * flowCodeDocument, ViewContainer *viewContainer, uint viewAreaId, const char *name ) + : ICNView( flowCodeDocument, viewContainer, viewAreaId, name ) +{ + KActionCollection * ac = actionCollection(); + + //BEGIN Convert To * Actions + KToolBarPopupAction * pa = new KToolBarPopupAction( i18n("Convert to ..."), "fork", 0, 0, 0, ac, "program_convert" ); + pa->setDelayed(false); + + KPopupMenu * m = pa->popupMenu(); + + m->insertTitle( i18n("Convert to") ); + m->insertItem( KGlobal::iconLoader()->loadIcon( "convert_to_microbe", KIcon::Small ), i18n("Microbe"), FlowCodeDocument::MicrobeOutput ); + m->insertItem( KGlobal::iconLoader()->loadIcon( "convert_to_assembly", KIcon::Small ), i18n("Assembly"), FlowCodeDocument::AssemblyOutput ); + m->insertItem( KGlobal::iconLoader()->loadIcon( "convert_to_hex", KIcon::Small ), i18n("Hex"), FlowCodeDocument::HexOutput ); + m->insertItem( KGlobal::iconLoader()->loadIcon( "convert_to_pic", KIcon::Small ), i18n("PIC (upload)"), FlowCodeDocument::PICOutput ); + connect( m, SIGNAL(activated(int)), flowCodeDocument, SLOT(slotConvertTo(int)) ); + //END Convert To * Actions + + + + +// new KAction( i18n("Convert to Microbe"), "convert_to_microbe", Qt::Key_F7, flowCodeDocument, SLOT(convertToMicrobe()), ac, "tools_to_microbe" ); +// new KAction( i18n("Convert to Assembly"), "convert_to_assembly", Qt::Key_F8, flowCodeDocument, SLOT(convertToAssembly()), ac, "tools_to_assembly" ); +// new KAction( i18n("Convert to Hex"), "convert_to_hex", Qt::Key_F9, flowCodeDocument, SLOT(convertToHex()), ac, "tools_to_hex" ); +// new KAction( i18n("Upload PIC Program"), "convert_to_pic", 0, flowCodeDocument, SLOT(convertToPIC()), ac, "tools_to_pic" ); + + + + + setXMLFile( "ktechlabflowcodeui.rc", true ); + + QWhatsThis::add( this, i18n( + "Construct a FlowCode document by dragging FlowParts from the list on the left. All FlowCharts require an initial \"Start\" part, of which there can only be one.<br><br>" + + "Some FlowParts, such as Subroutines, act as a container element for other FlowParts. Drag the items in or out of a container as appropritate. The container that will become the parent of the part being dragged is indicated by being selected.<br><br>" + + "Note that connections cannot be made between FlowParts in different containers, or at different levels." + ) ); + + m_pViewIface = new FlowCodeViewIface(this); +} + + +FlowCodeView::~FlowCodeView() +{ + delete m_pViewIface; + m_pViewIface = 0l; +} + + + +void FlowCodeView::dragEnterEvent( QDragEnterEvent * e ) +{ + ICNView::dragEnterEvent(e); + if ( e->isAccepted() ) + return; + + e->accept( e->provides("ktechlab/flowpart") ); +} + +#include "flowcodeview.moc" diff --git a/src/flowcodeview.h b/src/flowcodeview.h new file mode 100644 index 0000000..7641e2a --- /dev/null +++ b/src/flowcodeview.h @@ -0,0 +1,33 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef FLOWCODEVIEW_H +#define FLOWCODEVIEW_H + +#include <icnview.h> + +class FlowCodeDocument; + +/** +@author David Saxton +*/ +class FlowCodeView : public ICNView +{ + Q_OBJECT + public: + FlowCodeView( FlowCodeDocument *flowCodeDocument, ViewContainer *viewContainer, uint viewAreaId, const char *name = 0l ); + ~FlowCodeView(); + + protected: + virtual void dragEnterEvent( QDragEnterEvent * e ); + FlowCodeDocument *p_flowCodeDocument; +}; + +#endif diff --git a/src/flowcontainer.cpp b/src/flowcontainer.cpp new file mode 100644 index 0000000..32eb20e --- /dev/null +++ b/src/flowcontainer.cpp @@ -0,0 +1,407 @@ +/*************************************************************************** + * Copyright (C) 2003-2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "canvasitemparts.h" +#include "cells.h" +#include "icndocument.h" +#include "flowcontainer.h" +#include "fpnode.h" +#include "nodegroup.h" +#include "resizeoverlay.h" + +#include <kiconloader.h> +#include <qpainter.h> + +#include <cmath> + +const int topStrip = 24; +const int botStrip = 16; + +FlowContainer::FlowContainer( ICNDocument *_icnDocument, bool newItem, const QString &id ) + : FlowPart( _icnDocument, newItem, id ) +{ + m_ext_in = m_int_in = m_int_out = m_ext_out = 0l; + b_expanded = true; + + addButton( "expandBtn", QRect( offsetX(), offsetY()+24 - 11, 22, 22 ), KGlobal::iconLoader()->loadIcon( "down", KIcon::Small ), true ); + m_rectangularOverlay = new RectangularOverlay( this, 8, 8 ); + setSize( -160, -120, 320, 240 ); + + + m_int_in = (FPNode*)createNode( width()/2, 8+topStrip, Node::dir_down, "int_in", Node::fp_out ); + m_int_out = (FPNode*)createNode( width()/2, height()-8-botStrip, Node::dir_up, "int_out", Node::fp_in ); + + button("expandBtn")->setState(true); + + updateAttachedPositioning(); + updateNodeLevels(); +} + + +FlowContainer::~FlowContainer() +{ +} + + +void FlowContainer::updateNodeLevels() +{ + FlowPart::updateNodeLevels(); + + int l = level(); + + if (m_ext_in) + m_ext_in->setLevel(l); + if (m_ext_out) + m_ext_out->setLevel(l); + + if (m_int_in) + m_int_in->setLevel(l+1); + if (m_int_out) + m_int_out->setLevel(l+1); +} + + +void FlowContainer::filterEndPartIDs( QStringList *ids ) +{ + // Remove *all* nodes except for very bottom one + if (m_int_out) { + ids->remove(m_int_out->childId()); + } + if (m_ext_in) { + ids->remove(m_ext_in->childId()); + } + if (m_int_in) { + ids->remove(m_int_in->childId()); + } +} + + +void FlowContainer::createTopContainerNode() +{ + m_ext_in = (FPNode*)createNode( width()/2, -8, Node::dir_up, "ext_in", Node::fp_in ); + m_ext_in->setLevel( level() ); + m_rectangularOverlay->removeTopMiddle(); + updateAttachedPositioning(); +} + + +void FlowContainer::createBotContainerNode() +{ + m_ext_out = (FPNode*)createNode( width()/2, height()+8, Node::dir_down, "ext_out", Node::fp_out ); + m_ext_out->setLevel( level() ); + m_rectangularOverlay->removeBotMiddle(); + updateAttachedPositioning(); +} + + +QSize FlowContainer::minimumSize() const +{ + return QSize( 160, 64 ); +} + + +void FlowContainer::drawShape( QPainter &p ) +{ + if (b_deleted) + return; + + if ( !m_sizeRect.isValid() ) + return; + + const int _x = (int)x()+offsetX(); + const int _y = (int)y()+offsetY(); + + int col = 0xef + level()*0x6; + if ( col > 0xff ) col = 0xff; + p.setBrush( QColor( col, 0xff, col ) ); + if( b_expanded ) + { + p.setPen(Qt::DotLine); + p.drawRoundRect( _x, _y, width(), topStrip, 1500/width(), 1500/topStrip ); + p.drawRoundRect( _x, _y+height()-botStrip, width(), botStrip, 1500/width(), 1500/botStrip ); + } + else + { + p.setPen(QPen((isSelected()?m_selectedCol:Qt::black),1,Qt::SolidLine)); + p.drawRoundRect( _x, _y, width(), topStrip, 1500/width(), 1500/topStrip ); + } + + p.setPen( Qt::black ); + p.setFont( font() ); + p.drawText( QRect( 22 + _x+8, _y, width()-8, topStrip ), Qt::AlignLeft | Qt::AlignVCenter, m_caption ); + + if( b_expanded ) + { + p.setPen(Qt::SolidLine); + p.setBrush( Qt::NoBrush ); + p.drawRoundRect( _x, _y, width(), height(), 1500/width(), 1500/height() ); + } +} + + +void FlowContainer::childAdded( Item *child ) +{ + if (!child) + return; + + FlowPart::childAdded(child); + + connect( this, SIGNAL(movedBy(double, double )), child, SLOT(moveBy(double, double )) ); + child->setZ( ICNDocument::Z::Item + child->level() ); + + updateContainedVisibility(); +} + + +void FlowContainer::childRemoved( Item *child ) +{ + FlowPart::childRemoved(child); + + if (!b_expanded) + child->setVisible(true); + + disconnect( this, SIGNAL(movedBy(double, double )), child, SLOT(moveBy(double, double )) ); +} + + +void FlowContainer::updateConnectorPoints( bool add ) +{ + if ( b_deleted || !isVisible() ) + add = false; + + if ( b_pointsAdded == add ) + return; + + b_pointsAdded = add; + + Cells *cells = p_icnDocument->cells(); + if (!cells) return; + + int _x = (int)x()+offsetX(); + int _y = (int)y()+offsetY(); + int w = width(); + int h = b_expanded ? height() : topStrip; + + const int mult = add ? 1 : -1; + + // Top strip + for ( int y=_y; y <_y+24; ++y ) + { + for ( int x = _x; x<=_x+w; x += 8 ) + { + if ( p_icnDocument->isValidCellReference( x/8, y/8 ) ) + { + (*cells)[x/8][y/8].CIpenalty += mult*ICNDocument::hs_item; + } + } + } + + // Bottom strip + for ( int y = _y+h-16; y <= _y+h; ++y ) + { + for ( int x = _x; x<=_x+width(); x += 8 ) + { + if ( p_icnDocument->isValidCellReference( x/8, y/8 ) ) + { + (*cells)[x/8][y/8].CIpenalty += mult*ICNDocument::hs_item; + } + } + } + + // Left strip + int x = _x; + for ( int y = _y+24; y<_y+h-16; y += 8 ) + { + if ( p_icnDocument->isValidCellReference( x/8, y/8 ) ) + { + (*cells)[x/8][y/8].CIpenalty += mult*ICNDocument::hs_item; + } + } + + // Right strip + x = _x+width(); + for ( int y = _y+24; y<_y+h-16; y += 8 ) + { + if ( p_icnDocument->isValidCellReference( x/8, y/8 ) ) + { + (*cells)[x/8][y/8].CIpenalty += mult*ICNDocument::hs_item; + } + } +} + + +void FlowContainer::setFullBounds( bool full ) +{ + if ( full || !b_expanded ) + { + QRect bounds = b_expanded ? m_sizeRect : QRect( m_sizeRect.x(), m_sizeRect.y(), m_sizeRect.width(), topStrip ); + setPoints( QPointArray(bounds) ); + return; + } + +// kdDebug() << k_funcinfo << "width="<<width()<<" height="<<height()<<endl; + + QPointArray pa(10); + pa[0] = QPoint( 0, 0 ); + pa[1] = QPoint( width(), 0 ); + pa[2] = QPoint( width(), height() ); + pa[3] = QPoint( 0, height() ); + pa[4] = QPoint( 0, 0 ); + pa[5] = QPoint( 8, topStrip ); + pa[6] = QPoint( 8, height()-botStrip ); + pa[7] = QPoint( width()-8, height()-botStrip ); + pa[8] = QPoint( width()-8, topStrip ); + pa[9] = QPoint( 8, topStrip ); + pa.translate( offsetX(), offsetY() ); + setPoints(pa); +} + + +void FlowContainer::buttonStateChanged(const QString &/*id*/, bool state) +{ + setExpanded(state); +} + + +bool FlowContainer::parentIsCollapsed() const +{ + if ( !p_parentItem ) + return false; + + FlowContainer *fc = dynamic_cast<FlowContainer*>((Item*)(p_parentItem)); + return !fc->isExpanded() || fc->parentIsCollapsed(); +} + + +void FlowContainer::setSelected( bool yes ) +{ + if ( yes == isSelected() ) + return; + + FlowPart::setSelected(yes); + m_rectangularOverlay->showResizeHandles( yes && isVisible() ); +} + + +void FlowContainer::setExpanded( bool expanded ) +{ + if ( b_expanded == expanded ) + return; + + updateConnectorPoints(false); + + // Set this now, so that child items that we call know whether or not we actually are expanded + b_expanded = expanded; + + updateContainedVisibility(); + updateAttachedPositioning(); + + p_itemDocument->setModified(true); + m_rectangularOverlay->setVisible(expanded); + setFullBounds(false); + + bool nodesMoved = (m_ext_out != 0l); + if (nodesMoved) + p_icnDocument->requestRerouteInvalidatedConnectors(); + + p_icnDocument->requestStateSave(); +} + + +void FlowContainer::postResize() +{ +// kdDebug() << k_funcinfo << "width="<<width()<<endl; + setFullBounds(false); + FlowPart::postResize(); +} + + +void FlowContainer::updateAttachedPositioning() +{ + if (b_deleted) + return; + + int _x = int(x())+offsetX(); + int _y = int(y())+offsetY(); + int w = int((std::floor(float((width()+8)/16)))*16); + int h = height(); + + if (m_ext_in) + m_ext_in->move( _x+w/2, _y-8 ); + + if (m_int_in) + m_int_in->move( _x+w/2, _y+8+topStrip ); + + if (b_expanded) + { + if (m_int_out) + m_int_out->move( _x+w/2, _y+h-8-botStrip ); + + if (m_ext_out) + m_ext_out->move( _x+w/2, _y+h-8+botStrip ); + } + else + { + // (Note: dont really care where internal nodes are if not expanded) + + if (m_ext_out) + m_ext_out->move( _x+w/2, _y+8+topStrip ); + } + + button("expandBtn")->setGuiPartSize( 22, 22 ); + button("expandBtn")->move( int(x())+offsetX()+7, int(y())+offsetY()+1 ); +} + + +void FlowContainer::updateContainedVisibility() +{ + if (b_deleted) + return; + + if (m_ext_in) + m_ext_in->setVisible( isVisible() ); + if (m_int_in) + m_int_in->setVisible( isVisible() && b_expanded ); + if (m_int_out) + m_int_out->setVisible( isVisible() && b_expanded ); + if (m_ext_out) + m_ext_out->setVisible( isVisible() ); + + const ItemList::iterator cEnd = m_children.end(); + for ( ItemList::iterator it = m_children.begin(); it != cEnd; ++it ) + { + if (*it) + (*it)->setVisible( isVisible() && b_expanded ); + } + + m_rectangularOverlay->setVisible( isVisible() && b_expanded ); + + NodeGroupList hidableNodeGroups; + p_icnDocument->getTranslatable( children(true) += GuardedItem(this), 0, 0, &hidableNodeGroups ); + + NodeGroupList::iterator hngEnd = hidableNodeGroups.end(); + for ( NodeGroupList::iterator it = hidableNodeGroups.begin(); it != hngEnd; ++it ) + (*it)->setVisible(b_expanded); +} + + +void FlowContainer::setVisible( bool yes ) +{ + if (b_deleted) + { + FlowPart::setVisible(false); + return; + } + + FlowPart::setVisible(yes); + updateContainedVisibility(); +} + +#include "flowcontainer.moc" diff --git a/src/flowcontainer.h b/src/flowcontainer.h new file mode 100644 index 0000000..9afa401 --- /dev/null +++ b/src/flowcontainer.h @@ -0,0 +1,78 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef FLOWCONTAINER_H +#define FLOWCONTAINER_H + +#include "flowpart.h" + +class RectangularOverlay; + +/** +@author David Saxton +*/ +class FlowContainer : public FlowPart +{ +Q_OBJECT +public: + FlowContainer( ICNDocument *_icnView, bool newItem, const QString &id ); + virtual ~FlowContainer(); + + virtual bool canResize() const { return true; } + /** + * Sets the bound to a simple rectangle if true, so that ICNDocument + * can tell whether an item is being dropped into it + */ + void setFullBounds( bool full ); + + virtual void updateConnectorPoints( bool add = true ); + /** + * Returns whether the container is currently expanded or not + */ + bool isExpanded() const { return b_expanded; } + /** + * Returns true if one of this parents is collapsed. + */ + bool parentIsCollapsed() const; + void setExpanded( bool expanded ); + + virtual void setSelected( bool yes ); + virtual void setVisible( bool yes ); + + virtual QSize minimumSize() const; + /** + * Update the visibility of items, connectors, nodes in the flowcontainer + */ + void updateContainedVisibility(); + +protected: + virtual void itemPointsChanged() {}; + virtual void updateNodeLevels(); + virtual void childAdded( Item *child ); + virtual void childRemoved( Item *child ); + virtual void updateAttachedPositioning(); + virtual void postResize(); + virtual void filterEndPartIDs( QStringList *ids ); + virtual void drawShape( QPainter &p ); + void createTopContainerNode(); + void createBotContainerNode(); + + virtual void buttonStateChanged(const QString &id, bool state); + + FPNode *m_ext_in; + FPNode *m_int_in; + FPNode *m_int_out; + FPNode *m_ext_out; + RectangularOverlay *m_rectangularOverlay; + + bool b_expanded; +}; + +#endif diff --git a/src/flowparts/Makefile.am b/src/flowparts/Makefile.am new file mode 100644 index 0000000..44e89fa --- /dev/null +++ b/src/flowparts/Makefile.am @@ -0,0 +1,15 @@ +INCLUDES = -I$(top_srcdir)/src -I$(top_srcdir)/src/electronics \ + -I$(top_srcdir)/src/electronics/components -I$(top_srcdir)/src/gui -I$(top_srcdir)/src/languages \ + -I$(top_srcdir)/src/micro $(all_includes) +METASOURCES = AUTO +noinst_LTLIBRARIES = libflowparts.la +noinst_HEADERS = callsub.h delay.h end.h forloop.h readport.h setpin.h start.h \ + testpin.h unary.h varassignment.h varcomparison.h writeport.h repeat.h while.h \ + sub.h inputbutton.h flowpart.h pinmapping.h +libflowparts_la_SOURCES = callsub.cpp delay.cpp end.cpp forloop.cpp \ + readport.cpp setpin.cpp start.cpp testpin.cpp unary.cpp varassignment.cpp \ + varcomparison.cpp writeport.cpp repeat.cpp while.cpp sub.cpp count.cpp embed.cpp \ + interrupt.cpp keypad.cpp pulse.cpp sevenseg.cpp inputbutton.cpp flowpart.cpp \ + pinmapping.cpp + +libflowparts_la_PCH = AUTO diff --git a/src/flowparts/callsub.cpp b/src/flowparts/callsub.cpp new file mode 100644 index 0000000..8487ed0 --- /dev/null +++ b/src/flowparts/callsub.cpp @@ -0,0 +1,62 @@ +/*************************************************************************** + * Copyright (C) 2003 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "callsub.h" + +#include "libraryitem.h" +#include "flowcode.h" + +#include <klocale.h> + +Item* CallSub::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new CallSub( (ICNDocument*)itemDocument, newItem, id ); +} + +LibraryItem* CallSub::libraryItem() +{ + return new LibraryItem( + QString("flow/callsub"), + i18n("Sub Call"), + i18n("Common"), + "subcall.png", + LibraryItem::lit_flowpart, + CallSub::construct ); +} + +CallSub::CallSub( ICNDocument *icnDocument, bool newItem, const char *id ) + : FlowPart( icnDocument, newItem, (id) ? id : "callsub" ) +{ + m_name = i18n("Sub Call"); + m_desc = i18n("Call a subroutine. When the subroutine returns, the code will continue execution from this point."); + initCallSymbol(); + createStdInput(); + createStdOutput(); + + createProperty( "sub", Variant::Type::Combo ); + property("sub")->setCaption( i18n("Subroutine") ); + property("sub")->setValue("MySub"); +} + +CallSub::~CallSub() +{ +} + +void CallSub::dataChanged() +{ + setCaption( i18n("Call %1").arg(dataString("sub")) ); +} + +void CallSub::generateMicrobe( FlowCode *code ) +{ + code->addCode( "call " + dataString("sub") ); + code->addCodeBranch( outputPart("stdoutput" ) ); +} + diff --git a/src/flowparts/callsub.h b/src/flowparts/callsub.h new file mode 100644 index 0000000..5cba4f2 --- /dev/null +++ b/src/flowparts/callsub.h @@ -0,0 +1,35 @@ +/*************************************************************************** + * Copyright (C) 2003 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef CALLSUB_H +#define CALLSUB_H + +#include "flowpart.h" + +/** +@short FlowPart that calls a subroutine +@author David Saxton +*/ +class CallSub : public FlowPart +{ +public: + CallSub( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~CallSub(); + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + + virtual void generateMicrobe( FlowCode *code ); + +private: + void dataChanged(); +}; + +#endif diff --git a/src/flowparts/count.cpp b/src/flowparts/count.cpp new file mode 100644 index 0000000..dc0281f --- /dev/null +++ b/src/flowparts/count.cpp @@ -0,0 +1,70 @@ +/*************************************************************************** + * Copyright (C) 2003 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "count.h" + +#include "libraryitem.h" +#include "flowcode.h" + +#include <klocale.h> + +Item* Count::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new Count( (ICNDocument*)itemDocument, newItem, id ); +} + +LibraryItem* Count::libraryItem() +{ + return new LibraryItem( + QString::QString("flow/count"), + i18n("Count"), + i18n("Functions"), + "ppcount.png", + LibraryItem::lit_flowpart, + Count::construct ); +} + +Count::Count( ICNDocument *icnDocument, bool newItem, const char *id ) + : FlowPart( icnDocument, newItem, (id) ? id : "count" ) +{ + m_name = i18n("Count"); + m_desc = i18n("Count the number of pulses during a fixed interval. To avoid ambiguity, this increments the counter on either a rising or falling edge, as opposed to a high or a low."); + initProcessSymbol(); + createStdInput(); + createStdOutput(); + + createProperty( "0-trigger", Variant::Type::Select ); + property("0-trigger")->setAllowed( QStringList::split(',',"rising,falling") ); + property("0-triger")->setValue("rising"); + property("0-trigger")->setCaption( i18n("Trigger") ); + + createProperty( "1-length", Variant::Type::Double ); + property("1-length")->setUnit("sec"); + property("1-length")->setValue(10.0); + property("1-length")->setCaption("Interval"); +} + +Count::~Count() +{ +} + +void Count::dataChanged() +{ + double count = dataDouble("1-length"); + setCaption( i18n("Count %1 for %2 sec").arg(dataString("0-trigger")).arg(QString::number( count / getMultiplier(count), 'g', 3 ) + getNumberMag(count)) ); +} + +void Count::generateMicrobe( FlowCode *code ) +{ + const double count_ms = dataDouble("1-length")*1e3; + code->addCode( "count "+dataString("0-trigger")+" for "+QString::number(count_ms)+"ms" ); + code->addCodeBranch( outputPart("stdoutput") ); +} + diff --git a/src/flowparts/count.h b/src/flowparts/count.h new file mode 100644 index 0000000..516889d --- /dev/null +++ b/src/flowparts/count.h @@ -0,0 +1,35 @@ +/*************************************************************************** + * Copyright (C) 2003 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef COUNT_H +#define COUNT_H + +#include "flowpart.h" + +/** +@short FlowPart that provides a delay +@author David Saxton +*/ +class Count : public FlowPart +{ +public: + Count( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~Count(); + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + + virtual void generateMicrobe( FlowCode *code ); + +protected: + void dataChanged(); +}; + +#endif diff --git a/src/flowparts/delay.cpp b/src/flowparts/delay.cpp new file mode 100644 index 0000000..fa37770 --- /dev/null +++ b/src/flowparts/delay.cpp @@ -0,0 +1,136 @@ +/*************************************************************************** + * Copyright (C) 2003 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "delay.h" + +#include "libraryitem.h" +#include "flowcode.h" + +#include <klocale.h> + +Item* Delay::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new Delay( (ICNDocument*)itemDocument, newItem, id ); +} + +LibraryItem* Delay::libraryItem() +{ + return new LibraryItem( + QString::QString("flow/delay"), + i18n("Delay"), + i18n("Functions"), + "delay.png", + LibraryItem::lit_flowpart, + Delay::construct ); +} + +Delay::Delay( ICNDocument *icnDocument, bool newItem, const char *id ) + : FlowPart( icnDocument, newItem, (id) ? id : "delay" ) +{ + m_name = i18n("Delay"); + m_desc = i18n("Delay the program execution for a fixed period of time."); + initProcessSymbol(); + createStdInput(); + createStdOutput(); + + createProperty( "delay_length", Variant::Type::Double ); + property("delay_length")->setCaption( i18n("Pause Length") ); + property("delay_length")->setUnit("sec"); + property("delay_length")->setValue(1.0); +} + +Delay::~Delay() +{ +} + +void Delay::dataChanged() +{ + double delay = dataDouble("delay_length"); + setCaption( i18n("Delay for %1 sec").arg(QString::number( delay / getMultiplier(delay), 'g', 3 )+getNumberMag(delay)) ); +} + +void Delay::generateMicrobe( FlowCode *code ) +{ + const double delayLength_ms = dataDouble("delay_length")*1e3; + code->addCode( "delay "+QString::number(delayLength_ms) ); + code->addCodeBranch( outputPart("stdoutput") ); + +// code->addVariable("COUNT_REPEAT"); + +#if 0 + // Code for pauses less than 769uS + if ( pauseLength < 769 ) + { + code->addCodeBlock( id(), "movlw " + QString::number(pauseLength/3) + "\n" + "movwf COUNT_REPEAT\n" + "call count_3uS\n" + + gotoCode("stdoutput") ); + + code->addCodeBlock( "count_3uS", "decfsz COUNT_REPEAT,1\n" + "goto count_3uS\n" + "return" ); + } + else if ( pauseLength < 196609 ) + { + code->addVariable("COUNT_LOOP_1"); + + code->addCodeBlock( id(), "movlw " + QString::number(pauseLength/(3*256)) + "\n" + "movwf COUNT_REPEAT\n" + "call count_768uS\n" + + gotoCode("stdoutput") ); + + code->addCodeBlock( "count_768uS", "decfsz COUNT_LOOP_1,1\n" + "goto count_768uS\n" + "decfsz COUNT_REPEAT,1\n" + "goto count_768uS\n" + "return" ); + } + else if ( pauseLength < 50331649 ) + { + code->addVariable("COUNT_LOOP_1"); + code->addVariable("COUNT_LOOP_2"); + + code->addCodeBlock( id(), "movlw " + QString::number(pauseLength/(3*256*256)) + "\n" + "movwf COUNT_REPEAT\n" + "call count_200mS\n" + + gotoCode("stdoutput") ); + + code->addCodeBlock( "count_200mS", "decfsz COUNT_LOOP_1,1\n" + "goto count_200mS\n" + "decfsz COUNT_LOOP_2,1\n" + "goto count_200mS\n" + "decfsz COUNT_REPEAT,1\n" + "goto count_200mS\n" + "return" ); + } + else/* if ( pauseLength < 12884901889 )*/ + { + code->addVariable("COUNT_LOOP_1"); + code->addVariable("COUNT_LOOP_2"); + code->addVariable("COUNT_LOOP_3"); + + code->addCodeBlock( id(), "movlw " + QString::number(pauseLength/(3*256*256*256)) + "\n" + "movwf COUNT_REPEAT\n" + "call count_50S\n" + + gotoCode("stdoutput") ); + + code->addCodeBlock( "count_50S", "decfsz COUNT_LOOP_1,1\n" + "goto count_50S\n" + "decfsz COUNT_LOOP_2,1\n" + "goto count_50S\n" + "decfsz COUNT_LOOP_3,1\n" + "goto count_50S\n" + "decfsz COUNT_REPEAT,1\n" + "goto count_50S\n" + "return" ); + } +#endif +} + diff --git a/src/flowparts/delay.h b/src/flowparts/delay.h new file mode 100644 index 0000000..fbe6326 --- /dev/null +++ b/src/flowparts/delay.h @@ -0,0 +1,35 @@ +/*************************************************************************** + * Copyright (C) 2003 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef DELAY_H +#define DELAY_H + +#include "flowpart.h" + +/** +@short FlowPart that provides a delay +@author David Saxton +*/ +class Delay : public FlowPart +{ +public: + Delay( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~Delay(); + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + + virtual void generateMicrobe( FlowCode *code ); + +protected: + void dataChanged(); +}; + +#endif diff --git a/src/flowparts/embed.cpp b/src/flowparts/embed.cpp new file mode 100644 index 0000000..ca04c65 --- /dev/null +++ b/src/flowparts/embed.cpp @@ -0,0 +1,87 @@ +/*************************************************************************** + * Copyright (C) 2003 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "embed.h" + +#include "libraryitem.h" +#include "flowcode.h" + +#include <klocale.h> + +Item* Embed::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new Embed( (ICNDocument*)itemDocument, newItem, id ); +} + +LibraryItem* Embed::libraryItem() +{ + return new LibraryItem( + QString::QString("flow/embed"), + i18n("Embed"), + i18n("Common"), + "embed.png", + LibraryItem::lit_flowpart, + Embed::construct + ); +} + +Embed::Embed( ICNDocument *icnDocument, bool newItem, const char *id ) + : FlowPart( icnDocument, newItem, (id) ? id : "embed" ) +{ + m_name = i18n("Embed"); + m_desc = i18n("Doubleclick on the item to edit the embedded code."); + initProcessSymbol(); + createStdInput(); + createStdOutput(); + + createProperty( "type", Variant::Type::Select ); + property("type")->setAllowed( QStringList::split( ',', "Microbe,Assembly" ) ); + property("type")->setValue("Microbe"); + property("type")->setCaption( i18n("Type") ); // TODO: replace this with i18n( "the type", "Type" ); + + createProperty( "code", Variant::Type::Multiline ); + property("code")->setCaption( i18n("Code") ); + property("code")->setValue( i18n("// Embedded code:") ); +} + +Embed::~Embed() +{ +} + + +void Embed::dataChanged() +{ + const QString sample = dataString("code").left(10).replace("\n"," "); + setCaption( i18n("%1: %2...").arg(dataString("type")).arg(sample) ); +} + + +bool Embed::typeIsMicrobe() const +{ + return dataString("type") == "Microbe"; +} + + +void Embed::generateMicrobe( FlowCode *code ) +{ + if ( typeIsMicrobe() ) + code->addCode( dataString("code") ); + + else + { + // Is assembly code, we need to microbe as such + code->addCode("asm\n{"); + code->addCode( dataString("code") ); + code->addCode("}"); + } + + code->addCodeBranch( outputPart("stdoutput") ); +} + diff --git a/src/flowparts/embed.h b/src/flowparts/embed.h new file mode 100644 index 0000000..d891c39 --- /dev/null +++ b/src/flowparts/embed.h @@ -0,0 +1,36 @@ +/*************************************************************************** + * Copyright (C) 2003 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef EMBED_H +#define EMBED_H + +#include "flowpart.h" + +/** +@short FlowPart that provides a delay +@author David Saxton +*/ +class Embed : public FlowPart +{ +public: + Embed( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~Embed(); + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + + virtual void generateMicrobe( FlowCode *code ); + bool typeIsMicrobe() const; + +protected: + void dataChanged(); +}; + +#endif diff --git a/src/flowparts/end.cpp b/src/flowparts/end.cpp new file mode 100644 index 0000000..37d7e91 --- /dev/null +++ b/src/flowparts/end.cpp @@ -0,0 +1,51 @@ +/*************************************************************************** + * Copyright (C) 2003 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "end.h" + +#include "libraryitem.h" +#include "flowcode.h" + +#include <klocale.h> + +Item* End::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new End( (ICNDocument*)itemDocument, newItem, id ); +} + +LibraryItem* End::libraryItem() +{ + return new LibraryItem( + QString("flow/end"), + i18n("End"), + i18n("Common"), + "end.png", + LibraryItem::lit_flowpart, + End::construct ); +} + +End::End( ICNDocument *icnDocument, bool newItem, const char *id ) + : FlowPart( icnDocument, newItem, (id) ? id : "END" ) +{ + m_name = i18n("End"); + m_desc = i18n("End the program execution, putting the IC into sleep. Unlike Start, however, this FlowPart is not necessary for proper program execution"); + initRoundedRectSymbol(); + createStdInput(); + setCaption( i18n("End") ); +} + +End::~End() +{ +} + +void End::generateMicrobe( FlowCode */*code*/ ) +{ +} + diff --git a/src/flowparts/end.h b/src/flowparts/end.h new file mode 100644 index 0000000..a2c2a1e --- /dev/null +++ b/src/flowparts/end.h @@ -0,0 +1,32 @@ +/*************************************************************************** + * Copyright (C) 2003 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef END_H +#define END_H + +#include "flowpart.h" + +/** +@short FlowPart that tells the program where to end +@author David Saxton +*/ +class End : public FlowPart +{ +public: + End( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~End(); + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + + virtual void generateMicrobe( FlowCode * ); +}; + +#endif diff --git a/src/flowparts/flowpart.cpp b/src/flowparts/flowpart.cpp new file mode 100644 index 0000000..e12213c --- /dev/null +++ b/src/flowparts/flowpart.cpp @@ -0,0 +1,977 @@ +/*************************************************************************** + * Copyright (C) 2003-2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "canvasitemparts.h" +#include "connector.h" +#include "flowcodedocument.h" +#include "flowcode.h" +#include "flowpart.h" +#include "fpnode.h" +#include "itemdocument.h" +#include "itemdocumentdata.h" +#include "microsettings.h" +#include "micropackage.h" +#include "picinfo.h" +#include "pinmapping.h" +#include "variant.h" + +#include <kdebug.h> + +#include <qbitarray.h> +#include <qbitmap.h> +#include <qpainter.h> +#include <qpixmap.h> +#include <qregexp.h> + +#include <assert.h> +#include <algorithm> +#include <cmath> + +// The following arrays of numbers represent the positions of nodes in different configurations, +// with the numbers as NodeInfo::Position. + +Node::node_dir diamondNodePositioning[8][3] = { + {Node::dir_up, Node::dir_down, Node::dir_right}, + {Node::dir_up, Node::dir_down, Node::dir_left}, + {Node::dir_up, Node::dir_right,Node::dir_down}, + {Node::dir_up, Node::dir_right,Node::dir_left}, + {Node::dir_left,Node::dir_right,Node::dir_down}, + {Node::dir_left,Node::dir_right,Node::dir_up}, + {Node::dir_left,Node::dir_down, Node::dir_right}, + {Node::dir_left,Node::dir_down, Node::dir_up} }; + +Node::node_dir inOutNodePositioning[8][2] = { + {Node::dir_up,Node::dir_down}, + {Node::dir_up,Node::dir_right}, + {Node::dir_up,Node::dir_left}, + {Node::dir_right,Node::dir_right}, // (invalid) + {Node::dir_left,Node::dir_right}, + {Node::dir_left,Node::dir_down}, + {Node::dir_left,Node::dir_up}, + {Node::dir_right,Node::dir_right} }; // (invalid) + +Node::node_dir inNodePositioning[4] = {Node::dir_up,Node::dir_right,Node::dir_down,Node::dir_left}; + +Node::node_dir outNodePositioning[4] = {Node::dir_down,Node::dir_left,Node::dir_up,Node::dir_right}; + +FlowPart::FlowPart( ICNDocument *icnDocument, bool newItem, const QString &id ) + : CNItem( icnDocument, newItem, id ) +{ + icnDocument->registerItem(this); + m_pFlowCodeDocument = dynamic_cast<FlowCodeDocument*>(icnDocument); + assert( m_pFlowCodeDocument ); + + m_flowSymbol = FlowPart::ps_other; + m_orientation = 0; + m_stdInput = 0l; + m_stdOutput = 0l; + m_altOutput = 0l; + + connect( m_pFlowCodeDocument, SIGNAL(picTypeChanged()), this, SLOT(slotUpdateFlowPartVariables()) ); + connect( m_pFlowCodeDocument, SIGNAL(pinMappingsChanged()), this, SLOT(slotUpdateFlowPartVariables()) ); +} + + +FlowPart::~FlowPart() +{ + // We have to check view, as if the item is deleted before the CNItem constructor + // is called, then there will be no view + if (m_pFlowCodeDocument) + { + const VariantDataMap::iterator end = m_variantData.end(); + for ( VariantDataMap::iterator it = m_variantData.begin(); it != end; ++it ) + { + Variant *v = it.data(); + if (v) + m_pFlowCodeDocument->varNameChanged( "", v->value().toString() ); + } + } +} + + +void FlowPart::setCaption( const QString &caption ) +{ + if ( m_flowSymbol == FlowPart::ps_other ) + { + m_caption = caption; + return; + } + + QWidget *w = new QWidget(); + QPainter p(w); + p.setFont( font() ); + const int text_width = p.boundingRect( boundingRect(), (Qt::SingleLine | Qt::AlignHCenter | Qt::AlignVCenter), caption ).width(); + p.end(); + delete w; + int width = std::max( ((int)(text_width/16))*16, 48 ); + + switch(m_flowSymbol) + { + case FlowPart::ps_call: + { + width += 48; + break; + } + case FlowPart::ps_io: + case FlowPart::ps_round: + { + width += 32; + break; + } + case FlowPart::ps_decision: + { + width += 64; + break; + } + case FlowPart::ps_process: + default: + { + width += 32; + break; + } + } + + bool hasSideConnectors = m_flowSymbol == FlowPart::ps_decision; + if ( hasSideConnectors && (width != this->width()) ) + p_icnDocument->requestRerouteInvalidatedConnectors(); + + initSymbol( m_flowSymbol, width ); + m_caption = caption; +} +void FlowPart::postResize() +{ + updateNodePositions(); + CNItem::postResize(); +} + +void FlowPart::createStdInput() +{ + m_stdInput = (FPNode*)createNode( 0, 0, Node::dir_up, "stdinput", Node::fp_in ); + updateNodePositions(); +} +void FlowPart::createStdOutput() +{ + m_stdOutput = (FPNode*)createNode( 0, 0, Node::dir_down, "stdoutput", Node::fp_out ); + updateNodePositions(); +} +void FlowPart::createAltOutput() +{ + m_altOutput = (FPNode*)createNode( 0, 0, Node::dir_right, "altoutput", Node::fp_out ); + updateNodePositions(); +} + +void FlowPart::initSymbol( FlowPart::FlowSymbol symbol, int width ) +{ + m_flowSymbol = symbol; + + switch(symbol) + { + case FlowPart::ps_other: + { + return; + } + case FlowPart::ps_call: + case FlowPart::ps_process: + { + setItemPoints( QRect( -width/2, -16, width, 24 ) ); + break; + } + + case FlowPart::ps_io: + { + // define parallelogram shape + QPointArray pa(4); + pa[0] = QPoint( -(width-10)/2, -16 ); + pa[1] = QPoint( width/2, -16 ); + pa[2] = QPoint( (width-10)/2, 8 ); + pa[3] = QPoint( -width/2, 8 ); + setItemPoints(pa); + break; + } + + case FlowPart::ps_round: + { + // define rounded rectangles as two semicricles with RP_NUM/2 points with gap inbetween + // These points are not used for drawing; merely for passing to qcanvaspolygonitem for collision detection + // If there is a better way for a rounder rectangle + collision detection, please let me know... + + int halfHeight = 12; + + // Draw semicircle + double x; + const int RP_NUM = 48; + QPointArray pa(RP_NUM); + int point = 0; + for ( double y = -1.0; y <= 1.0; y+= 4.0/(RP_NUM-2) ) + { + x = sqrt(1-y*y)*halfHeight; + pa[point] = QPoint( (int)(width+x)-halfHeight, (int)(halfHeight*y) ); + pa[RP_NUM-1-point] = QPoint ( (int)(halfHeight-x), (int)(halfHeight*y) ); + point++; + } + + pa.translate( -width/2, 4 ); + setItemPoints(pa); + break; + } + + case FlowPart::ps_decision: + { + // define rhombus + QPointArray pa(6); + pa[0] = QPoint( 0, -24 ); + pa[1] = QPoint( width/2, -6 ); + pa[2] = QPoint( width/2, 6 ); + pa[3] = QPoint( 0, 24 ); + pa[4] = QPoint( -width/2, 6 ); + pa[5] = QPoint( -width/2, -6 ); + setItemPoints(pa); + break; + } + default: kdError() << k_funcinfo << "Unknown flowSymbol: "<<symbol<<endl; + } +} + +void FlowPart::drawShape( QPainter &p ) +{ + initPainter(p); + + const double _x = int( x() + offsetX() ); + const double _y = int( y() + offsetY() ); + const double w = width(); + double h = height(); + + switch (m_flowSymbol) + { + case FlowPart::ps_other: + { + CNItem::drawShape(p); + break; + } + + case FlowPart::ps_io: + { + h--; + double roundSize = 8; + double slantIndent = 5; + + const double pi = 3.1415926536; + const double DPR = 180./pi; +// CNItem::drawShape(p); + double inner = std::atan(h/slantIndent); + double outer = pi-inner; + + int inner16 = int(16*inner*DPR); + int outer16 = int(16*outer*DPR); + + p.save(); + p.setPen( Qt::NoPen ); + p.drawPolygon( areaPoints() ); + p.restore(); + + p.drawLine( int(_x+slantIndent+roundSize/2), int(_y), int(_x+w-roundSize/2), int(_y) ); + p.drawLine( int(_x-slantIndent+w-roundSize/2), int(_y+h), int(_x+roundSize/2), int(_y+h) ); + p.drawLine( int(_x+w+(std::sin(outer)-1)*(roundSize/2)), int(_y+(1-std::cos(outer))*(roundSize/2)), + int(_x+w-slantIndent+(std::sin(inner)-1)*(roundSize/2)), int(_y+h+(std::cos(inner)-1)*(roundSize/2)) ); + p.drawLine( int(_x+(1-std::sin(outer))*(roundSize/2)), int(_y+h+(std::cos(outer)-1)*(roundSize/2)), + int(_x+slantIndent+(1-std::sin(inner))*(roundSize/2)), int(_y+(1-std::cos(inner))*(roundSize/2)) ); + + p.drawArc( int(_x+slantIndent), int(_y), int(roundSize), int(roundSize), 90*16, inner16 ); + p.drawArc( int(_x+w-roundSize), int(_y), int(roundSize), int(roundSize), 270*16+inner16, outer16 ); + p.drawArc( int(_x-slantIndent+w-roundSize), int(_y+h-roundSize), int(roundSize), int(roundSize), 270*16, inner16 ); + p.drawArc( int(_x), int(_y+h-roundSize), int(roundSize), int(roundSize), 90*16+inner16, outer16) ; + break; + } + + case FlowPart::ps_decision: + { + // TODO Make the shape nice and pretty with rounded corners + CNItem::drawShape(p); + break; + } + + case FlowPart::ps_call: + { + p.drawRoundRect( int(_x), int(_y), int(w), int(h+1), int(1000./w), int(1000./h) ); + p.drawLine( int(_x+8), int(_y), int(_x+8), int(_y+h) ); + p.drawLine( int(_x+w-8), int(_y), int(_x+w-8), int(_y+h) ); + break; + } + case FlowPart::ps_process: + { + p.drawRoundRect( int(_x), int(_y), int(w), int(h+1), int(1000./w), int(1000./h) ); + break; + } + + case FlowPart::ps_round: + { + p.drawRoundRect( int(_x), int(_y), int(w), int(h+1), 30, 100 ); + break; + } + } + + p.setPen( Qt::black ); + p.setFont( font() ); + p.drawText( boundingRect(), (Qt::WordBreak | Qt::AlignHCenter | Qt::AlignVCenter), m_caption ); +} + +QString FlowPart::gotoCode( const QString& internalNodeId ) +{ + FlowPart *end = outputPart(internalNodeId); + if (!end) return ""; + return "goto "+end->id(); +} + +FlowPart* FlowPart::outputPart( const QString& internalNodeId ) +{ + Node *node = p_icnDocument->nodeWithID( nodeId(internalNodeId) ); + + FPNode *fpnode = dynamic_cast<FPNode*>(node); + if ( !fpnode || fpnode->type() == Node::fp_in ) + return 0l; + + return fpnode->outputFlowPart(); +} + +FlowPartList FlowPart::inputParts( const QString& id ) +{ + Node *node = p_icnDocument->nodeWithID(id); + + if ( FPNode *fpNode = dynamic_cast<FPNode*>(node) ) + return fpNode->inputFlowParts(); + + return FlowPartList(); +} + +FlowPartList FlowPart::inputParts() +{ + FlowPartList list; + + const NodeMap::iterator nEnd = m_nodeMap.end(); + for ( NodeMap::iterator it = m_nodeMap.begin(); it != nEnd; ++it ) + { + Node *node = p_icnDocument->nodeWithID( it.data().id ); + FlowPartList newList; + + if ( FPNode *fpNode = dynamic_cast<FPNode*>(node) ) + newList = fpNode->inputFlowParts(); + + const FlowPartList::iterator nlEnd = newList.end(); + for ( FlowPartList::iterator it = newList.begin(); it != nlEnd; ++it ) + { + if (*it) list.append(*it); + } + } + + return list; +} + +FlowPartList FlowPart::outputParts() +{ + FlowPartList list; + + const NodeMap::iterator end = m_nodeMap.end(); + for ( NodeMap::iterator it = m_nodeMap.begin(); it != end; ++it ) + { + FlowPart *part = outputPart( it.key() ); + if (part) list.append(part); + } + + return list; +} + + +FlowPart* FlowPart::endPart( QStringList ids, FlowPartList *previousParts ) +{ + if ( ids.empty() ) + { + const NodeMap::iterator end = m_nodeMap.end(); + for ( NodeMap::iterator it = m_nodeMap.begin(); it != end; ++it ) + { + ids.append( it.key() ); + } + filterEndPartIDs( &ids ); + } + + const bool createdList = (!previousParts); + if (createdList) { + previousParts = new FlowPartList; + } else if ( previousParts->contains(this) ) { + return 0l; + } + previousParts->append(this); + + if ( ids.empty() ) { + return 0l; + } + if ( ids.size() == 1 ) { + return outputPart( *(ids.begin()) ); + } + + typedef QValueList<FlowPartList> ValidPartsList; + ValidPartsList validPartsList; + + const QStringList::iterator idsEnd = ids.end(); + for ( QStringList::iterator it = ids.begin(); it != idsEnd; ++it ) + { + int prevLevel = level(); + FlowPartList validParts; + FlowPart *part = outputPart(*it); + while (part) + { + if ( !validParts.contains(part) ) + { + validParts.append(part); +// if ( part->level() >= level() ) { + const int _l = part->level(); + part = part->endPart( QStringList(), previousParts ); + prevLevel = _l; +// } else { +// part = 0l; +// } + } + else { + part = 0l; + } + } + if ( !validParts.empty() ) { + validPartsList.append(validParts); + } + } + + if (createdList) + { + delete previousParts; + previousParts = 0l; + } + + if ( validPartsList.empty() ) return 0l; + + FlowPartList firstList = *(validPartsList.begin()); + const FlowPartList::iterator flEnd = firstList.end(); + const ValidPartsList::iterator vplEnd = validPartsList.end(); + for ( FlowPartList::iterator it = firstList.begin(); it != flEnd; ++it ) + { + bool ok = true; + for ( ValidPartsList::iterator vplit = validPartsList.begin(); vplit != vplEnd; ++vplit ) + { + if ( !(*vplit).contains(*it) ) ok = false; + } + if (ok) return *it; + } + + return 0l; +} + + +void FlowPart::handleIfElse( FlowCode *code, const QString &case1Statement, const QString &case2Statement, + const QString &case1, const QString &case2 ) +{ + if (!code) return; + + FlowPart *stop = 0l; + FlowPart *part1 = outputPart(case1); + FlowPart *part2 = outputPart(case2); + + if ( part1 && part2 ) stop = endPart( QStringList::split( ',', case1+","+case2 ) ); + + if ( (!part1 && !part2) || (part1 == stop && part2 == stop) ) return; + + code->addStopPart(stop); + + if ( part1 && part1 != stop && code->isValidBranch(part1) ) + { + // Use the case1 statement + code->addCode( "if "+case1Statement+" then "+"\n{" ); + code->addCodeBranch(part1); + code->addCode("}"); + + if ( part2 && part2 != stop && code->isValidBranch(part2) ) + { + code->addCode( "else\n{" ); + code->addCodeBranch(part2); + code->addCode("}"); + } + } + else if ( code->isValidBranch(part2) ) + { + // Use the case2 statement + code->addCode( "if "+case2Statement+" then "+"\n{" ); + code->addCodeBranch(part2); + code->addCode("}"); + } + + code->removeStopPart(stop); + code->addCodeBranch(stop); +} + + +Variant * FlowPart::createProperty( const QString & id, Variant::Type::Value type ) +{ + if ( type != Variant::Type::Port + && type != Variant::Type::Pin + && type != Variant::Type::VarName + && type != Variant::Type::SevenSegment + && type != Variant::Type::KeyPad ) + return CNItem::createProperty( id, type ); + + Variant * v = createProperty( id, Variant::Type::String ); + v->setType(type); + + if ( type == Variant::Type::VarName ) + { + if ( MicroSettings * settings = m_pFlowCodeDocument->microSettings() ) + v->setAllowed( settings->variableNames() ); + connect( property(id), SIGNAL(valueChanged(QVariant, QVariant )), this, SLOT(varNameChanged(QVariant, QVariant )) ); + } + else + slotUpdateFlowPartVariables(); + + return v; +} + + +void FlowPart::slotUpdateFlowPartVariables() +{ + if (!m_pFlowCodeDocument) + return; + + MicroSettings *s = m_pFlowCodeDocument->microSettings(); + if (!s) + return; + + const PinMappingMap pinMappings = s->pinMappings(); + QStringList sevenSegMaps; + QStringList keyPadMaps; + PinMappingMap::const_iterator pEnd = pinMappings.end(); + for ( PinMappingMap::const_iterator it = pinMappings.begin(); it != pEnd; ++it ) + { + switch ( it.data().type() ) + { + case PinMapping::SevenSegment: + sevenSegMaps << it.key(); + break; + + case PinMapping::Keypad_4x3: + case PinMapping::Keypad_4x4: + keyPadMaps << it.key(); + break; + + case PinMapping::Invalid: + break; + } + } + + QStringList ports = s->microInfo()->package()->portNames(); + ports.sort(); + + QStringList pins = s->microInfo()->package()->pinIDs(PicPin::type_bidir | PicPin::type_input | PicPin::type_open); + pins.sort(); + + const VariantDataMap::iterator vEnd = m_variantData.end(); + for ( VariantDataMap::iterator it = m_variantData.begin(); it != vEnd; ++it ) + { + Variant * v = it.data(); + if ( !v ) + continue; + + if ( v->type() == Variant::Type::Port ) + v->setAllowed( ports ); + + else if ( v->type() == Variant::Type::Pin ) + v->setAllowed( pins ); + + else if ( v->type() == Variant::Type::SevenSegment ) + { + v->setAllowed( sevenSegMaps ); + if ( !sevenSegMaps.isEmpty() && !sevenSegMaps.contains( v->value().toString() ) ) + v->setValue( sevenSegMaps.first() ); + } + + else if ( v->type() == Variant::Type::KeyPad ) + { + v->setAllowed( keyPadMaps ); + if ( !keyPadMaps.isEmpty() && !keyPadMaps.contains( v->value().toString() ) ) + v->setValue( keyPadMaps.first() ); + } + } +} + + +void FlowPart::updateVarNames() +{ + if (!m_pFlowCodeDocument) + return; + + MicroSettings *s = m_pFlowCodeDocument->microSettings(); + if (!s) + return; + + const QStringList names = s->variableNames(); + const VariantDataMap::iterator end = m_variantData.end(); + for ( VariantDataMap::iterator it = m_variantData.begin(); it != end; ++it ) + { + Variant *v = it.data(); + if ( v && v->type() == Variant::Type::VarName ) + v->setAllowed(names); + } +} + + +void FlowPart::varNameChanged( QVariant newValue, QVariant oldValue ) +{ + if (!m_pFlowCodeDocument) + return; + m_pFlowCodeDocument->varNameChanged( newValue.asString(), oldValue.asString() ); +} + + +inline int nodeDirToPos( Node::node_dir dir ) +{ + switch (dir) + { + case Node::dir_right: + return 0; + case Node::dir_up: + return 1; + case Node::dir_left: + return 2; + case Node::dir_down: + return 3; + } + return 0; +} + + +void FlowPart::updateAttachedPositioning( ) +{ + if (b_deleted) + return; + + //BEGIN Rearrange text if appropriate + const QRect textPos[4] = { + QRect( offsetX()+width(), 6, 40, 16 ), + QRect( 0, offsetY()-16, 40, 16 ), + QRect( offsetX()-40, 6, 40, 16 ), + QRect( 0, offsetY()+height(), 40, 16 ) }; + + NodeInfo * stdOutputInfo = m_stdOutput ? &m_nodeMap["stdoutput"] : 0; + NodeInfo * altOutputInfo = m_altOutput ? &m_nodeMap["altoutput"] : 0l; + + Text *outputTrueText = m_textMap.contains("output_true") ? m_textMap["output_true"] : 0l; + Text *outputFalseText = m_textMap.contains("output_false") ? m_textMap["output_false"] : 0l; + + if ( stdOutputInfo && outputTrueText ) + outputTrueText->setOriginalRect( textPos[ nodeDirToPos( (Node::node_dir)stdOutputInfo->orientation ) ] ); + + if ( altOutputInfo && outputFalseText ) + outputFalseText->setOriginalRect( textPos[ nodeDirToPos( (Node::node_dir)altOutputInfo->orientation ) ] ); + + const TextMap::iterator textMapEnd = m_textMap.end(); + for ( TextMap::iterator it = m_textMap.begin(); it != textMapEnd; ++it ) + { + QRect pos = it.data()->recommendedRect(); + it.data()->move( pos.x() + x(), pos.y() + y() ); + it.data()->setGuiPartSize( pos.width(), pos.height() ); + } + //END Rearrange text if appropriate + + const NodeMap::iterator end = m_nodeMap.end(); + for ( NodeMap::iterator it = m_nodeMap.begin(); it != end; ++it ) + { + if ( !it.data().node ) + { + kdError() << k_funcinfo << "Node in nodemap is null" << endl; + continue; + } + + double nx = it.data().x; + double ny = it.data().y; + +#define round_8(x) (((x) > 0) ? int(((x)+4)/8)*8 : int(((x)-4)/8)*8) + nx = round_8(nx); + ny = round_8(ny); +#undef round_8 + + it.data().node->move( int(nx+x()), int(ny+y()) ); + it.data().node->setOrientation( (Node::node_dir)it.data().orientation ); + } +} + + +ItemData FlowPart::itemData( ) const +{ + ItemData itemData = CNItem::itemData(); + itemData.orientation = m_orientation; + return itemData; +} + + +void FlowPart::restoreFromItemData( const ItemData & itemData ) +{ + CNItem::restoreFromItemData(itemData); + if ( itemData.orientation >= 0 ) + setOrientation( uint(itemData.orientation) ); +} + + +void FlowPart::updateNodePositions() +{ + if ( m_orientation > 7 ) + { + kdWarning() << k_funcinfo << "Invalid orientation: "<<m_orientation<<endl; + return; + } + + NodeInfo * stdInputInfo = m_stdInput ? &m_nodeMap["stdinput"] : 0l; + NodeInfo * stdOutputInfo = m_stdOutput ? &m_nodeMap["stdoutput"] : 0; + NodeInfo * altOutputInfo = m_altOutput ? &m_nodeMap["altoutput"] : 0l; + + if ( m_stdInput && m_stdOutput && m_altOutput ) + { + stdInputInfo->orientation = diamondNodePositioning[m_orientation][0]; + stdOutputInfo->orientation = diamondNodePositioning[m_orientation][1]; + altOutputInfo->orientation = diamondNodePositioning[m_orientation][2]; + } + else if ( m_stdInput && m_stdOutput ) + { + stdInputInfo->orientation = inOutNodePositioning[m_orientation][0]; + stdOutputInfo->orientation = inOutNodePositioning[m_orientation][1]; + } + else if ( m_orientation < 4 ) + { + if (stdInputInfo) + stdInputInfo->orientation = inNodePositioning[m_orientation]; + else if (stdOutputInfo) + stdOutputInfo->orientation = outNodePositioning[m_orientation]; + } + else + { + kdWarning() << k_funcinfo << "Invalid orientation: "<<m_orientation<<endl; + return; + } + + const NodeMap::iterator end = m_nodeMap.end(); + for ( NodeMap::iterator it = m_nodeMap.begin(); it != end; ++it ) + { + if ( !it.data().node ) + kdError() << k_funcinfo << "Node in nodemap is null" << endl; + + else + { + switch ( it.data().orientation ) + { + case Node::dir_right: + it.data().x = offsetX()+width()+8; + it.data().y = 0; + break; + case Node::dir_up: + it.data().x = 0; + it.data().y = offsetY()-8; + break; + case Node::dir_left: + it.data().x = offsetX()-8; + it.data().y = 0; + break; + case Node::dir_down: + it.data().x = 0; + it.data().y = offsetY()+height()+8;; + break; + } + } + } + + updateAttachedPositioning(); +} + + +void FlowPart::setOrientation( uint orientation ) +{ + if ( orientation == m_orientation ) + return; + + m_orientation = orientation; + updateNodePositions(); + p_icnDocument->requestRerouteInvalidatedConnectors(); +} + + +uint FlowPart::allowedOrientations( ) const +{ + // The bit positions shown here represent whether or not that orientation is allowed, the orientation being + // what is displayed in the i'th position (0 to 3 on top, 4 to 7 on bottom) of orientation widget + + if ( m_stdInput && m_stdOutput && m_altOutput ) + return 255; + + if ( m_stdInput && m_stdOutput ) + return 119; + + if ( m_stdInput || m_stdOutput ) + return 15; + + return 0; +} + +void FlowPart::orientationPixmap( uint orientation, QPixmap & pm ) const +{ + const QSize size = pm.size(); + + if ( ! ( allowedOrientations() & ( 1 << orientation ) ) ) + { + kdWarning() << k_funcinfo << "Requesting invalid orientation of " << orientation << endl; + return; + } + + QBitmap mask( 50, 50 ); + QPainter maskPainter(&mask); + mask.fill( Qt::color0 ); + maskPainter.setBrush(Qt::color1); + maskPainter.setPen(Qt::color1); + + QPainter p(&pm); + p.setBrush(m_brushCol); + p.setPen( Qt::black ); + + // In order: right corner, top corner, left corner, bottom corner + + QPoint c[4] = { + QPoint( int(0.7*size.width()), int(0.5*size.height()) ), + QPoint( int(0.5*size.width()), int(0.4*size.height()) ), + QPoint( int(0.3*size.width()), int(0.5*size.height()) ), + QPoint( int(0.5*size.width()), int(0.6*size.height()) ) }; + + QPoint d[4]; + d[0] = c[0] + QPoint( 7, 0 ); + d[1] = c[1] + QPoint( 0, -7 ); + d[2] = c[2] + QPoint( -7, 0 ); + d[3] = c[3] + QPoint( 0, 7 ); + + if ( m_stdInput && m_stdOutput && m_altOutput ) + { + //BEGIN Draw diamond outline + QPointArray diamond(4); + for ( uint i=0; i<4; ++i ) + diamond[i] = c[i]; + + p.drawPolygon(diamond); + maskPainter.drawPolygon(diamond); + //END Draw diamond outline + + + //BEGIN Draw input + int pos0 = nodeDirToPos( diamondNodePositioning[orientation][0] ); + p.drawLine( c[pos0], d[pos0] ); + maskPainter.drawLine( c[pos0], d[pos0] ); + //END Draw input + + + //BEGIN Draw "true" output as a tick + QPointArray tick(4); + tick[0] = QPoint( -3, 0 ); + tick[1] = QPoint( 0, 2 ); + tick[2] = QPoint( 0, 2 ); + tick[3] = QPoint( 4, -2 ); + + int pos1 = nodeDirToPos( diamondNodePositioning[orientation][1] ); + tick.translate( d[pos1].x(), d[pos1].y() ); + p.drawLineSegments(tick); + maskPainter.drawLineSegments(tick); + //END Draw "true" output as a tick + + + //BEGIN Draw "false" output as a cross + QPointArray cross(4); + cross[0] = QPoint( -2, -2 ); + cross[1] = QPoint( 2, 2 ); + cross[2] = QPoint( -2, 2 ); + cross[3] = QPoint( 2, -2 ); + + int pos2 = nodeDirToPos( diamondNodePositioning[orientation][2] ); + cross.translate( d[pos2].x(), d[pos2].y() ); + p.drawLineSegments(cross); + maskPainter.drawLineSegments(cross); + //END Draw "false" output as a cross + } + + else if ( m_stdInput || m_stdOutput ) + { + p.drawRoundRect( int(0.3*size.width()), int(0.4*size.height()), int(0.4*size.width()), int(0.2*size.height()) ); + maskPainter.drawRoundRect( int(0.3*size.width()), int(0.4*size.height()), int(0.4*size.width()), int(0.2*size.height()) ); + + int hal = 5; // half arrow length + int haw = 3; // half arrow width + + QPoint arrows[4][6] = { + { QPoint( hal, 0 ), QPoint( 0, -haw ), + QPoint( hal, 0 ), QPoint( -hal, 0 ), + QPoint( hal, 0 ), QPoint( 0, haw ) }, + + { QPoint( 0, -hal ), QPoint( -haw, 0 ), + QPoint( 0, -hal ), QPoint( 0, hal ), + QPoint( 0, -hal ), QPoint( haw, 0 ) }, + + { QPoint( -hal, 0 ), QPoint( 0, -haw ), + QPoint( -hal, 0 ), QPoint( hal, 0 ), + QPoint( -hal, 0 ), QPoint( 0, haw ) }, + + { QPoint( 0, hal ), QPoint( -haw, 0 ), + QPoint( 0, hal ), QPoint( 0, -hal ), + QPoint( 0, hal ), QPoint( haw, 0 ) } }; + + int inPos = -1; + int outPos = -1; + + if ( m_stdInput && m_stdOutput ) + { + inPos = nodeDirToPos( inOutNodePositioning[orientation][0] ); + outPos = nodeDirToPos( inOutNodePositioning[orientation][1] ); + } + else if ( m_stdInput ) + { + inPos = nodeDirToPos( inNodePositioning[orientation] ); + } + else if ( m_stdOutput ) + { + outPos = nodeDirToPos( outNodePositioning[orientation] ); + } + + if ( inPos != -1 ) + { + QPointArray inArrow(6); + for ( int i=0; i<6; ++i ) + { + inArrow[i] = arrows[(inPos+2)%4][i]; + } + inArrow.translate( d[inPos].x(), d[inPos].y() ); + p.drawPolygon(inArrow); + maskPainter.drawPolygon(inArrow); + } + + if ( outPos != -1 ) + { + QPointArray outArrow(6); + for ( int i=0; i<6; ++i ) + { + outArrow[i] = arrows[outPos][i]; + } + outArrow.translate( d[outPos].x(), d[outPos].y() ); + p.drawPolygon(outArrow); + maskPainter.drawPolygon(outArrow); + } + } + + pm.setMask(mask); +} + + +#include "flowpart.moc" + + diff --git a/src/flowparts/flowpart.h b/src/flowparts/flowpart.h new file mode 100644 index 0000000..d689ebf --- /dev/null +++ b/src/flowparts/flowpart.h @@ -0,0 +1,197 @@ +/*************************************************************************** + * Copyright (C) 2003-2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef FLOWPART_H +#define FLOWPART_H + +#include "cnitem.h" + +class ICNDocument; +class Node; +class FlowCode; +class FlowCodeDocument; +class FlowPart; +class FPNode; +class QPixmap; +class QSize; + +typedef QValueList<FlowPart*> FlowPartList; + +/** +All flow parts (eg 'CallSub', 'Read from Port' ,etc) should inherit from this class. +It provides basic functionality for creating commonly used nodes, as well as a virtual function +that you should reinherit for generating the assembly code. +@short Base class for all FlowParts +@author David Saxton +*/ +class FlowPart : public CNItem +{ +Q_OBJECT +public: + enum FlowSymbol + { + ps_process, // Process - Plain rectangular box + ps_call, // Call - Rectangular box with double vertical lines at either end + ps_io, // I/O - Slanter rectangular box + ps_round, // Start/End - Rounded rectangular box + ps_decision, // Decision - Diamond shape + ps_other // Custom shape, which is ignored by FlowPart + }; + FlowPart( ICNDocument *icnDocument, bool newItem, const QString &id ); + virtual ~FlowPart(); + + virtual void generateMicrobe( FlowCode */*code*/ ) = 0; + /** + * Set a preset "orientation" of this item - 0 through 7 + */ + void setOrientation( uint orientation ); + uint orientation() const { return m_orientation; } + /** + * The allowed orientations, as bit positions of 0 through 7 + */ + uint allowedOrientations() const; + virtual ItemData itemData() const; + virtual void restoreFromItemData( const ItemData &itemData ); + /** + * Sets the caption displayed in the flowpart, resizes the item as necessary + */ + virtual void setCaption( const QString &caption ); + /** + * Traces the FlowCode document route from the nodes with the given internal + * ids, and returns the FlowPart to which: + * @li all the routes from the given nodes are eventually connected to downwards + * @li their exists one (possibly internally branched) route for each node to that part + * @param ids The list of internal ids of the nodes for the paths to begin from - if empty, + * all nodes internal nodes are used + * @param previousParts A list of parts in the calling tree. This avoids infinite recursion. + * @returns The first FlowPart satisfying these conditions, or NULL if no such part exists + */ + FlowPart* endPart( QStringList ids, FlowPartList *previousParts = 0l ); + /** + * Handles the addition of a if-else statement to the given FlowCode. This will + * order the code as necessary, adding the branches in the appropriate places + * @param code The FlowCode where the if-else will be added + * @param case1Statement The statement (e.g. "PORTA.0 is high") used for the first case + * @param case2Statement The logically opposite statement (e.g. "PORTA.0 is low") (note + that only one of the two statements will be used. + * @param case1 The internal node id for case1 + * @param case2 The internal node id for case2 + */ + void handleIfElse( FlowCode *code, const QString &case1Statement, const QString &case2Statement, + const QString &case1, const QString &case2 ); + /** + * Returns a pointer to the FlowPart that is connected to the node with the + * given internal id, or NULL if no such node / no connected part + */ + FlowPart* outputPart( const QString& internalNodeId ); + /** + * Returns the FlowParts connected to the given node + * @see outputPart + */ + FlowPartList inputParts( const QString& id ); + /** + * Returns a list of parts that are connected to *all* input parts + */ + FlowPartList inputParts(); + /** + * Returns a list of parts that are connected to *all* output parts. Note that if + * the same FlowPart is connected to more than one output, that flowpart will appear + * in the FlowPartList the number of times it is connected. + */ + FlowPartList outputParts(); + /** + * Draw the picture of the flowpart in the given orientation onto the pixmap + */ + void orientationPixmap( uint orientation, QPixmap & pm ) const; + virtual Variant * createProperty( const QString & id, Variant::Type::Value type ); + +public slots: + /** + * Called when variable name data for MicroSettings changes, and so our + * data needs updating + */ + void updateVarNames(); + /** + * Called when a variable name has changed (from an entry box) + */ + void varNameChanged( QVariant newValue, QVariant oldValue ); + /** + * Called when some of the FlowPart-specific variables (e.g. Pin or + * SevenSegment) requiring updating, such as when the PIC type changes + * or the list of Pin Maps changes. + */ + void slotUpdateFlowPartVariables(); + +protected: + virtual void updateAttachedPositioning(); + /** + * Removes the node ids that shouldn't be used for finding the end part + */ + virtual void filterEndPartIDs( QStringList *ids ) { Q_UNUSED(ids); } + /** + * Normally just passes the paint request onto CNItem::drawShape, + * although in the case of some FlowSymbols (e.g. decision), it will handle + * the drawing itself + */ + virtual void drawShape( QPainter &p ); + /** + * Returns the goto instruction that will goto the FlowPart that is connected + * to the node with the given internal id. + * For example, gotoCode("stdOutput") might return "goto delay__13" + */ + QString gotoCode( const QString& internalNodeId ); + /** + * Creates a FPNode with an internal id of "stdinput". + * The node is positioned half-way along the top of the FlowPart, + * as determined by width(), height(), x() and y() + */ + void createStdInput(); + /** + * Creates a FPNode with an internal id of "stdoutput". + * The node is positioned half-way along the bottom of the FlowPart, + * as determined by width(), height(), x() and y() + */ + void createStdOutput(); + /** + * Creates a FPNode with an internal id of "altoutput". + * The node is positioned half-way along the right of the FlowPart, + * as determined by width(), height(), x() and y() + */ + void createAltOutput(); + /** + * Initialises a symbol, by calling setItemPoints with the shape + */ + void initSymbol( FlowPart::FlowSymbol symbol, int width = 48 ); + + void initProcessSymbol() { initSymbol( FlowPart::ps_process ); } + void initCallSymbol() { initSymbol( FlowPart::ps_call ); } + void initIOSymbol() { initSymbol( FlowPart::ps_io ); } + void initRoundedRectSymbol() { initSymbol( FlowPart::ps_round ); } + void initDecisionSymbol() { initSymbol( FlowPart::ps_decision ); } + + QString m_caption; + uint m_orientation; + FPNode *m_stdInput; + FPNode *m_stdOutput; + FPNode *m_altOutput; + QGuardedPtr<FlowCodeDocument> m_pFlowCodeDocument; + + virtual void postResize(); + void updateNodePositions(); + +private: + FlowSymbol m_flowSymbol; +}; +typedef QValueList<FlowPart*> FlowPartList; + +#endif + + + diff --git a/src/flowparts/forloop.cpp b/src/flowparts/forloop.cpp new file mode 100644 index 0000000..d2fe88b --- /dev/null +++ b/src/flowparts/forloop.cpp @@ -0,0 +1,84 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "forloop.h" + +#include "libraryitem.h" +#include "flowcode.h" + +#include <klocale.h> + +Item* ForLoop::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new ForLoop( (ICNDocument*)itemDocument, newItem, id ); +} + +LibraryItem* ForLoop::libraryItem() +{ + return new LibraryItem( + QString("flow/forloop"), + i18n("For"), + i18n("Loops"), + "for.png", + LibraryItem::lit_flowpart, + ForLoop::construct ); +} + +ForLoop::ForLoop( ICNDocument *icnDocument, bool newItem, const char *id ) + : FlowContainer( icnDocument, newItem, (id) ? id : "forloop" ) +{ + m_name = i18n("For Loop"); + m_desc = i18n("The code contained in the foor loop is repeatedly executed. By default, the variable used will be incremented every time. This can be changed by entering a value other than 1 into Step.<br><br>The for loop will exit when the value contained in the variable is equal to the end value."); + + createTopContainerNode(); + createBotContainerNode(); + + createProperty( "0-var", Variant::Type::Combo ); + property("0-var")->setToolbarCaption("for"); + property("0-var")->setEditorCaption( i18n("Variable") ); + property("0-var")->setValue("x"); + + createProperty( "1-initial", Variant::Type::Combo ); + property("1-initial")->setToolbarCaption("="); + property("1-initial")->setEditorCaption( i18n("Initial Value") ); + property("1-initial")->setValue("1"); + + createProperty( "2-end", Variant::Type::Combo ); + property("2-end")->setToolbarCaption("to"); + property("2-end")->setEditorCaption( i18n("End Value") ); + property("2-end")->setValue("10"); + + createProperty( "3-step", Variant::Type::Combo ); + property("3-step")->setToolbarCaption("step"); + property("3-step")->setEditorCaption( i18n("Step") ); + property("3-step")->setValue("1"); + property("3-step")->setAdvanced(true); +} + +ForLoop::~ForLoop() +{ +} + +void ForLoop::dataChanged() +{ + if( dataString("3-step").toInt() == 1 ) + setCaption( "for " + dataString("0-var") + " = " + dataString("1-initial") + " to " + dataString("2-end") ); + else setCaption( "for " + dataString("0-var") + " = " + dataString("1-initial") + " to " + dataString("2-end") + " step " + dataString("3-step")); +} + +void ForLoop::generateMicrobe( FlowCode *code ) +{ + if( dataString("3-step").toInt() == 1 ) code->addCode( "for " + dataString("0-var") + " = " + dataString("1-initial") + " to " + dataString("2-end") + "\n{" ); + else code->addCode( "for " + dataString("0-var") + " = " + dataString("1-initial") + " to " + dataString("2-end") + " step " + dataString("3-step") +"\n{" ); + code->addCodeBranch( outputPart("int_in") ); + code->addCode("}"); + code->addCodeBranch( outputPart("ext_out") ); +} + diff --git a/src/flowparts/forloop.h b/src/flowparts/forloop.h new file mode 100644 index 0000000..c6b61b0 --- /dev/null +++ b/src/flowparts/forloop.h @@ -0,0 +1,34 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef FORLOOP_H +#define FORLOOP_H + +#include "flowcontainer.h" + +/** +@author David Saxton +*/ +class ForLoop : public FlowContainer +{ +public: + ForLoop( ICNDocument *icnDocument, bool newItem, const char *id ); + ~ForLoop(); + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + + virtual void generateMicrobe( FlowCode * ); + +protected: + void dataChanged(); +}; + +#endif diff --git a/src/flowparts/inputbutton.cpp b/src/flowparts/inputbutton.cpp new file mode 100644 index 0000000..a0564d4 --- /dev/null +++ b/src/flowparts/inputbutton.cpp @@ -0,0 +1,70 @@ +/*************************************************************************** + * Copyright (C) 2003 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "inputbutton.h" +#include "libraryitem.h" +#include "flowcode.h" + +#include <klocale.h> + +Item* InputButton::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new InputButton( (ICNDocument*)itemDocument, newItem, id ); +} + + +LibraryItem* InputButton::libraryItem() +{ + return new LibraryItem( + "flow/inputbutton", + i18n("InputButton"), + i18n("Functions"), + "ppinputbutton.png", + LibraryItem::lit_flowpart, + InputButton::construct + ); +} + + +InputButton::InputButton( ICNDocument *icnDocument, bool newItem, const char *id ) + : FlowPart( icnDocument, newItem, (id) ? id : "inputbutton" ) +{ + m_name = i18n("InputButton"); + m_desc = i18n("Pauses program execution until a inputbutton has been pressed or released (i.e. on rising or falling input), after performing debouncing."); + initProcessSymbol(); + createStdInput(); + createStdOutput(); + + createProperty( "0-trigger", Variant::Type::Select ); + property("0-trigger")->setCaption( i18n("Trigger") ); + property("0-trigger")->setAllowed( QStringList::split(',',"rising,falling") ); + property("0-trigger")->setValue("rising"); + + createProperty( "1-pin", Variant::Type::Pin ); + property("1-pin")->setCaption( i18n("Pin") ); + property("1-pin")->setValue("RA0"); +} + +InputButton::~InputButton() +{ +} + + +void InputButton::dataChanged() +{ + setCaption( i18n("Continue on %1 %2").arg(dataString("0-trigger")).arg(dataString("1-pin")) ); +} + + +void InputButton::generateMicrobe( FlowCode *code ) +{ + code->addCodeBranch( outputPart("stdoutput") ); +} + diff --git a/src/flowparts/inputbutton.h b/src/flowparts/inputbutton.h new file mode 100644 index 0000000..7467b42 --- /dev/null +++ b/src/flowparts/inputbutton.h @@ -0,0 +1,35 @@ +/*************************************************************************** + * Copyright (C) 2003 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef INPUTBUTTON_H +#define INPUTBUTTON_H + +#include "flowpart.h" + +/** +@short InputButton - does debouncing of input +@author David Saxton +*/ +class InputButton : public FlowPart +{ +public: + InputButton( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~InputButton(); + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + + virtual void generateMicrobe( FlowCode *code ); + +protected: + void dataChanged(); +}; + +#endif diff --git a/src/flowparts/interrupt.cpp b/src/flowparts/interrupt.cpp new file mode 100644 index 0000000..5e8c42f --- /dev/null +++ b/src/flowparts/interrupt.cpp @@ -0,0 +1,68 @@ +/*************************************************************************** + * Copyright (C) 2003 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "interrupt.h" + +#include "libraryitem.h" +#include "flowcode.h" + +#include <klocale.h> + +Item* Interrupt::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new Interrupt( (ICNDocument*)itemDocument, newItem, id ); +} + +LibraryItem* Interrupt::libraryItem() +{ + return new LibraryItem( + "flow/interrupt", + i18n("Interrupt"), + i18n("Common"), + "interrupt.png", + LibraryItem::lit_flowpart, + Interrupt::construct ); +} + +Interrupt::Interrupt( ICNDocument *icnDocument, bool newItem, const char *id ) + : FlowContainer( icnDocument, newItem, id ? id : "interrupt" ) +{ + m_name = i18n("Interrupt"); + m_desc = i18n("Defines the starting point of a interrupt handler."); + + QStringList interruptTypes; + interruptTypes.append("changed"); + interruptTypes.append("external"); + interruptTypes.append("timer"); + interruptTypes.append("trigger"); + + createProperty( "interrupt", Variant::Type::Select ); + property("interrupt")->setAllowed(interruptTypes); + property("interrupt")->setCaption( i18n("Interrupt") ); + property("interrupt")->setValue("trigger"); +} + +Interrupt::~Interrupt() +{ +} + +void Interrupt::dataChanged() +{ + setCaption( i18n("Interrupt %1").arg(dataString("interrupt")) ); +} + +void Interrupt::generateMicrobe( FlowCode *code ) +{ + code->addCode( "\ninterrupt "+dataString("interrupt")+"\n{" ); + code->addCodeBranch( outputPart("int_in") ); + code->addCode("}"); +} + + diff --git a/src/flowparts/interrupt.h b/src/flowparts/interrupt.h new file mode 100644 index 0000000..807cc30 --- /dev/null +++ b/src/flowparts/interrupt.h @@ -0,0 +1,35 @@ +/*************************************************************************** + * Copyright (C) 2003 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef INTERRUPT_H +#define INTERRUPT_H + +#include "flowcontainer.h" + +/** +@short FlowPart that defines the start of a interrupt +@author David Saxton +*/ +class Interrupt : public FlowContainer +{ +public: + Interrupt( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~Interrupt(); + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + + virtual void generateMicrobe( FlowCode *code ); + +protected: + void dataChanged(); +}; + +#endif diff --git a/src/flowparts/keypad.cpp b/src/flowparts/keypad.cpp new file mode 100644 index 0000000..f0d7136 --- /dev/null +++ b/src/flowparts/keypad.cpp @@ -0,0 +1,67 @@ +/*************************************************************************** + * Copyright (C) 2003 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "keypad.h" + +#include "libraryitem.h" +#include "flowcode.h" + +#include <klocale.h> + +Item* Keypad::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new Keypad( (ICNDocument*)itemDocument, newItem, id ); +} + +LibraryItem* Keypad::libraryItem() +{ + return new LibraryItem( + "flow/keypad", + i18n("Keypad"), + i18n("Functions"), + "keypad.png", + LibraryItem::lit_flowpart, + Keypad::construct + ); +} + +Keypad::Keypad( ICNDocument *icnDocument, bool newItem, const char *id ) + : FlowPart( icnDocument, newItem, id ? id : "keypad" ) +{ + m_name = i18n("Keypad"); + m_desc = i18n("Gets a key from a keypad connected to the PIC."); + initProcessSymbol(); + createStdInput(); + createStdOutput(); + + createProperty( "variable", Variant::Type::VarName ); + property("variable")->setValue("x"); + property("variable")->setCaption( i18n("Variable") ); + + Variant * v = createProperty( "keypad", Variant::Type::KeyPad ); + v->setCaption( i18n("Pin map") ); +} + +Keypad::~Keypad() +{ +} + +void Keypad::dataChanged() +{ + setCaption( i18n("Read %1 to %2").arg( dataString( "keypad" ) ).arg( dataString( "variable" ) ) ); +} + +void Keypad::generateMicrobe( FlowCode *code ) +{ + code->addCode( QString("%1 = %2").arg( dataString("variable") ).arg( dataString("keypad") ) ); + code->addCodeBranch( outputPart("stdoutput") ); +} + + diff --git a/src/flowparts/keypad.h b/src/flowparts/keypad.h new file mode 100644 index 0000000..e2ddba8 --- /dev/null +++ b/src/flowparts/keypad.h @@ -0,0 +1,35 @@ +/*************************************************************************** + * Copyright (C) 2003 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef KEYPAD_H +#define KEYPAD_H + +#include "flowpart.h" + +/** +@short FlowPart that provides a keypad +@author David Saxton +*/ +class Keypad : public FlowPart +{ +public: + Keypad( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~Keypad(); + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + + virtual void generateMicrobe( FlowCode *code ); + +protected: + void dataChanged(); +}; + +#endif diff --git a/src/flowparts/pinmapping.cpp b/src/flowparts/pinmapping.cpp new file mode 100644 index 0000000..3b85b46 --- /dev/null +++ b/src/flowparts/pinmapping.cpp @@ -0,0 +1,398 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "canvasmanipulator.h" +#include "cnitemgroup.h" +#include "eckeypad.h" +#include "ecsevensegment.h" +#include "libraryitem.h" +#include "microinfo.h" +#include "micropackage.h" +#include "node.h" +#include "pinmapping.h" +#include "viewcontainer.h" + +#include <kdebug.h> +#include <klocale.h> +#include <kstdaccel.h> + +#include <qaccel.h> +#include <qapplication.h> +#include <qframe.h> +#include <qlayout.h> + + +//BEGIN class PinMapping +PinMapping::PinMapping( Type type ) +{ + m_type = type; +} + + +PinMapping::PinMapping() +{ + m_type = Invalid; +} + + +PinMapping::~PinMapping() +{ +} +//END class PinMapping + + + +//BEGIN class PinMapEditor +PinMapEditor::PinMapEditor( PinMapping * pinMapping, MicroInfo * picInfo, QWidget * parent, const char * name ) + : KDialogBase( parent, name, true, i18n("Pin Map Editor"), Ok|Apply|Cancel, KDialogBase::Ok, true ) +{ + m_pPinMapping = pinMapping; + + m_pPinMapDocument = new PinMapDocument(); + + QAccel * accel = new QAccel( this ); + accel->connectItem( accel->insertItem( Key_Delete ), + m_pPinMapDocument, + SLOT(deleteSelection()) ); + + accel->connectItem( accel->insertItem( KStdAccel::selectAll().keyCodeQt() ), + m_pPinMapDocument, + SLOT(selectAll()) ); + + accel->connectItem( accel->insertItem( KStdAccel::undo().keyCodeQt() ), + m_pPinMapDocument, + SLOT(undo()) ); + + accel->connectItem( accel->insertItem( KStdAccel::redo().keyCodeQt() ), + m_pPinMapDocument, + SLOT(redo()) ); + + + QFrame * f = new QFrame(this); + f->setMinimumWidth( 480 ); + f->setMinimumHeight( 480 ); + + f->setFrameShape( QFrame::Box ); + f->setFrameShadow( QFrame::Plain ); + QVBoxLayout * fLayout = new QVBoxLayout( f, 1, 0, "fLayout" ); + + ViewContainer * vc = new ViewContainer( 0, 0, f ); + fLayout->addWidget( vc ); + + m_pPinMapView = static_cast<PinMapView*>(m_pPinMapDocument->createView( vc, 0 )); + + qApp->processEvents(); + + m_pPinMapDocument->init( *m_pPinMapping, picInfo ); + + enableButtonSeparator( false ); + setMainWidget(f); +} + + +void PinMapEditor::slotApply() +{ + savePinMapping(); + KDialogBase::slotApply(); +} + + +void PinMapEditor::slotOk() +{ + savePinMapping(); + KDialogBase::slotOk(); +} + + +void PinMapEditor::savePinMapping() +{ + *m_pPinMapping = m_pPinMapDocument->pinMapping(); +} +//END class PinMapEditor + + + +//BEGIN class PinMapDocument +PinMapDocument::PinMapDocument() + : ICNDocument( 0, 0, 0 ) +{ + m_pPicComponent = 0l; + m_pKeypad = 0l; + m_pSevenSegment = 0l; + m_type = dt_pinMapEditor; + + m_cmManager->addManipulatorInfo( CMSelect::manipulatorInfo() ); +} + + +PinMapDocument::~PinMapDocument() +{ +} + + +void PinMapDocument::init( const PinMapping & pinMapping, MicroInfo * microInfo ) +{ + m_pinMappingType = pinMapping.type(); + + m_pPicComponent = static_cast<PIC_IC*>( addItem( "PIC_IC", QPoint( 336, 224 ), true ) ); + m_pPicComponent->initPackage( microInfo ); + + const QStringList pins = pinMapping.pins(); + const QStringList::const_iterator end = pins.end(); + + int keypadCols = -1; // -1 means no keypad + + switch ( m_pinMappingType ) + { + case PinMapping::SevenSegment: + { + m_pSevenSegment = static_cast<ECSevenSegment*>( addItem( "ec/seven_segment", QPoint( 144, 232 ), true ) ); + + char ssPin = 'a'; + for ( QStringList::const_iterator it = pins.begin(); it != end; ++it ) + { + createConnector( m_pSevenSegment->childNode( QChar(ssPin) ), m_pPicComponent->childNode(*it) ); + ssPin++; + } + + break; + } + + case PinMapping::Keypad_4x3: + m_pKeypad = static_cast<ECKeyPad*>( addItem( "ec/keypad", QPoint( 144, 232 ), true ) ); + m_pKeypad->property("numCols")->setValue(3); + keypadCols = 3; + break; + + case PinMapping::Keypad_4x4: + m_pKeypad = static_cast<ECKeyPad*>( addItem( "ec/keypad", QPoint( 144, 232 ), true ) ); + m_pKeypad->property("numCols")->setValue(4); + keypadCols = 4; + break; + + case PinMapping::Invalid: + kdDebug() << k_funcinfo << "m_pinMappingType == Invalid" << endl; + break; + } + + if ( keypadCols != -1 ) + { + QStringList::const_iterator it = pins.begin(); + for ( unsigned row = 0; (row < 4) && (it != end); ++row, ++it ) + createConnector( m_pKeypad->childNode( QString("row_%1").arg( row ) ), m_pPicComponent->childNode( *it ) ); + + for ( unsigned col = 0; (col < keypadCols) && (it != end); ++col, ++it ) + createConnector( m_pKeypad->childNode( QString("col_%1").arg( col ) ), m_pPicComponent->childNode( *it ) ); + } + + clearHistory(); // Don't allow undoing of initial creation of stuff +} + + +bool PinMapDocument::isValidItem( Item * item ) +{ + return isValidItem( item->type() ); +} + + +bool PinMapDocument::isValidItem( const QString & id ) +{ + if ( !m_pPicComponent && id == "PIC_IC" ) + return true; + + switch ( m_pinMappingType ) + { + case PinMapping::SevenSegment: + return ( !m_pSevenSegment && id == "ec/seven_segment" ); + + case PinMapping::Keypad_4x3: + return ( !m_pKeypad && id == "ec/keypad" ); + + case PinMapping::Keypad_4x4: + return ( !m_pKeypad && id == "ec/keypad" ); + + case PinMapping::Invalid: + return false; + } + + return false; +} + + +void PinMapDocument::deleteSelection() +{ + m_selectList->removeQCanvasItem( m_pPicComponent ); + m_selectList->removeQCanvasItem( m_pSevenSegment ); + m_selectList->removeQCanvasItem( m_pKeypad ); + + ICNDocument::deleteSelection(); +} + + +PinMapping PinMapDocument::pinMapping() const +{ + const NodeMap picNodeMap = m_pPicComponent->nodeMap(); + const NodeMap::const_iterator picNodeMapEnd = picNodeMap.end(); + + QStringList picPinIDs; + QStringList attachedIDs; + Component * attached = 0l; + + switch ( m_pinMappingType ) + { + case PinMapping::SevenSegment: + for ( unsigned i = 0; i < 7; ++i ) + attachedIDs << QChar('a'+i); + attached = m_pSevenSegment; + break; + + case PinMapping::Keypad_4x3: + for ( unsigned i = 0; i < 4; ++i ) + attachedIDs << QString("row_%1").arg(i); + for ( unsigned i = 0; i < 3; ++i ) + attachedIDs << QString("col_%1").arg(i); + attached = m_pKeypad; + break; + + case PinMapping::Keypad_4x4: + for ( unsigned i = 0; i < 4; ++i ) + attachedIDs << QString("row_%1").arg(i); + for ( unsigned i = 0; i < 4; ++i ) + attachedIDs << QString("col_%1").arg(i); + attached = m_pKeypad; + break; + + case PinMapping::Invalid: + break; + } + + if ( !attached ) + return PinMapping(); + + QStringList::iterator end = attachedIDs.end(); + for ( QStringList::iterator attachedIt = attachedIDs.begin(); attachedIt != end; ++ attachedIt ) + { + Node * node = attached->childNode( *attachedIt ); + QString pinID; + + for ( NodeMap::const_iterator it = picNodeMap.begin(); it != picNodeMapEnd; ++it ) + { + if ( it.data().node->isConnected( node ) ) + { + pinID = it.key(); + break; + } + } + + picPinIDs << pinID; + } + + PinMapping pinMapping( m_pinMappingType ); + pinMapping.setPins( picPinIDs ); + + return pinMapping; +} +//END class PinMapDocument + + + +//BEGIN class PinMapView +PinMapView::PinMapView( PinMapDocument * pinMapDocument, ViewContainer * viewContainer, uint viewAreaId, const char * name ) + : ICNView( pinMapDocument, viewContainer, viewAreaId, name ) +{ +} + + +PinMapView::~PinMapView() +{ +} +//END class PinMapView + + + +//BEGIN class PIC_IC +Item* PIC_IC::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new PIC_IC( (ICNDocument*)itemDocument, newItem, id ); +} + + +LibraryItem* PIC_IC::libraryItem() +{ + return new LibraryItem( "PIC_IC", 0, 0, LibraryItem::lit_other, PIC_IC::construct ); +} + + +PIC_IC::PIC_IC( ICNDocument * icnDocument, bool newItem, const char * id ) + : Component( icnDocument, newItem, id ? id : "PIC_IC" ) +{ +} + + +PIC_IC::~PIC_IC() +{ +} + + +void PIC_IC::initPackage( MicroInfo * microInfo ) +{ + // The code in this function is a stripped down version of that in PICComponent::initPackage + + if (!microInfo) + return; + + MicroPackage * microPackage = microInfo->package(); + if (!microPackage) + return; + + //BEGIN Get pin IDs + QStringList allPinIDs = microPackage->pinIDs(); + QStringList ioPinIDs = microPackage->pinIDs( PicPin::type_bidir | PicPin::type_input | PicPin::type_open ); + + // Now, we make the unwanted pin ids blank, so a pin is not created for them + const QStringList::iterator allPinIDsEnd = allPinIDs.end(); + for ( QStringList::iterator it = allPinIDs.begin(); it != allPinIDsEnd; ++it ) + { + if ( !ioPinIDs.contains(*it) ) + *it = ""; + } + //END Get pin IDs + + + //BEGIN Remove old stuff + // Remove old text + TextMap textMapCopy = m_textMap; + const TextMap::iterator textMapEnd = textMapCopy.end(); + for ( TextMap::iterator it = textMapCopy.begin(); it != textMapEnd; ++it ) + removeDisplayText(it.key()); + + // Remove old nodes + NodeMap nodeMapCopy = m_nodeMap; + const NodeMap::iterator nodeMapEnd = nodeMapCopy.end(); + for ( NodeMap::iterator it = nodeMapCopy.begin(); it != nodeMapEnd; ++it ) + { + if ( !ioPinIDs.contains(it.key()) ) + removeNode( it.key() ); + } + //END Remove old stuff + + + + //BEGIN Create new stuff + initDIPSymbol( allPinIDs, 80 ); + initDIP(allPinIDs); + //END Create new stuff + + + addDisplayText( "picid", QRect(offsetX(), offsetY()-16, width(), 16), microInfo->id() ); +} +//END class PIC_IC + +#include "pinmapping.moc" diff --git a/src/flowparts/pinmapping.h b/src/flowparts/pinmapping.h new file mode 100644 index 0000000..6131e45 --- /dev/null +++ b/src/flowparts/pinmapping.h @@ -0,0 +1,142 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef PINMAPPING_H +#define PINMAPPING_H + +#include "component.h" +#include "icndocument.h" +#include "icnview.h" + +#include <kdialogbase.h> + +class ECKeyPad; +class ECSevenSegment; +class MicroInfo; +class PIC_IC; +class PinMapDocument; +class PinMapView; + + +/** +Stores a pin mapping Pic <--> [component] where component is set by the Type +(e.g. Keypad or Seven Segment). Used for FlowCode. +@author David Saxton +*/ +class PinMapping +{ + public: + enum Type + { + SevenSegment, + Keypad_4x3, + Keypad_4x4, + Invalid + }; + + /** + * Creates an invalid PinMapping, required by Qt templates. + */ + PinMapping(); + /** + * Creates a PinMapping with the given type. + */ + PinMapping( Type type ); + ~PinMapping(); + + Type type() const { return m_type; } + + QStringList pins() const { return m_pins; } + void setPins( const QStringList & pins ) { m_pins = pins; } + + protected: + QStringList m_pins; + Type m_type; +}; +typedef QMap< QString, PinMapping > PinMappingMap; + + + +/** +Dialog for editing a Pin Mapping +@author David Saxton +*/ +class PinMapEditor : public KDialogBase +{ + Q_OBJECT + public: + PinMapEditor( PinMapping * PinMapping, MicroInfo * Info, QWidget * parent, const char * name ); + + protected: + virtual void slotApply(); + virtual void slotOk(); + void savePinMapping(); + + PinMapping * m_pPinMapping; + PinMapDocument * m_pPinMapDocument; + PinMapView * m_pPinMapView; +}; + + + +/** +For use with FlowParts that require a pin map (e.g. Keypad and Seven Segment). +@author David Saxton +*/ +class PinMapDocument : public ICNDocument +{ + Q_OBJECT + public: + PinMapDocument(); + ~PinMapDocument(); + + void init( const PinMapping & PinMapping, MicroInfo * microInfo ); + + virtual bool isValidItem( Item * item ); + virtual bool isValidItem( const QString &itemId ); + + PinMapping pinMapping() const; + + virtual void deleteSelection(); + + protected: + PinMapping::Type m_pinMappingType; + ECKeyPad * m_pKeypad; + ECSevenSegment * m_pSevenSegment; + PIC_IC * m_pPicComponent; +}; + + +/** +@author David Saxton +*/ +class PinMapView : public ICNView +{ + Q_OBJECT + public: + PinMapView( PinMapDocument * pinMapDocument, ViewContainer * viewContainer, uint viewAreaId, const char * name = 0l ); + ~PinMapView(); +}; + + +class PIC_IC : public Component +{ + public: + PIC_IC( ICNDocument * icnDocument, bool newItem, const char *id = 0L ); + virtual ~PIC_IC(); + + virtual bool canFlip() const { return true; } + static Item * construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem * libraryItem(); + + void initPackage( MicroInfo * info ); +}; + +#endif diff --git a/src/flowparts/pulse.cpp b/src/flowparts/pulse.cpp new file mode 100644 index 0000000..2037d2f --- /dev/null +++ b/src/flowparts/pulse.cpp @@ -0,0 +1,85 @@ +/*************************************************************************** + * Copyright (C) 2003 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "libraryitem.h" +#include "flowcode.h" +#include "pulse.h" + +#include <klocale.h> + +Item* Pulse::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new Pulse( (ICNDocument*)itemDocument, newItem, id ); +} + +LibraryItem* Pulse::libraryItem() +{ + return new LibraryItem( + QString::QString("flow/pulse"), + i18n("Pulse"), + i18n("Functions"), + "pppulse.png", + LibraryItem::lit_flowpart, + Pulse::construct + ); +} + +Pulse::Pulse( ICNDocument *icnDocument, bool newItem, const char *id ) + : FlowPart( icnDocument, newItem, (id) ? id : "pulse" ) +{ + m_name = i18n("Pulse"); + m_desc = i18n("Pulse a pin high/low for a given duration."); + initProcessSymbol(); + createStdInput(); + createStdOutput(); + + createProperty( "0-duration", Variant::Type::Double ); + property("0-duration")->setCaption( i18n("Duration") ); + property("0-duration")->setUnit("sec"); + property("0-duration")->setValue(2.0); + + createProperty( "1-high", Variant::Type::Double ); + property("1-high")->setCaption( i18n("High Time") ); + property("1-high")->setUnit("sec"); + property("1-high")->setValue(0.5); + + createProperty( "2-low", Variant::Type::Double ); + property("2-low")->setCaption( i18n("High Time") ); + property("2-low")->setUnit("sec"); + property("2-low")->setValue(0.5); + + createProperty( "3-pin", Variant::Type::Pin ); + property("3-pin")->setCaption( i18n("Pin") ); + property("3-pin")->setValue("RA0"); +} + +Pulse::~Pulse() +{ +} + + +void Pulse::dataChanged() +{ + double pulse = dataDouble("0-duration"); + setCaption( i18n("Pulse %1 for %2 sec").arg(dataString("3-pin")).arg(QString::number( pulse / getMultiplier(pulse), 'f', 1 ) + getNumberMag(pulse)) ); +} + +void Pulse::generateMicrobe( FlowCode *code ) +{ + const double duration_ms = dataDouble("0-duration")*1e3; + const double high_ms = dataDouble("1-high")*1e3; + const double low_ms = dataDouble("2-low")*1e3; + const QString pin = dataString("3-pin"); + + // TODO Do we want to change the format for pulsing? + code->addCode( "pulse "+pin+" "+QString::number(duration_ms)+" "+QString::number(high_ms)+" "+QString::number(low_ms) ); + code->addCodeBranch( outputPart("stdoutput") ); +} + diff --git a/src/flowparts/pulse.h b/src/flowparts/pulse.h new file mode 100644 index 0000000..db40aee --- /dev/null +++ b/src/flowparts/pulse.h @@ -0,0 +1,35 @@ +/*************************************************************************** + * Copyright (C) 2003 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef PULSE_H +#define PULSE_H + +#include "flowpart.h" + +/** +@short FlowPart that provides a pulse +@author David Saxton +*/ +class Pulse : public FlowPart +{ +public: + Pulse( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~Pulse(); + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + + virtual void generateMicrobe( FlowCode *code ); + +protected: + void dataChanged(); +}; + +#endif diff --git a/src/flowparts/readport.cpp b/src/flowparts/readport.cpp new file mode 100644 index 0000000..d87fe85 --- /dev/null +++ b/src/flowparts/readport.cpp @@ -0,0 +1,69 @@ +/*************************************************************************** + * Copyright (C) 2003 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "readport.h" + +#include "libraryitem.h" +#include "flowcode.h" + +#include <klocale.h> + +Item* ReadPort::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new ReadPort( (ICNDocument*)itemDocument, newItem, id ); +} + +LibraryItem* ReadPort::libraryItem() +{ + return new LibraryItem( + QString("flow/readport"), + i18n("Read from Port"), + i18n("I\\/O"), + "portread.png", + LibraryItem::lit_flowpart, + ReadPort::construct ); +} + +ReadPort::ReadPort( ICNDocument *icnDocument, bool newItem, const char *id ) + : FlowPart( icnDocument, newItem, (id) ? id : "readport" ) +{ + m_name = i18n("Read from Port"); + m_desc = i18n("Assign the value of a port to a variable."); + initIOSymbol(); + createStdInput(); + createStdOutput(); + + createProperty( "0-port", Variant::Type::Port ); + property("0-port")->setToolbarCaption( i18n("Read") ); + property("0-port")->setEditorCaption( i18n("Port") ); + property("0-port")->setValue("PORTA"); + + createProperty( "1-var", Variant::Type::VarName ); + property("1-var")->setToolbarCaption( "to" ); + property("1-var")->setEditorCaption( i18n("Variable") ); + property("1-var")->setValue("x"); +} + + +ReadPort::~ReadPort() +{ +} + +void ReadPort::dataChanged() +{ + setCaption( i18n("Read %1 to %2").arg(dataString("0-port")).arg(dataString("1-var")) ); +} + +void ReadPort::generateMicrobe( FlowCode *code ) +{ + code->addCode( dataString("1-var")+" = "+dataString("0-port") ); + code->addCodeBranch( outputPart("stdoutput") ); +} + diff --git a/src/flowparts/readport.h b/src/flowparts/readport.h new file mode 100644 index 0000000..a150f64 --- /dev/null +++ b/src/flowparts/readport.h @@ -0,0 +1,35 @@ +/*************************************************************************** + * Copyright (C) 2003 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef READPORT_H +#define READPORT_H + +#include "flowpart.h" + +/** +@short FlowPart that reads from a port +@author David Saxton +*/ +class ReadPort : public FlowPart +{ +public: + ReadPort( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~ReadPort(); + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + + virtual void generateMicrobe( FlowCode * ); + +protected: + void dataChanged(); +}; + +#endif diff --git a/src/flowparts/repeat.cpp b/src/flowparts/repeat.cpp new file mode 100644 index 0000000..562bb03 --- /dev/null +++ b/src/flowparts/repeat.cpp @@ -0,0 +1,80 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "repeat.h" + +#include "libraryitem.h" +#include "flowcode.h" + +#include <klocale.h> + +Item* Repeat::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new Repeat( (ICNDocument*)itemDocument, newItem, id ); +} + +LibraryItem* Repeat::libraryItem() +{ + return new LibraryItem( + QString("flow/repeat"), + i18n("Repeat"), + i18n("Loops"), + "repeat.png", + LibraryItem::lit_flowpart, + Repeat::construct ); +} + +Repeat::Repeat( ICNDocument *icnDocument, bool newItem, const char *id ) + : FlowContainer( icnDocument, newItem, (id) ? id : "repeatloop" ) +{ + m_name = i18n("Repeat"); + m_desc = i18n("Repeatedly execute code, until the given condition is false. The condition is checked after the code has been executed.<br><br>This is different from \"While\", which checks for the condition to be true before the code is executed."); + createTopContainerNode(); + createBotContainerNode(); + + createProperty( "0var1", Variant::Type::Combo ); + property("0var1")->setToolbarCaption( "repeat until" ); + property("0var1")->setEditorCaption( i18n("Variable") ); + property("0var1")->setValue("x"); + + createProperty( "1op", Variant::Type::Select ); + property("1op")->setToolbarCaption(" "); + property("1op")->setEditorCaption( i18n("Operation") ); + property("1op")->setAllowed( QStringList::split( ',', "==,<,>,<=,>=,!=" ) ); + property("1op")->setValue("=="); + + createProperty( "2var2", Variant::Type::Combo ); + property("2var2")->setToolbarCaption(" "); + property("2var2")->setEditorCaption( i18n("Value") ); + property("2var2")->setValue("0"); +} + +Repeat::~Repeat() +{ +} + +void Repeat::dataChanged() +{ + setCaption( i18n("repeat until %1 %2 %3").arg(dataString("0var1")).arg(dataString("1op")).arg(dataString("2var2")) ); +} + +void Repeat::generateMicrobe( FlowCode *code ) +{ + code->addCode("repeat\n{\n"); + code->addCodeBranch( outputPart("int_in") ); + code->addCode("}\n"); + code->addCode("until "+dataString("0var1")+" "+dataString("1op")+" " + dataString("2var2") ); + code->addCodeBranch( outputPart("ext_out") ); +} + + + + + diff --git a/src/flowparts/repeat.h b/src/flowparts/repeat.h new file mode 100644 index 0000000..c363275 --- /dev/null +++ b/src/flowparts/repeat.h @@ -0,0 +1,34 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef REPEAT_H +#define REPEAT_H + +#include "flowcontainer.h" + +/** +@author David Saxton +*/ +class Repeat : public FlowContainer +{ +public: + Repeat( ICNDocument *icnDocument, bool newItem, const char *id ); + ~Repeat(); + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + + virtual void generateMicrobe( FlowCode * ); + +protected: + void dataChanged(); +}; + +#endif diff --git a/src/flowparts/setpin.cpp b/src/flowparts/setpin.cpp new file mode 100644 index 0000000..b92ac8d --- /dev/null +++ b/src/flowparts/setpin.cpp @@ -0,0 +1,91 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "setpin.h" + +#include "libraryitem.h" +#include "flowcode.h" +#include "picinfo.h" +#include "flowcodedocument.h" +#include "microsettings.h" + +#include <klocale.h> + +Item* SetPin::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new SetPin( (ICNDocument*)itemDocument, newItem, id ); +} + +LibraryItem* SetPin::libraryItem() +{ + return new LibraryItem( + QString("flow/setpin"), + i18n("Set Pin State"), + i18n("I\\/O"), + "pinwrite.png", + LibraryItem::lit_flowpart, + SetPin::construct ); +} + +SetPin::SetPin( ICNDocument *icnDocument, bool newItem, const char *id ) + : FlowPart( icnDocument, newItem, (id) ? id : "setpin" ) +{ + m_name = i18n("Set Pin State"); + m_desc = i18n("Set a pin on a port high or low. The pin needs to be set as an output pin."); + initIOSymbol(); + createStdInput(); + createStdOutput(); + + createProperty( "state", Variant::Type::Select ); + property("state")->setCaption( i18n("State") ); + property("state")->setAllowed( QStringList::split( ',', "high,low" ) ); + property("state")->setValue("high"); + + createProperty( "pin", Variant::Type::Pin ); + property("pin")->setCaption( i18n("Pin") ); + property("pin")->setValue("RA0"); +} + +SetPin::~SetPin() +{ +} + +void SetPin::dataChanged() +{ + setCaption( i18n("Set %1 %2").arg(dataString("pin")).arg(dataString("state")) ); +} + +void SetPin::generateMicrobe( FlowCode *code ) +{ + const QString pin = dataString("pin"); + const QString port = "PORT" + QString((QChar)pin[1]); + const QString bit = (QChar)pin[2]; + code->addCode( port+"."+bit+" = "+dataString("state") ); + code->addCodeBranch( outputPart("stdoutput") ); + +#if 0 + const QString pin = dataString("pin"); + const bool isHigh = (dataString("state") == "High"); + const QString port = "PORT" + QString((QChar)pin[1]); + const QString bit = (QChar)pin[2]; + + QString newCode; + if (isHigh) + { + newCode += "bsf " + port + "," + bit + " ; Set bit high\n"; + } + else + { + newCode += "bcf " + port + "," + bit + " ; Set bit low\n"; + } + + code->addCodeBlock( id(), newCode ); +#endif +} diff --git a/src/flowparts/setpin.h b/src/flowparts/setpin.h new file mode 100644 index 0000000..0e662bc --- /dev/null +++ b/src/flowparts/setpin.h @@ -0,0 +1,35 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef SETPIN_H +#define SETPIN_H + +#include "flowpart.h" + +/** +@short FlowPart that writes a high/low to a pin +@author David Saxton +*/ +class SetPin : public FlowPart +{ +public: + SetPin( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~SetPin(); + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + + virtual void generateMicrobe( FlowCode * ); + +private: + void dataChanged(); +}; + +#endif diff --git a/src/flowparts/sevenseg.cpp b/src/flowparts/sevenseg.cpp new file mode 100644 index 0000000..c376f00 --- /dev/null +++ b/src/flowparts/sevenseg.cpp @@ -0,0 +1,71 @@ +/*************************************************************************** + * Copyright (C) 2003 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "sevenseg.h" + +#include "libraryitem.h" +#include "flowcode.h" + +#include <klocale.h> + +Item* SevenSeg::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new SevenSeg( (ICNDocument*)itemDocument, newItem, id ); +} + + +LibraryItem* SevenSeg::libraryItem() +{ + return new LibraryItem( + "flow/sevenseg", + i18n("Seven Segment"), + "Functions", + "seven_segment.png", + LibraryItem::lit_flowpart, + SevenSeg::construct + ); +} + + +SevenSeg::SevenSeg( ICNDocument *icnDocument, bool newItem, const char *id ) + : FlowPart( icnDocument, newItem, id ? id : "sevenseg" ) +{ + m_name = i18n("SevenSeg"); + m_desc = i18n("Output to a Seven Segment display."); + initProcessSymbol(); + createStdInput(); + createStdOutput(); + + createProperty( "expression", Variant::Type::Combo ); + property("expression")->setValue("x"); + property("expression")->setCaption( i18n("Variable") ); + + createProperty( "sevenseg", Variant::Type::SevenSegment ); + property("sevenseg")->setCaption( i18n("Pin map") ); +} + + +SevenSeg::~SevenSeg() +{ +} + + +void SevenSeg::dataChanged() +{ + setCaption( i18n("Display %1 on %2").arg( dataString("expression") ).arg( dataString("sevenseg") ) ); +} + + +void SevenSeg::generateMicrobe( FlowCode *code ) +{ + code->addCode( QString("%1 = %2").arg( dataString("sevenseg") ).arg( dataString("expression") ) ); + code->addCodeBranch( outputPart("stdoutput") ); +} + diff --git a/src/flowparts/sevenseg.h b/src/flowparts/sevenseg.h new file mode 100644 index 0000000..47b2979 --- /dev/null +++ b/src/flowparts/sevenseg.h @@ -0,0 +1,35 @@ +/*************************************************************************** + * Copyright (C) 2003 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef SEVENSEG_H +#define SEVENSEG_H + +#include "flowpart.h" + +/** +@short Allows a configurable output to a seven segment display +@author David Saxton +*/ +class SevenSeg : public FlowPart +{ +public: + SevenSeg( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~SevenSeg(); + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + + virtual void generateMicrobe( FlowCode *code ); + +protected: + void dataChanged(); +}; + +#endif diff --git a/src/flowparts/start.cpp b/src/flowparts/start.cpp new file mode 100644 index 0000000..6fd8af0 --- /dev/null +++ b/src/flowparts/start.cpp @@ -0,0 +1,52 @@ +/*************************************************************************** + * Copyright (C) 2003 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "start.h" + +#include "libraryitem.h" +#include "flowcode.h" + +#include <klocale.h> + +Item* Start::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new Start( (ICNDocument*)itemDocument, newItem, id ); +} + +LibraryItem* Start::libraryItem() +{ + return new LibraryItem( + QString::QString("flow/start"), + i18n("Start"), + i18n("Common"), + "start.png", + LibraryItem::lit_flowpart, + Start::construct ); +} + +Start::Start( ICNDocument *icnDocument, bool newItem, const char *id ) + : FlowPart( icnDocument, newItem, (id) ? id : "START" ) +{ + m_name = i18n("Start"); + m_desc = i18n("Determines the initial program execution point."); + initRoundedRectSymbol(); + createStdOutput(); + setCaption( i18n("Start") ); +} + +Start::~Start() +{ +} + +void Start::generateMicrobe( FlowCode *code ) +{ + code->addCodeBranch( outputPart("stdoutput") ); +} + diff --git a/src/flowparts/start.h b/src/flowparts/start.h new file mode 100644 index 0000000..8585b4a --- /dev/null +++ b/src/flowparts/start.h @@ -0,0 +1,32 @@ +/*************************************************************************** + * Copyright (C) 2003 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef START_H +#define START_H + +#include "flowpart.h" + +/** +@short FlowPart that tells the program where to start +@author David Saxton +*/ +class Start : public FlowPart +{ +public: + Start( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~Start(); + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + + virtual void generateMicrobe( FlowCode * ); +}; + +#endif diff --git a/src/flowparts/sub.cpp b/src/flowparts/sub.cpp new file mode 100644 index 0000000..222b167 --- /dev/null +++ b/src/flowparts/sub.cpp @@ -0,0 +1,62 @@ +/*************************************************************************** + * Copyright (C) 2003 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "sub.h" + +#include "libraryitem.h" +#include "flowcode.h" + +#include <klocale.h> + +Item* Sub::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new Sub( (ICNDocument*)itemDocument, newItem, id ); +} + +LibraryItem* Sub::libraryItem() +{ + return new LibraryItem( + QString("flow/sub"), + i18n("Subroutine"), + i18n("Common"), + "sub.png", + LibraryItem::lit_flowpart, + Sub::construct ); +} + +Sub::Sub( ICNDocument *icnDocument, bool newItem, const char *id ) + : FlowContainer( icnDocument, newItem, (id) ? id : "sub" ) +{ + m_name = i18n("Sub"); + m_desc = i18n("Defines the starting point of a subroutine. Call this subroutine using \"Call Sub\""); + + createProperty( "sub", Variant::Type::Combo ); + property("sub")->setCaption( i18n("Subroutine") ); + property("sub")->setValue("MySub"); +} + +Sub::~Sub() +{ +} + +void Sub::dataChanged() +{ + setCaption( "Sub " + dataString("sub") ); +} + +void Sub::generateMicrobe( FlowCode *code ) +{ + code->addCode( "\nsub "+dataString("sub")+"\n{" ); + code->addCodeBranch( outputPart("int_in") ); + code->addCode("}"); +} + + +// #include "sub.moc" diff --git a/src/flowparts/sub.h b/src/flowparts/sub.h new file mode 100644 index 0000000..aca2005 --- /dev/null +++ b/src/flowparts/sub.h @@ -0,0 +1,35 @@ +/*************************************************************************** + * Copyright (C) 2003 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef SUB_H +#define SUB_H + +#include "flowcontainer.h" + +/** +@short FlowPart that defines the start of a subroutine +@author David Saxton +*/ +class Sub : public FlowContainer +{ +public: + Sub( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~Sub(); + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + + virtual void generateMicrobe( FlowCode *code ); + +protected: + void dataChanged(); +}; + +#endif diff --git a/src/flowparts/testpin.cpp b/src/flowparts/testpin.cpp new file mode 100644 index 0000000..85fdb37 --- /dev/null +++ b/src/flowparts/testpin.cpp @@ -0,0 +1,83 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "testpin.h" + +#include "libraryitem.h" +#include "flowcode.h" + +#include <klocale.h> + +Item* TestPin::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new TestPin( (ICNDocument*)itemDocument, newItem, id ); +} + +LibraryItem* TestPin::libraryItem() +{ + return new LibraryItem( + QString("flow/testpin"), + i18n("Test Pin State"), + i18n("I\\/O"), + "pinread.png", + LibraryItem::lit_flowpart, + TestPin::construct ); +} + +TestPin::TestPin( ICNDocument *icnDocument, bool newItem, const char *id ) + : FlowPart( icnDocument, newItem, (id) ? id : "testpin" ) +{ + m_name = i18n("Test Pin State"); + m_desc = i18n("Conditional branch point, depending on the high/low state of a pin."); + initDecisionSymbol(); + createStdInput(); + createStdOutput(); + createAltOutput(); + + createProperty( "pin", Variant::Type::Pin ); + property("pin")->setCaption( i18n("Pin") ); + property("pin")->setValue("RA0"); + + addDisplayText( "output_false", QRect( offsetX()+width(), 2, 40, 20 ), "Low" ); + addDisplayText( "output_true", QRect( 0, offsetY()+height(), 50, 20 ), "High" ); +} + + +TestPin::~TestPin() +{ +} + + +void TestPin::dataChanged() +{ + setCaption( "Test " + dataString("pin") ); +} + + +void TestPin::generateMicrobe( FlowCode *code ) +{ + const QString pin = dataString("pin"); + const QString port = "PORT" + QString((QChar)pin[1]); + const QString bit = (QChar)pin[2]; + + handleIfElse( code, port+"."+bit+" is high", port+"."+bit+" is low", "stdoutput", "altoutput" ); + +#if 0 + QString newCode; + + newCode += "btfss "+port+","+bit+" ; Check if pin is clear\n"; + newCode += gotoCode("altoutput") + " ; Pin is low\n"; + newCode += gotoCode("stdoutput") + " ; Pin is high, continue on from this point\n"; + + code->addCodeBlock( id(), newCode ); +#endif +} + + diff --git a/src/flowparts/testpin.h b/src/flowparts/testpin.h new file mode 100644 index 0000000..72a1411 --- /dev/null +++ b/src/flowparts/testpin.h @@ -0,0 +1,35 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef TESTPIN_H +#define TESTPIN_H + +#include "flowpart.h" + +/** +@short FlowPart that tests a pin to see if it's high +@author David Saxton +*/ +class TestPin : public FlowPart +{ +public: + TestPin( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~TestPin(); + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + + virtual void generateMicrobe( FlowCode *code ); + +protected: + void dataChanged(); +}; + +#endif diff --git a/src/flowparts/unary.cpp b/src/flowparts/unary.cpp new file mode 100644 index 0000000..fe0b549 --- /dev/null +++ b/src/flowparts/unary.cpp @@ -0,0 +1,90 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "unary.h" + +#include "libraryitem.h" +#include "flowcode.h" + +#include <klocale.h> + +Item* Unary::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new Unary( (ICNDocument*)itemDocument, newItem, id ); +} + +LibraryItem* Unary::libraryItem() +{ + return new LibraryItem( + QString("flow/unary"), + i18n("Unary"), + i18n("Variables"), + "unary.png", + LibraryItem::lit_flowpart, + Unary::construct ); +} + +Unary::Unary( ICNDocument *icnDocument, bool newItem, const char *id ) + : FlowPart( icnDocument, newItem, (id) ? id : "unary" ) +{ + m_name = i18n("Unary"); + m_desc = i18n("A unary operation involves only one variable. Suppo operations are:<br><ul><li><b>Rotate Left</b> rotates the binary bits of the variable left (discarding the end bits).</li><li><b>Rotate Right</b> rotates the binary bits right (discarding the start bits).</li><li><b>Increment</b> increases the value of the variable by 1. A value of 255 wraps around to 0.</li><li><b>Decrement</b> decreases the value of a variable by 1. A value of 0 wraps around to 255.</li></ul>"); + initProcessSymbol(); + createStdInput(); + createStdOutput(); + + createProperty( "0-var", Variant::Type::VarName ); + property("0-var")->setValue("x"); + property("0-var")->setCaption( i18n("Variable") ); + + createProperty( "1-op", Variant::Type::Select ); + property("1-op")->setCaption( i18n("Operation") ); + property("1-op")->setAllowed( QStringList::split( ',', "Rotate Left,Rotate Right,Increment,Decrement" ) ); + property("1-op")->setValue("Rotate Left"); +} + +Unary::~Unary() +{ +} + +void Unary::dataChanged() +{ + setCaption( dataString("0-var") + " " + dataString("1-op") ); +} + +void Unary::generateMicrobe( FlowCode *code ) +{ + const QString var = dataString("0-var"); + const QString op = dataString("1-op"); + + if ( op == "Rotate Left" ) code->addCode( "rotateleft "+var ); + else if ( op == "Rotate Right" ) code->addCode( "rotateright "+var ); + else if ( op == "Increment" ) code->addCode( "increment "+var ); + else if ( op == "Decrement" ) code->addCode( "decrement "+var ); + else; // Hmm... + code->addCodeBranch( outputPart("stdoutput") ); + +#if 0 + QString rot = dataString("1-rot"); + + if ( FlowCode::isLiteral(var) ) return; + + QString newCode; + + code->addVariable(var); + if ( rot == "Left" ) newCode += "rlf " + var + ",1 ; Unary " + var + " left through Carry, place result back in " + var + "\n"; + else newCode += "rrf " + var + ",1 ; Unary " + var + " right through Carry, place result back in " + var + "\n"; + + newCode += gotoCode("stdoutput"); + code->addCodeBlock( id(), newCode ); +#endif +} + + diff --git a/src/flowparts/unary.h b/src/flowparts/unary.h new file mode 100644 index 0000000..ec830d6 --- /dev/null +++ b/src/flowparts/unary.h @@ -0,0 +1,35 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef ROTATE_H +#define ROTATE_H + +#include "flowpart.h" + +/** +@short FlowPart that rotates a variable +@author David Saxton +*/ +class Unary : public FlowPart +{ +public: + Unary( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~Unary(); + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + + virtual void generateMicrobe( FlowCode *code ); + +protected: + void dataChanged(); +}; + +#endif diff --git a/src/flowparts/varassignment.cpp b/src/flowparts/varassignment.cpp new file mode 100644 index 0000000..44b76fd --- /dev/null +++ b/src/flowparts/varassignment.cpp @@ -0,0 +1,100 @@ +/*************************************************************************** + * Copyright (C) 2003 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "varassignment.h" + +#include "libraryitem.h" +#include "flowcode.h" + +#include <klocale.h> + +Item* VarAssignment::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new VarAssignment( (ICNDocument*)itemDocument, newItem, id ); +} + +LibraryItem* VarAssignment::libraryItem() +{ + return new LibraryItem( + QString::QString("flow/varassignment"), + i18n("Assignment"), + i18n("Variables"), + "assignment.png", + LibraryItem::lit_flowpart, + VarAssignment::construct ); +} + +VarAssignment::VarAssignment( ICNDocument *icnDocument, bool newItem, const char *id ) + : FlowPart( icnDocument, newItem, (id) ? id : "varassignment" ) +{ + m_name = i18n("Variable Assignment"); + m_desc = i18n("Assigns the evaluation of an expression to a variable. The expression can take many forms. For example:<ul><li><b>x = 2</b></li><li><b>x = y + 3</b></li><li><b>x = y + z</b></li><li><b>x = 2 * y</b></ul>"); + initProcessSymbol(); + createStdInput(); + createStdOutput(); + + createProperty( "0-var1", Variant::Type::VarName ); + property("0-var1")->setCaption( i18n("Variable") ); + property("0-var1")->setValue("x"); + + createProperty( "2-var2", Variant::Type::Combo ); + property("2-var2")->setToolbarCaption(" = "); + property("2-var2")->setEditorCaption( i18n("Value") ); + property("2-var2")->setValue("0"); + +} + +VarAssignment::~VarAssignment() +{ +} + +void VarAssignment::dataChanged() +{ + setCaption( dataString("0-var1") + " " + "=" /*dataString("1-op")*/ + " " + dataString("2-var2") ); +} + +void VarAssignment::generateMicrobe( FlowCode *code ) +{ + code->addCode( dataString("0-var1")+" "+"="/*dataString("1-op")*/+" "+dataString("2-var2") ); + code->addCodeBranch( outputPart("stdoutput") ); + +#if 0 + QString var1 = dataString("0-var1"); + QString var2 = dataString("2-var2"); + QString op = dataString("1-op"); + + if ( FlowCode::isLiteral(var1) ) return; + code->addVariable(var1); + + QString newCode; + + if ( !FlowCode::isLiteral(var1) ) + { + if ( FlowCode::isLiteral(var2) ) newCode += "movlw " + var2 + " ; Assign " + var2 + " to w register\n"; + } + + if ( !FlowCode::isLiteral(var2) ) + { + code->addVariable(var2); + newCode += "movf " + var2 + ",0 ; Move " + var2 + " to w register\n"; + } + + if ( op == "=" ) newCode += "movwf " + var1 + " ; Move contents of w register to " + var1 + "\n"; + else if ( op == "+=" ) newCode += "addwf " + var1 + ",1 ; Add contents of w register to " + var1 + " and place result back in " + var1 + "\n"; + else if ( op == "-=" ) newCode += "subwf " + var1 + ",1 ; Subtract contents of w register from " + var1 + " and place result back in " + var1 + "\n"; + else if ( op == "&=" ) newCode += "andwf " + var1 + ",1 ; Binary AND contents of w register with " + var1 + " and place result back in " + var1 + "\n"; + else if ( op == "or=" ) newCode += "iorwf " + var1 + ",1 ; Binary inclusive OR contents of w register with " + var1 + " and place result back in " + var1 + "\n"; + else if ( op == "xor=" ) newCode += "xorwf " + var1 + ",1 ; Binary exclusive OR contents of w register with " + var1 + " and place result back in " + var1 + "\n"; + + newCode += gotoCode("stdoutput"); + + code->addCodeBlock( id(), newCode ); +#endif +} diff --git a/src/flowparts/varassignment.h b/src/flowparts/varassignment.h new file mode 100644 index 0000000..25cb78c --- /dev/null +++ b/src/flowparts/varassignment.h @@ -0,0 +1,35 @@ +/*************************************************************************** + * Copyright (C) 2003 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef VARASSIGNMENT_H +#define VARASSIGNMENT_H + +#include "flowpart.h" + +/** +@short FlowPart that assigns a value to a variable +@author David Saxton +*/ +class VarAssignment : public FlowPart +{ +public: + VarAssignment( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~VarAssignment(); + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + + virtual void generateMicrobe( FlowCode *code ); + +protected: + void dataChanged(); +}; + +#endif diff --git a/src/flowparts/varcomparison.cpp b/src/flowparts/varcomparison.cpp new file mode 100644 index 0000000..e01ebf2 --- /dev/null +++ b/src/flowparts/varcomparison.cpp @@ -0,0 +1,170 @@ +/*************************************************************************** + * Copyright (C) 2003 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "varcomparison.h" + +#include "libraryitem.h" +#include "flowcode.h" + +#include <klocale.h> + +Item* VarComparison::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new VarComparison( (ICNDocument*)itemDocument, newItem, id ); +} + +LibraryItem* VarComparison::libraryItem() +{ + return new LibraryItem( + QString("flow/varcomparison"), + i18n("Comparison"), + i18n("Variables"), + "branch.png", + LibraryItem::lit_flowpart, + VarComparison::construct ); +} + +VarComparison::VarComparison( ICNDocument *icnDocument, bool newItem, const char *id ) + : FlowPart( icnDocument, newItem, (id) ? id : "varcomparison" ) +{ + m_name = i18n("Variable Comparison"); + m_desc = i18n("Conditional branch point, depending on the comparison of two values. The supported comparisons are:<ul><li><b>x == y</b> - Equality: true if x has the same value as y.</li><li><b>x < y</b> - Less than: true if x is smaller than y.</li><li><b>x > y</b> - Greater than: true if x is bigger than y.</li><li><b>x <= y</b> - Less than or equal: true if x is less than or equal to y.</li><li><b>x >= y</b> - Greater than or equal: true if x is greater than or equal to y.</li><li><b>x != y</b> - Does not equal: true if x does not have the same value as y.</li></ul>"); + initDecisionSymbol(); + createStdInput(); + createStdOutput(); + createAltOutput(); + + createProperty( "0var1", Variant::Type::Combo ); + property("0var1")->setCaption( i18n("Variable") ); + property("0var1")->setValue("x"); + + createProperty( "1op", Variant::Type::Select ); + property("1op")->setAllowed( QStringList::split( ',', "==,<,>,<=,>=,!=" ) ); + property("1op")->setValue("=="); + property("1op")->setToolbarCaption(" "); + property("1op")->setEditorCaption( i18n("Operation") ); + + createProperty( "2var2", Variant::Type::Combo ); + property("2var2")->setToolbarCaption(" "); + property("2var2")->setEditorCaption( i18n("Value") ); + property("2var2")->setValue("0"); + + addDisplayText( "output_false", QRect( offsetX()+width(), 2, 40, 20 ), "No" ); + addDisplayText( "output_true", QRect( 0, offsetY()+height(), 50, 20 ), "Yes" ); +} + +VarComparison::~VarComparison() +{ +} + +void VarComparison::dataChanged() +{ + setCaption( dataString("0var1") + " " + dataString("1op") + " " + dataString("2var2") + " ?" ); +} + +QString VarComparison::oppOp( const QString &op ) +{ + if ( op == "==" ) return "!="; + if ( op == "!=" ) return "=="; + else if ( op == "<" ) return ">="; + else if ( op == ">=" ) return "<"; + else if ( op == ">" ) return "<="; + else if ( op == "<=" ) return ">"; + else return "__UNKNOWN_OP__"; +} + +void VarComparison::generateMicrobe( FlowCode *code ) +{ + QString var1 = dataString("0var1"); + QString var2 = dataString("2var2"); + QString test = dataString("1op"); + + handleIfElse( code, var1+" "+test+" "+var2, var1+" "+oppOp(test)+" "+var2, "stdoutput", "altoutput" ); + +#if 0 + code->addCode( "if "+var1+" "+test+" "+var2+"\n{\n" ); + code->addCodeBranch( outputPart("stdoutput") ); + code->addCode("}"); + if ( outputPart("altoutput") ) + { + code->addCode("else\n{"); + code->addCodeBranch( outputPart("altoutput") ); + code->addCode("}"); + } +#endif + +#if 0 + QString newCode; + + if ( FlowCode::isLiteral(var2) ) newCode += "movlw " + var2 + " ; Move literal to register w\n"; + else + { + code->addVariable(var2); + newCode += "movf " + var2 + ",0 ; Move " + var2 + " to register w\n"; + } + + if ( FlowCode::isLiteral(var1) ) newCode += "sublw " + var1 + " ; Subtract register w from " + var1 + ", placing result in w\n"; + else + { + code->addVariable(var1); + newCode += "subwf " + var1 + ",0 ; Subtract register w from " + var1 + ", placing result in w\n"; + } + + + if ( test == "==" ) + { + // check: works + newCode += "btfss STATUS,2 ; Check if zero flag is set\n"; + newCode += gotoCode("altoutput") + " ; Result from calculation was non-zero; hence comparison is false\n"; + newCode += gotoCode("stdoutput") + " ; Ouput was zero; hence comparison is true, so continue from this point\n"; + } + else if ( test == "!=" ) + { + // check: works + newCode += "btfsc STATUS,2 ; Check if zero flag is clear\n"; + newCode += gotoCode("altoutput") + " ; Result from calculation was zero; hence comparison is false\n"; + newCode += gotoCode("stdoutput") + " ; Output was non-zero; hence comparison is true, so continue from this point\n"; + } + else if ( test == ">=" ) + { + // check: works + newCode += "btfss STATUS,0 ; Check if carry flag is set\n"; + newCode += gotoCode("altoutput") + " ; Result from calculation is negative; hence comparison is false\n"; + newCode += gotoCode("stdoutput") + " ; Result from calculation is positive or zero; so continue from this point\n"; + } + else if ( test == ">" ) + { + // check: works + newCode += "btfss STATUS,0 ; Check if carry flag is set\n"; + newCode += gotoCode("altoutput") + " ; Result is negative; hence comparison is false\n"; + newCode += "btfsc STATUS,2 ; Check if zero flag is set\n"; + newCode += gotoCode("altoutput") + " ; Result is zero; hence comparison is false\n"; + newCode += gotoCode("stdoutput") + " ; Comparison is true, so continue from this point\n"; + } + else if ( test == "<" ) + { + // check: works + newCode += "btfsc STATUS,0 ; Check if carry flag is set\n"; + newCode += gotoCode("altoutput"); + newCode += gotoCode("stdoutput"); + } + else if ( test == "<=" ) + { + // check: works + newCode += "btfsc STATUS,2 ; Check if result is zero\n"; + newCode += gotoCode("stdoutput") + " ; Result is zero; hence comparison is true\n"; + newCode += "btfsc STATUS,0 ; Check if carry flag is set\n"; + newCode += gotoCode("altoutput") + " ; Result is positive (not zero, has already tested for this); hence comparison is false\n"; + newCode += gotoCode("stdoutput") + " ; Result is negative, hence comparison is true\n"; + } + + code->addCodeBlock( id(), newCode ); +#endif +} diff --git a/src/flowparts/varcomparison.h b/src/flowparts/varcomparison.h new file mode 100644 index 0000000..b1220c8 --- /dev/null +++ b/src/flowparts/varcomparison.h @@ -0,0 +1,40 @@ +/*************************************************************************** + * Copyright (C) 2003 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef VARCOMPARISON_H +#define VARCOMPARISON_H + +#include "flowpart.h" + +/** +@short FlowPart that compares two values +@author David Saxton +*/ +class VarComparison : public FlowPart +{ +public: + VarComparison( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~VarComparison(); + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + + virtual void generateMicrobe( FlowCode *code ); + +protected: + void dataChanged(); + /** + * Use this to find the logically opposite comparison (e.g. "==" returns "!=", + * ">=" returns "<", etc). Supoorted ops: != == <= >= < > + */ + QString oppOp( const QString &op ); +}; + +#endif diff --git a/src/flowparts/while.cpp b/src/flowparts/while.cpp new file mode 100644 index 0000000..b0461df --- /dev/null +++ b/src/flowparts/while.cpp @@ -0,0 +1,79 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "while.h" + +#include "libraryitem.h" +#include "flowcode.h" + +#include <klocale.h> + +Item* While::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new While( (ICNDocument*)itemDocument, newItem, id ); +} + +LibraryItem* While::libraryItem() +{ + return new LibraryItem( + QString("flow/while"), + i18n("While"), + i18n("Loops"), + "while.png", + LibraryItem::lit_flowpart, + While::construct ); +} + +While::While( ICNDocument *icnDocument, bool newItem, const char *id ) + : FlowContainer( icnDocument, newItem, (id) ? id : "whileloop" ) +{ + m_name = i18n("While"); + m_desc = i18n("Repeatedly execute code, until the given condition is false. The condition is checked before the code has been executed.<br><br>This is different from \"Repeat\", which checks for the condition to be true after the code is executed."); + createTopContainerNode(); + createBotContainerNode(); + + createProperty( "0var1", Variant::Type::Combo ); + property("0var1")->setToolbarCaption( "while" ); + property("0var1")->setEditorCaption( i18n("Variable") ); + property("0var1")->setValue("x"); + + createProperty( "1op", Variant::Type::Select ); + property("1op")->setToolbarCaption(" "); + property("1op")->setEditorCaption( i18n("Operation") ); + property("1op")->setAllowed( QStringList::split( ',', "==,<,>,<=,>=,!=" ) ); + property("1op")->setValue("=="); + + createProperty( "2var2", Variant::Type::Combo ); + property("2var2")->setToolbarCaption(" "); + property("2var2")->setEditorCaption( i18n("Value") ); + property("2var2")->setValue("0"); +} + +While::~While() +{ +} + +void While::dataChanged() +{ + setCaption( i18n("while %1 %2 %3").arg(dataString("0var1")).arg(dataString("1op")).arg(dataString("2var2")) ); +} + +void While::generateMicrobe( FlowCode *code ) +{ + code->addCode("while "+dataString("0var1")+" "+dataString("1op")+" " + dataString("2var2")+"\n{" ); + code->addCodeBranch( outputPart("int_in") ); + code->addCode("}"); + code->addCodeBranch( outputPart("ext_out") ); +} + + + + + diff --git a/src/flowparts/while.h b/src/flowparts/while.h new file mode 100644 index 0000000..51f851a --- /dev/null +++ b/src/flowparts/while.h @@ -0,0 +1,34 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef WHILE_H +#define WHILE_H + +#include "flowcontainer.h" + +/** +@author David Saxton +*/ +class While : public FlowContainer +{ +public: + While( ICNDocument *icnDocument, bool newItem, const char *id ); + ~While(); + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + + virtual void generateMicrobe( FlowCode *code ); + +protected: + void dataChanged(); +}; + +#endif diff --git a/src/flowparts/writeport.cpp b/src/flowparts/writeport.cpp new file mode 100644 index 0000000..dede6f1 --- /dev/null +++ b/src/flowparts/writeport.cpp @@ -0,0 +1,95 @@ +/*************************************************************************** + * Copyright (C) 2003 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "writeport.h" + +#include "libraryitem.h" +#include "flowcode.h" + +#include <klocale.h> + +Item* WritePort::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new WritePort( (ICNDocument*)itemDocument, newItem, id ); +} + +LibraryItem* WritePort::libraryItem() +{ + return new LibraryItem( + QString("flow/writeport"), + i18n("Write to Port"), + i18n("I\\/O"), + "portwrite.png", + LibraryItem::lit_flowpart, + WritePort::construct ); +} + +WritePort::WritePort( ICNDocument *icnDocument, bool newItem, const char *id ) + : FlowPart( icnDocument, newItem, (id) ? id : "writeport" ) +{ + m_name = i18n("Write to Port"); + m_desc = i18n("Sets the port's pins state to high/low from the given value. Only pins that have been configured as output pins will take on the value assigned to them."); + initIOSymbol(); + createStdInput(); + createStdOutput(); + + createProperty( "0-var", Variant::Type::Combo ); + property("0-var")->setToolbarCaption( i18n("Write") ); + property("0-var")->setEditorCaption( i18n("Variable") ); + property("0-var")->setValue("x"); + + createProperty( "1-port", Variant::Type::Port ); + property("1-port")->setToolbarCaption( "to" ); + property("1-port")->setEditorCaption( i18n("Port") ); + property("1-port")->setValue("PORTA"); +} + + +WritePort::~WritePort() +{ +} + + +void WritePort::dataChanged() +{ + setCaption( i18n("Write %1 to %2").arg(dataString("0-var")).arg(dataString("1-port")) ); +} + + +void WritePort::generateMicrobe( FlowCode *code ) +{ + code->addCode( dataString("1-port")+" = "+dataString("0-var") ); + code->addCodeBranch( outputPart("stdoutput") ); + +#if 0 + QString var = dataString("var"); + QString port = dataString("port"); + + // WTF? I don't want to do this! +// QString newCode = "bsf STATUS,5 ; Move to bank 1\n"; + QString newCode; + + if ( FlowCode::isLiteral(var) ) newCode += "movlw " + var + " ; Move " + var + " to working register w\n"; + else + { + code->addVariable(var); + newCode += "movf " + var + ",0 ; Move " + var + " to working register w\n"; + } + + newCode += "movwf " + port + " ; Move register w to port\n"; + + // Same for below as for above +// newCode += "bcf STATUS,5 ; Come back to bank 0\n"; + + newCode += gotoCode("stdoutput") + "\n"; + + code->addCodeBlock( id(), newCode ); +#endif +} diff --git a/src/flowparts/writeport.h b/src/flowparts/writeport.h new file mode 100644 index 0000000..335db3b --- /dev/null +++ b/src/flowparts/writeport.h @@ -0,0 +1,35 @@ +/*************************************************************************** + * Copyright (C) 2003 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef WRITEPORT_H +#define WRITEPORT_H + +#include "flowpart.h" + +/** +@short FlowPart that writes to a port +@author David Saxton +*/ +class WritePort : public FlowPart +{ +public: + WritePort( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~WritePort(); + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + + virtual void generateMicrobe( FlowCode *code ); + +private: + void dataChanged(); +}; + +#endif diff --git a/src/fpnode.cpp b/src/fpnode.cpp new file mode 100644 index 0000000..39901c6 --- /dev/null +++ b/src/fpnode.cpp @@ -0,0 +1,212 @@ +/*************************************************************************** + * Copyright (C) 2003-2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "icndocument.h" +#include "connector.h" +#include "flowpart.h" +#include "fpnode.h" + +#include <kdebug.h> +#include <qpainter.h> + +FPNode::FPNode( ICNDocument *icnDocument, Node::node_type type, node_dir dir, const QPoint &pos, QString *id ) + : Node( icnDocument, type, dir, pos, id ) +{ + icnDocument->registerItem(this); +} + + +FPNode::~FPNode() +{ +} + + +FlowPart *FPNode::outputFlowPart() const +{ + FlowPart *flowPart = dynamic_cast<FlowPart*>(parentItem()); + + if ( type() == fp_in ) + return flowPart; + + if ( m_outputConnectorList.size() > 1 ) + kdError() << "FpNode::outputFlowPart(): outputConnectorList() size is greater than 1"<<endl; + + else if ( m_outputConnectorList.size() < 1 ) + return 0l; + + ConnectorList::const_iterator it = m_outputConnectorList.begin(); + if ( it == m_outputConnectorList.end() || !*it || !(*it)->endNode() ) + return 0L; + + return (dynamic_cast<FPNode*>((*it)->endNode()))->outputFlowPart(); +} + + +FlowPartList FPNode::inputFlowParts() const +{ + FlowPartList list; + FlowPart *flowPart = dynamic_cast<FlowPart*>(parentItem()); + if ( type() != fp_in && flowPart ) + { + list.append(flowPart); + return list; + } + const ConnectorList::const_iterator end = m_inputConnectorList.end(); + for ( ConnectorList::const_iterator it = m_inputConnectorList.begin(); it != end; ++it ) + { + if (*it) + { + Node *startNode = (*it)->startNode(); + FlowPart *flowPart = startNode ? dynamic_cast<FlowPart*>(startNode->parentItem()) : 0l; + if (flowPart) + list.append(flowPart); + } + } + return list; +} + + +inline QPointArray arrowPoints( Node::node_dir dir ) +{ + QPointArray pa(3); + switch (dir) + { + case Node::dir_right: + pa[0] = QPoint( 3, 0 ); + pa[1] = QPoint( 0, 2 ); + pa[2] = QPoint( 0, -2 ); + break; + case Node::dir_left: + pa[0] = QPoint( -3, 0 ); + pa[1] = QPoint( 0, 2 ); + pa[2] = QPoint( 0, -2 ); + break; + case Node::dir_down: + pa[0] = QPoint( 2, 0 ); + pa[1] = QPoint( -2, 0 ); + pa[2] = QPoint( 0, 3 ); + break; + case Node::dir_up: + pa[0] = QPoint( 2, 0 ); + pa[1] = QPoint( -2, 0 ); + pa[2] = QPoint( 0, -3 ); + break; + }; + return pa; +} + + +void FPNode::drawShape( QPainter &p ) +{ + const int _x = (int)x(); + const int _y = (int)y(); + + if ( type() == fp_junction && !m_inputConnectorList.isEmpty() ) + { + const ConnectorList::iterator end = m_inputConnectorList.end(); + for ( ConnectorList::iterator it = m_inputConnectorList.begin(); it != end; ++ it) + { + Connector * connector = *it; + if (!connector) + continue; + + // Work out the direction of the connector + const QPointList points = connector->connectorPoints(false); + + const int count = points.size(); + if ( count < 2 ) + continue; + + QPoint end_0 = points[count-1]; + QPoint end_1 = points[count-2]; + + QPointArray pa; + if ( end_0.x() < end_1.x() ) + { + pa = arrowPoints( Node::dir_left ); + pa.translate( 4, 0 ); + } + else if ( end_0.x() > end_1.x() ) + { + pa = arrowPoints( Node::dir_right ); + pa.translate( -4, 0 ); + } + else if ( end_0.y() < end_1.y() ) + { + pa = arrowPoints( Node::dir_up ); + pa.translate( 0, 4 ); + } + else if ( end_0.y() > end_1.y() ) + { + pa = arrowPoints( Node::dir_down ); + pa.translate( 0, -4 ); + } + else + continue; + + pa.translate( _x, _y ); + p.setPen( connector->isSelected() ? m_selectedColor : Qt::black ); + p.drawPolygon(pa); + } + return; + } + + if ( m_dir == Node::dir_right ) p.drawLine( _x, _y, _x-8, _y ); + else if ( m_dir == Node::dir_down ) p.drawLine( _x, _y, _x, _y-8 ); + else if ( m_dir == Node::dir_left ) p.drawLine( _x, _y, _x+8, _y ); + else if ( m_dir == Node::dir_up ) p.drawLine( _x, _y, _x, _y+8 ); + + QPointArray pa(3); + + // Right facing arrow + if ( (type() == fp_out && m_dir == Node::dir_right) || + (type() == fp_in && m_dir == Node::dir_left ) ) + pa = arrowPoints( Node::dir_right ); + + // Left facing arrow + else if ( (type() == fp_out && m_dir == Node::dir_left) || + (type() == fp_in && m_dir == Node::dir_right) ) + pa = arrowPoints( Node::dir_left ); + + // Down facing arrow + else if ( (type() == fp_out && m_dir == Node::dir_down) || + (type() == fp_in && m_dir == Node::dir_up) ) + pa = arrowPoints( Node::dir_down ); + + // Up facing arrow + else + pa = arrowPoints( Node::dir_up ); + + + // Note: I have not tested the positioning of the arrows for all combinations. + // In fact, most almost definitely do not work. So feel free to change the code + // as you see fit if necessary. + + if ( type() == fp_out ) + { + if ( m_dir == Node::dir_right ) pa.translate( -5, 0 ); + else if ( m_dir == Node::dir_down ) pa.translate( 0, -5 ); + else if ( m_dir == Node::dir_left ) pa.translate( 5, 0 ); + else if ( m_dir == Node::dir_up ) pa.translate( 0, 5 ); + } + else if ( type() == fp_in ) + { + if ( m_dir == Node::dir_right ); + else if ( m_dir == Node::dir_down ); + else if ( m_dir == Node::dir_left ) pa.translate( 3, 0 ); + else if ( m_dir == Node::dir_up ) pa.translate( 0, 3 ); + } + else return; + + pa.translate( _x, _y ); + p.drawPolygon(pa); +} + +#include "fpnode.moc" diff --git a/src/fpnode.h b/src/fpnode.h new file mode 100644 index 0000000..43d1834 --- /dev/null +++ b/src/fpnode.h @@ -0,0 +1,53 @@ +/*************************************************************************** + * Copyright (C) 2003-2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef FPNODE_H +#define FPNODE_H + +#include "node.h" + + +class FlowPart; +typedef QValueList<FlowPart*> FlowPartList; + +/** +You should use this node for all FlowParts. It ensures that connections between FlowParts are +always valid (eg not more than two outputs from one node, which makes no sense) +@short FlowPart node +@author David Saxton +*/ +class FPNode : public Node +{ +Q_OBJECT +public: + FPNode( ICNDocument *_icnView, Node::node_type type, node_dir dir, const QPoint &pos, QString *id = 0L ); + ~FPNode(); + + /** + * Returns a pointer to the FlowPart attached to this node if this node isInput, or + * to the other end of the connector (if one exists) if it isOutput() + */ + FlowPart *outputFlowPart() const; + /** + * Returns a list of FlowParts attached to the node - either a single-item list containing + * the FlowPart attached to this node if isOutput, or a list of FlowParts connected to the + * input (?) connectors + */ + FlowPartList inputFlowParts() const; + +protected: + virtual void drawShape( QPainter & p ); + +private: + bool m_isInput; +}; + +#endif + diff --git a/src/gui/Makefile.am b/src/gui/Makefile.am new file mode 100644 index 0000000..07b9dbb --- /dev/null +++ b/src/gui/Makefile.am @@ -0,0 +1,23 @@ +INCLUDES = -I$(top_srcdir)/src -I$(top_srcdir)/src/core \ + -I$(top_srcdir)/src/electronics -I$(top_srcdir)/src/electronics/components \ + -I$(top_srcdir)/src/electronics/simulation -I$(top_srcdir)/src/flowparts -I$(top_srcdir)/src/gui \ + -I$(top_srcdir)/src/languages -I$(top_srcdir)/src/mechanics -I$(top_srcdir)/src/micro \ + -I$(top_srcdir)/gpsim-interface $(all_includes) +METASOURCES = AUTO +noinst_LTLIBRARIES = libgui.la +libgui_la_SOURCES = contexthelp.cpp doublespinbox.cpp itemeditor.cpp \ + itemselector.cpp microsettingsdlg.cpp newfiledlg.cpp orientationwidget.cpp \ + outputmethoddlg.cpp pieditor.cpp plvitem.cpp propertieslistview.cpp asmformattingwidget.ui \ + gpasmsettingswidget.ui logicwidget.ui newfilewidget.ui newprojectwidget.ui \ + outputmethodwidget.ui microsettingswidget.ui settingsdlg.cpp oscilloscope.cpp \ + oscilloscopewidget.ui oscilloscopeview.cpp probepositioner.cpp generaloptionswidget.ui \ + logview.cpp createsubprojectwidget.ui processingoptionswidget.ui \ + sdccoptionswidget.ui projectdlgs.cpp linkeroptionswidget.ui microselectwidget.cpp \ + symbolviewer.cpp picprogrammerconfigwidget.ui newpinmappingwidget.ui programmerwidget.ui \ + programmerdlg.cpp colorcombo.cpp + +libgui_la_PCH = AUTO + +noinst_HEADERS = settingsdlg.h oscilloscope.h oscilloscopeview.h \ + probepositioner.h projectdlgs.h microselectwidget.h symbolviewer.h programmerdlg.h \ + colorcombo.h diff --git a/src/gui/asmformattingwidget.ui b/src/gui/asmformattingwidget.ui new file mode 100644 index 0000000..ecfa529 --- /dev/null +++ b/src/gui/asmformattingwidget.ui @@ -0,0 +1,213 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>AsmFormattingWidget</class> +<widget class="QWidget"> + <property name="name"> + <cstring>AsmFormattingWidget</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>425</width> + <height>219</height> + </rect> + </property> + <property name="caption"> + <string>Asm Formatting</string> + </property> + <property name="whatsThis" stdset="0"> + <string>The values control the indentation from the left margin of the various types of assembly code.</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <widget class="QGroupBox"> + <property name="name"> + <cstring>groupBox64</cstring> + </property> + <property name="title"> + <string>Output Code Indentation</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel" row="3" column="0"> + <property name="name"> + <cstring>textLabel11</cstring> + </property> + <property name="text"> + <string>'equ' Value</string> + </property> + </widget> + <widget class="QLabel" row="1" column="0"> + <property name="name"> + <cstring>textLabel8</cstring> + </property> + <property name="text"> + <string>Instruction Data</string> + </property> + </widget> + <widget class="QLabel" row="4" column="0"> + <property name="name"> + <cstring>textLabel9</cstring> + </property> + <property name="text"> + <string>Comment</string> + </property> + </widget> + <widget class="QLabel" row="2" column="0"> + <property name="name"> + <cstring>textLabel10</cstring> + </property> + <property name="text"> + <string>'equ'</string> + </property> + </widget> + <widget class="QLabel" row="0" column="0"> + <property name="name"> + <cstring>textLabel7</cstring> + </property> + <property name="text"> + <string>Instruction Name</string> + </property> + </widget> + <widget class="KIntSpinBox" row="0" column="1"> + <property name="name"> + <cstring>kcfg_IndentAsmName</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>5</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="value"> + <number>4</number> + </property> + <property name="toolTip" stdset="0"> + <string></string> + </property> + </widget> + <widget class="KIntSpinBox" row="1" column="1"> + <property name="name"> + <cstring>kcfg_IndentAsmData</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>5</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="value"> + <number>14</number> + </property> + </widget> + <widget class="KIntSpinBox" row="3" column="1"> + <property name="name"> + <cstring>kcfg_IndentEqu</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>5</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="value"> + <number>14</number> + </property> + </widget> + <widget class="KIntSpinBox" row="2" column="1"> + <property name="name"> + <cstring>kcfg_IndentEquValue</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>5</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="value"> + <number>20</number> + </property> + </widget> + <widget class="KIntSpinBox" row="4" column="1"> + <property name="name"> + <cstring>kcfg_IndentComment</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>5</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minValue"> + <number>-1</number> + </property> + <property name="value"> + <number>40</number> + </property> + </widget> + </grid> + </widget> + <widget class="QCheckBox"> + <property name="name"> + <cstring>kcfg_AutoFormatMBOutput</cstring> + </property> + <property name="text"> + <string>&Automatically format Microbe output</string> + </property> + <property name="accel"> + <string>Alt+A</string> + </property> + </widget> + <spacer> + <property name="name"> + <cstring>spacer57</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>484</height> + </size> + </property> + </spacer> + </vbox> +</widget> +<tabstops> + <tabstop>kcfg_IndentAsmName</tabstop> + <tabstop>kcfg_IndentAsmData</tabstop> + <tabstop>kcfg_IndentEquValue</tabstop> + <tabstop>kcfg_IndentEqu</tabstop> + <tabstop>kcfg_IndentComment</tabstop> + <tabstop>kcfg_AutoFormatMBOutput</tabstop> +</tabstops> +<layoutdefaults spacing="6" margin="11"/> +<includehints> + <includehint>knuminput.h</includehint> + <includehint>knuminput.h</includehint> + <includehint>knuminput.h</includehint> + <includehint>knuminput.h</includehint> + <includehint>knuminput.h</includehint> +</includehints> +</UI> diff --git a/src/gui/colorcombo.cpp b/src/gui/colorcombo.cpp new file mode 100644 index 0000000..aec39ae --- /dev/null +++ b/src/gui/colorcombo.cpp @@ -0,0 +1,194 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "colorcombo.h" + +#include <kcolordialog.h> +#include <klocale.h> +#include <qpainter.h> + +bool ColorCombo::createdPalettes = false; +QColor * ColorCombo::palette[ NumberOfSchemes ]; +int ColorCombo::paletteSize[ NumberOfSchemes ]; + +ColorCombo::ColorCombo( ColorScheme colorScheme, QWidget *parent, const char *name ) + : QComboBox( parent, name ) +{ + m_colorScheme = colorScheme; + + customColor.setRgb( 255, 255, 255 ); + internalColor.setRgb( 255, 255, 255 ); + + createPalettes(); + + addColors(); + + connect( this, SIGNAL( activated(int) ), SLOT( slotActivated(int) ) ); + connect( this, SIGNAL( highlighted(int) ), SLOT( slotHighlighted(int) ) ); +} + + +ColorCombo::~ColorCombo() +{ +} + + +void ColorCombo::createPalettes() +{ + if ( createdPalettes ) + return; + createdPalettes = true; + + paletteSize[ QtStandard ] = 17; + palette[ QtStandard ] = new QColor[ paletteSize[ QtStandard ] ]; + + int i = 0; + + palette[ QtStandard ][i++] = Qt::red; + palette[ QtStandard ][i++] = Qt::green; + palette[ QtStandard ][i++] = Qt::blue; + palette[ QtStandard ][i++] = Qt::cyan; + palette[ QtStandard ][i++] = Qt::magenta; + palette[ QtStandard ][i++] = Qt::yellow; + palette[ QtStandard ][i++] = Qt::darkRed; + palette[ QtStandard ][i++] = Qt::darkGreen; + palette[ QtStandard ][i++] = Qt::darkBlue; + palette[ QtStandard ][i++] = Qt::darkCyan; + palette[ QtStandard ][i++] = Qt::darkMagenta; + palette[ QtStandard ][i++] = Qt::darkYellow; + palette[ QtStandard ][i++] = Qt::white; + palette[ QtStandard ][i++] = Qt::lightGray; + palette[ QtStandard ][i++] = Qt::gray; + palette[ QtStandard ][i++] = Qt::darkGray; + palette[ QtStandard ][i++] = Qt::black; + + + paletteSize[ LED ] = 6; + palette[ LED ] = new QColor[ paletteSize[ LED ] ]; + + i = 0; + palette[ LED ][i++] = "#f62a2a"; + palette[ LED ][i++] = "#ff7733"; + palette[ LED ][i++] = "#ffbb33"; + palette[ LED ][i++] = "#eeee22"; + palette[ LED ][i++] = "#4cc308"; + palette[ LED ][i++] = "#22aaee"; +} + + +void ColorCombo::setColor( const QColor &col ) +{ + internalColor = col; + addColors(); +} + + +void ColorCombo::resizeEvent( QResizeEvent *re ) +{ + QComboBox::resizeEvent( re ); + addColors(); +} + + +void ColorCombo::slotActivated( int index ) +{ + if ( index == 0 ) + { + if ( KColorDialog::getColor( customColor, this ) == QDialog::Accepted ) + { + QPainter painter; + QPen pen; + QRect rect( 0, 0, width(), QFontMetrics(painter.font()).height()+4); + QPixmap pixmap( rect.width(), rect.height() ); + + if ( qGray( customColor.rgb() ) < 128 ) + pen.setColor( white ); + else + pen.setColor( black ); + + painter.begin( &pixmap ); + QBrush brush( customColor ); + painter.fillRect( rect, brush ); + painter.setPen( pen ); + painter.drawText( 2, QFontMetrics(painter.font()).ascent()+2, i18n("Custom...") ); + painter.end(); + + changeItem( pixmap, 0 ); + pixmap.detach(); + } + + internalColor = customColor; + } + else + internalColor = palette[ m_colorScheme ][ index - 1 ]; + + emit activated( internalColor ); +} + +void ColorCombo::slotHighlighted( int index ) +{ + if ( index == 0 ) + internalColor = customColor; + else + internalColor = palette[ m_colorScheme ][ index - 1 ]; + + emit highlighted( internalColor ); +} + +void ColorCombo::addColors() +{ + QPainter painter; + QPen pen; + QRect rect( 0, 0, width(), QFontMetrics(painter.font()).height()+4 ); + QPixmap pixmap( rect.width(), rect.height() ); + int i; + + clear(); + + createPalettes(); + + for ( i = 0; i < paletteSize[ m_colorScheme ]; i++ ) + if ( palette[ m_colorScheme ][i] == internalColor ) break; + + if ( i == paletteSize[ m_colorScheme ] ) + customColor = internalColor; + + if ( qGray( customColor.rgb() ) < 128 ) + pen.setColor( white ); + else + pen.setColor( black ); + + painter.begin( &pixmap ); + QBrush brush( customColor ); + painter.fillRect( rect, brush ); + painter.setPen( pen ); + painter.drawText( 2, QFontMetrics(painter.font()).ascent()+2, i18n("Custom...") ); + painter.end(); + + insertItem( pixmap ); + pixmap.detach(); + + for ( i = 0; i < paletteSize[ m_colorScheme ]; i++ ) + { + painter.begin( &pixmap ); + QBrush brush( palette[ m_colorScheme ][i] ); + painter.fillRect( rect, brush ); + painter.end(); + + insertItem( pixmap ); + pixmap.detach(); + + if ( palette[ m_colorScheme ][i] == internalColor ) + setCurrentItem( i + 1 ); + } +} + + +#include "colorcombo.moc" diff --git a/src/gui/colorcombo.h b/src/gui/colorcombo.h new file mode 100644 index 0000000..65b2714 --- /dev/null +++ b/src/gui/colorcombo.h @@ -0,0 +1,79 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef COLORCOMBO_H +#define COLORCOMBO_H + +#include <qcolor.h> +#include <qcombobox.h> + +/** +Based on KColorCombo, Copyright (C) 1997 Martin Jones (mjones@kde.org). Allows +which colours are displayed to be changed. + +@author David Saxton +*/ +class ColorCombo : public QComboBox +{ + Q_OBJECT + Q_PROPERTY( QColor color READ color WRITE setColor ) + + public: + enum ColorScheme + { + QtStandard = 0, + LED = 1, + NumberOfSchemes = 2, ///< for internal usage; this should be one less than the number of items in the enum + }; + + /** + * Constructs a color combo box. + */ + ColorCombo( ColorScheme colorScheme, QWidget *parent, const char *name = 0L ); + ~ColorCombo(); + + /** + * Selects the color @p col. + */ + void setColor( const QColor & col ); + /** + * Returns the currently selected color. + **/ + QColor color() const { return internalColor; } + + signals: + /** + * Emitted when a new color box has been selected. + */ + void activated( const QColor &col ); + /** + * Emitted when a new item has been highlighted. + */ + void highlighted( const QColor &col ); + + protected slots: + void slotActivated( int index ); + void slotHighlighted( int index ); + + protected: + virtual void resizeEvent( QResizeEvent *re ); + void addColors(); + void createPalettes(); + + QColor customColor; + QColor internalColor; + ColorScheme m_colorScheme; + + static bool createdPalettes; + static QColor * palette[ NumberOfSchemes ]; + static int paletteSize[ NumberOfSchemes ]; +}; + +#endif diff --git a/src/gui/contexthelp.cpp b/src/gui/contexthelp.cpp new file mode 100644 index 0000000..3444737 --- /dev/null +++ b/src/gui/contexthelp.cpp @@ -0,0 +1,107 @@ +/*************************************************************************** + * Copyright (C) 2003-2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "cnitem.h" +#include "cnitemgroup.h" +#include "contexthelp.h" +#include "itemlibrary.h" +#include "katemdi.h" + +#include <klocale.h> + +#include <qlayout.h> +#include <qlabel.h> +#include <qregexp.h> +#include <qtextbrowser.h> +#include <qwhatsthis.h> + +#include <assert.h> + +ContextHelp * ContextHelp::m_pSelf = 0l; + +ContextHelp * ContextHelp::self( KateMDI::ToolView * parent ) +{ + if (!m_pSelf) + { + assert(parent); + m_pSelf = new ContextHelp(parent); + } + return m_pSelf; +} + + +ContextHelp::ContextHelp( KateMDI::ToolView * parent ) + : QWidget( parent, "Context Help" ) +{ + QWhatsThis::add( this, i18n("Provides context-sensitive help relevant to the current editing being performed.") ); + + QVBoxLayout *vlayout = new QVBoxLayout( this, 0, 6 ); + + m_nameLbl = new QLabel( this, "" ); + vlayout->addWidget(m_nameLbl); + vlayout->addSpacing(8); + + m_info = new QTextBrowser( this, "" ); + vlayout->addWidget(m_info); + m_info->setSizePolicy( QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding ); + + QSpacerItem *spacer3 = new QSpacerItem( 1, 1, QSizePolicy::Preferred, QSizePolicy::Preferred ); + vlayout->addItem(spacer3); + + slotClear(); +} + + +ContextHelp::~ContextHelp() +{ +} + + +void ContextHelp::slotUpdate( Item *item ) +{ + if (!item) + { + slotClear(); + return; + } + m_nameLbl->setText("<h2>"+item->name()+"</h2>"); + m_info->setText( "<b></b>"+item->description() ); +} + + +void ContextHelp::slotClear() +{ + m_nameLbl->setText(i18n("<h2>No Item Selected</h2>")); + m_info->setText(""); +} + + +void ContextHelp::slotMultipleSelected() +{ + m_nameLbl->setText(i18n("<h2>Multiple Items</h2>")); + m_info->setText(""); +} + + +void ContextHelp::setContextHelp(const QString& name, const QString& help) +{ + m_nameLbl->setText("<h2>"+name+"</h2>"); + QString parsed = help; + parseInfo(parsed); + m_info->setText( "<b></b>"+parsed ); +} + +void ContextHelp::parseInfo( QString &info ) +{ + info.replace("<example>","<br><br><b>Example:</b><blockquote>"); + info.replace("</example>","</blockquote>"); +} + +#include "contexthelp.moc" diff --git a/src/gui/contexthelp.h b/src/gui/contexthelp.h new file mode 100644 index 0000000..90159b7 --- /dev/null +++ b/src/gui/contexthelp.h @@ -0,0 +1,58 @@ +/*************************************************************************** + * Copyright (C) 2003-2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef CONTEXTHELP_H +#define CONTEXTHELP_H + +#include <qwidget.h> + +class Item; +class ContextHelp; +class QLabel; +class QTextBrowser; +namespace KateMDI { class ToolView; } + +/** +Sidebar that provides context-sensitive help for whatever the user is currently +helping (e.g. pinouts, command references, etc). Not to be confused with +ItemEditor, which which allows editing of data specific to the selected CNItem +in a ICNDocument. + +@author David Saxton +*/ +class ContextHelp : public QWidget +{ + Q_OBJECT + public: + static ContextHelp * self( KateMDI::ToolView * parent = 0l ); + static QString toolViewIdentifier() { return "ContextHelp"; } + + ~ContextHelp(); + /** + * Replace special tags with appropriate html formatting code. + */ + void parseInfo( QString &info ); + + public slots: + void slotClear(); + void slotMultipleSelected(); + void slotUpdate( Item *item ); + void setContextHelp(const QString& name, const QString &help); + + private: + ContextHelp( KateMDI::ToolView * parent ); + + QLabel *m_nameLbl; + QTextBrowser *m_info; + static ContextHelp * m_pSelf; +}; + +#endif + diff --git a/src/gui/createsubprojectwidget.ui b/src/gui/createsubprojectwidget.ui new file mode 100644 index 0000000..267739e --- /dev/null +++ b/src/gui/createsubprojectwidget.ui @@ -0,0 +1,99 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>CreateSubprojectWidget</class> +<widget class="QWidget"> + <property name="name"> + <cstring>CreateSubprojectWidget</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>489</width> + <height>82</height> + </rect> + </property> + <property name="caption"> + <string>Create Subproject</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <widget class="QLabel" row="0" column="0" rowspan="1" colspan="2"> + <property name="name"> + <cstring>textLabel1</cstring> + </property> + <property name="font"> + <font> + <bold>1</bold> + </font> + </property> + <property name="text"> + <string>Subproject Details</string> + </property> + </widget> + <widget class="QLabel" row="2" column="0"> + <property name="name"> + <cstring>textLabel2</cstring> + </property> + <property name="text"> + <string>Target File:</string> + </property> + </widget> + <widget class="QLabel" row="1" column="0"> + <property name="name"> + <cstring>textLabel1_2</cstring> + </property> + <property name="text"> + <string>Type:</string> + </property> + </widget> + <widget class="QComboBox" row="1" column="1"> + <item> + <property name="text"> + <string>Program</string> + </property> + </item> + <item> + <property name="text"> + <string>Library</string> + </property> + </item> + <property name="name"> + <cstring>m_typeCombo</cstring> + </property> + </widget> + <widget class="KURLRequester" row="2" column="1"> + <property name="name"> + <cstring>m_targetFile</cstring> + </property> + </widget> + <spacer row="3" column="1"> + <property name="name"> + <cstring>spacer3</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>20</height> + </size> + </property> + </spacer> + </grid> +</widget> +<layoutdefaults spacing="6" margin="11"/> +<includehints> + <includehint>kurlrequester.h</includehint> + <includehint>klineedit.h</includehint> + <includehint>kpushbutton.h</includehint> +</includehints> +</UI> diff --git a/src/gui/doublespinbox.cpp b/src/gui/doublespinbox.cpp new file mode 100644 index 0000000..b8672b5 --- /dev/null +++ b/src/gui/doublespinbox.cpp @@ -0,0 +1,278 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "cnitem.h" +#include "doublespinbox.h" + +#include <kdebug.h> +#include <kglobal.h> +#include <klocale.h> + +#include <qlineedit.h> +#include <qregexp.h> +#include <qtimer.h> + +#include <algorithm> +#include <cmath> +using namespace std; + + +inline int roundDouble( double val ) +{ + return (val > 0) ? int(val+0.5) : int(val-0.5); +} + + +DoubleSpinBox::DoubleSpinBox( double lower, double upper, double minAbs, double value, const QString &unit, QWidget * parent ) + : QSpinBox( parent ) +{ + m_lastEmittedValue = value; + m_unit = unit; + m_minValue = lower; + m_maxValue = upper; + m_minAbsValue = minAbs; + m_queuedSuffix = QString::null; + + editor()->setAlignment( Qt::AlignRight ); + + connect( this, SIGNAL(valueChanged(int)), this, SLOT(checkIfChanged()) ); + QSpinBox::setMinValue( -(1<<30) ); + QSpinBox::setMaxValue( +(1<<30) ); + setValue( value ); + + setValidator( 0 ); +} + + +DoubleSpinBox::~DoubleSpinBox() +{ +} + + +double DoubleSpinBox::value() +{ + return getDisplayedNumber( 0 ) * getMult(); +} + + +void DoubleSpinBox::setValue( double value ) +{ + if ( value > maxValue() ) + value = maxValue(); + + else if ( value < minValue() ) + value = minValue(); + + if ( std::abs(value) < m_minAbsValue*0.9999 ) + value = 0.0; + + updateSuffix( value ); + + QSpinBox::setValue( roundDouble( (value / Item::getMultiplier( value )) * 100 ) ); +} + + +void DoubleSpinBox::setUnit( const QString & unit ) +{ + updateSuffix( value() ); + m_unit = unit; +} + + +void DoubleSpinBox::updateSuffix( double value ) +{ + m_queuedSuffix = " " + CNItem::getNumberMag( value ) + m_unit; + + // Set suffix to be empty if it is nothing but white space + if ( m_queuedSuffix.stripWhiteSpace().isEmpty() ) + m_queuedSuffix = ""; + + QTimer::singleShot( 0, this, SLOT(setQueuedSuffix()) ); +} + + +void DoubleSpinBox::setQueuedSuffix() +{ + bool changed = false; + if ( !m_queuedSuffix.isNull() && suffix() != m_queuedSuffix ) + { + setSuffix( m_queuedSuffix ); + changed = true; + } + m_queuedSuffix = QString::null; + + if ( changed ) + emit valueChanged( value() ); +} + + +double DoubleSpinBox::getMult() +{ + QString text = this->text().stripWhiteSpace(); + if ( !m_queuedSuffix.isNull() ) + { + QString nsSuffix = suffix().stripWhiteSpace(); + + if ( nsSuffix.isEmpty() ) + text.append( m_queuedSuffix ); + else + text.replace( nsSuffix, m_queuedSuffix ); + } + + if ( text.length() == 0 ) + return 1.0; + + if ( text.endsWith( m_unit, false ) ) + text = text.remove( text.length() - m_unit.length(), m_unit.length() ); + + text.stripWhiteSpace(); + + QChar siExp = text[ text.length()-1 ]; + + if ( siExp.isLetter() || siExp.isSymbol() ) + return CNItem::getMultiplier((QString)siExp); + + else + return 1; +} + + +double DoubleSpinBox::getDisplayedNumber( bool * ok ) +{ + KLocale * locale = KGlobal::locale(); + + // Fetch the characters that we don't want to discard + const QString exclude = locale->decimalSymbol() + + locale->thousandsSeparator() + + locale->positiveSign() + + locale->negativeSign(); + + QString number = cleanText().remove( QRegExp("[^"+exclude+"\\d]") ); + + return locale->readNumber( number, ok ); +} + + +int DoubleSpinBox::mapTextToValue( bool * ok ) +{ + (void)ok; + + double value = this->value(); + + if ( value > maxValue() ) + value = maxValue(); + + else if ( value < minValue() ) + value = minValue(); + + if ( std::abs(value) < m_minAbsValue*0.9999 ) + value = 0.0; + + updateSuffix( value ); + + value /= Item::getMultiplier( value ); + + // Precision of 2 extra digits + return int( value * 100 ); +} + + +QString DoubleSpinBox::mapValueToText( int v ) +{ + double val = double(v)/100.0; + + int leftDigits = (int)floor( log10( abs(val) ) ) + 1; + if ( leftDigits < 0 ) + leftDigits = 0; + else if ( leftDigits > 3 ) + leftDigits = 3; + + KLocale * locale = KGlobal::locale(); + return locale->formatNumber( val, 3-leftDigits ); +} + + +void DoubleSpinBox::checkIfChanged() +{ + double newValue = value(); + + if ( m_lastEmittedValue == newValue ) + return; + + m_lastEmittedValue = newValue; + emit valueChanged( m_lastEmittedValue ); +} + + +double DoubleSpinBox::roundToOneSF( double value ) +{ + if ( value == 0.0 ) + return 0.0; + + value *= 1.000001; + double tens = pow( 10.0, floor(log10( abs(value) )) ); + + return int ( value / tens ) * tens; +} + + +void DoubleSpinBox::stepUp() +{ + double value = roundToOneSF( this->value() ); + + if ( value == 0 ) + value = m_minAbsValue; + + else if ( value > 0 ) + value += std::pow( 10., std::floor( std::log10(value) ) ); + + else + { + double sub = std::pow(10., std::floor( std::log10(std::abs(value))-1) ); + value += std::pow( 10., std::floor( std::log10(std::abs(value)-sub) ) ); + } + + value *= 1.00001; + + if ( std::abs(value) < m_minAbsValue ) + value = 0.; + + setValue( value ); +} + + +void DoubleSpinBox::stepDown() +{ + double value = roundToOneSF( this->value() ); + + if ( value == 0 ) + value = -m_minAbsValue; + + else if ( value > 0 ) + { + double sub = std::pow(10., std::floor( std::log10(value)-1) ); + value -= std::pow( 10., std::floor( std::log10(value-sub) ) ); + } + else + { + double add = std::pow(10., std::floor( std::log10(std::abs(value))-1) ); + value -= std::pow( 10., std::floor( std::log10(std::abs(value)+add) ) ); + } + + value *= 1.00001; + + if ( std::abs(value) < m_minAbsValue ) + value = 0.; + + setValue( value ); +} + +#include "doublespinbox.moc" + diff --git a/src/gui/doublespinbox.h b/src/gui/doublespinbox.h new file mode 100644 index 0000000..75f6c90 --- /dev/null +++ b/src/gui/doublespinbox.h @@ -0,0 +1,131 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef DOUBLESPINBOX_H +#define DOUBLESPINBOX_H + +#include <qspinbox.h> + +/** +Where appropriate, function names with value in them should +be prefixed with "real" - e.g. realValue() - to get the value stored in the +spin box plus the SI magnitude symbol it is showing + +@author David Saxton +*/ +class DoubleSpinBox : public QSpinBox +{ + Q_OBJECT + public: + DoubleSpinBox( double lower, double upper, double minAbs, double value, const QString & unit, QWidget * parent = 0 ); + virtual ~DoubleSpinBox(); + + /** + * The minimum value is the lowest number that the user can enter. + */ + double minValue() const { return m_minValue; } + /** + * @see minValue + */ + void setMinValue( double minValue ) { m_minValue = minValue; } + /** + * The minimum value is the lowest number that the user can enter. + */ + void setMinValue( int minValue ) { m_minValue = minValue; } + /** + * The maximum value is the highest number that the user can enter. + */ + double maxValue() const { return m_maxValue; } + /** + * @see maxValue + */ + void setMaxValue( double maxValue ) { m_maxValue = maxValue; } + /** + * @see maxValue + */ + void setMaxValue( int maxValue ) { m_maxValue = maxValue; } + /** + * The minimum absolute value is the smallest value that the user can + * enter before the value is considered 0. + */ + void setMinAbsValue( double minAbsValue ) { m_minAbsValue = minAbsValue; } + /** + * The actual value that the user has entered - e.g. if the spinbox + * displays "100 kF", then the value returned will be 1e5. + */ + double value(); + /** + * Set the value to be displayed - e.g. if value is 1e5, then the + * spinbox might display "100 kF". + */ + void setValue( double value ); + /** + * Sets the unit used, e.g. "F" + */ + void setUnit( const QString & unit ); + + public slots: + virtual void stepUp(); + virtual void stepDown(); + + signals: + /** + * This value is emitted whenever the value of the spinbox changes. + */ + void valueChanged( double value ); + + protected slots: + /** + * Checks if the value has changed - and if so, emits a valueChanged + * signal. + */ + void checkIfChanged(); + /** + * Sets the suffix from m_queuedSuffix. Called from QTimer::singleShot + * to avoid strange recursion problems. + */ + void setQueuedSuffix(); + + protected: + /** + * Updates the suffix using m_unit and value. + */ + void updateSuffix( double value ); + /** + * Returns the multiplication number from what is displayed + * in the box, e.g. "10 kV" will return "1000" due to the letter "k" presence + */ + double getMult(); + /** + * Returns the number currently displayed in the spin box. + */ + double getDisplayedNumber( bool * ok ); + /** + * Overloaded the method in QSpinxBox to allow SI prefixes to be entered + */ + virtual int mapTextToValue( bool * ok ); + /** + * Overloaded the method in QSpinxBox to allow SI prefixes to be entered + */ + virtual QString mapValueToText( int v ); + /** + * Returns value rounded off to one significant figure. + */ + double roundToOneSF( double value ); + + QString m_queuedSuffix; ///< Used + QString m_unit; + double m_minValue; + double m_maxValue; + double m_minAbsValue; + double m_lastEmittedValue; +}; + +#endif diff --git a/src/gui/generaloptionswidget.ui b/src/gui/generaloptionswidget.ui new file mode 100644 index 0000000..0f3ee2a --- /dev/null +++ b/src/gui/generaloptionswidget.ui @@ -0,0 +1,269 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>GeneralOptionsWidget</class> +<widget class="QWidget"> + <property name="name"> + <cstring>GeneralOptionsWidget</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>456</width> + <height>488</height> + </rect> + </property> + <property name="caption"> + <string>General Options</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <widget class="QCheckBox" row="0" column="0" rowspan="1" colspan="2"> + <property name="name"> + <cstring>kcfg_ReuseSameViewForOutput</cstring> + </property> + <property name="text"> + <string>Reuse the same output view for code generation</string> + </property> + <property name="accel"> + <string></string> + </property> + </widget> + <widget class="QCheckBox" row="1" column="0" rowspan="1" colspan="2"> + <property name="name"> + <cstring>kcfg_ShowVoltageBars</cstring> + </property> + <property name="text"> + <string>Show voltage bars &on electronic components</string> + </property> + <property name="accel"> + <string>Alt+O</string> + </property> + </widget> + <widget class="KColorButton" row="2" column="1"> + <property name="name"> + <cstring>kcfg_GridColor</cstring> + </property> + <property name="text"> + <string>Grid Colour</string> + </property> + <property name="toolTip" stdset="0"> + <string>The grid color in the work area.</string> + </property> + </widget> + <widget class="QCheckBox" row="2" column="0"> + <property name="name"> + <cstring>kcfg_ShowGrid</cstring> + </property> + <property name="text"> + <string>Show &grid:</string> + </property> + <property name="accel"> + <string>Alt+G</string> + </property> + </widget> + <widget class="KIntSpinBox" row="3" column="1"> + <property name="name"> + <cstring>kcfg_MaxUndo</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>5</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="maxValue"> + <number>100000</number> + </property> + </widget> + <widget class="QLabel" row="3" column="0"> + <property name="name"> + <cstring>textLabel1</cstring> + </property> + <property name="text"> + <string>Maximum undo steps for work area:</string> + </property> + <property name="toolTip" stdset="0"> + <string>Maximum undo steps for work area. This doesn't apply to text documents - that is configurable seperately under Configure Editor.</string> + </property> + </widget> + <widget class="QGroupBox" row="4" column="0" rowspan="1" colspan="2"> + <property name="name"> + <cstring>groupBox4</cstring> + </property> + <property name="title"> + <string>Convenience</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QCheckBox"> + <property name="name"> + <cstring>kcfg_RestoreDocumentsOnStartup</cstring> + </property> + <property name="text"> + <string>Restore opened doc&uments on startup</string> + </property> + <property name="accel"> + <string>Alt+U</string> + </property> + </widget> + <widget class="QCheckBox"> + <property name="name"> + <cstring>kcfg_RaiseItemSelectors</cstring> + </property> + <property name="text"> + <string>Raise the &appropriate item selector on creating a new document</string> + </property> + <property name="accel"> + <string>Alt+A</string> + </property> + </widget> + <widget class="QCheckBox"> + <property name="name"> + <cstring>kcfg_RaiseMessagesLog</cstring> + </property> + <property name="text"> + <string>Raise the &Messages log when compiling</string> + </property> + <property name="accel"> + <string>Alt+M</string> + </property> + </widget> + </vbox> + </widget> + <widget class="QGroupBox" row="5" column="0" rowspan="1" colspan="2"> + <property name="name"> + <cstring>groupBox21</cstring> + </property> + <property name="title"> + <string>Display Refresh Rate</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout8_2</cstring> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QSlider" row="0" column="1"> + <property name="name"> + <cstring>refreshRateSlider</cstring> + </property> + <property name="maxValue"> + <number>4</number> + </property> + <property name="pageStep"> + <number>1</number> + </property> + <property name="value"> + <number>2</number> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="tickmarks"> + <enum>Below</enum> + </property> + </widget> + <widget class="QLabel" row="0" column="0"> + <property name="name"> + <cstring>textLabel3_2</cstring> + </property> + <property name="text"> + <string>Refresh rate:</string> + </property> + </widget> + <widget class="QLabel" row="1" column="1"> + <property name="name"> + <cstring>refreshRateLabel</cstring> + </property> + <property name="text"> + <string>Medium (50 FPS)</string> + </property> + </widget> + </grid> + </widget> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel9_2</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>5</hsizetype> + <vsizetype>1</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>This is the number of times per second that the work area view is updated; a compromise between CPU usage and smoothness of display.</string> + </property> + <property name="textFormat"> + <enum>PlainText</enum> + </property> + <property name="scaledContents"> + <bool>false</bool> + </property> + <property name="alignment"> + <set>WordBreak|AlignVCenter</set> + </property> + </widget> + </vbox> + </widget> + <spacer row="6" column="1"> + <property name="name"> + <cstring>spacer3</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </grid> +</widget> +<connections> + <connection> + <sender>kcfg_ShowGrid</sender> + <signal>toggled(bool)</signal> + <receiver>kcfg_GridColor</receiver> + <slot>setEnabled(bool)</slot> + </connection> +</connections> +<tabstops> + <tabstop>kcfg_ReuseSameViewForOutput</tabstop> + <tabstop>kcfg_ShowVoltageBars</tabstop> + <tabstop>kcfg_ShowGrid</tabstop> + <tabstop>kcfg_GridColor</tabstop> + <tabstop>kcfg_MaxUndo</tabstop> + <tabstop>kcfg_RestoreDocumentsOnStartup</tabstop> + <tabstop>kcfg_RaiseItemSelectors</tabstop> + <tabstop>kcfg_RaiseMessagesLog</tabstop> + <tabstop>refreshRateSlider</tabstop> +</tabstops> +<layoutdefaults spacing="6" margin="11"/> +<includehints> + <includehint>kcolorbutton.h</includehint> + <includehint>knuminput.h</includehint> +</includehints> +</UI> diff --git a/src/gui/gpasmsettingswidget.ui b/src/gui/gpasmsettingswidget.ui new file mode 100644 index 0000000..069711f --- /dev/null +++ b/src/gui/gpasmsettingswidget.ui @@ -0,0 +1,288 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>GpasmSettingsWidget</class> +<widget class="QWidget"> + <property name="name"> + <cstring>GpasmSettingsWidget</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>278</width> + <height>168</height> + </rect> + </property> + <property name="caption"> + <string>Gpasm Settings</string> + </property> + <property name="toolTip" stdset="0"> + <string></string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <widget class="QLabel" row="1" column="0" rowspan="1" colspan="2"> + <property name="name"> + <cstring>textLabel2</cstring> + </property> + <property name="text"> + <string>Radix (-r):</string> + </property> + </widget> + <widget class="QLabel" row="2" column="0" rowspan="1" colspan="2"> + <property name="name"> + <cstring>textLabel3</cstring> + </property> + <property name="text"> + <string>Warning level (-w):</string> + </property> + </widget> + <widget class="QLabel" row="0" column="0" rowspan="1" colspan="2"> + <property name="name"> + <cstring>textLabel1</cstring> + </property> + <property name="text"> + <string>Hex Format (-a):</string> + </property> + </widget> + <widget class="QCheckBox" row="3" column="0" rowspan="1" colspan="4"> + <property name="name"> + <cstring>kcfg_IgnoreCase</cstring> + </property> + <property name="text"> + <string>Ign&ore case (-i)</string> + </property> + <property name="accel"> + <string>Alt+O</string> + </property> + <property name="toolTip" stdset="0"> + <string>All user defined symbols and macros are case sensitive. This option makes them case insensitive.</string> + </property> + </widget> + <widget class="QCheckBox" row="4" column="0" rowspan="1" colspan="4"> + <property name="name"> + <cstring>kcfg_DosFormat</cstring> + </property> + <property name="text"> + <string>Generate DOS-formated hex file (-&n)</string> + </property> + <property name="accel"> + <string>Alt+N</string> + </property> + <property name="toolTip" stdset="0"> + <string>By default, gpasm generates hex files using ISO format. However, some device programmers required a DOS formatted file. This option will cause gpasm to generate a DOS formatted hex file. </string> + </property> + </widget> + <spacer row="6" column="0"> + <property name="name"> + <cstring>spacer18</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>16</height> + </size> + </property> + </spacer> + <widget class="KComboBox" row="0" column="3"> + <item> + <property name="text"> + <string>inhx32</string> + </property> + </item> + <item> + <property name="text"> + <string>inhx8m</string> + </property> + </item> + <item> + <property name="text"> + <string>inhx8s</string> + </property> + </item> + <item> + <property name="text"> + <string>inhx16</string> + </property> + </item> + <property name="name"> + <cstring>kcfg_HexFormat</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>1</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="insertionPolicy"> + <enum>AtBottom</enum> + </property> + <property name="toolTip" stdset="0"> + <string>GPASM supports inhx8m, inhx8s, inhx16, and inhx32 hex file formats. This option controls which hex file format is used.</string> + </property> + </widget> + <widget class="KComboBox" row="1" column="3"> + <item> + <property name="text"> + <string>Decimal</string> + </property> + </item> + <item> + <property name="text"> + <string>Binary</string> + </property> + </item> + <item> + <property name="text"> + <string>Octal</string> + </property> + </item> + <item> + <property name="text"> + <string>Hexadecimal</string> + </property> + </item> + <property name="name"> + <cstring>kcfg_Radix</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>1</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + </widget> + <widget class="KComboBox" row="2" column="3"> + <item> + <property name="text"> + <string>All</string> + </property> + </item> + <item> + <property name="text"> + <string>Warnings</string> + </property> + </item> + <item> + <property name="text"> + <string>Errors</string> + </property> + </item> + <property name="name"> + <cstring>kcfg_GpasmWarningLevel</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>1</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="toolTip" stdset="0"> + <string>This sets the threshold of messages displayed in the log view.<ul><li>"All" will display all output - information, warnings and errors.<li>"Warnings" will supress messages. <li>"Errors" will supress both messages and warnings.</ul></string> + </property> + </widget> + <spacer row="1" column="2"> + <property name="name"> + <cstring>spacer10</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + <spacer row="0" column="2"> + <property name="name"> + <cstring>spacer11</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + <spacer row="2" column="2"> + <property name="name"> + <cstring>spacer12</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + <widget class="QLabel" row="5" column="0"> + <property name="name"> + <cstring>textLabel1_2</cstring> + </property> + <property name="text"> + <string>Other options:</string> + </property> + </widget> + <widget class="KLineEdit" row="5" column="1" rowspan="1" colspan="3"> + <property name="name"> + <cstring>kcfg_MiscGpasmOptions</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>7</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + </widget> + </grid> +</widget> +<tabstops> + <tabstop>kcfg_HexFormat</tabstop> + <tabstop>kcfg_Radix</tabstop> + <tabstop>kcfg_GpasmWarningLevel</tabstop> + <tabstop>kcfg_IgnoreCase</tabstop> + <tabstop>kcfg_DosFormat</tabstop> +</tabstops> +<layoutdefaults spacing="6" margin="11"/> +<includehints> + <includehint>kcombobox.h</includehint> + <includehint>kcombobox.h</includehint> + <includehint>kcombobox.h</includehint> + <includehint>klineedit.h</includehint> +</includehints> +</UI> diff --git a/src/gui/itemeditor.cpp b/src/gui/itemeditor.cpp new file mode 100644 index 0000000..bdfe539 --- /dev/null +++ b/src/gui/itemeditor.cpp @@ -0,0 +1,156 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "cnitem.h" +#include "cnitemgroup.h" +#include "itemeditor.h" +#include "orientationwidget.h" +#include "propertieslistview.h" + +#include <klocale.h> +#include <kstandarddirs.h> + +#include <qlayout.h> +#include <qlabel.h> +#include <qpushbutton.h> +#include <qwhatsthis.h> + +#include <assert.h> + +ItemEditor * ItemEditor::m_pSelf = 0l; + +ItemEditor * ItemEditor::self( KateMDI::ToolView * parent ) +{ + if (!m_pSelf) + { + assert(parent); + m_pSelf = new ItemEditor(parent); + } + return m_pSelf; +} + + +ItemEditor::ItemEditor( KateMDI::ToolView * parent ) + : QWidget( (QWidget*)parent, "Item Editor" ) +{ + QWhatsThis::add( this, i18n("This allows editing of advanced properties of the selected item(s). Right click on the picture of the item to set the orientation.") ); + + QVBoxLayout *vlayout = new QVBoxLayout( this, 0, 6 ); + + m_nameLbl = new QLabel( this, "" ); + vlayout->addWidget(m_nameLbl); + vlayout->addSpacing(8); + + propList = new PropertiesListView(this); + vlayout->addWidget(propList); + QWhatsThis::add(propList,i18n("<qt>Shows properties associated with the currently selected item(s).<p>Select a property to change its value. If multiple items are selected with different values then the property will appear greyed out, use ""Merge Properties"" to make them the same.<p>Select ""Defaults to set all properties to their default values""")); + + QHBoxLayout *h1Layout = new QHBoxLayout( vlayout, 4 ); + QSpacerItem *spacer1 = new QSpacerItem( 1, 1 ); + h1Layout->addItem(spacer1); + + m_defaultsBtn = new QPushButton( i18n("Defaults"), this); + m_defaultsBtn->setEnabled(false); + connect(m_defaultsBtn,SIGNAL(clicked()),propList,SLOT(slotSetDefaults())); + h1Layout->addWidget(m_defaultsBtn); + + m_mergeBtn = new QPushButton( i18n("Merge properties"), this ); + m_mergeBtn->setEnabled(false); + connect(m_mergeBtn,SIGNAL(clicked()),this,SLOT(mergeProperties())); + h1Layout->addWidget(m_mergeBtn); + + // Orientation widget stuff + QHBoxLayout *h2Layout = new QHBoxLayout( vlayout, 6 ); + QSpacerItem *spacer2 = new QSpacerItem( 1, 1 ); + h2Layout->addItem(spacer2); + m_orientationWidget = new OrientationWidget(this); + h2Layout->addWidget(m_orientationWidget); + QWhatsThis::add(m_orientationWidget,i18n("Change the orientation of the selected item by selecting the appropriate button")); + QSpacerItem *spacer3 = new QSpacerItem( 1, 1 ); + h2Layout->addItem(spacer3); + + slotClear(); +} + + +ItemEditor::~ItemEditor() +{ +} + + +void ItemEditor::mergeProperties() +{ + propList->slotMergeProperties(); + m_mergeBtn->setEnabled(false); +} + + +void ItemEditor::slotClear() +{ + propList->slotClear(); + m_orientationWidget->slotClear(); + m_defaultsBtn->setEnabled(false); + m_mergeBtn->setEnabled(false); + updateNameLabel(0l); +} + + +void ItemEditor::slotMultipleSelected() +{ + slotClear(); + m_nameLbl->setText( i18n("<h2>Multiple Items</h2>") ); +} + + +void ItemEditor::slotUpdate( ItemGroup *itemGroup ) +{ + if (!itemGroup) { + slotClear(); + return; + } + + updateMergeDefaults(itemGroup); + propList->slotCreate(itemGroup); + updateNameLabel(itemGroup->activeItem()); +} + + +void ItemEditor::updateMergeDefaults( ItemGroup *itemGroup ) +{ + if (!itemGroup) + { + m_defaultsBtn->setEnabled(false); + m_mergeBtn->setEnabled(false); + return; + } + + m_mergeBtn->setEnabled( !itemGroup->itemsHaveSameData() ); + m_defaultsBtn->setEnabled( !itemGroup->itemsHaveDefaultData() ); + propList->slotUpdate(itemGroup); +} + + +void ItemEditor::slotUpdate( CNItem *item ) +{ + m_orientationWidget->slotUpdate(item); +} + + +void ItemEditor::updateNameLabel( Item *item ) +{ + if (item) { + m_nameLbl->setText( "<h2>" + item->name() + "</h2>" ); + } else { + m_nameLbl->setText( i18n("<h2>No Item Selected</h2>") ); + } +} + + +#include "itemeditor.moc" diff --git a/src/gui/itemeditor.h b/src/gui/itemeditor.h new file mode 100644 index 0000000..8c21e49 --- /dev/null +++ b/src/gui/itemeditor.h @@ -0,0 +1,82 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef ITEMEDITOR_H +#define ITEMEDITOR_H + +#include <qwidget.h> +#include <qguardedptr.h> + +class ItemEditor; + +class CNItem; +class CNItemGroup; +class Item; +class CNItemGroup; +class ICNDocument; +class ItemGroup; +class OrientationWidget; +class PropertiesListView; +class QPushButton; +class QLabel; + +namespace KateMDI { class ToolView; } + +/** +@author Daniel Clarke +@author David Saxton +*/ +class ItemEditor : public QWidget +{ + Q_OBJECT + public: + static ItemEditor * self( KateMDI::ToolView * parent = 0l ); + ~ItemEditor(); + static QString toolViewIdentifier() { return "ItemEditor"; } + + public slots: + /** + * Update the Properties Editor + */ + void slotUpdate( ItemGroup *itemGroup ); + /** + * Update the orientation widget + */ + void slotUpdate( CNItem *item ); + /** + * Clear the properties editor and orientation widget + */ + void slotClear(); + void slotMultipleSelected(); + /** + * Updates the merge / reset data parts (e.g. enabling or disabling the + * "Defaults" button) + */ + void updateMergeDefaults( ItemGroup *itemGroup ); + + protected: + void updateNameLabel( Item *item ); + PropertiesListView * propList; + static ItemEditor * m_pSelf; + + private slots: + void mergeProperties(); + + private: + ItemEditor( KateMDI::ToolView * parent ); + + QLabel *m_nameLbl; + QPushButton *m_defaultsBtn; + QPushButton *m_mergeBtn; + OrientationWidget *m_orientationWidget; +}; + + +#endif diff --git a/src/gui/itemselector.cpp b/src/gui/itemselector.cpp new file mode 100644 index 0000000..f85756e --- /dev/null +++ b/src/gui/itemselector.cpp @@ -0,0 +1,372 @@ +/*************************************************************************** + * Copyright (C) 2003,2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include <vector> // Temporay fix for pthread.h problem +#include "circuitdocument.h" +#include "docmanager.h" +#include "flowcodedocument.h" +#include "itemdocument.h" +#include "itemlibrary.h" +#include "itemselector.h" +#include "libraryitem.h" +#include "mechanicsdocument.h" + +#include <kapplication.h> +#include <kconfig.h> +#include <kdebug.h> +#include <klocale.h> + +#include <qdragobject.h> +#include <qlayout.h> +#include <qpopupmenu.h> +#include <qwhatsthis.h> + +#include <assert.h> + +ILVItem::ILVItem( QListView* parent, const QString &id ) + : KListViewItem( parent, 0 ) +{ + m_id = id; + b_isRemovable = false; + m_pProjectItem = 0l; +} + +ILVItem::ILVItem( QListViewItem* parent, const QString &id ) + : KListViewItem( parent, 0 ) +{ + m_id = id; + b_isRemovable = false; + m_pProjectItem = 0l; +} + + +ItemSelector::ItemSelector( QWidget *parent, const char *name ) + : KListView( parent, name ) +{ + addColumn( i18n( "Component" ) ); + setFullWidth(true); + setSorting( -1, FALSE ); + setRootIsDecorated(true); + setDragEnabled(true); + +// connect( this, SIGNAL(executed(QListViewItem*) ), this, SLOT(slotItemExecuted(QListViewItem*)) ); + connect( this, SIGNAL(clicked(QListViewItem*)), this, SLOT(slotItemClicked(QListViewItem*)) ); + connect( this, SIGNAL(doubleClicked(QListViewItem*)), this, SLOT(slotItemDoubleClicked(QListViewItem*)) ); + connect( this, SIGNAL(contextMenuRequested(QListViewItem*, const QPoint&, int )), this, SLOT(slotContextMenuRequested(QListViewItem*, const QPoint&, int )) ); +} + +ItemSelector::~ItemSelector() +{ + writeOpenStates(); +} + + +void ItemSelector::clear() +{ + m_categories.clear(); + KListView::clear(); +} + + +void ItemSelector::addItem( const QString & caption, const QString & id, const QString & _category, const QPixmap & icon, bool removable ) +{ + ILVItem *parentItem = 0L; + + QString category = _category; + if ( !category.startsWith("/") ) { + category.prepend('/'); + } + + do + { + category.remove(0,1); + QString cat; + category.replace( "\\/", "|" ); + int pos = category.find('/'); + if ( pos == -1 ) cat = category; + else cat = category.left( pos ); + + cat.replace( "|", "/" ); + + if ( m_categories.findIndex(cat) == -1 ) + { + m_categories.append(cat); + + if (parentItem) { + parentItem = new ILVItem( parentItem, "" ); + } + else { + parentItem = new ILVItem( this, "" ); + } + parentItem->setOpen( readOpenState(cat) ); + + parentItem->setExpandable(true); + parentItem->setText( 0, cat ); + } + else + { + parentItem = (ILVItem*)findItem( cat, 0 ); + } + + category.remove( 0, pos ); + } while ( category.contains('/') ); + + if ( !parentItem ) + { + kdError() << "Unexpected error in finding parent item for category list"<<endl; + return; + } + + ILVItem *item = new ILVItem( parentItem, id ); + item->setPixmap( 0, icon ); + item->setText( 0, caption ); + item->setRemovable(removable); +} + + +void ItemSelector::writeOpenStates() +{ + KConfig *config = kapp->config(); + config->setGroup( name() ); + + const QStringList::iterator end = m_categories.end(); + for ( QStringList::iterator it = m_categories.begin(); it != end; ++it ) + { + QListViewItem *item = findItem( *it, 0 ); + if (item) { + config->writeEntry( *it+"IsOpen", item->isOpen() ); + } + } +} + + +bool ItemSelector::readOpenState( const QString &id ) +{ + KConfig *config = kapp->config(); + config->setGroup( name() ); + + return config->readBoolEntry( id+"IsOpen", true ); +} + + +void ItemSelector::slotContextMenuRequested( QListViewItem* item, const QPoint& pos, int /*col*/ ) +{ + if ( !item || !(static_cast<ILVItem*>(item))->isRemovable() ) { + return; + } + + QPopupMenu *menu = new QPopupMenu(this); + menu->insertItem( i18n("Remove %1").arg(item->text(0)), this, SLOT(slotRemoveSelectedItem()), Qt::Key_Delete ); + menu->popup(pos); +} + + +void ItemSelector::slotRemoveSelectedItem() +{ + ILVItem *item = dynamic_cast<ILVItem*>(selectedItem()); + if (!item) + return; + + emit itemRemoved( item->key( 0, 0 ) ); + ILVItem *parent = dynamic_cast<ILVItem*>(item->QListViewItem::parent()); + delete item; + // Get rid of the category as well if it has no children + if ( parent && !parent->firstChild() ) + { + m_categories.remove(parent->text(0)); + delete parent; + } +} + + +void ItemSelector::setListCaption( const QString &caption ) +{ + setColumnText( 0, caption ); +} + + + +void ItemSelector::slotItemClicked( QListViewItem *item ) +{ + if (!item) + return; + + if ( ItemDocument * itemDocument = dynamic_cast<ItemDocument*>(DocManager::self()->getFocusedDocument()) ) + itemDocument->slotUnsetRepeatedItemId(); + + emit itemClicked( item->key( 0, 0 ) ); +} + + +void ItemSelector::slotItemDoubleClicked( QListViewItem *item ) +{ + if (!item) + return; + + QString id = item->key( 0, 0 ); + + if ( Document * doc = DocManager::self()->getFocusedDocument() ) + { + if ( doc->type() == Document::dt_flowcode && id.startsWith("flow/") ) + (static_cast<FlowCodeDocument*>(doc))->slotSetRepeatedItemId(id); + + else if ( doc->type() == Document::dt_circuit && (id.startsWith("ec/") || id.startsWith("sc/")) ) + (static_cast<CircuitDocument*>(doc))->slotSetRepeatedItemId(id); + + else if ( doc->type() == Document::dt_mechanics && id.startsWith("mech/") ) + (static_cast<MechanicsDocument*>(doc))->slotSetRepeatedItemId(id); + } + + emit itemDoubleClicked(id); +} + + +QDragObject* ItemSelector::dragObject() +{ + const QString id = currentItem()->key(0,0); + + QStoredDrag * d = 0l; + + if ( id.startsWith("flow/") ) + d = new QStoredDrag( "ktechlab/flowpart", this ); + + else if ( id.startsWith("ec/") ) + d = new QStoredDrag( "ktechlab/component", this ); + + else if ( id.startsWith("sc/") ) + d = new QStoredDrag( "ktechlab/subcircuit", this ); + + else if ( id.startsWith("mech/") ) + d = new QStoredDrag( "ktechlab/mechanical", this ); + + if (d) + { + QByteArray data; + QDataStream stream( data, IO_WriteOnly ); + stream << id; + d->setEncodedData(data); + } + + // A pixmap cursor is often hard to make out +// QPixmap *pixmap = const_cast<QPixmap*>(currentItem()->pixmap(0)); +// if (pixmap) d->setPixmap(*pixmap); + + return d; +} + + + +//BEGIN class ComponentSelector +ComponentSelector * ComponentSelector::m_pSelf = 0l; + + +ComponentSelector * ComponentSelector::self( KateMDI::ToolView * parent ) +{ + if (!m_pSelf) + { + assert(parent); + m_pSelf = new ComponentSelector(parent); + } + return m_pSelf; +} + + +ComponentSelector::ComponentSelector( KateMDI::ToolView * parent ) + : ItemSelector( (QWidget*)parent, "Component Selector" ) +{ + QWhatsThis::add( this, i18n( + "Add components to the circuit diagram by dragging them into the circuit.<br><br>" + + "To add more than one component of the same type, doubleclick on a component, and click repeatedly in the circuit to place the component. Right click to stop placement.<br><br>" + + "Some components (such as subcircuits) can be removed by right clicking on the item and selecting \"Remove\"." + ) ); + + setListCaption( i18n("Component") ); + + LibraryItemList *items = itemLibrary()->items(); + const LibraryItemList::iterator end = items->end(); + for ( LibraryItemList::iterator it = items->begin(); it != end; ++it ) + { + if ( (*it)->type() == LibraryItem::lit_component ) + addItem( (*it)->name(), (*it)->activeID(), (*it)->category(), (*it)->icon16() ); + } +} +//END class ComponentSelector + + + +//BEGIN class FlowPartSelector +FlowPartSelector * FlowPartSelector::m_pSelf = 0l; + + +FlowPartSelector * FlowPartSelector::self( KateMDI::ToolView * parent ) +{ + if (!m_pSelf) + { + assert(parent); + m_pSelf = new FlowPartSelector(parent); + } + return m_pSelf; +} + + +FlowPartSelector::FlowPartSelector( KateMDI::ToolView * parent ) + : ItemSelector( (QWidget*)parent, "Part Selector" ) +{ + QWhatsThis::add( this, i18n("Add FlowPart to the FlowCode document by dragging them there.<br><br>To add more than one FlowPart of the same type, doubleclick on a FlowPart, and click repeatedly in the FlowChart to place the component. Right click to stop placement.") ); + + setListCaption( i18n("Flow Part") ); + + LibraryItemList *items = itemLibrary()->items(); + const LibraryItemList::iterator end = items->end(); + for ( LibraryItemList::iterator it = items->begin(); it != end; ++it ) + { + if ( (*it)->type() == LibraryItem::lit_flowpart ) + addItem( (*it)->name(), (*it)->activeID(), (*it)->category(), (*it)->icon16() ); + } +} +//END class FlowPartSelector + + +//BEGIN class MechanicsSelector +MechanicsSelector * MechanicsSelector::m_pSelf = 0l; + + +MechanicsSelector * MechanicsSelector::self( KateMDI::ToolView * parent ) +{ + if (!m_pSelf) + { + assert(parent); + m_pSelf = new MechanicsSelector( (QWidget*)parent ); + } + return m_pSelf; +} + + +MechanicsSelector::MechanicsSelector( QWidget *parent ) + : ItemSelector( (QWidget*)parent, "Mechanics Selector" ) +{ + QWhatsThis::add( this, i18n("Add mechanical parts to the mechanics work area by dragging them there.") ); + + LibraryItemList *items = itemLibrary()->items(); + const LibraryItemList::iterator end = items->end(); + for ( LibraryItemList::iterator it = items->begin(); it != end; ++it ) + { + if ( (*it)->type() == LibraryItem::lit_mechanical ) + { + addItem( (*it)->name(), (*it)->activeID(), (*it)->category(), (*it)->icon16() ); + } + } +} +//END class MechanicsSelector + + +#include "itemselector.moc" diff --git a/src/gui/itemselector.h b/src/gui/itemselector.h new file mode 100644 index 0000000..ab9c11c --- /dev/null +++ b/src/gui/itemselector.h @@ -0,0 +1,167 @@ +/*************************************************************************** + * Copyright (C) 2003,2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef ITEMSELECTOR_H +#define ITEMSELECTOR_H + +#include <klistview.h> + +#include <qpixmap.h> +#include <qstring.h> + +class ProjectItem; +class QStoredDrag; +namespace KateMDI { class ToolView; } + +/** +@short Contains info about item for ItemSelector +@author David Saxton +*/ +class ILVItem : public QObject, public KListViewItem +{ + public: + ILVItem( QListView *parent, const QString &id ); + ILVItem( QListViewItem *parent, const QString &id ); + + void setProjectItem( ProjectItem * projectItem ) { m_pProjectItem = projectItem; } + ProjectItem * projectItem() const { return m_pProjectItem; } + + QString id() const { return m_id; } + + QString key( int, bool ) const { return m_id; } + /** + * Set whether the item can be removed from the listview by the user + */ + void setRemovable( bool isRemovable ) { b_isRemovable = isRemovable; } + /** + * Whether the item can be removed from the listview by the user + */ + bool isRemovable() const { return b_isRemovable; } + + protected: + QString m_id; + bool b_isRemovable; + ProjectItem * m_pProjectItem; +}; + +/** +@short Allows selection of generic items for dragging / clicking +@author David Saxton +*/ +class ItemSelector : public KListView +{ + Q_OBJECT + public: + ItemSelector( QWidget *parent, const char *name ); + ~ItemSelector(); + /** + * Adds a listview item to the ListView + * @param caption The displayed text + * @param id A unique identification for when it is dragged or activated + * @param category The category it is in, eg "Integrated Circuits + * @param icon The icon to be displayed to the left of the text + * @param removable Whether the user can right-click on the item and select Remove + */ + void addItem( const QString & caption, const QString & id, const QString & category, const QPixmap & icon = QPixmap(), bool removable = false ); + + public slots: + virtual void slotContextMenuRequested( QListViewItem *item, const QPoint &pos, int col ); + virtual void clear(); + void slotRemoveSelectedItem(); + + signals: + /** + * Emitted when a user selects an item and removes it + */ + void itemRemoved( const QString &id ); + void itemDoubleClicked( const QString &id ); + void itemClicked( const QString &id ); + + protected: + /** + * Sets the caption of the ListView (eg 'Components' or 'Files') + */ + void setListCaption( const QString &caption ); + /** + * Writes the open status (folded or unfolded) of "parent" items in the view + * to the config file. + */ + void writeOpenStates(); + /** + * Reads the open status (folded or unfolded) of the given item. The default + * status for non-existant items is true. + */ + bool readOpenState( const QString &id ); + + private slots: + void slotItemClicked( QListViewItem *item ); + void slotItemDoubleClicked( QListViewItem *item ); + + private: + /** + * @return a dragobject encoding the currently selected component item. + */ + QDragObject * dragObject(); + + QStringList m_categories; +}; + + +/** +@short Allows selection of electrical components +@author David Saxton + */ +class ComponentSelector : public ItemSelector +{ + Q_OBJECT + public: + static ComponentSelector * self( KateMDI::ToolView * parent = 0l ); + static QString toolViewIdentifier() { return "ComponentSelector"; } + + private: + ComponentSelector( KateMDI::ToolView * parent ); + static ComponentSelector * m_pSelf; +}; + + +/** +@short Allows selection of PIC parts (eg 'Pause') +@author David Saxton + */ +class FlowPartSelector : public ItemSelector +{ + Q_OBJECT + public: + static FlowPartSelector * self( KateMDI::ToolView * parent = 0l ); + static QString toolViewIdentifier() { return "FlowPartSelector"; } + + private: + FlowPartSelector( KateMDI::ToolView * parent ); + static FlowPartSelector * m_pSelf; +}; + + +/** +@author David Saxton + */ +class MechanicsSelector : public ItemSelector +{ + Q_OBJECT + public: + static MechanicsSelector * self( KateMDI::ToolView * parent = 0l ); + static QString toolViewIdentifier() { return "MechanicsSelector"; } + + private: + MechanicsSelector( QWidget *parent = 0L ); + static MechanicsSelector * m_pSelf; +}; + + +#endif diff --git a/src/gui/linkeroptionswidget.ui b/src/gui/linkeroptionswidget.ui new file mode 100644 index 0000000..e2c48d8 --- /dev/null +++ b/src/gui/linkeroptionswidget.ui @@ -0,0 +1,179 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>LinkerOptionsWidget</class> +<widget class="QWidget"> + <property name="name"> + <cstring>LinkerOptionsWidget</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>401</width> + <height>475</height> + </rect> + </property> + <property name="caption"> + <string>Linker Options</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <widget class="KComboBox" row="1" column="1"> + <item> + <property name="text"> + <string>inhx32</string> + </property> + </item> + <item> + <property name="text"> + <string>inhx8m</string> + </property> + </item> + <item> + <property name="text"> + <string>inhx8s</string> + </property> + </item> + <item> + <property name="text"> + <string>inhx16</string> + </property> + </item> + <property name="name"> + <cstring>m_pHexFormat</cstring> + </property> + <property name="insertionPolicy"> + <enum>AtBottom</enum> + </property> + <property name="toolTip" stdset="0"> + <string>GPASM supports inhx8m, inhx8s, inhx16, and inhx32 hex file formats. This option controls which hex file format is used.</string> + </property> + </widget> + <widget class="QLabel" row="3" column="0"> + <property name="name"> + <cstring>textLabel3</cstring> + </property> + <property name="text"> + <string>Library Directory (-I):</string> + </property> + </widget> + <widget class="KLineEdit" row="3" column="1"> + <property name="name"> + <cstring>m_pLibraryDir</cstring> + </property> + </widget> + <widget class="QLabel" row="1" column="0"> + <property name="name"> + <cstring>textLabel1</cstring> + </property> + <property name="text"> + <string>Hex Format (-a):</string> + </property> + </widget> + <widget class="QCheckBox" row="2" column="0" rowspan="1" colspan="2"> + <property name="name"> + <cstring>m_pOutputMap</cstring> + </property> + <property name="text"> + <string>&Output a map file (-m)</string> + </property> + <property name="accel"> + <string>Alt+O</string> + </property> + </widget> + <widget class="QLabel" row="4" column="0"> + <property name="name"> + <cstring>textLabel8</cstring> + </property> + <property name="text"> + <string>Linker Script (-s):</string> + </property> + </widget> + <widget class="QLineEdit" row="4" column="1"> + <property name="name"> + <cstring>m_pLinkerScript</cstring> + </property> + </widget> + <widget class="KLineEdit" row="5" column="1"> + <property name="name"> + <cstring>m_pOther</cstring> + </property> + </widget> + <widget class="QLabel" row="5" column="0"> + <property name="name"> + <cstring>textLabel4</cstring> + </property> + <property name="text"> + <string>Other:</string> + </property> + </widget> + <widget class="QGroupBox" row="6" column="0" rowspan="1" colspan="2"> + <property name="name"> + <cstring>groupBox2</cstring> + </property> + <property name="title"> + <string>Link libraries inside project</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="KListView"> + <column> + <property name="text"> + <string>Library</string> + </property> + <property name="clickable"> + <bool>true</bool> + </property> + <property name="resizable"> + <bool>true</bool> + </property> + </column> + <property name="name"> + <cstring>m_pInternalLibraries</cstring> + </property> + <property name="selectionMode" stdset="0"> + <enum>NoSelection</enum> + </property> + <property name="resizeMode"> + <enum>LastColumn</enum> + </property> + <property name="tooltipColumn"> + <number>0</number> + </property> + </widget> + </vbox> + </widget> + <widget class="KEditListBox" row="7" column="0" rowspan="1" colspan="2"> + <property name="name"> + <cstring>m_pExternalLibraries</cstring> + </property> + <property name="title"> + <string></string> + </property> + </widget> + </grid> +</widget> +<tabstops> + <tabstop>m_pHexFormat</tabstop> + <tabstop>m_pOutputMap</tabstop> + <tabstop>m_pLibraryDir</tabstop> + <tabstop>m_pLinkerScript</tabstop> + <tabstop>m_pOther</tabstop> + <tabstop>m_pInternalLibraries</tabstop> +</tabstops> +<layoutdefaults spacing="6" margin="11"/> +<includehints> + <includehint>kcombobox.h</includehint> + <includehint>klineedit.h</includehint> + <includehint>klineedit.h</includehint> + <includehint>klistview.h</includehint> + <includehint>keditlistbox.h</includehint> + <includehint>klineedit.h</includehint> +</includehints> +</UI> diff --git a/src/gui/logicwidget.ui b/src/gui/logicwidget.ui new file mode 100644 index 0000000..18c1860 --- /dev/null +++ b/src/gui/logicwidget.ui @@ -0,0 +1,276 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>LogicWidget</class> +<widget class="QWidget"> + <property name="name"> + <cstring>LogicWidget</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>338</width> + <height>347</height> + </rect> + </property> + <property name="caption"> + <string>Logic</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <widget class="QGroupBox"> + <property name="name"> + <cstring>groupBox3</cstring> + </property> + <property name="title"> + <string>Input</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="KDoubleNumInput" row="0" column="1"> + <property name="name"> + <cstring>kcfg_LogicRisingTrigger</cstring> + </property> + <property name="value"> + <number>0.3</number> + </property> + <property name="minValue"> + <number>0.1</number> + </property> + <property name="maxValue"> + <number>1.5</number> + </property> + <property name="suffix"> + <string> V</string> + </property> + <property name="precision"> + <number>1</number> + </property> + <property name="toolTip" stdset="0"> + <string>Required voltage level before the input will be considered high.</string> + </property> + <property name="whatsThis" stdset="0"> + <string></string> + </property> + </widget> + <widget class="QLabel" row="1" column="0"> + <property name="name"> + <cstring>textLabel2_2</cstring> + </property> + <property name="text"> + <string>Falling Trigger Threshold:</string> + </property> + <property name="toolTip" stdset="0"> + <string>Required voltage level before an input will be considered low.</string> + </property> + </widget> + <widget class="KDoubleNumInput" row="1" column="1"> + <property name="name"> + <cstring>kcfg_LogicFallingTrigger</cstring> + </property> + <property name="value"> + <number>0.2</number> + </property> + <property name="minValue"> + <number>0.1</number> + </property> + <property name="maxValue"> + <number>1.6</number> + </property> + <property name="suffix"> + <string> V</string> + </property> + <property name="precision"> + <number>1</number> + </property> + <property name="toolTip" stdset="0"> + <string>Required voltage level before an input will be considered low.</string> + </property> + <property name="whatsThis" stdset="0"> + <string></string> + </property> + </widget> + <widget class="QLabel" row="0" column="0"> + <property name="name"> + <cstring>textLabel1</cstring> + </property> + <property name="text"> + <string>Rising Trigger Threshold:</string> + </property> + <property name="toolTip" stdset="0"> + <string>Required voltage level before the input will be considered high.</string> + </property> + </widget> + </grid> + </widget> + <widget class="QGroupBox"> + <property name="name"> + <cstring>groupBox4</cstring> + </property> + <property name="title"> + <string>Output</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel" row="0" column="0"> + <property name="name"> + <cstring>textLabel2</cstring> + </property> + <property name="text"> + <string>Output High:</string> + </property> + <property name="toolTip" stdset="0"> + <string>Voltage level for high on logic components.</string> + </property> + </widget> + <widget class="KDoubleNumInput" row="0" column="1"> + <property name="name"> + <cstring>kcfg_LogicOutputHigh</cstring> + </property> + <property name="value"> + <number>0.5</number> + </property> + <property name="minValue"> + <number>0.1</number> + </property> + <property name="maxValue"> + <number>1.6</number> + </property> + <property name="suffix"> + <string> V</string> + </property> + <property name="precision"> + <number>1</number> + </property> + <property name="toolTip" stdset="0"> + <string>Voltage level for high on logic components.</string> + </property> + <property name="whatsThis" stdset="0"> + <string></string> + </property> + </widget> + <widget class="QLabel" row="1" column="0"> + <property name="name"> + <cstring>textLabel4</cstring> + </property> + <property name="text"> + <string>High Output Impedance:</string> + </property> + <property name="toolTip" stdset="0"> + <string>This is the output impedance when the output is high.</string> + </property> + </widget> + <widget class="QLabel" row="2" column="0"> + <property name="name"> + <cstring>textLabel1_2</cstring> + </property> + <property name="text"> + <string>Low Output Impedance:</string> + </property> + <property name="toolTip" stdset="0"> + <string>This is the output impedance when the output is low.</string> + </property> + </widget> + <widget class="KIntSpinBox" row="1" column="1"> + <property name="name"> + <cstring>kcfg_LogicOutputHighImpedance</cstring> + </property> + <property name="suffix"> + <string></string> + </property> + <property name="maxValue"> + <number>1000000000</number> + </property> + <property name="minValue"> + <number>1</number> + </property> + <property name="value"> + <number>15</number> + </property> + <property name="toolTip" stdset="0"> + <string>This is the output impedance when the output is high.</string> + </property> + <property name="whatsThis" stdset="0"> + <string></string> + </property> + </widget> + <widget class="KIntSpinBox" row="2" column="1"> + <property name="name"> + <cstring>kcfg_LogicOutputLowImpedance</cstring> + </property> + <property name="suffix"> + <string></string> + </property> + <property name="specialValueText"> + <string>Floating</string> + </property> + <property name="maxValue"> + <number>1000000000</number> + </property> + <property name="minValue"> + <number>0</number> + </property> + <property name="value"> + <number>0</number> + </property> + <property name="toolTip" stdset="0"> + <string>This is the output impedance when the output is low.</string> + </property> + <property name="whatsThis" stdset="0"> + <string></string> + </property> + </widget> + </grid> + </widget> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel2_3</cstring> + </property> + <property name="text"> + <string>Here, you can configure the behaviour of logic components. + +These values will apply to all components, apart from the PIC, whose pins' impedances depend on the pin in use.</string> + </property> + <property name="alignment"> + <set>WordBreak|AlignVCenter</set> + </property> + </widget> + <spacer> + <property name="name"> + <cstring>spacer10</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>16</height> + </size> + </property> + </spacer> + </vbox> +</widget> +<layoutdefaults spacing="6" margin="11"/> +<includehints> + <includehint>knuminput.h</includehint> + <includehint>knuminput.h</includehint> + <includehint>knuminput.h</includehint> + <includehint>knuminput.h</includehint> + <includehint>knuminput.h</includehint> + <includehint>knuminput.h</includehint> + <includehint>knuminput.h</includehint> + <includehint>knuminput.h</includehint> +</includehints> +</UI> diff --git a/src/gui/logview.cpp b/src/gui/logview.cpp new file mode 100644 index 0000000..fcb8caf --- /dev/null +++ b/src/gui/logview.cpp @@ -0,0 +1,132 @@ +/*************************************************************************** + * Copyright (C) 2003-2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "logview.h" + +#include <kdebug.h> +#include <kiconloader.h> +#include <katemdi.h> +#include <klocale.h> +#include <qpopupmenu.h> + + +//BEGIN class LogView +LogView::LogView( KateMDI::ToolView * parent, const char *name ) + : KTextEdit( parent, name ) +{ + setReadOnly(true); + setPaper( Qt::white ); + setTextFormat( LogText ); + setWordWrap( WidgetWidth ); + + // Connect up signal emitted when the user doubleclicks on a paragraph in the log view + connect( this, SIGNAL(clicked(int,int)), this, SLOT(slotParaClicked(int,int)) ); +} + + +LogView::~LogView() +{ +} + + +void LogView::clear() +{ + m_messageInfoMap.clear(); + KTextEdit::clear(); +} + + +void LogView::addOutput( QString text, OutputType outputType, MessageInfo messageInfo ) +{ + tidyText(text); + switch(outputType) + { + case LogView::ot_important: + append( QString("<font color=\"#000000\"><b>%1</b></font>").arg(text) ); + break; + + case LogView::ot_info: + append( QString("<font color=\"#000000\"><i>%1</i></font>").arg(text) ); + break; + + case LogView::ot_message: + append( QString("<font color=\"#000000\">%1</font>").arg(text) ); + break; + + case LogView::ot_warning: + append( QString("<font color=\"#666666\">%1</font>").arg(text) ); + break; + + case LogView::ot_error: + append( QString("<font color=\"#800000\">%1</font>").arg(text) ); + break; + } + + m_messageInfoMap[ paragraphs()-1 ] = messageInfo; +} + + +void LogView::slotParaClicked( int para, int /*pos*/ ) +{ + QString t = text(para); + untidyText(t); + emit paraClicked( t, m_messageInfoMap[para] ); +} + + +void LogView::tidyText( QString &t ) +{ + t.replace( "&", "&" ); + t.replace( "<", "<" ); + t.replace( ">", ">" ); +} + + +void LogView::untidyText( QString &t ) +{ + t.replace( "<", "<" ); + t.replace( ">", ">" ); + t.replace( "&", "&" ); +} + + +QPopupMenu * LogView::createPopupMenu( const QPoint & pos ) +{ + QPopupMenu * menu = KTextEdit::createPopupMenu( pos ); + + menu->insertSeparator(); + int id = menu->insertItem( i18n("Clear All"), this, SLOT(clear()) ); + + // "an empty textedit is always considered to have one paragraph" - qt documentation + // although this does not always seem to be the case, so I don't know... + menu->setItemEnabled( id, paragraphs() > 1 ); + + return menu; +} +//END class LogView + + + +//BEGIN class MessageInfo +MessageInfo::MessageInfo() +{ + m_fileLine = -1; +} + + +MessageInfo::MessageInfo( QString fileURL, int fileLine ) +{ + m_fileURL = fileURL; + m_fileLine = fileLine; +} +//END class MessageInfo + + +#include "logview.moc" diff --git a/src/gui/logview.h b/src/gui/logview.h new file mode 100644 index 0000000..c568da8 --- /dev/null +++ b/src/gui/logview.h @@ -0,0 +1,85 @@ +/*************************************************************************** + * Copyright (C) 2003-2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef LOGVIEW_H +#define LOGVIEW_H + +class KTechlab; + +#include <ktextedit.h> +#include <qmap.h> + +namespace KateMDI { class ToolView; } + +class MessageInfo +{ + public: + MessageInfo(); + MessageInfo( QString fileURL, int fileLine ); + + QString fileURL() const { return m_fileURL; } + int fileLine() const { return m_fileLine; } + + protected: + QString m_fileURL; + int m_fileLine; +}; +typedef QMap<int,MessageInfo> MessageInfoMap; + + +/** +Base class for logviews (eg GpasmInterface) which output information, warnings, errors to a viewable log +@short Dockable logview +@author David Saxton +*/ +class LogView : public KTextEdit +{ + Q_OBJECT + public: + LogView( KateMDI::ToolView * parent, const char *name = 0 ); + ~LogView(); + + enum OutputType + { + ot_important, // Bold + ot_info, // Italic + ot_message, // Plain + ot_warning, // Grey + ot_error // Red + }; + + signals: + /** + * Emitted when the user clicks on a paragraph in the log view + */ + void paraClicked( const QString &text, MessageInfo messageInfo ); + + public slots: + virtual void clear(); + void addOutput( QString text, OutputType outputType, MessageInfo messageInfo = MessageInfo() ); + + protected: + virtual QPopupMenu * createPopupMenu( const QPoint & pos ); + /** + * Replaces "&" with &, "<" with <, etc + */ + void tidyText( QString &t ); + /** + * Replaces "<" with "<", "&" with "&", etc + */ + void untidyText( QString &t ); + + MessageInfoMap m_messageInfoMap; + + private slots: + void slotParaClicked( int para, int pos ); +}; + +#endif diff --git a/src/gui/microselectwidget.cpp b/src/gui/microselectwidget.cpp new file mode 100644 index 0000000..1c4150b --- /dev/null +++ b/src/gui/microselectwidget.cpp @@ -0,0 +1,129 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "asminfo.h" +#include "microinfo.h" +#include "microlibrary.h" +#include "microselectwidget.h" + +#include <kcombobox.h> +#include <klocale.h> +#include <qgroupbox.h> +#include <qlabel.h> +#include <qlayout.h> +#include <qtooltip.h> +#include <qvariant.h> +#include <qwhatsthis.h> + +MicroSelectWidget::MicroSelectWidget( QWidget* parent, const char* name, WFlags ) + : QGroupBox( 4, Qt::Horizontal, i18n("Microprocessor"), parent, name ) +{ + m_allowedAsmSet = AsmInfo::AsmSetAll; + m_allowedGpsimSupport = m_allowedFlowCodeSupport = m_allowedMicrobeSupport = MicroInfo::AllSupport; + + if ( !name ) + setName( "MicroSelectWidget" ); + + m_pMicroFamilyLabel = new QLabel( this, "m_pMicroFamilyLabel" ); + m_pMicroFamilyLabel->setText( i18n("Family") ); + + m_pMicroFamily = new KComboBox( FALSE, this, "m_pMicroFamily" ); + m_pMicroFamily->setSizePolicy( QSizePolicy::MinimumExpanding, QSizePolicy::Preferred ); + + m_pMicroLabel = new QLabel( this, "m_pMicroLabel" ); + m_pMicroLabel->setText( i18n("Micro") ); + + m_pMicro = new KComboBox( FALSE, this, "m_pMicro" ); + m_pMicro->setSizePolicy( QSizePolicy::MinimumExpanding, QSizePolicy::Preferred ); + m_pMicro->setEditable( TRUE ); + m_pMicro->setAutoCompletion(true); + updateFromAllowed(); + setMicro("P16F84"); + connect( m_pMicroFamily, SIGNAL(activated(const QString & )), this, SLOT(microFamilyChanged(const QString& )) ); +} + + +MicroSelectWidget::~MicroSelectWidget() +{ +} + +void MicroSelectWidget::setAllowedAsmSet( unsigned allowed ) +{ + m_allowedAsmSet = allowed; + updateFromAllowed(); +} +void MicroSelectWidget::setAllowedGpsimSupport( unsigned allowed ) +{ + m_allowedGpsimSupport = allowed; + updateFromAllowed(); +} +void MicroSelectWidget::setAllowedFlowCodeSupport( unsigned allowed ) +{ + m_allowedFlowCodeSupport = allowed; + updateFromAllowed(); +} +void MicroSelectWidget::setAllowedMicrobeSupport( unsigned allowed ) +{ + m_allowedMicrobeSupport = allowed; + updateFromAllowed(); +} + + +void MicroSelectWidget::updateFromAllowed() +{ + QString oldFamily = m_pMicroFamily->currentText(); + + m_pMicroFamily->clear(); + +#define CHECK_ADD(family) if ( (m_allowedAsmSet & AsmInfo::family) && !MicroLibrary::self()->microIDs( AsmInfo::family, m_allowedGpsimSupport, m_allowedFlowCodeSupport, m_allowedMicrobeSupport ).isEmpty() ) m_pMicroFamily->insertItem( AsmInfo::setToString(AsmInfo::family) ); + CHECK_ADD(PIC12) + CHECK_ADD(PIC14) + CHECK_ADD(PIC16); +#undef CHECK_ADD + + if ( m_pMicroFamily->contains(oldFamily) ) + m_pMicroFamily->setCurrentText(oldFamily); + + microFamilyChanged(oldFamily); +} + + +void MicroSelectWidget::setMicro( const QString & id ) +{ + MicroInfo * info = MicroLibrary::self()->microInfoWithID(id); + if (!info) + return; + + m_pMicro->clear(); + m_pMicro->insertStringList( MicroLibrary::self()->microIDs( info->instructionSet()->set() ) ); + m_pMicro->setCurrentText(id); + + m_pMicroFamily->setCurrentText( AsmInfo::setToString( info->instructionSet()->set() ) ); +} + + +QString MicroSelectWidget::micro() const +{ + return m_pMicro->currentText(); +} + + +void MicroSelectWidget::microFamilyChanged( const QString & family ) +{ + QString oldID = m_pMicro->currentText(); + + m_pMicro->clear(); + m_pMicro->insertStringList( MicroLibrary::self()->microIDs( AsmInfo::stringToSet(family), m_allowedGpsimSupport, m_allowedFlowCodeSupport, m_allowedMicrobeSupport ) ); + + if ( m_pMicro->contains(oldID) ) + m_pMicro->setCurrentText(oldID); +} + +#include "microselectwidget.moc" diff --git a/src/gui/microselectwidget.h b/src/gui/microselectwidget.h new file mode 100644 index 0000000..3730c8f --- /dev/null +++ b/src/gui/microselectwidget.h @@ -0,0 +1,73 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef MICROSELECTWIDGET_H +#define MICROSELECTWIDGET_H + +#include <qgroupbox.h> + +class QVBoxLayout; +class QHBoxLayout; +class QGridLayout; +class QSpacerItem; +class QGroupBox; +class QLabel; +class KComboBox; + +/** +@author David Saxton +*/ +class MicroSelectWidget : public QGroupBox +{ + Q_OBJECT + + public: + MicroSelectWidget( QWidget* parent = 0, const char* name = 0, WFlags fl = 0 ); + ~MicroSelectWidget(); + + void setMicro( const QString & id ); + QString micro() const; + + /** + * @see MicroLibrary::microIDs + */ + void setAllowedAsmSet( unsigned allowed ); + /** + * @see MicroLibrary::microIDs + */ + void setAllowedGpsimSupport( unsigned allowed ); + /** + * @see MicroLibrary::microIDs + */ + void setAllowedFlowCodeSupport( unsigned allowed ); + /** + * @see MicroLibrary::microIDs + */ + void setAllowedMicrobeSupport( unsigned allowed ); + + protected slots: + void microFamilyChanged( const QString & family ); + + protected: + void updateFromAllowed(); + + unsigned int m_allowedAsmSet; + unsigned int m_allowedGpsimSupport; + unsigned int m_allowedFlowCodeSupport; + unsigned int m_allowedMicrobeSupport; + + QHBoxLayout * m_pWidgetLayout; + QLabel * m_pMicroFamilyLabel; + KComboBox * m_pMicroFamily; + QLabel * m_pMicroLabel; + KComboBox * m_pMicro; +}; + +#endif diff --git a/src/gui/microsettingsdlg.cpp b/src/gui/microsettingsdlg.cpp new file mode 100644 index 0000000..c3915e8 --- /dev/null +++ b/src/gui/microsettingsdlg.cpp @@ -0,0 +1,437 @@ +/*************************************************************************** + * Copyright (C) 2003-2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "microinfo.h" +#include "microsettings.h" +#include "microsettingsdlg.h" +#include "microsettingswidget.h" +#include "micropackage.h" +#include "newpinmappingwidget.h" +#include "pinmapping.h" + +#include <kcombobox.h> +#include <kdebug.h> +#include <kinputdialog.h> +#include <klineedit.h> +#include <klocale.h> +#include <kmessagebox.h> +#include <kpushbutton.h> + +#include <qgroupbox.h> +#include <qlabel.h> +#include <qlayout.h> +#include <qregexp.h> +#include <qtable.h> +#include <qwhatsthis.h> + +MicroSettingsDlg::MicroSettingsDlg( MicroSettings * microSettings, QWidget *parent, const char *name ) + : KDialogBase( parent, name, true, i18n("PIC Settings"), KDialogBase::Ok|KDialogBase::Apply|KDialogBase::Cancel, KDialogBase::Ok, true ) +{ + m_pMicroSettings = microSettings; + m_pNewPinMappingWidget = 0l; + m_pNewPinMappingDlg = 0l; + m_pWidget = new MicroSettingsWidget(this); + + QWhatsThis::add( this, i18n("This dialog allows editing of the initial properties of the PIC") ); + QWhatsThis::add( m_pWidget->portsGroupBox, i18n("Edit the initial value of the ports here. For each binary number, the order from right-to-left is pins 0 through 7.<br><br>The \"Type (TRIS)\" edit shows the initial input/output state of the ports; 1 represents an input, and 0 an output.<br><br>The \"State (PORT)\" edit shows the initial high/low state of the ports; 1 represents a high, and 0 a low.") ); + QWhatsThis::add( m_pWidget->variables, i18n("Edit the initial value of the variables here.<br><br>Note that the value of the variable can only be in the range 0->255. These variables will be initialized before any other code is executed.") ); + + + //BEGIN Initialize initial port settings + m_portNames = microSettings->microInfo()->package()->portNames(); + + m_portTypeEdit.resize( m_portNames.size(), 0 ); + m_portStateEdit.resize( m_portNames.size(), 0 ); + + uint row = 0; + QStringList::iterator end = m_portNames.end(); + for ( QStringList::iterator it = m_portNames.begin(); it != end; ++it, ++row ) + { + //BEGIN Get current Type / State text + QString portType = QString::number( microSettings->portType(*it), 2 ); + QString portState = QString::number( microSettings->portState(*it), 2 ); + + QString fill; + fill.fill( '0', 8-portType.length() ); + portType.prepend(fill); + fill.fill( '0', 8-portState.length() ); + portState.prepend(fill); + //END Get current Type / State text + + + QGroupBox * groupBox = new QGroupBox( *it, m_pWidget->portsGroupBox ); + + groupBox->setColumnLayout(0, Qt::Vertical ); + groupBox->layout()->setSpacing( 6 ); + groupBox->layout()->setMargin( 11 ); + QGridLayout * groupBoxLayout = new QGridLayout( groupBox->layout() ); + groupBoxLayout->setAlignment( Qt::AlignTop ); + + // TODO: replace this with i18n( "the type", "Type (TRIS register):" ); + groupBoxLayout->addWidget( new QLabel( i18n("Type (TRIS register):"), groupBox ), 0, 0 ); + groupBoxLayout->addWidget( new QLabel( i18n("State (PORT register):"), groupBox ), 1, 0 ); + + m_portTypeEdit[row] = new KLineEdit( portType, groupBox ); + groupBoxLayout->addWidget( m_portTypeEdit[row], 0, 1 ); + + m_portStateEdit[row] = new KLineEdit( portState, groupBox ); + groupBoxLayout->addWidget( m_portStateEdit[row], 1, 1 ); + +// (dynamic_cast<QVBoxLayout*>(m_pWidget->portsGroupBox->layout()))->insertWidget( row, groupBox ); + (dynamic_cast<QVBoxLayout*>(m_pWidget->portsGroupBox->layout()))->addWidget( groupBox ); + } + //END Initialize initial port settings + + + + //BEGIN Initialize initial variable settings + // Hide row headers + m_pWidget->variables->setLeftMargin(0); + + // Make columns as thin as possible + m_pWidget->variables->setColumnStretchable( 0, true ); + m_pWidget->variables->setColumnStretchable( 1, true ); + + QStringList variables = microSettings->variableNames(); + row = 0; + end = variables.end(); + for ( QStringList::iterator it = variables.begin(); it != end; ++it ) + { + VariableInfo *info = microSettings->variableInfo(*it); + if (info) + { + m_pWidget->variables->insertRows( row, 1 ); + m_pWidget->variables->setText( row, 0, *it ); + m_pWidget->variables->setText( row, 1, info->valueAsString() ); + ++row; + } + } + m_pWidget->variables->insertRows( row, 1 ); + + connect( m_pWidget->variables, SIGNAL(valueChanged(int,int)), this, SLOT(checkAddVariableRow()) ); + //END Initialize initial variable settings + + + + //BEGIN Initialize pin maps + connect( m_pWidget->pinMapAdd, SIGNAL(clicked()), this, SLOT(slotCreatePinMap()) ); + connect( m_pWidget->pinMapModify, SIGNAL(clicked()), this, SLOT(slotModifyPinMap()) ); + connect( m_pWidget->pinMapRename, SIGNAL(clicked()), this, SLOT(slotRenamePinMap()) ); + connect( m_pWidget->pinMapRemove, SIGNAL(clicked()), this, SLOT(slotRemovePinMap()) ); + + m_pinMappings = microSettings->pinMappings(); + m_pWidget->pinMapCombo->insertStringList( m_pinMappings.keys() ); + + updatePinMapButtons(); + //END Initialize pin maps + + + enableButtonSeparator( false ); + setMainWidget(m_pWidget); + m_pWidget->adjustSize(); + adjustSize(); + + connect( this, SIGNAL(applyClicked()), this, SLOT(slotSaveStuff()) ); +} + + +MicroSettingsDlg::~MicroSettingsDlg() +{ +} + + +void MicroSettingsDlg::accept() +{ + hide(); + slotSaveStuff(); + deleteLater(); +} + + +void MicroSettingsDlg::slotSaveStuff() +{ + for ( unsigned i = 0; i < m_portNames.size(); i++ ) + savePort(i); + + m_pMicroSettings->removeAllVariables(); + for ( int i=0; i< m_pWidget->variables->numRows(); i++ ) + saveVariable(i); + + m_pMicroSettings->setPinMappings( m_pinMappings ); +} + + +void MicroSettingsDlg::reject() +{ + deleteLater(); +} + + +QValidator::State MicroSettingsDlg::validatePinMapName( QString & name ) const +{ + name.replace( ' ', '_' ); + + if ( name.isEmpty() ) + return QValidator::Intermediate; + + for ( unsigned i = 0; i < name.length(); ++i ) + { + if ( !name[i].isLetterOrNumber() && name[i] != '_' ) + return QValidator::Invalid; + } + + if ( name[0].isNumber() ) + return QValidator::Intermediate; + + if ( m_pWidget->pinMapCombo->contains( name ) ) + return QValidator::Intermediate; + + return QValidator::Acceptable; +} + + +class PinMappingNameValidator : public QValidator +{ + public: + /** + * Create a validator. If oldName is not empty, then the input is + * allowed to be oldName. + */ + PinMappingNameValidator( MicroSettingsDlg * dlg, const QString & oldName = 0 ) + : QValidator(0) + { + m_pDlg = dlg; + m_oldName = oldName; + } + + virtual State validate( QString & input, int & ) const + { + if ( (!m_oldName.isEmpty()) && (input == m_oldName) ) + return QValidator::Acceptable; + + return m_pDlg->validatePinMapName( input ); + } + + protected: + MicroSettingsDlg * m_pDlg; + QString m_oldName; +}; + + +void MicroSettingsDlg::slotCheckNewPinMappingName( const QString & name ) +{ + // Validate name might change the name so that it is valid + QString newName = name; + + if ( m_pNewPinMappingWidget ) + m_pNewPinMappingDlg->enableButtonOK( validatePinMapName( newName ) == QValidator::Acceptable ); + + if ( newName != name ) + m_pNewPinMappingWidget->nameEdit->setText( newName ); +} + + +void MicroSettingsDlg::slotCreatePinMap() +{ + m_pNewPinMappingDlg = new KDialogBase( this, "New Pin Mapping Dlg", true, i18n("New Pin Mapping"), Ok | Cancel ); + m_pNewPinMappingDlg->setButtonText( Ok, i18n("Create") ); + m_pNewPinMappingWidget = new NewPinMappingWidget( m_pNewPinMappingDlg ); + m_pNewPinMappingDlg->setMainWidget( m_pNewPinMappingWidget ); + + PinMappingNameValidator * validator = new PinMappingNameValidator( this ); + m_pNewPinMappingWidget->nameEdit->setValidator( validator ); + + connect( m_pNewPinMappingWidget->nameEdit, SIGNAL(textChanged(const QString &)), this, SLOT(slotCheckNewPinMappingName(const QString &)) ); + slotCheckNewPinMappingName( 0 ); + + int accepted = m_pNewPinMappingDlg->exec(); + unsigned selectedType = m_pNewPinMappingWidget->typeCombo->currentItem(); + QString name = m_pNewPinMappingWidget->nameEdit->text(); + + delete m_pNewPinMappingDlg; + delete validator; + m_pNewPinMappingDlg = 0l; + m_pNewPinMappingWidget = 0l; + if ( accepted != QDialog::Accepted ) + return; + + PinMapping::Type type = PinMapping::Invalid; + + switch ( selectedType ) + { + case 0: + type = PinMapping::SevenSegment; + break; + + case 1: + type = PinMapping::Keypad_4x3; + break; + + case 2: + type = PinMapping::Keypad_4x4; + break; + + default: + kdError() << k_funcinfo << "Unknown selected type " << type << endl; + break; + } + + m_pinMappings[name] = PinMapping( type ); + m_pWidget->pinMapCombo->insertItem( name ); + m_pWidget->pinMapCombo->setCurrentItem( m_pWidget->pinMapCombo->count() - 1 ); + + updatePinMapButtons(); + slotModifyPinMap(); +} + + +void MicroSettingsDlg::slotRenamePinMap() +{ + KComboBox * combo = m_pWidget->pinMapCombo; + + QString oldName = combo->currentText(); + if ( oldName.isEmpty() ) + return; + + PinMappingNameValidator * validator = new PinMappingNameValidator( this, oldName ); + + bool ok = false; + QString newName = KInputDialog::getText( i18n("New Pin Map Name"), i18n("Name"), oldName, & ok, this, 0, validator ); + + delete validator; + + if ( !ok ) + return; + + if ( newName == oldName ) + return; + + m_pinMappings[ newName ] = m_pinMappings[ oldName ]; + m_pinMappings.remove( oldName ); + + combo->setCurrentText( newName ); +} + + +void MicroSettingsDlg::slotModifyPinMap() +{ + QString name = m_pWidget->pinMapCombo->currentText(); + PinMapping pinMapping = m_pinMappings[ name ]; + + PinMapEditor * pinMapEditor = new PinMapEditor( & pinMapping, m_pMicroSettings->microInfo(), this, "PinMapEditor" ); + int accepted = pinMapEditor->exec(); + + delete pinMapEditor; + + if ( accepted != QDialog::Accepted ) + return; + + m_pinMappings[ name ] = pinMapping; +} + + +void MicroSettingsDlg::slotRemovePinMap() +{ + KComboBox * combo = m_pWidget->pinMapCombo; + + QString pinMapID = combo->currentText(); + if ( pinMapID.isEmpty() ) + return; + + m_pinMappings.remove( pinMapID ); + combo->removeItem( combo->currentItem() ); + + updatePinMapButtons(); +} + + +void MicroSettingsDlg::updatePinMapButtons() +{ + bool havePinMaps = (m_pWidget->pinMapCombo->count() != 0); + + m_pWidget->pinMapModify->setEnabled( havePinMaps ); + m_pWidget->pinMapRename->setEnabled( havePinMaps ); + m_pWidget->pinMapRemove->setEnabled( havePinMaps ); +} + + +void MicroSettingsDlg::savePort( int row ) +{ + QString port = m_portNames[row]; + + int type, state; + + QString typeText = m_portTypeEdit[row]->text(); + bool typeOk = true; + if ( typeText.startsWith( "0x", false ) ) type = typeText.remove(0,2).toInt( &typeOk, 16 ); + else if ( typeText.contains( QRegExp("[^01]") ) ) type = typeText.toInt( &typeOk, 10 ); + else type = typeText.toInt( &typeOk, 2 ); + + if ( !typeOk ) + { +// KMessageBox::sorry( this, i18n("Unregnised Port Type: %1").arg(typeText) ); + return; + } + + + QString stateText = m_portStateEdit[row]->text(); + bool stateOk = true; + if ( stateText.startsWith( "0x", false ) ) state = stateText.remove(0,2).toInt( &stateOk, 16 ); + else if ( stateText.contains( QRegExp("[^01]") ) ) state = stateText.toInt( &stateOk, 10 ); + else state = stateText.toInt( &stateOk, 2 ); + + if ( !stateOk ) + { +// KMessageBox::sorry( this, i18n("Unregnised Port State: %1").arg(stateText) ); + return; + } + + m_pMicroSettings->setPortState( port, state ); + m_pMicroSettings->setPortType( port, type ); +} + + +void MicroSettingsDlg::saveVariable( int row ) +{ + QString name = m_pWidget->variables->text( row, 0 ); + if ( name.isEmpty() ) return; + + QString valueText = m_pWidget->variables->text( row, 1 ); + int value; + bool ok = true; + if ( valueText.startsWith( "0x", false ) ) value = valueText.remove(0,2).toInt( &ok, 16 ); + else value = valueText.toInt( &ok, 10 ); + + if (!ok) + { + KMessageBox::sorry( this, i18n("Invalid variable value: %1").arg(valueText) ); + return; + } + + m_pMicroSettings->setVariable( name, value, true ); + VariableInfo *info = m_pMicroSettings->variableInfo(name); + if ( info && info->valueAsString().toInt() != value ) + { +// info->setValue(value); +// info->permanent = true; + info->initAtStart = true; + } +} + + +void MicroSettingsDlg::checkAddVariableRow() +{ + int lastRow = m_pWidget->variables->numRows()-1; + if ( !m_pWidget->variables->text( lastRow, 0 ).isEmpty() ) m_pWidget->variables->insertRows( lastRow+1, 1 ); +} + + + +#include "microsettingsdlg.moc" diff --git a/src/gui/microsettingsdlg.h b/src/gui/microsettingsdlg.h new file mode 100644 index 0000000..54582dd --- /dev/null +++ b/src/gui/microsettingsdlg.h @@ -0,0 +1,105 @@ +/*************************************************************************** + * Copyright (C) 2003-2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef MICROSETTINGSDLG_H +#define MICROSETTINGSDLG_H + +#include <kdialogbase.h> + +#include <qmap.h> +#include <qvalidator.h> +#include <qvaluevector.h> + +class KLineEdit; +class MicroSettings; +class MicroSettingsWidget; +class NewPinMappingWidget; +class PinMapping; + +typedef QMap< QString, PinMapping > PinMappingMap; + +/** +@author David Saxton +*/ +class MicroSettingsDlg : public KDialogBase +{ + Q_OBJECT + public: + MicroSettingsDlg( MicroSettings *_microSettings, QWidget *parent = 0L, const char *name = 0L ); + ~MicroSettingsDlg(); + + void reject(); + void accept(); + + /** + * @param pinMapName the pinMapName; may be changed to make it valid + * (e.g. spaces replaced with underscores). + * @returns Invalid for a pinMapName containing a non-variable name, + * Intermediate for a pinMapName that starts with a number or is already + * in use, and Acceptable otherwise. + */ + QValidator::State validatePinMapName( QString & pinMapName ) const; + + public slots: + /** + * Saves the port details in the given row to the MicroSettings class. + * Usually called when the value is changed, or on 'Apply' of the + * dialog. + */ + void savePort( int row ); + /** + * Saves the variable details to the MicroSettings class. + */ + void saveVariable( int row ); + /** + * Adds an extra row to the list of variable if one is required. + */ + void checkAddVariableRow(); + /** + * Called when the pinMapAdd button is pressed. + */ + void slotCreatePinMap(); + /** + * Called when the pinMapModify button is pressed. + */ + void slotModifyPinMap(); + /** + * Called when the pinMapRename button is pressed. + */ + void slotRenamePinMap(); + /** + * Called when the pinMapRemove button is pressed. + */ + void slotRemovePinMap(); + /** + * Called when the dialog is Applied or OK'd. + */ + void slotSaveStuff(); + + protected slots: + void slotCheckNewPinMappingName( const QString & name ); + + protected: + /** + * Set each button enabled / disabled as appropriate. + */ + void updatePinMapButtons(); + + NewPinMappingWidget * m_pNewPinMappingWidget; // Used for checking that the variable name is ok + KDialogBase * m_pNewPinMappingDlg; + MicroSettingsWidget * m_pWidget; + MicroSettings * m_pMicroSettings; + PinMappingMap m_pinMappings; + QValueVector< KLineEdit * > m_portTypeEdit; + QValueVector< KLineEdit * > m_portStateEdit; + QStringList m_portNames; +}; + +#endif diff --git a/src/gui/microsettingswidget.ui b/src/gui/microsettingswidget.ui new file mode 100644 index 0000000..5c63a1b --- /dev/null +++ b/src/gui/microsettingswidget.ui @@ -0,0 +1,197 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>MicroSettingsWidget</class> +<widget class="QWidget"> + <property name="name"> + <cstring>MicroSettingsWidget</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>447</width> + <height>401</height> + </rect> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>1</hsizetype> + <vsizetype>1</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="caption"> + <string>PIC Settings</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <widget class="QGroupBox"> + <property name="name"> + <cstring>portsGroupBox</cstring> + </property> + <property name="title"> + <string>Initial Port Settings</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel1</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>3</hsizetype> + <vsizetype>1</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Tip: Toggle the initial state (high/low) of a pin by clicking its picture. +Drag it to set the type (input/output).</string> + </property> + <property name="textFormat"> + <enum>PlainText</enum> + </property> + </widget> + </vbox> + </widget> + <widget class="QGroupBox"> + <property name="name"> + <cstring>groupBox6</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>1</hsizetype> + <vsizetype>1</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="title"> + <string>Initial Variable Values</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QTable"> + <column> + <property name="text"> + <string>Variable</string> + </property> + </column> + <column> + <property name="text"> + <string>Value</string> + </property> + </column> + <property name="name"> + <cstring>variables</cstring> + </property> + <property name="minimumSize"> + <size> + <width>0</width> + <height>64</height> + </size> + </property> + <property name="numRows"> + <number>0</number> + </property> + <property name="numCols"> + <number>2</number> + </property> + </widget> + </vbox> + </widget> + <widget class="QGroupBox"> + <property name="name"> + <cstring>groupBox3</cstring> + </property> + <property name="title"> + <string>Pin Map Definitions</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="KComboBox" row="0" column="0" rowspan="1" colspan="5"> + <property name="name"> + <cstring>pinMapCombo</cstring> + </property> + </widget> + <widget class="KPushButton" row="1" column="4"> + <property name="name"> + <cstring>pinMapRemove</cstring> + </property> + <property name="text"> + <string>Remove</string> + </property> + </widget> + <spacer row="1" column="0"> + <property name="name"> + <cstring>spacer3</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>60</width> + <height>20</height> + </size> + </property> + </spacer> + <widget class="KPushButton" row="1" column="1"> + <property name="name"> + <cstring>pinMapAdd</cstring> + </property> + <property name="text"> + <string>Create</string> + </property> + </widget> + <widget class="KPushButton" row="1" column="2"> + <property name="name"> + <cstring>pinMapModify</cstring> + </property> + <property name="text"> + <string>Modif&y</string> + </property> + <property name="accel"> + <string>Alt+Y</string> + </property> + </widget> + <widget class="KPushButton" row="1" column="3"> + <property name="name"> + <cstring>pinMapRename</cstring> + </property> + <property name="text"> + <string>Rename</string> + </property> + <property name="accel"> + <string></string> + </property> + </widget> + </grid> + </widget> + </vbox> +</widget> +<layoutdefaults spacing="6" margin="11"/> +<includehints> + <includehint>kcombobox.h</includehint> + <includehint>kpushbutton.h</includehint> + <includehint>kpushbutton.h</includehint> + <includehint>kpushbutton.h</includehint> + <includehint>kpushbutton.h</includehint> +</includehints> +</UI> diff --git a/src/gui/newfiledlg.cpp b/src/gui/newfiledlg.cpp new file mode 100644 index 0000000..485b3bf --- /dev/null +++ b/src/gui/newfiledlg.cpp @@ -0,0 +1,151 @@ +/*************************************************************************** + * Copyright (C) 2003-2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "config.h" +#include "document.h" +#include "microinfo.h" +#include "newfiledlg.h" +#include "newfilewidget.h" +#include "microlibrary.h" +#include "microselectwidget.h" +#include "projectmanager.h" +#include "textdocument.h" + +#include <kcombobox.h> +#include <kdebug.h> +#include <klineedit.h> +#include <kiconview.h> +#include <klocale.h> +#include <kiconloader.h> + +#include <qcanvas.h> +#include <qcheckbox.h> +#include <qdir.h> +#include <qfile.h> +#include <qlabel.h> +#include <qpaintdevicemetrics.h> + + +NewFileDlg::NewFileDlg( QWidget *parent ) + : KDialogBase( parent, "newfiledlg", true, "New File", KDialogBase::Ok|KDialogBase::Cancel, KDialogBase::Ok, true ) +{ + m_pMainParent = parent; + m_bAccepted = false; + m_pNewFileWidget = new NewFileWidget(this); + + m_pNewFileWidget->typeIconView->setSelectionMode(QIconView::Single); + m_pNewFileWidget->typeIconView->setMode(KIconView::Select); + + KIconLoader *loader = KGlobal::iconLoader(); + + QValueList<QIconViewItem*> items; + + items << new QIconViewItem(m_pNewFileWidget->typeIconView,"Assembly Code (.asm)", loader->loadIcon( "source", KIcon::NoGroup, KIcon::SizeHuge ) ); + items << new QIconViewItem(m_pNewFileWidget->typeIconView,"C (.c)", loader->loadIcon( "source_c", KIcon::NoGroup, KIcon::SizeHuge ) ); + items << new QIconViewItem(m_pNewFileWidget->typeIconView,"Circuit (.circuit)", loader->loadIcon( "ktechlab_circuit", KIcon::NoGroup, KIcon::SizeHuge ) ); + items << new QIconViewItem(m_pNewFileWidget->typeIconView,"FlowCode (.flowcode)", loader->loadIcon( "ktechlab_flowcode", KIcon::NoGroup, KIcon::SizeHuge ) ); +#ifdef MECHANICS + items << new QIconViewItem(m_pNewFileWidget->typeIconView,"Mechanics (.mechanics)", loader->loadIcon( "exec", KIcon::NoGroup, KIcon::SizeHuge ) ); +#endif + items << new QIconViewItem(m_pNewFileWidget->typeIconView,"Microbe (.microbe)", loader->loadIcon( "ktechlab_microbe", KIcon::NoGroup, KIcon::SizeHuge ) ); + + unsigned minWidth = 20 + m_pNewFileWidget->typeIconView->spacing() * items.size(); + int minHeight = 0; + + const QValueList<QIconViewItem*>::iterator end = items.end(); + for ( QValueList<QIconViewItem*>::iterator it = items.begin(); it != end; ++it ) + { + (*it)->setDragEnabled(false); + minWidth += (*it)->width(); + minHeight = QMAX( minHeight, (*it)->height()+20 ); + } + + m_pNewFileWidget->typeIconView->setMinimumSize( minWidth, minHeight ); + m_pNewFileWidget->typeIconView->setCurrentItem(items[3]); + m_pNewFileWidget->addToProjectCheck->setChecked( ProjectManager::self()->currentProject() ); + m_pNewFileWidget->addToProjectCheck->setEnabled( ProjectManager::self()->currentProject() ); + microSelectWidget()->setAllowedFlowCodeSupport( MicroInfo::FullSupport | MicroInfo::PartialSupport ); + + setMainWidget(m_pNewFileWidget); + + // Our behaviour is to have single click selects and double click accepts the dialog + connect( m_pNewFileWidget->typeIconView, SIGNAL(selectionChanged(QIconViewItem*)), this, SLOT(fileTypeChanged(QIconViewItem*)) ); + connect( m_pNewFileWidget->typeIconView, SIGNAL(doubleClicked(QIconViewItem*)), this, SLOT(accept())); + + setAcceptDrops(true); + + m_pNewFileWidget->typeIconView->adjustSize(); + m_pNewFileWidget->adjustSize(); + adjustSize(); +} + +void NewFileDlg::accept() +{ + hide(); + m_bAccepted = true; + + const QString fileText = m_pNewFileWidget->typeIconView->currentItem()->text(); + + if ( fileText.contains(".flowcode") ) + m_fileType = Document::dt_flowcode; + + else if ( fileText.contains(".circuit") ) + m_fileType = Document::dt_circuit; + + else if ( fileText.contains(".mechanics") ) + m_fileType = Document::dt_mechanics; + + else if ( fileText.contains(".asm") ) + { + m_fileType = Document::dt_text; + m_codeType = TextDocument::ct_asm; + } + + else if ( fileText.contains(".basic") || fileText.contains(".microbe") ) + { + m_fileType = Document::dt_text; + m_codeType = TextDocument::ct_microbe; + } + + else if (fileText.contains(".c") ) + { + m_fileType = Document::dt_text; + m_codeType = TextDocument::ct_c; + } + + else + m_fileType = Document::dt_text; + + m_bAddToProject = m_pNewFileWidget->addToProjectCheck->isChecked(); + + m_microID = m_pNewFileWidget->m_pMicroSelect->micro(); +} + + +void NewFileDlg::reject() +{ + m_bAccepted = false; +} + + +void NewFileDlg::fileTypeChanged( QIconViewItem *item ) +{ + m_pNewFileWidget->m_pMicroSelect->setEnabled( + item->text().contains(".flowcode") ); +} + + +MicroSelectWidget * NewFileDlg::microSelectWidget() const +{ + return m_pNewFileWidget->m_pMicroSelect; +} + + +#include "newfiledlg.moc" diff --git a/src/gui/newfiledlg.h b/src/gui/newfiledlg.h new file mode 100644 index 0000000..fc20800 --- /dev/null +++ b/src/gui/newfiledlg.h @@ -0,0 +1,55 @@ +/*************************************************************************** + * Copyright (C) 2003-2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef NEWFILEDLG_H +#define NEWFILEDLG_H + +#include <kdialogbase.h> + +class MicroSelectWidget; +class NewFileWidget; +class QIconViewItem; + +/** +A standard dialog for getting file details from the user for a new project +@short Dialog for new file details +@author David Saxton +*/ +class NewFileDlg : public KDialogBase +{ + Q_OBJECT + public: + NewFileDlg( QWidget *parent ); + + void reject(); + void accept(); + + bool accepted() const { return m_bAccepted; } + int fileType() const { return m_fileType; } + int codeType() const { return m_codeType; } + bool addToProject() const { return m_bAddToProject; } + QString microID() const { return m_microID; } + MicroSelectWidget * microSelectWidget() const; + + public slots: + void fileTypeChanged( QIconViewItem *item ); + + protected: + bool m_bAccepted; + int m_fileType; + int m_codeType; + bool m_bAddToProject; + QString m_microID; + + NewFileWidget * m_pNewFileWidget; + QWidget * m_pMainParent; +}; + +#endif diff --git a/src/gui/newfilewidget.ui b/src/gui/newfilewidget.ui new file mode 100644 index 0000000..0e02524 --- /dev/null +++ b/src/gui/newfilewidget.ui @@ -0,0 +1,195 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>NewFileWidget</class> +<widget class="QWidget"> + <property name="name"> + <cstring>NewFileWidget</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>600</width> + <height>213</height> + </rect> + </property> + <property name="caption"> + <string>New File</string> + </property> + <property name="whatsThis" stdset="0"> + <string>Select the type of file you wish to create.<br> +<p> +<b>Pic Program</b><br> +Creates a new PIC program, with flow chart editor. Select the target device for your program below. +<p> +<b>Circuit</b><br> +Creates a new circuit, with drag and drop editor. Real time simulation of the circuit occurs automatically.</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <widget class="QLabel" row="0" column="0" rowspan="1" colspan="2"> + <property name="name"> + <cstring>textLabel1</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>5</hsizetype> + <vsizetype>4</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <bold>1</bold> + </font> + </property> + <property name="text"> + <string>New File Details</string> + </property> + </widget> + <widget class="QLabel" row="1" column="0"> + <property name="name"> + <cstring>textLabel1_2_2</cstring> + </property> + <property name="text"> + <string>File Type:</string> + </property> + <property name="alignment"> + <set>AlignVCenter</set> + </property> + </widget> + <widget class="KIconView" row="1" column="1"> + <property name="name"> + <cstring>typeIconView</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>1</hsizetype> + <vsizetype>1</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>500</width> + <height>150</height> + </size> + </property> + <property name="acceptDrops"> + <bool>false</bool> + </property> + <property name="resizePolicy"> + <enum>AutoOneFit</enum> + </property> + <property name="autoArrange"> + <bool>true</bool> + </property> + <property name="itemsMovable"> + <bool>false</bool> + </property> + </widget> + <widget class="QCheckBox" row="3" column="1"> + <property name="name"> + <cstring>addToProjectCheck</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>1</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>&Add to project</string> + </property> + <property name="accel"> + <string>Alt+A</string> + </property> + <property name="checked"> + <bool>true</bool> + </property> + </widget> + <widget class="MicroSelectWidget" row="2" column="0" rowspan="1" colspan="2"> + <property name="name"> + <cstring>m_pMicroSelect</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>5</hsizetype> + <vsizetype>5</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>0</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>32767</width> + <height>32767</height> + </size> + </property> + </widget> + <spacer row="4" column="1"> + <property name="name"> + <cstring>spacer1</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>0</height> + </size> + </property> + </spacer> + </grid> +</widget> +<customwidgets> + <customwidget> + <class>MicroSelectWidget</class> + <header location="local">microselectwidget.h</header> + <sizehint> + <width>-1</width> + <height>-1</height> + </sizehint> + <container>0</container> + <sizepolicy> + <hordata>5</hordata> + <verdata>5</verdata> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + <pixmap>image0</pixmap> + </customwidget> +</customwidgets> +<images> + <image name="image0"> + <data format="PNG" length="1122">89504e470d0a1a0a0000000d4948445200000016000000160806000000c4b46c3b0000042949444154388db5954d6c545514c77ff7de37eff1a6a550da994e5ba798868f948fc847a2a2911816c436b0a02ed0083161a1981877ee10d90aa94656063491882c5cd4c4b8103f20cd806909a98604da8482341de80cb69de9bcd799799d79efba980f1da3a80b4f727273939bdff99f9b93f3175a6b6a21849080020c4002a29a8f0a5dcd002803bed63a1035f0d0d0504b6f6fef51d7758fa4d3e98d8ee3fc03af128ee3303939399548242eb8aefb09300f78464de9f0f0f0d148cc7caf3d5424de97a7ec17104220242805520a44557bbd4b21f083801d33e63a6d1bc7bf19c6064e0319a35a58b9ae7ba43d54e4d7c54b64dc097c7f09d30ef04b36736983cc7c11b4456b9b492c5e20dc54a6540e00896a8268a7017010f8bcae1830d2e9f4c69ebe3c197782b2ce60d94ddc9b30f9feeb45ae5d7181c5ead35636ef08d3ffe26a76ee3208b4031a56d80aa00768fda362e9380e25bf80ef3b587613d7afc099533380a4b5dda46fcb5aa42998b9eb7173dce5e6788e434763ec7fc942532008ea436003660d2caa7f8d69c3bd8910674e4d033e83877ad8d36fb0aa4d6058658ac5358c5df6383bf480f31fdda5a3632bbbfb6da4ccd5c00a10b2012cc12fd97cfb5516f0187c25cee0619396480ec3ca909d0f3372d1e5f9fe66de3ad605587c712ec55256a2540d5519d19ae24a29057329839f7ecc138944d833102290f3184ae1e6d6f0e98739c61229e6d21eafbed1c9c8c5558c8f3ee4ceed76946a1c43d97091828585223e2eebfbc295f60d45de89707628cb58224577bc85a79e5d4d786581cddb9b0148259711b20145836221400416e0232d1f6595c9ce4538773ac7b54406f0b15784e8e80aa1ac2594190220d021e49f2437960156b70ba095e95f96f18acd8c263cc6122962b1167a7ba34cdd7ec8c977a699bed546722a8fc2a02b6e2185fff78ab586ce78894ddb9ab9f5738ed14b1e030756929d8ff2e4ae5544632b38f96e89a99b298ebd6950cc2d12ed0eb3618b26f520788462ad09877df60db600f0f1fb49c646f21c7ebd836dcf94e9d9b4c0db27d6d3bd36c6426a96bce731703046d7da129a4670836284a054869d4f1b1c7e2dca67676ef3c1898091ef5ad8bc358c3205c93bf7c9a40ad4c660f4728edd7bbb3142ea2fc1da711cfc200011e08b45f6bd1c261adbce9717928c5f9d63fc6a19f0014567773303073790f8214bd6f5282dfbb8b9fa1f6b40d7c1939393533b92e63ad9240990484aec7e41b27d579cbb531e0f67023492d86316ebb768ba1e0f786e6f37e56550c62237aee7011e54abd7c1412291b8a06de378b4d3c0b215da9708e5a3a442192085404a83d4accfecac067c0c43e13a70e3fa12e96409e0325000966bbb354465339d04a6f9dd15fe6dde07ce030780278088d05ad72cc9a6b2f2daaaa74d75a1f0e8d0d5f60b40868a8364ead6248430000b0857a126ffcdf396abf03ce089ffcb4c7f033046c6b4a995e7a00000000049454e44ae426082</data> + </image> +</images> +<tabstops> + <tabstop>typeIconView</tabstop> + <tabstop>addToProjectCheck</tabstop> +</tabstops> +<layoutdefaults spacing="6" margin="11"/> +<includehints> + <includehint>kiconview.h</includehint> + <includehint>microselectwidget.h</includehint> +</includehints> +</UI> diff --git a/src/gui/newpinmappingwidget.ui b/src/gui/newpinmappingwidget.ui new file mode 100644 index 0000000..4ac5932 --- /dev/null +++ b/src/gui/newpinmappingwidget.ui @@ -0,0 +1,130 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>NewPinMappingWidget</class> +<widget class="QWidget"> + <property name="name"> + <cstring>NewPinMappingWidget</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>350</width> + <height>80</height> + </rect> + </property> + <property name="caption"> + <string>New Pin Mapping</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <widget class="QLabel" row="2" column="0"> + <property name="name"> + <cstring>textLabel2</cstring> + </property> + <property name="text"> + <string>Type:</string> + </property> + </widget> + <widget class="QLabel" row="1" column="0"> + <property name="name"> + <cstring>textLabel1</cstring> + </property> + <property name="text"> + <string>Name:</string> + </property> + </widget> + <widget class="QLabel" row="0" column="0" rowspan="1" colspan="2"> + <property name="name"> + <cstring>textLabel3</cstring> + </property> + <property name="font"> + <font> + <bold>1</bold> + </font> + </property> + <property name="text"> + <string>New Pin Mapping</string> + </property> + <property name="textFormat"> + <enum>PlainText</enum> + </property> + </widget> + <spacer row="3" column="1"> + <property name="name"> + <cstring>spacer6</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>21</height> + </size> + </property> + </spacer> + <widget class="KLineEdit" row="1" column="1"> + <property name="name"> + <cstring>nameEdit</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>3</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>300</width> + <height>0</height> + </size> + </property> + <property name="toolTip" stdset="0"> + <string>The variable name of the pin mapping - this must be a valid Microbe variable name.</string> + </property> + </widget> + <widget class="QComboBox" row="2" column="1"> + <item> + <property name="text"> + <string>Seven Segment</string> + </property> + </item> + <item> + <property name="text"> + <string>Keypad (4x3)</string> + </property> + </item> + <item> + <property name="text"> + <string>Keypad (4x4)</string> + </property> + </item> + <property name="name"> + <cstring>typeCombo</cstring> + </property> + <property name="minimumSize"> + <size> + <width>200</width> + <height>0</height> + </size> + </property> + </widget> + </grid> +</widget> +<customwidgets> +</customwidgets> +<layoutdefaults spacing="6" margin="11"/> +<includehints> + <includehint>klineedit.h</includehint> +</includehints> +</UI> diff --git a/src/gui/newprojectwidget.ui b/src/gui/newprojectwidget.ui new file mode 100644 index 0000000..9d8648f --- /dev/null +++ b/src/gui/newprojectwidget.ui @@ -0,0 +1,110 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>NewProjectWidget</class> +<widget class="QWidget"> + <property name="name"> + <cstring>NewProjectWidget</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>532</width> + <height>103</height> + </rect> + </property> + <property name="caption"> + <string>New Project</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <widget class="QLabel" row="4" column="0"> + <property name="name"> + <cstring>textLabel8</cstring> + </property> + <property name="text"> + <string>Final location:</string> + </property> + </widget> + <widget class="QLabel" row="4" column="1" rowspan="1" colspan="3"> + <property name="name"> + <cstring>locationLabel</cstring> + </property> + <property name="text"> + <string>/</string> + </property> + </widget> + <spacer row="5" column="1"> + <property name="name"> + <cstring>spacer2</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>16</height> + </size> + </property> + </spacer> + <widget class="QLabel" row="2" column="0"> + <property name="name"> + <cstring>textLabel4</cstring> + </property> + <property name="text"> + <string>Location:</string> + </property> + </widget> + <widget class="QLabel" row="0" column="0" rowspan="1" colspan="4"> + <property name="name"> + <cstring>textLabel1</cstring> + </property> + <property name="font"> + <font> + <bold>1</bold> + </font> + </property> + <property name="text"> + <string>New Project Details</string> + </property> + </widget> + <widget class="QLabel" row="1" column="0"> + <property name="name"> + <cstring>textLabel10</cstring> + </property> + <property name="text"> + <string>Project Name:</string> + </property> + </widget> + <widget class="KLineEdit" row="1" column="1" rowspan="1" colspan="3"> + <property name="name"> + <cstring>projectNameEdit</cstring> + </property> + </widget> + <widget class="KURLRequester" row="2" column="1" rowspan="1" colspan="3"> + <property name="name"> + <cstring>projectLocationURL</cstring> + </property> + </widget> + </grid> +</widget> +<tabstops> + <tabstop>projectNameEdit</tabstop> + <tabstop>projectLocationURL</tabstop> +</tabstops> +<layoutdefaults spacing="6" margin="11"/> +<includehints> + <includehint>klineedit.h</includehint> + <includehint>kurlrequester.h</includehint> + <includehint>klineedit.h</includehint> + <includehint>kpushbutton.h</includehint> +</includehints> +</UI> diff --git a/src/gui/orientationwidget.cpp b/src/gui/orientationwidget.cpp new file mode 100644 index 0000000..28e10c7 --- /dev/null +++ b/src/gui/orientationwidget.cpp @@ -0,0 +1,255 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "cnitem.h" +#include "cnitemgroup.h" +#include "component.h" +#include "flowpart.h" +#include "iteminterface.h" +#include "itemlibrary.h" +#include "orientationwidget.h" +#include "node.h" + +#include <kstandarddirs.h> +#include <qbitmap.h> +#include <qlayout.h> +#include <qimage.h> +#include <qpixmap.h> +#include <qpainter.h> +#include <qpushbutton.h> + +const int _size = 44; + +OrientationWidget::OrientationWidget(QWidget *parent, const char *name) + : QWidget(parent, name) +{ + QGridLayout *layout = new QGridLayout( this, 2, 4, 0, 4 ); + p_activeFlowPart = 0l; + + for ( int row=0; row<2; ++row ) + { + for ( int col=0; col<4; ++col ) + { + QPushButton *btn = new QPushButton(this); + m_toolBtn[row][col] = btn; + layout->addWidget( btn, row, col ); + btn->setFixedSize( _size+6, _size+6 ); +// btn->setFlat(true); + btn->setEnabled(false); + } + } + + connect( m_toolBtn[0][0], SIGNAL(clicked()), this, SLOT(set_cio_noflip_0()) ); + connect( m_toolBtn[0][1], SIGNAL(clicked()), this, SLOT(set_cio_noflip_90()) ); + connect( m_toolBtn[0][2], SIGNAL(clicked()), this, SLOT(set_cio_noflip_180()) ); + connect( m_toolBtn[0][3], SIGNAL(clicked()), this, SLOT(set_cio_noflip_270()) ); + connect( m_toolBtn[1][0], SIGNAL(clicked()), this, SLOT(set_cio_flip_0()) ); + connect( m_toolBtn[1][1], SIGNAL(clicked()), this, SLOT(set_cio_flip_90()) ); + connect( m_toolBtn[1][2], SIGNAL(clicked()), this, SLOT(set_cio_flip_180()) ); + connect( m_toolBtn[1][3], SIGNAL(clicked()), this, SLOT(set_cio_flip_270()) ); +} + + +OrientationWidget::~OrientationWidget() +{ +} + +void OrientationWidget::slotUpdate( CNItem *activeCNItem ) +{ + p_activeFlowPart = dynamic_cast<FlowPart*>(activeCNItem); + if (p_activeFlowPart) + { + initFromFlowPart(p_activeFlowPart); + return; + } + + Component *activeComponent = dynamic_cast<Component*>(activeCNItem); + + if ( activeComponent && (activeComponent->canRotate() || activeComponent->canFlip()) ) + { + initFromComponent(activeComponent); + return; + } + + slotClear(); +} + + +void OrientationWidget::initFromFlowPart( FlowPart *flowPart ) +{ + if (!flowPart) + return; + + uint valid = flowPart->allowedOrientations(); + +// m_toolBtn[0][0]->setText("b00"); + for ( uint i=0; i<2; ++i ) + { + for ( uint j=0; j<4; ++j ) + { + uint o = j + 4*i; + if ( valid & (1<<o) ) + { + m_toolBtn[i][j]->setEnabled(true); + QPixmap pm( 50, 50 ); + flowPart->orientationPixmap( o, pm ); + m_toolBtn[i][j]->setPixmap(pm); + } + } + } +} + + +void OrientationWidget::initFromComponent( Component *component ) +{ + const QImage im = itemLibrary()->itemImage(component); + + QRect bound = component->boundingRect(); + + // We want a nice square bounding rect + const int dy = bound.width() - bound.height(); + if ( dy > 0 ) + { + bound.setTop( bound.top()-(dy/2) ); + bound.setBottom( bound.bottom()+(dy/2) ); + } + else if ( dy < 0 ) + { + bound.setLeft( bound.left()+(dy/2) ); + bound.setRight( bound.right()-(dy/2) ); + } + + + QPixmap tbPm; + tbPm.convertFromImage(im); + m_toolBtn[0][0]->setPixmap(tbPm); + m_toolBtn[0][0]->setEnabled(true); + + if ( component->canRotate() ) + { +// QPixmap tbPm; + tbPm.convertFromImage( im.xForm( Component::transMatrix( 90, false, bound.width()/2, bound.height()/2 ) ) ); + m_toolBtn[0][1]->setPixmap(tbPm); + m_toolBtn[0][1]->setEnabled(true); + +// QPixmap tbPm; + tbPm.convertFromImage( im.xForm( Component::transMatrix( 180, false, bound.width()/2, bound.height()/2 ) ) ); + m_toolBtn[0][2]->setPixmap(tbPm); + m_toolBtn[0][2]->setEnabled(true); + +// QPixmap tbPm; + tbPm.convertFromImage( im.xForm( Component::transMatrix( 270, false, bound.width()/2, bound.height()/2 ) ) ); + m_toolBtn[0][3]->setPixmap(tbPm); + m_toolBtn[0][3]->setEnabled(true); + } + + if ( component->canFlip() ) + { +// QPixmap tbPm; + tbPm.convertFromImage( im.xForm( Component::transMatrix( 0, true, bound.width()/2, bound.height()/2 ) ) ); + m_toolBtn[1][0]->setPixmap(tbPm); + m_toolBtn[1][0]->setEnabled(true); + + if ( component->canRotate() ) + { +// QPixmap tbPm; + tbPm.convertFromImage( im.xForm( Component::transMatrix( 90, true, bound.width()/2, bound.height()/2 ) ) ); + m_toolBtn[1][1]->setPixmap(tbPm); + m_toolBtn[1][1]->setEnabled(true); + +// QPixmap tbPm; + tbPm.convertFromImage( im.xForm( Component::transMatrix( 180, true, bound.width()/2, bound.height()/2 ) ) ); + m_toolBtn[1][2]->setPixmap(tbPm); + m_toolBtn[1][2]->setEnabled(true); + +// QPixmap tbPm; + tbPm.convertFromImage( im.xForm( Component::transMatrix( 270, true, bound.width()/2, bound.height()/2 ) ) ); + m_toolBtn[1][3]->setPixmap(tbPm); + m_toolBtn[1][3]->setEnabled(true); + } + } +} + + +void OrientationWidget::slotClear() +{ + for ( int row=0; row<2; ++row ) + { + for ( int col=0; col<4; ++col ) + { + // Hmm...this line has crashed before + m_toolBtn[row][col]->setPixmap( QPixmap::QPixmap() ); + m_toolBtn[row][col]->setEnabled(false); + } + } +} + + +void OrientationWidget::set_cio_noflip_0() +{ + if (p_activeFlowPart) + ItemInterface::self()->setFlowPartOrientation(0); + else + ItemInterface::self()->setComponentOrientation( 0, false ); +} +void OrientationWidget::set_cio_noflip_90() +{ + if (p_activeFlowPart) + ItemInterface::self()->setFlowPartOrientation(1); + else + ItemInterface::self()->setComponentOrientation( 90, false ); +} +void OrientationWidget::set_cio_noflip_180() +{ + if (p_activeFlowPart) + ItemInterface::self()->setFlowPartOrientation(2); + else + ItemInterface::self()->setComponentOrientation( 180, false ); +} +void OrientationWidget::set_cio_noflip_270() +{ + if (p_activeFlowPart) + ItemInterface::self()->setFlowPartOrientation(3); + else + ItemInterface::self()->setComponentOrientation( 270, false ); +} +void OrientationWidget::set_cio_flip_0() +{ + if (p_activeFlowPart) + ItemInterface::self()->setFlowPartOrientation(4); + else + ItemInterface::self()->setComponentOrientation( 0, true ); +} +void OrientationWidget::set_cio_flip_90() +{ + if (p_activeFlowPart) + ItemInterface::self()->setFlowPartOrientation(5); + else + ItemInterface::self()->setComponentOrientation( 90, true ); +} +void OrientationWidget::set_cio_flip_180() +{ + if (p_activeFlowPart) + ItemInterface::self()->setFlowPartOrientation(6); + else + ItemInterface::self()->setComponentOrientation( 180, true ); +} +void OrientationWidget::set_cio_flip_270() +{ + if (p_activeFlowPart) + ItemInterface::self()->setFlowPartOrientation(7); + else + ItemInterface::self()->setComponentOrientation( 270, true ); +} + +#include "orientationwidget.moc" + + + diff --git a/src/gui/orientationwidget.h b/src/gui/orientationwidget.h new file mode 100644 index 0000000..6a6b6ce --- /dev/null +++ b/src/gui/orientationwidget.h @@ -0,0 +1,56 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef ORIENTATIONWIDGET_H +#define ORIENTATIONWIDGET_H + +#include <qguardedptr.h> +#include <qwidget.h> + +class CNItem; +class CNItemGroup; +class FlowPart; +class QPushButton; + +/** +@author David Saxton +*/ +class OrientationWidget : public QWidget +{ +Q_OBJECT +public: + OrientationWidget( QWidget *parent = 0l, const char *name = 0l ); + ~OrientationWidget(); + +public slots: + void slotUpdate( CNItem *item ); + void slotClear(); + + void set_cio_noflip_0(); + void set_cio_noflip_90(); + void set_cio_noflip_180(); + void set_cio_noflip_270(); + void set_cio_flip_0(); + void set_cio_flip_90(); + void set_cio_flip_180(); + void set_cio_flip_270(); + +signals: + void orientationSelected( uint orientation ); + +protected: + void initFromComponent( Component *component ); + void initFromFlowPart( FlowPart *flowPart ); + + QPushButton *m_toolBtn[2][4]; + QGuardedPtr<FlowPart> p_activeFlowPart; +}; + +#endif diff --git a/src/gui/oscilloscope.cpp b/src/gui/oscilloscope.cpp new file mode 100644 index 0000000..e969f72 --- /dev/null +++ b/src/gui/oscilloscope.cpp @@ -0,0 +1,354 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "oscilloscope.h" +#include "oscilloscopedata.h" +#include "oscilloscopeview.h" +#include "probe.h" +#include "probepositioner.h" +#include "simulator.h" +#include "ktechlab.h" + +#include <cmath> +#include <kcombobox.h> +#include <kconfig.h> +#include <kdebug.h> +#include <kglobal.h> +#include <kiconloader.h> +#include <klocale.h> +#include <knuminput.h> +#include <qbutton.h> +#include <qlabel.h> +#include <qscrollbar.h> +#include <qslider.h> +#include <qtimer.h> +#include <qtoolbutton.h> + +#include <assert.h> + +//BEGIN Oscilloscope Class +QColor probeColors[9] = { + QColor( 0x52, 0x22, 0x00 ), + QColor( 0xB5, 0x00, 0x2F ), + QColor( 0xF9, 0xBA, 0x07 ), + QColor( 0x53, 0x93, 0x16 ), + QColor( 0x00, 0x66, 0x2F ), + QColor( 0x00, 0x41, 0x88 ), + QColor( 0x1B, 0x2D, 0x83 ), + QColor( 0x55, 0x12, 0x7B ), + QColor( 0x7B, 0x0C, 0x82 ) }; + +Oscilloscope * Oscilloscope::m_pSelf = 0l; + +Oscilloscope * Oscilloscope::self( KateMDI::ToolView * parent ) +{ + if ( !m_pSelf ) + { + assert(parent); + m_pSelf = new Oscilloscope(parent); + } + return m_pSelf; +} + + +Oscilloscope::Oscilloscope( KateMDI::ToolView * parent ) + : OscilloscopeWidget(parent) +{ + m_nextColor = 0; + m_nextId = 1; + m_oldestId = -1; + m_oldestProbe = 0l; +// b_isPaused = false; + m_zoomLevel = 0.5; + m_pSimulator = Simulator::self(); + + horizontalScroll->setLineStep(32); + horizontalScroll->setPageStep( oscilloscopeView->width() ); + + connect( resetBtn, SIGNAL(clicked()), this, SLOT(reset()) ); + connect( zoomSlider, SIGNAL(valueChanged(int)), this, SLOT(slotZoomSliderChanged(int)) ); + connect( horizontalScroll, SIGNAL(valueChanged(int )), this, SLOT(slotSliderValueChanged(int )) ); + +// connect( pauseBtn, SIGNAL(clicked()), this, SLOT(slotTogglePause()) ); + + QTimer * updateScrollTmr = new QTimer(this); + connect( updateScrollTmr, SIGNAL(timeout()), this, SLOT(updateScrollbars()) ); + updateScrollTmr->start(20); + + KGlobal::config()->setGroup("Oscilloscope"); + setZoomLevel( KGlobal::config()->readDoubleNumEntry( "ZoomLevel", 0.5 ) ); + + connect( this, SIGNAL(probeRegistered(int, ProbeData *)), probePositioner, SLOT(slotProbeDataRegistered(int, ProbeData *)) ); + connect( this, SIGNAL(probeUnregistered(int )), probePositioner, SLOT(slotProbeDataUnregistered(int )) ); +} + + +Oscilloscope::~Oscilloscope() +{ +} + + +void Oscilloscope::slotTogglePause() +{ +// b_isPaused = !b_isPaused; +// pauseBtn->setText( b_isPaused ? i18n("Resume") : i18n("Pause") ); +// const ProbeDataMap::iterator end = m_probeDataMap.end(); +// for ( ProbeDataMap::iterator it = m_probeDataMap.begin(); it != end; ++it ) +// (*it)->setPaused(b_isPaused); +} + + +int Oscilloscope::sliderTicksPerSecond() const +{ + return int(1e4); +} + + +void Oscilloscope::setZoomLevel( double zoomLevel ) +{ + if ( zoomLevel < 0.0 ) + zoomLevel = 0.0; + + else if ( zoomLevel > 1.0 ) + zoomLevel = 1.0; + + KGlobal::config()->setGroup("Oscilloscope"); + KGlobal::config()->writeEntry( "ZoomLevel", zoomLevel ); + + // We want to maintain the position of the *center* of the view, not the + // left edge, so have to record time at center of view... We also have to + // handle the case where the scroll is at the end separately. + bool wasAtUpperEnd = horizontalScroll->maxValue() == horizontalScroll->value(); + int pageLength = int(oscilloscopeView->width()*sliderTicksPerSecond()/pixelsPerSecond()); + int at_ticks = horizontalScroll->value() + (pageLength/2); + + m_zoomLevel = zoomLevel; + zoomSlider->setValue( int((double(zoomSlider->maxValue())*zoomLevel)+0.5) ); + updateScrollbars(); + + // And restore the center position of the slider + if (!wasAtUpperEnd) + { + int pageLength = int(oscilloscopeView->width()*sliderTicksPerSecond()/pixelsPerSecond()); + horizontalScroll->setValue( at_ticks - (pageLength/2) ); + oscilloscopeView->updateView(); + } +} + + +void Oscilloscope::slotZoomSliderChanged( int value ) +{ + setZoomLevel( double(value)/double(zoomSlider->maxValue()) ); +} + + +ProbeData * Oscilloscope::registerProbe( Probe * probe ) +{ + if (!probe) + return 0l; + + const uint id = m_nextId++; + + ProbeData * probeData = 0l; + + if ( dynamic_cast<LogicProbe*>(probe) ) + { + probeData = new LogicProbeData(id); + m_logicProbeDataMap[id] = static_cast<LogicProbeData*>(probeData); + } + + else + { + probeData = new FloatingProbeData(id); + m_floatingProbeDataMap[id] = static_cast<FloatingProbeData*>(probeData); + } + + m_probeDataMap[id] = probeData; + + if (!m_oldestProbe) + { + m_oldestProbe = probeData; + m_oldestId = id; + } + + probeData->setColor( probeColors[m_nextColor] ); + m_nextColor = (m_nextColor+1)%9; +// probeData->setPaused(b_isPaused); + + emit probeRegistered( id, probeData ); + return probeData; +} + + +void Oscilloscope::unregisterProbe( int id ) +{ + ProbeDataMap::iterator it = m_probeDataMap.find(id); + + if ( it == m_probeDataMap.end() ) + return; + + m_logicProbeDataMap.remove(id); + m_floatingProbeDataMap.remove(id); + + bool oldestDestroyed = it.data() == m_oldestProbe; + + if ( it != m_probeDataMap.end() ) + m_probeDataMap.erase(it); + + if (oldestDestroyed) + getOldestProbe(); + + emit probeUnregistered(id); +} + + +ProbeData * Oscilloscope::probeData( int id ) const +{ + const ProbeDataMap::const_iterator bit = m_probeDataMap.find(id); + if ( bit != m_probeDataMap.end() ) + return bit.data(); + + return 0l; +} + + +int Oscilloscope::probeNumber( int id ) const +{ + const ProbeDataMap::const_iterator end = m_probeDataMap.end(); + int i=0; + for ( ProbeDataMap::const_iterator it = m_probeDataMap.begin(); it != end; ++it ) + { + if ( it.key() == id ) + return i; + i++; + } + return -1; +} + + +int Oscilloscope::numberOfProbes() const +{ + return m_probeDataMap.size(); +} + + +void Oscilloscope::getOldestProbe() +{ + if ( m_probeDataMap.isEmpty() ) + { + m_oldestProbe = 0l; + m_oldestId = -1; + return; + } + + m_oldestProbe = m_probeDataMap.begin().data(); + m_oldestId = m_probeDataMap.begin().key(); +} + + +void Oscilloscope::reset() +{ + const ProbeDataMap::iterator end = m_probeDataMap.end(); + for ( ProbeDataMap::iterator it = m_probeDataMap.begin(); it != end; ++it ) + (*it)->eraseData(); + + oscilloscopeView->updateView(); +} + + +void Oscilloscope::slotSliderValueChanged( int value ) +{ + Q_UNUSED(value); + oscilloscopeView->updateView(); +} + + +void Oscilloscope::updateScrollbars() +{ + bool wasAtUpperEnd = horizontalScroll->maxValue() == horizontalScroll->value(); + + const float pps = pixelsPerSecond(); + + int pageLength = int(oscilloscopeView->width()*sliderTicksPerSecond()/pps); + llong timeAsTicks = time()*sliderTicksPerSecond()/LOGIC_UPDATE_RATE; + llong upper = (timeAsTicks > pageLength) ? (timeAsTicks - pageLength) : 0; + horizontalScroll->setRange( 0, upper ); + + horizontalScroll->setPageStep( ullong(oscilloscopeView->width()*sliderTicksPerSecond()/pps) ); + + if (wasAtUpperEnd) + { + horizontalScroll->setValue( horizontalScroll->maxValue() ); + oscilloscopeView->updateView(); + } +} + + +ullong Oscilloscope::time() const +{ + if (!m_oldestProbe) + return 0; + + return ullong( m_pSimulator->time() - m_oldestProbe->resetTime() ); +} + + +llong Oscilloscope::scrollTime() const +{ +// if ( b_isPaused || numberOfProbes() == 0 ) +// return 0; + + if ( numberOfProbes() == 0 ) + return 0; + + if ( horizontalScroll->maxValue() == 0 ) + { + llong lengthAsTime = llong( oscilloscopeView->width() * LOGIC_UPDATE_RATE / pixelsPerSecond() ); + return m_pSimulator->time() - lengthAsTime; + } + + else + return llong( m_oldestProbe->resetTime() + (llong(horizontalScroll->value()) * LOGIC_UPDATE_RATE / sliderTicksPerSecond()) ); +} + + +double Oscilloscope::pixelsPerSecond() const +{ + return 2 * MIN_BITS_PER_S * std::pow( 2.0, m_zoomLevel * MIN_MAX_LOG_2_DIFF ); +} +//END Oscilloscope Class + + + +void addOscilloscopeAsToolView( KTechlab *ktechlab ) +{ + KateMDI::ToolView * tv; + tv = ktechlab->createToolView( Oscilloscope::toolViewIdentifier(), + KMultiTabBar::Bottom, + KGlobal::iconLoader()->loadIcon( "oscilloscope", KIcon::Small ), + i18n("Oscilloscope") ); + + Oscilloscope::self(tv); +} + + +ProbeData * registerProbe( Probe * probe ) +{ + return Oscilloscope::self()->registerProbe(probe); +} + + +void unregisterProbe( int id ) +{ + Oscilloscope::self()->unregisterProbe(id); +} + + +#include "oscilloscope.moc" diff --git a/src/gui/oscilloscope.h b/src/gui/oscilloscope.h new file mode 100644 index 0000000..79e0dbd --- /dev/null +++ b/src/gui/oscilloscope.h @@ -0,0 +1,192 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef OSCILLOSCOPE_H +#define OSCILLOSCOPE_H + +#ifndef PROBE_H +#ifndef KTECHLAB_H +#ifndef OSCILLOSCOPEDATA_H +#include "oscilloscopewidget.h" +#endif +#endif +#endif + +#include "simulator.h" +#include <qmap.h> + +class FloatingProbeData; +class LogicProbe; +class LogicProbeData; +class KTechlab; +class Oscilloscope; +class Probe; +class ProbeData; +class VoltageProbe; +class QTimer; +namespace KateMDI { class ToolView; } + +typedef QMap< int, ProbeData * > ProbeDataMap; +typedef QMap< int, LogicProbeData * > LogicProbeDataMap; +typedef QMap< int, FloatingProbeData * > FloatingProbeDataMap; + +typedef unsigned long long ullong; +typedef long long llong; + + +#if 0 +const double MAX_BITS_PER_S = 100000; + +// NOTE: The 10 has to agree with the 2^10 = 1024.0 +const int MIN_MAX_LOG_2_DIFF = 10; +const double MIN_BITS_PER_S = MAX_BITS_PER_S / 1024.0; +#else +const double MAX_BITS_PER_S = LOGIC_UPDATE_RATE * 4; + +// NOTE: The 18 has to agree with the 2^18 = 262144.0 +const int MIN_MAX_LOG_2_DIFF = 18; +const double MIN_BITS_PER_S = MAX_BITS_PER_S / 262144.0; +#endif + + +/* +Due to strangeness with generation of .[cpp/h] files from .ui files (that is, +my inability to sort it out neatly), files other than those in /src/gui can't +see header files such as "oscilloscopewidget.h", so we have to provide some +interface functions for accessing the functionality in this class +*/ +ProbeData * registerProbe( Probe * probe ); +void unregisterProbe( int id ); +void addOscilloscopeAsToolView( KTechlab *ktechlab ); + + +#ifndef PROBE_H +#ifndef KTECHLAB_H +#ifndef OSCILLOSCOPEDATA_H +/** +@author David Saxton +*/ +class Oscilloscope : public OscilloscopeWidget +{ + Q_OBJECT + public: + static Oscilloscope * self( KateMDI::ToolView * parent = 0l ); + static QString toolViewIdentifier() { return "Oscilloscope"; } + virtual ~Oscilloscope(); + + /** + * Register a probe (that outputs boolean data) with the oscilloscope. + * Returns a unique id that the probe can use to add data points + */ + ProbeData * registerProbe( Probe * probe ); + void unregisterProbe( int id ); + /** + * Returns the Simulator time since recording started. + */ + ullong time() const; + /** + * Returns how much of an increment in value of the oscilloscope slider + * is equivalent to one second. + */ + int sliderTicksPerSecond() const; + /** + * Returns the number of pixels per second the user has requested to be + * displayed. + */ + double pixelsPerSecond() const; + /** + * Zoom level; a value between 0 and 1. 0 is maximum zoom out, and 1 is + * maximum zoom in. + */ + double zoomLevel() const { return m_zoomLevel; } + /** + * Sets the zoom level (and in the process, checks that it is within the + * bounds allowed). + */ + void setZoomLevel( double zoomLevel ); + /** + * Returns the Simulator time as given by the current scrollbar + * position. + */ + llong scrollTime() const; + /** + * @returns pointer to probe with given id, or NULL if no such probe exists + */ + ProbeData * probeData( int id ) const; + /** + * @returns the total number of probes + */ + int numberOfProbes() const; + /** + * @returns number of the probe with the given id, starting from 0, or -1 if no such probe + */ + int probeNumber( int id ) const; + + signals: + /** + * Emitted when a probe is registered + */ + void probeRegistered( int id, ProbeData * probe ); + /** + * Emitted when a probe is unregistered + */ + void probeUnregistered( int id ); + + public slots: + /** + * Resets all recorded data + */ + void reset(); + /** + * Called when the zoom slider value was changed. + */ + void slotZoomSliderChanged( int value ); + /** + * Called when the horizontal scrollbar was scrolled by the user + */ + void slotSliderValueChanged( int value ); + /** + * Pause the data capture (e.g. user clicked on pause button) + */ + void slotTogglePause(); + + protected: + void getOldestProbe(); + +// bool b_isPaused; + int m_nextId; + ProbeData * m_oldestProbe; + int m_oldestId; + int m_nextColor; // For giving the probes colours + + ProbeDataMap m_probeDataMap; + LogicProbeDataMap m_logicProbeDataMap; + FloatingProbeDataMap m_floatingProbeDataMap; + + Simulator * m_pSimulator; + + protected slots: + void updateScrollbars(); + + private: + Oscilloscope( KateMDI::ToolView * parent ); + + static Oscilloscope * m_pSelf; + double m_zoomLevel; + + friend class OscilloscopeView; + friend class ProbePositioner; +}; + +#endif // OSCILLOSCOPEDATA_H +#endif // KTECHLAB_H +#endif // PROBE_H + +#endif // OSCILLOSCOPE_H diff --git a/src/gui/oscilloscopeview.cpp b/src/gui/oscilloscopeview.cpp new file mode 100644 index 0000000..3d2a40a --- /dev/null +++ b/src/gui/oscilloscopeview.cpp @@ -0,0 +1,431 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "oscilloscope.h" +#include "oscilloscopedata.h" +#include "oscilloscopeview.h" +#include "probepositioner.h" +#include "simulator.h" + +#include <kconfig.h> +#include <kdebug.h> +#include <klocale.h> +#include <kglobal.h> +#include <kpopupmenu.h> +#include <qcheckbox.h> +#include <qcursor.h> +#include <qevent.h> +#include <qlabel.h> +#include <qpainter.h> +#include <qpixmap.h> +#include <qscrollbar.h> +#include <qtimer.h> + +#include <algorithm> +#include <cmath> + +inline ullong min( ullong a, ullong b ) +{ + return a < b ? a : b; +} + + +OscilloscopeView::OscilloscopeView( QWidget *parent, const char *name ) + : QFrame( parent, name, WNoAutoErase ), + b_needRedraw(true), + m_pixmap(0l), + m_fps(10), + m_sliderValueAtClick(-1), + m_clickOffsetPos(-1), + m_pSimulator( Simulator::self() ), + m_halfOutputHeight(0.0) +{ + KGlobal::config()->setGroup("Oscilloscope"); + m_fps = KGlobal::config()->readNumEntry( "FPS", 25 ); + + setBackgroundMode(NoBackground); + setMouseTracking(true); + + m_updateViewTmr = new QTimer(this); + connect( m_updateViewTmr, SIGNAL(timeout()), this, SLOT(updateViewTimeout()) ); +} + + +OscilloscopeView::~OscilloscopeView() +{ + delete m_pixmap; + m_pixmap = 0l; +} + + +void OscilloscopeView::updateView() +{ + if (m_updateViewTmr->isActive() ) + return; + + m_updateViewTmr->start( 1000/m_fps, true ); +} + + +void OscilloscopeView::updateViewTimeout() +{ + b_needRedraw = true; + repaint(false); + updateTimeLabel(); +} + + +void OscilloscopeView::updateTimeLabel() +{ + if ( hasMouse() ) + { + int x = mapFromGlobal( QCursor::pos() ).x(); + double time = (double(Oscilloscope::self()->scrollTime()) / LOGIC_UPDATE_RATE) + (x / Oscilloscope::self()->pixelsPerSecond()); + Oscilloscope::self()->timeLabel->setText( QString::number( time, 'f', 6 ) ); + } + + else + Oscilloscope::self()->timeLabel->setText( QString::null ); +} + + +void OscilloscopeView::resizeEvent( QResizeEvent *e ) +{ + delete m_pixmap; + m_pixmap = new QPixmap( e->size() ); + b_needRedraw = true; + QFrame::resizeEvent(e); +} + + +void OscilloscopeView::mousePressEvent( QMouseEvent *event ) +{ + switch ( event->button() ) + { + case Qt::LeftButton: + { + event->accept(); + m_clickOffsetPos = event->pos().x(); + m_sliderValueAtClick = Oscilloscope::self()->horizontalScroll->value(); + setCursor( Qt::SizeAllCursor ); + return; + } + + case Qt::RightButton: + { + event->accept(); + + KPopupMenu fpsMenu; + fpsMenu.insertTitle( i18n("Framerate") ); + + const int fps[] = { 10, 25, 50, 75, 100 }; + + for ( uint i=0; i<5; ++i ) + { + const int num = fps[i]; + fpsMenu.insertItem( i18n("%1 fps").arg(num), num ); + fpsMenu.setItemChecked( num, num == m_fps ); + } + + connect( &fpsMenu, SIGNAL(activated(int )), this, SLOT(slotSetFrameRate(int )) ); + fpsMenu.exec( event->globalPos() ); + return; + } + + default: + { + QFrame::mousePressEvent(event); + return; + } + } +} + + +void OscilloscopeView::mouseMoveEvent( QMouseEvent *event ) +{ + event->accept(); + updateTimeLabel(); + + if ( m_sliderValueAtClick != -1 ) + { + int dx = event->pos().x() - m_clickOffsetPos; + int dTick = int( dx * Oscilloscope::self()->sliderTicksPerSecond() / Oscilloscope::self()->pixelsPerSecond() ); + Oscilloscope::self()->horizontalScroll->setValue( m_sliderValueAtClick - dTick ); + } +} + + +void OscilloscopeView::mouseReleaseEvent( QMouseEvent *event ) +{ + if ( m_sliderValueAtClick == -1 ) + return QFrame::mouseReleaseEvent(event); + + event->accept(); + m_sliderValueAtClick = -1; + setCursor( Qt::ArrowCursor ); +} + + +void OscilloscopeView::slotSetFrameRate( int fps ) +{ + m_fps = fps; + KGlobal::config()->setGroup("Oscilloscope"); + KGlobal::config()->writeEntry( "FPS", m_fps ); +} + + +// returns a % b +static double lld_modulus( llong a, double b ) +{ + return double(a) - llong(a/b)*b; +} + + +void OscilloscopeView::paintEvent( QPaintEvent *e ) +{ + QRect r = e->rect(); + + if (b_needRedraw) + { + updateOutputHeight(); + const double pixelsPerSecond = Oscilloscope::self()->pixelsPerSecond(); + + QPainter p; + m_pixmap->fill( paletteBackgroundColor() ); + p.begin(m_pixmap); + p.setClipRegion(e->region()); + + //BEGIN Draw vertical marker lines + const double divisions = 5.0; + const double min_sep = 10.0; + + double spacing = pixelsPerSecond/(std::pow( divisions, std::floor(std::log(pixelsPerSecond/min_sep)/std::log(divisions)) )); + + // Pixels offset is the number of pixels that the view is scrolled along + const llong pixelsOffset = llong(Oscilloscope::self()->scrollTime()*pixelsPerSecond/LOGIC_UPDATE_RATE); + double linesOffset = - lld_modulus( pixelsOffset, spacing ); + + int blackness = 256 - int(184.0 * spacing / (min_sep*divisions*divisions)); + p.setPen( QColor( blackness, blackness, blackness ) ); + + for ( double i = linesOffset; i <= frameRect().width(); i += spacing ) + p.drawLine( int(i), 1, int(i), frameRect().height()-2 ); + + + + spacing *= divisions; + linesOffset = - lld_modulus( pixelsOffset, spacing ); + + blackness = 256 - int(184.0 * spacing / (min_sep*divisions*divisions)); + p.setPen( QColor( blackness, blackness, blackness ) ); + + for ( double i = linesOffset; i <= frameRect().width(); i += spacing ) + p.drawLine( int(i), 1, int(i), frameRect().height()-2 ); + + + + spacing *= divisions; + linesOffset = - lld_modulus( pixelsOffset, spacing ); + + blackness = 256 - int(184.0); + p.setPen( QColor( blackness, blackness, blackness ) ); + + for ( double i = linesOffset; i <= frameRect().width(); i += spacing ) + p.drawLine( int(i), 1, int(i), frameRect().height()-2 ); + //END Draw vertical marker lines + + drawLogicData(p); + drawFloatingData(p); + + p.setPen(Qt::black); + p.drawRect( frameRect() ); + + b_needRedraw = false; + } + + bitBlt( this, r.x(), r.y(), m_pixmap, r.x(), r.y(), r.width(), r.height() ); +} + + +void OscilloscopeView::updateOutputHeight() +{ + m_halfOutputHeight = int((Oscilloscope::self()->probePositioner->probeOutputHeight() - (probeArrowWidth/Oscilloscope::self()->numberOfProbes()))/2)-1; +} + + +void OscilloscopeView::drawLogicData( QPainter & p ) +{ + const double pixelsPerSecond = Oscilloscope::self()->pixelsPerSecond(); + + const LogicProbeDataMap::iterator end = Oscilloscope::self()->m_logicProbeDataMap.end(); + for ( LogicProbeDataMap::iterator it = Oscilloscope::self()->m_logicProbeDataMap.begin(); it != end; ++it ) + { + // When searching for the next logic value to display, we look along + // until there is a recorded point which is at least one pixel along + // If we are zoomed out far, there might be thousands of data points + // between each pixel. It is time consuming searching for the next point + // to display one at a time, so we record the average number of data points + // between pixels ( = deltaAt / totalDeltaAt ) + llong deltaAt = 1; + int totalDeltaAt = 1; + + LogicProbeData * probe = it.data(); + StoredData<LogicDataPoint> * data = &(probe->m_data); + + if ( data->allocatedUpTo() == 0 ) + continue; + + const int midHeight = Oscilloscope::self()->probePositioner->probePosition(probe); + const llong timeOffset = Oscilloscope::self()->scrollTime(); + + // Draw the horizontal line indicating the midpoint of our output + p.setPen( QColor( 228, 228, 228 ) ); + p.drawLine( 0, midHeight, width(), midHeight ); + + // Set the pen colour according to the colour the user has selected for the probe + p.setPen( probe->color() ); + + // The smallest time step that will display in our oscilloscope + const int minTimeStep = int(LOGIC_UPDATE_RATE/pixelsPerSecond); + + llong at = probe->findPos(timeOffset); + const llong maxAt = probe->insertPos(); + llong prevTime = data->dataAt(at).time; + int prevX = (at > 0) ? 0 : int((prevTime - timeOffset)*(pixelsPerSecond/LOGIC_UPDATE_RATE)); + bool prevHigh = data->dataAt(at).value; + int prevY = midHeight + int(prevHigh ? -m_halfOutputHeight : +m_halfOutputHeight); + while ( at < maxAt ) + { + // Search for the next pos which will show up at our zoom level + llong previousAt = at; + llong dAt = deltaAt / totalDeltaAt; + + while ( (dAt > 1) && (at < maxAt) && ( (llong(data->dataAt(at).time) - prevTime) != minTimeStep ) ) + { + // Search forwards until we overshoot + while ( at < maxAt && ( llong(data->dataAt(at).time) - prevTime ) < minTimeStep ) + at += dAt; + dAt /= 2; + + // Search backwards until we undershoot + while ( (at < maxAt) && ( llong(data->dataAt(at).time) - prevTime ) > minTimeStep ) + { + at -= dAt; + if ( at < 0 ) + at = 0; + } + dAt /= 2; + } + + // Possibly increment the value of at found by one (or more if this is the first go) + while ( (previousAt == at) || ((at < maxAt) && ( llong(data->dataAt(at).time) - prevTime ) < minTimeStep) ) + at++; + + if ( at >= maxAt ) + break; + + // Update the average values + deltaAt += at - previousAt; + totalDeltaAt++; + + bool nextHigh = data->dataAt(at).value; + if ( nextHigh == prevHigh ) + continue; + llong nextTime = data->dataAt(at).time; + int nextX = int((nextTime - timeOffset)*(pixelsPerSecond/LOGIC_UPDATE_RATE)); + int nextY = midHeight + int(nextHigh ? -m_halfOutputHeight : +m_halfOutputHeight); + + p.drawLine( prevX, prevY, nextX, prevY ); + p.drawLine( nextX, prevY, nextX, nextY ); + + prevHigh = nextHigh; + prevTime = nextTime; + prevX = nextX; + prevY = nextY; + + if ( nextX > width() ) + break; + }; + + // If we could not draw right to the end; it is because we exceeded + // maxAt + if ( prevX < width() ) + p.drawLine( prevX, prevY, width(), prevY ); + } +} + + +#define v_to_y int(midHeight - (logarithmic ? ( (v>0) ? log(v/lowerAbsValue) : -log(-v/lowerAbsValue) ) : v) * sf) + + +void OscilloscopeView::drawFloatingData( QPainter & p ) +{ + const double pixelsPerSecond = Oscilloscope::self()->pixelsPerSecond(); + + const FloatingProbeDataMap::iterator end = Oscilloscope::self()->m_floatingProbeDataMap.end(); + for ( FloatingProbeDataMap::iterator it = Oscilloscope::self()->m_floatingProbeDataMap.begin(); it != end; ++it ) + { + FloatingProbeData * probe = it.data(); + StoredData<float> * data = &(probe->m_data); + + if ( data->allocatedUpTo() == 0 ) + continue; + + bool logarithmic = probe->scaling() == FloatingProbeData::Logarithmic; + double lowerAbsValue = probe->lowerAbsValue(); + double sf = m_halfOutputHeight / (logarithmic ? log(probe->upperAbsValue()/lowerAbsValue) : probe->upperAbsValue()); + + const int midHeight = Oscilloscope::self()->probePositioner->probePosition(probe); + const llong timeOffset = Oscilloscope::self()->scrollTime(); + + // Draw the horizontal line indicating the midpoint of our output + p.setPen( QColor( 228, 228, 228 ) ); + p.drawLine( 0, midHeight, width(), midHeight ); + + // Set the pen colour according to the colour the user has selected for the probe + p.setPen( probe->color() ); + + llong at = probe->findPos(timeOffset); + const llong maxAt = probe->insertPos(); + llong prevTime = probe->toTime(at); + + double v = data->dataAt((at>0)?at:0); + int prevY = v_to_y; + int prevX = int((prevTime - timeOffset)*(pixelsPerSecond/LOGIC_UPDATE_RATE)); + + while ( at < maxAt-1 ) + { + at++; + + ullong nextTime = prevTime + ullong(LOGIC_UPDATE_RATE/LINEAR_UPDATE_RATE); + + double v = data->dataAt((at>0)?at:0); + int nextY = v_to_y; + int nextX = int((nextTime - timeOffset)*(pixelsPerSecond/LOGIC_UPDATE_RATE)); + + p.drawLine( prevX, prevY, nextX, nextY ); + + prevTime = nextTime; + prevX = nextX; + prevY = nextY; + + if ( nextX > width() ) + break; + }; + + // If we could not draw right to the end; it is because we exceeded + // maxAt + if ( prevX < width() ) + p.drawLine( prevX, prevY, width(), prevY ); + } +} + + +#include "oscilloscopeview.moc" diff --git a/src/gui/oscilloscopeview.h b/src/gui/oscilloscopeview.h new file mode 100644 index 0000000..97cb595 --- /dev/null +++ b/src/gui/oscilloscopeview.h @@ -0,0 +1,65 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef OSCILLOSCOPEVIEW_H +#define OSCILLOSCOPEVIEW_H + +#include <qframe.h> + +class Oscilloscope; +class Simulator; +class QMouseEvent; +class QPaintEvent; +class QPixmap; +class QTimer; + +/** +@author David Saxton +*/ +class OscilloscopeView : public QFrame +{ + Q_OBJECT + public: + OscilloscopeView( QWidget *parent, const char *name = 0 ); + virtual ~OscilloscopeView(); + + public slots: + /** + * Sets the needRedraw flag to true, and then class repaint + */ + void updateView(); + void slotSetFrameRate( int fps ); + + protected slots: + void updateViewTimeout(); + + protected: + virtual void mousePressEvent( QMouseEvent *event ); + virtual void mouseMoveEvent( QMouseEvent *event ); + virtual void mouseReleaseEvent( QMouseEvent *event ); + virtual void paintEvent( QPaintEvent *event ); + virtual void resizeEvent( QResizeEvent *event ); + + void drawLogicData( QPainter & p ); + void drawFloatingData( QPainter & p ); + void updateOutputHeight(); + void updateTimeLabel(); + + bool b_needRedraw; + QPixmap *m_pixmap; + QTimer *m_updateViewTmr; + int m_fps; + int m_sliderValueAtClick; + int m_clickOffsetPos; + Simulator * m_pSimulator; + double m_halfOutputHeight; +}; + +#endif diff --git a/src/gui/oscilloscopewidget.ui b/src/gui/oscilloscopewidget.ui new file mode 100644 index 0000000..eb8002b --- /dev/null +++ b/src/gui/oscilloscopewidget.ui @@ -0,0 +1,289 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>OscilloscopeWidget</class> +<widget class="QWidget"> + <property name="name"> + <cstring>OscilloscopeWidget</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>563</width> + <height>195</height> + </rect> + </property> + <property name="caption"> + <string>Form1</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <property name="spacing"> + <number>0</number> + </property> + <widget class="QLayoutWidget" row="0" column="3" rowspan="2" colspan="1"> + <property name="name"> + <cstring>layout7</cstring> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="spacing"> + <number>0</number> + </property> + <widget class="OscilloscopeView" row="0" column="1"> + <property name="name"> + <cstring>oscilloscopeView</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>3</hsizetype> + <vsizetype>3</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="paletteForegroundColor"> + <color> + <red>0</red> + <green>0</green> + <blue>0</blue> + </color> + </property> + <property name="paletteBackgroundColor"> + <color> + <red>255</red> + <green>255</green> + <blue>255</blue> + </color> + </property> + </widget> + <widget class="ProbePositioner" row="0" column="0"> + <property name="name"> + <cstring>probePositioner</cstring> + </property> + <property name="minimumSize"> + <size> + <width>16</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>16</width> + <height>32767</height> + </size> + </property> + </widget> + <widget class="QScrollBar" row="1" column="1"> + <property name="name"> + <cstring>horizontalScroll</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + </widget> + </grid> + </widget> + <widget class="QLayoutWidget" row="0" column="1" rowspan="2" colspan="1"> + <property name="name"> + <cstring>layout9</cstring> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <property name="spacing"> + <number>0</number> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel1</cstring> + </property> + <property name="text"> + <string>Zoom</string> + </property> + <property name="alignment"> + <set>AlignCenter</set> + </property> + </widget> + <widget class="QSlider"> + <property name="name"> + <cstring>zoomSlider</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>4</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="maxValue"> + <number>1000</number> + </property> + <property name="pageStep"> + <number>10</number> + </property> + <property name="value"> + <number>50</number> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + </widget> + <spacer> + <property name="name"> + <cstring>spacer1</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>MinimumExpanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>30</height> + </size> + </property> + </spacer> + <widget class="QLabel"> + <property name="name"> + <cstring>timeLabel</cstring> + </property> + <property name="text"> + <string></string> + </property> + <property name="alignment"> + <set>AlignCenter</set> + </property> + </widget> + <spacer> + <property name="name"> + <cstring>spacer1_2</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Fixed</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>6</height> + </size> + </property> + </spacer> + <widget class="QPushButton"> + <property name="name"> + <cstring>resetBtn</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>1</hsizetype> + <vsizetype>1</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Reset</string> + </property> + <property name="accel"> + <string></string> + </property> + </widget> + </vbox> + </widget> + <spacer row="1" column="2"> + <property name="name"> + <cstring>spacer2_3</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Fixed</enum> + </property> + <property name="sizeHint"> + <size> + <width>6</width> + <height>6</height> + </size> + </property> + </spacer> + <spacer row="2" column="3"> + <property name="name"> + <cstring>spacer2_2</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Fixed</enum> + </property> + <property name="sizeHint"> + <size> + <width>6</width> + <height>6</height> + </size> + </property> + </spacer> + </grid> +</widget> +<customwidgets> + <customwidget> + <class>OscilloscopeView</class> + <header location="local">oscilloscopeview.h</header> + <sizehint> + <width>-1</width> + <height>-1</height> + </sizehint> + <container>0</container> + <sizepolicy> + <hordata>1</hordata> + <verdata>3</verdata> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + <pixmap>image0</pixmap> + </customwidget> + <customwidget> + <class>ProbePositioner</class> + <header location="local">probepositioner.h</header> + <sizehint> + <width>14</width> + <height>-1</height> + </sizehint> + <container>0</container> + <sizepolicy> + <hordata>0</hordata> + <verdata>5</verdata> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + <pixmap>image0</pixmap> + </customwidget> +</customwidgets> +<images> + <image name="image0"> + <data format="PNG" length="1125">89504e470d0a1a0a0000000d4948445200000016000000160806000000c4b46c3b0000042c49444154388db5954f6c14551cc73fefcd7476b65bdaae4bb78bb5502a14d404e4801c88182d1c4c2c693da847400f9c24c68b878684238660e2b1e01f12c19493012ef2478c814412d354a46017a8a564bb6da5bbedccee767776e63d0ffb073751d483bfe49799974c3eeffb7ebf37df9fd05a530b2184040cc0042420aaf9a4d0d554800f045a6b256ae0e1e1e1d6bebebe838ee31c48a7d39b5cd7fd075e251cc7617272f2ded8d8d819cff33e0316819259537aead4a9839d5dd6d1784f91f55b0a94830242088404d304292bef68a89f520802a598fecddaa04f1a876f5c250c7c0a64cdeac686e33807e23d45e6b297c8b877f1831542614550b6599835c83c2a81b6786a75134faf2f1169f12997350881d9021d0903e06de0745d3160a6d3e94dbd5b0a64dcbb94b5831d0e3375ab892b1772dcf9790528543f8dd0d367b36768153b5e31503a0f1aecb004580b44ffac58baae8b1714f0833c7638cc8dab303a320f4822ab4c7a37c69196203de3319d5ce1c4d13c733331dedc67a129a154fd128401ab0616d55a130ac3d42d93d1913940d13fd0c9ee0183685c60da01c5421bd72f7a8c8efccef9afd374267ad93d642365be0636a0d28ec7600941d9e6f23917f0e97f23ce5bef35d19ec863da0ed9059b2be70bec196c66dfa10ec0e49b338f7017258651bf95021035c595429bb0903248fe52a2b5b595dd7b4d945cc2340cdca536be389ee3f67886c5798f773fe8e0dac508c989659277a2180da4ca4ff07821058b8b251445d63d6b13ed1098a6417e39cac85197dbe31962ab9bd9f1f22a226d45366f6d0620fdb08c900d281af6110284b20085b414861d905d88f2e52739ee8cbb8022143259d3dd84691730aa2d52da441a8de0c6958068870022a41e9629ad3473fd3b8fdbe319dadb9b4924da994d2d716c7896fbe35152f78b48245d6b2da4507faf582be8eaf159b721cc837b05ae7debb1f79d08cb8b515edad942a22bc4b1c33eb3d34b1c797f06af90a72d16e2f96d9a74aa11dca8586b222d01af0fb60070f6c402d72f15d97f28c6f6d7027a5f5ce6c3233dc4e2ede496b278be4fff608cee8d3e1add806aeca51094cbb06397c1ecc328e746537c7e3ccdb5cb1136bf60635882d4d41c6ec6836ab37efa214f72208ed9f4d7cdd38ee310280542e38b1c43fb6de26b3672e1ec3cc99bcb246f66a938a3241ab3e91f7c861fbf77710b1e5e49915bae974203ba0e9e9c9cbc373d6d6d305a040a89c2a77f50b27d5782bbbf7acccf28349235dd16cf6dd374f7295e1de8a45c02d37499182b01cc0201a085d61a2144d8b2ac8fb6ed340e77240c4261890e04c250185262546d534a032154b59e0ad394e41c98182bf268ce6721ed9f064e0253356f6da2e24c1f030f783c15fe6da680af8021602bd051532ca9b8521488559f61aa86c29343578fbf0264a94c906c7d3409214c20043457a116ff6de6795578012889ff6b98fe016ea0ce1c6a2573410000000049454e44ae426082</data> + </image> +</images> +<layoutdefaults spacing="6" margin="11"/> +<includehints> + <includehint>oscilloscopeview.h</includehint> + <includehint>probepositioner.h</includehint> +</includehints> +</UI> diff --git a/src/gui/outputmethoddlg.cpp b/src/gui/outputmethoddlg.cpp new file mode 100644 index 0000000..bcb189e --- /dev/null +++ b/src/gui/outputmethoddlg.cpp @@ -0,0 +1,162 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "docmanager.h" +#include "filemetainfo.h" +#include "textdocument.h" +#include "outputmethodwidget.h" +#include "outputmethoddlg.h" +#include "microlibrary.h" +#include "microselectwidget.h" +#include "projectmanager.h" + +#include <kcombobox.h> +#include <kconfigskeleton.h> +#include <kdebug.h> +#include <ktempfile.h> +#include <kurlrequester.h> + +#include <qcheckbox.h> +#include <qfile.h> +#include <qradiobutton.h> + + +//BEGIN class OutputMethodInfo +OutputMethodInfo::OutputMethodInfo() +{ + m_method = Method::Direct; + m_bAddToProject = false; +} + + +void OutputMethodInfo::initialize( OutputMethodDlg * dlg ) +{ + if ( dlg->m_widget->displayDirectCheck->isChecked() ) + { + m_method = Method::Direct; + KTempFile f( QString::null, dlg->m_outputExtension ); + f.close(); + m_outputFile = f.name(); + m_bAddToProject = false; + } + + else + { + if ( dlg->m_widget->loadFileCheck->isChecked() ) + m_method = Method::SaveAndLoad; + + else + m_method = Method::SaveAndForget; + + m_outputFile = dlg->m_widget->outputFileURL->url(); + m_bAddToProject = dlg->m_widget->addToProjectCheck->isChecked(); + } + + m_picID = dlg->m_widget->m_pMicroSelect->micro(); +} +//END class OutputMethodInfo + + + +//BEGIN class OutputMethodDlg +OutputMethodDlg::OutputMethodDlg( const QString &caption, const KURL & inputURL, bool showPICSelect, QWidget *parent, const char *name ) + : KDialogBase( parent, name, true, caption, Ok|Cancel ) +{ + m_inputURL = inputURL; + m_bAccepted = false; + m_widget = new OutputMethodWidget(this); + + m_widget->addToProjectCheck->setEnabled( ProjectManager::self()->currentProject() ); + + if (!showPICSelect) + { + m_widget->m_pMicroSelect->hide(); + m_widget->adjustSize(); + } + + fileMetaInfo()->initializeFromMetaInfo( m_inputURL, this ); + + setMainWidget(m_widget); +} + + +OutputMethodDlg::~OutputMethodDlg() +{ +} + + +void OutputMethodDlg::setOutputExtension( const QString & extension ) +{ + m_outputExtension = extension; +} + + +void OutputMethodDlg::setFilter( const QString &filter ) +{ + m_widget->outputFileURL->setFilter(filter); +} + + +void OutputMethodDlg::setMethod( OutputMethodInfo::Method::Type m ) +{ + switch (m) + { + case OutputMethodInfo::Method::Direct: + m_widget->displayDirectCheck->setChecked(true); + break; + + case OutputMethodInfo::Method::SaveAndForget: + m_widget->saveFileCheck->setChecked(true); + m_widget->loadFileCheck->setChecked(false); + break; + + case OutputMethodInfo::Method::SaveAndLoad: + m_widget->saveFileCheck->setChecked(true); + m_widget->loadFileCheck->setChecked(true); + break; + }; +} + + +void OutputMethodDlg::setPicID( const QString & id ) +{ + m_widget->m_pMicroSelect->setMicro(id); +} + + +void OutputMethodDlg::setOutputFile( const KURL & out ) +{ + m_widget->outputFileURL->setURL(out.prettyURL()); +} + + +void OutputMethodDlg::accept() +{ + m_bAccepted = true; + m_outputMethodInfo.initialize(this); + fileMetaInfo()->grabMetaInfo( m_inputURL, this ); + hide(); +} + + +void OutputMethodDlg::reject() +{ + m_bAccepted = false; +} + + +MicroSelectWidget * OutputMethodDlg::microSelect() const +{ + return m_widget->m_pMicroSelect; +} +//END class OutputMethodDlg + + +#include "outputmethoddlg.moc" diff --git a/src/gui/outputmethoddlg.h b/src/gui/outputmethoddlg.h new file mode 100644 index 0000000..14dff25 --- /dev/null +++ b/src/gui/outputmethoddlg.h @@ -0,0 +1,98 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef OUTPUTMETHODDLG_H +#define OUTPUTMETHODDLG_H + +#include <kdialogbase.h> +#include <kurl.h> + +class TextDocument; +class KTechlab; +class MicroSelectWidget; +class OutputMethodDlg; +class OutputMethodWidget; + +class OutputMethodInfo +{ + public: + class Method + { + public: + enum Type + { + Direct, + SaveAndForget, + SaveAndLoad + }; + }; + + OutputMethodInfo(); + void initialize( OutputMethodDlg * dlg ); + + Method::Type method() const { return m_method; } + void setMethod( Method::Type method ) { m_method = method; } + + bool addToProject() const { return m_bAddToProject; } + void setAddToProject( bool add ) { m_bAddToProject = add; } + + QString picID() const { return m_picID; } + void setPicID( const QString & id ) { m_picID = id; } + + KURL outputFile() const { return m_outputFile; } + void setOutputFile( const KURL & outputFile ) { m_outputFile = outputFile; } + + protected: + Method::Type m_method; + bool m_bAddToProject; + QString m_picID; + KURL m_outputFile; +}; + +/** +@author David Saxton +*/ +class OutputMethodDlg : public KDialogBase +{ + Q_OBJECT + public: + /** + * @param Caption The caption of the dialog window + * @param inputURL Used for saving/restoring previous options the user has selected for this file; set this to null if temporary file + * @param showPICSelect Whether to show the combo boxes for selecting a PIC + */ + OutputMethodDlg( const QString & caption, const KURL & inputURL, bool showPICSelect = false, QWidget *parent = 0, const char *name = 0); + ~OutputMethodDlg(); + + void setOutputExtension( const QString & outputExtension ); + void setFilter( const QString &filter ); + void setMethod( OutputMethodInfo::Method::Type m ); + void setOutputFile( const KURL & out ); + void setPicID( const QString & id ); + + virtual void reject(); + virtual void accept(); + bool isAccepted() const { return m_bAccepted; } + + OutputMethodInfo info() const { return m_outputMethodInfo; } + + MicroSelectWidget * microSelect() const; + + protected: + OutputMethodWidget *m_widget; + QString m_outputExtension; + KURL m_inputURL; + OutputMethodInfo m_outputMethodInfo; + bool m_bAccepted; + + friend class OutputMethodInfo; +}; + +#endif diff --git a/src/gui/outputmethodwidget.ui b/src/gui/outputmethodwidget.ui new file mode 100644 index 0000000..0182105 --- /dev/null +++ b/src/gui/outputmethodwidget.ui @@ -0,0 +1,182 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>OutputMethodWidget</class> +<widget class="QWidget"> + <property name="name"> + <cstring>OutputMethodWidget</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>450</width> + <height>208</height> + </rect> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>5</hsizetype> + <vsizetype>5</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>450</width> + <height>0</height> + </size> + </property> + <property name="caption"> + <string>Output Method</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <widget class="QButtonGroup"> + <property name="name"> + <cstring>buttonGroup2</cstring> + </property> + <property name="title"> + <string>Output Method</string> + </property> + <property name="checkable"> + <bool>false</bool> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QRadioButton"> + <property name="name"> + <cstring>displayDirectCheck</cstring> + </property> + <property name="text"> + <string>Displa&y directly</string> + </property> + <property name="checked"> + <bool>true</bool> + </property> + </widget> + <widget class="QRadioButton"> + <property name="name"> + <cstring>saveFileCheck</cstring> + </property> + <property name="text"> + <string>Save to file</string> + </property> + <property name="checked"> + <bool>false</bool> + </property> + </widget> + <widget class="QGroupBox"> + <property name="name"> + <cstring>groupBox16</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="title"> + <string>Output File Options</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="KURLRequester"> + <property name="name"> + <cstring>outputFileURL</cstring> + </property> + </widget> + <widget class="QCheckBox"> + <property name="name"> + <cstring>loadFileCheck</cstring> + </property> + <property name="text"> + <string>Load File in &New View</string> + </property> + </widget> + <widget class="QCheckBox"> + <property name="name"> + <cstring>addToProjectCheck</cstring> + </property> + <property name="text"> + <string>&Add to Project</string> + </property> + </widget> + </vbox> + </widget> + </vbox> + </widget> + <widget class="MicroSelectWidget"> + <property name="name"> + <cstring>m_pMicroSelect</cstring> + </property> + <property name="minimumSize"> + <size> + <width>0</width> + <height>0</height> + </size> + </property> + </widget> + <spacer> + <property name="name"> + <cstring>spacer14</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>0</height> + </size> + </property> + </spacer> + </vbox> +</widget> +<customwidgets> + <customwidget> + <class>MicroSelectWidget</class> + <header location="local">microselectwidget.h</header> + <sizehint> + <width>-1</width> + <height>-1</height> + </sizehint> + <container>0</container> + <sizepolicy> + <hordata>5</hordata> + <verdata>5</verdata> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + <pixmap>image0</pixmap> + </customwidget> +</customwidgets> +<images> + <image name="image0"> + <data format="PNG" length="1122">89504e470d0a1a0a0000000d4948445200000016000000160806000000c4b46c3b0000042949444154388db5954d6c545514c77ff7de37eff1a6a550da994e5ba798868f948fc847a2a2911816c436b0a02ed0083161a1981877ee10d90aa94656063491882c5cd4c4b8103f20cd806909a98604da8482341de80cb69de9bcd799799d79efba980f1da3a80b4f727273939bdff99f9b93f3175a6b6a21849080020c4002a29a8f0a5dcd002803bed63a1035f0d0d0504b6f6fef51d7758fa4d3e98d8ee3fc03af128ee3303939399548242eb8aefb09300f78464de9f0f0f0d148cc7caf3d5424de97a7ec17104220242805520a44557bbd4b21f083801d33e63a6d1bc7bf19c6064e0319a35a58b9ae7ba43d54e4d7c54b64dc097c7f09d30ef04b36736983cc7c11b4456b9b492c5e20dc54a6540e00896a8268a7017010f8bcae1830d2e9f4c69ebe3c197782b2ce60d94ddc9b30f9feeb45ae5d7181c5ead35636ef08d3ffe26a76ee3208b4031a56d80aa00768fda362e9380e25bf80ef3b587613d7afc099533380a4b5dda46fcb5aa42998b9eb7173dce5e6788e434763ec7fc942532008ea436003660d2caa7f8d69c3bd8910674e4d033e83877ad8d36fb0aa4d6058658ac5358c5df6383bf480f31fdda5a3632bbbfb6da4ccd5c00a10b2012cc12fd97cfb5516f0187c25cee0619396480ec3ca909d0f3372d1e5f9fe66de3ad605587c712ec55256a2540d5519d19ae24a29057329839f7ecc138944d833102290f3184ae1e6d6f0e98739c61229e6d21eafbed1c9c8c5558c8f3ee4ceed76946a1c43d97091828585223e2eebfbc295f60d45de89707628cb58224577bc85a79e5d4d786581cddb9b0148259711b20145836221400416e0232d1f6595c9ce4538773ac7b54406f0b15784e8e80aa1ac2594190220d021e49f2437960156b70ba095e95f96f18acd8c263cc6122962b1167a7ba34cdd7ec8c977a699bed546722a8fc2a02b6e2185fff78ab586ce78894ddb9ab9f5738ed14b1e030756929d8ff2e4ae5544632b38f96e89a99b298ebd6950cc2d12ed0eb3618b26f520788462ad09877df60db600f0f1fb49c646f21c7ebd836dcf94e9d9b4c0db27d6d3bd36c6426a96bce731703046d7da129a4670836284a054869d4f1b1c7e2dca67676ef3c1898091ef5ad8bc358c3205c93bf7c9a40ad4c660f4728edd7bbb3142ea2fc1da711cfc200011e08b45f6bd1c261adbce9717928c5f9d63fc6a19f0014567773303073790f8214bd6f5282dfbb8b9fa1f6b40d7c1939393533b92e63ad9240990484aec7e41b27d579cbb531e0f67023492d86316ebb768ba1e0f786e6f37e56550c62237aee7011e54abd7c1412291b8a06de378b4d3c0b215da9708e5a3a442192085404a83d4accfecac067c0c43e13a70e3fa12e96409e0325000966bbb354465339d04a6f9dd15fe6dde07ce030780278088d05ad72cc9a6b2f2daaaa74d75a1f0e8d0d5f60b40868a8364ead6248430000b0857a126ffcdf396abf03ce089ffcb4c7f033046c6b4a995e7a00000000049454e44ae426082</data> + </image> +</images> +<connections> + <connection> + <sender>saveFileCheck</sender> + <signal>toggled(bool)</signal> + <receiver>groupBox16</receiver> + <slot>setEnabled(bool)</slot> + </connection> +</connections> +<layoutdefaults spacing="6" margin="11"/> +<includehints> + <includehint>kurlrequester.h</includehint> + <includehint>klineedit.h</includehint> + <includehint>kpushbutton.h</includehint> + <includehint>microselectwidget.h</includehint> +</includehints> +</UI> diff --git a/src/gui/picprogrammerconfigwidget.ui b/src/gui/picprogrammerconfigwidget.ui new file mode 100644 index 0000000..64c5ac8 --- /dev/null +++ b/src/gui/picprogrammerconfigwidget.ui @@ -0,0 +1,285 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>PicProgrammerConfigWidget</class> +<widget class="QWidget"> + <property name="name"> + <cstring>PicProgrammerConfigWidget</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>464</width> + <height>493</height> + </rect> + </property> + <property name="caption"> + <string>PIC Programmer Config</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <widget class="QGroupBox"> + <property name="name"> + <cstring>groupBox3</cstring> + </property> + <property name="title"> + <string>Programmer Configuration</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <spacer row="1" column="0" rowspan="1" colspan="2"> + <property name="name"> + <cstring>spacer3</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>295</width> + <height>20</height> + </size> + </property> + </spacer> + <widget class="KPushButton" row="1" column="2"> + <property name="name"> + <cstring>addButton</cstring> + </property> + <property name="text"> + <string>&Add ...</string> + </property> + <property name="accel"> + <string>Alt+A</string> + </property> + </widget> + <widget class="KPushButton" row="1" column="3"> + <property name="name"> + <cstring>removeButton</cstring> + </property> + <property name="text"> + <string>Re&move</string> + </property> + <property name="accel"> + <string>Alt+M</string> + </property> + </widget> + <widget class="QLabel" row="0" column="0"> + <property name="name"> + <cstring>textLabel1_3</cstring> + </property> + <property name="text"> + <string>Program:</string> + </property> + </widget> + <widget class="KComboBox" row="0" column="1" rowspan="1" colspan="3"> + <property name="name"> + <cstring>kcfg_PicProgrammerProgram</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>1</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>1</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="contextMenuEnabled"> + <bool>false</bool> + </property> + </widget> + <widget class="QLabel" row="2" column="0" rowspan="1" colspan="4"> + <property name="name"> + <cstring>m_pProgrammerDescription</cstring> + </property> + <property name="text"> + <string>(Program Description)</string> + </property> + </widget> + </grid> + </widget> + <widget class="QGroupBox"> + <property name="name"> + <cstring>commandsGroupBox</cstring> + </property> + <property name="title"> + <string>Commands</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel" row="0" column="0"> + <property name="name"> + <cstring>textLabel1_2</cstring> + </property> + <property name="text"> + <string>Initialization:</string> + </property> + </widget> + <widget class="KLineEdit" row="3" column="1"> + <property name="name"> + <cstring>verifyCommand</cstring> + </property> + </widget> + <widget class="KLineEdit" row="2" column="1"> + <property name="name"> + <cstring>writeCommand</cstring> + </property> + </widget> + <widget class="KLineEdit" row="1" column="1"> + <property name="name"> + <cstring>readCommand</cstring> + </property> + </widget> + <widget class="KLineEdit" row="0" column="1"> + <property name="name"> + <cstring>initCommand</cstring> + </property> + </widget> + <widget class="QLabel" row="1" column="0"> + <property name="name"> + <cstring>textLabel4</cstring> + </property> + <property name="text"> + <string>Read:</string> + </property> + </widget> + <widget class="QLabel" row="2" column="0"> + <property name="name"> + <cstring>textLabel3</cstring> + </property> + <property name="text"> + <string>Write:</string> + </property> + </widget> + <widget class="KLineEdit" row="4" column="1"> + <property name="name"> + <cstring>blankCheckCommand</cstring> + </property> + </widget> + <widget class="KLineEdit" row="5" column="1"> + <property name="name"> + <cstring>eraseCommand</cstring> + </property> + </widget> + <widget class="QLabel" row="6" column="0" rowspan="1" colspan="2"> + <property name="name"> + <cstring>textLabel2</cstring> + </property> + <property name="text"> + <string>The following strings will be replaced when the command is run: +<ul> +<li><b>%port</b> - Port that the programmer is connected to</li> +<li><b>%device</b> - PIC device</li> +<li><b>%file</b> - File to read from or write to</li> +</ul></string> + </property> + <property name="textFormat"> + <enum>RichText</enum> + </property> + </widget> + <widget class="QLabel" row="5" column="0"> + <property name="name"> + <cstring>textLabel5</cstring> + </property> + <property name="text"> + <string>Erase:</string> + </property> + </widget> + <widget class="QLabel" row="3" column="0"> + <property name="name"> + <cstring>textLabel2_3</cstring> + </property> + <property name="text"> + <string>Verify:</string> + </property> + </widget> + <widget class="QLabel" row="4" column="0"> + <property name="name"> + <cstring>textLabel1_4</cstring> + </property> + <property name="text"> + <string>Blank Check:</string> + </property> + </widget> + </grid> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout5</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel1</cstring> + </property> + <property name="text"> + <string>Default port:</string> + </property> + </widget> + <widget class="QComboBox"> + <property name="name"> + <cstring>kcfg_PicProgrammerPort</cstring> + </property> + <property name="editable"> + <bool>true</bool> + </property> + </widget> + </hbox> + </widget> + <spacer> + <property name="name"> + <cstring>spacer2</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>16</height> + </size> + </property> + </spacer> + </vbox> +</widget> +<tabstops> + <tabstop>kcfg_PicProgrammerProgram</tabstop> + <tabstop>addButton</tabstop> + <tabstop>removeButton</tabstop> + <tabstop>initCommand</tabstop> + <tabstop>readCommand</tabstop> + <tabstop>writeCommand</tabstop> + <tabstop>verifyCommand</tabstop> + <tabstop>blankCheckCommand</tabstop> + <tabstop>eraseCommand</tabstop> + <tabstop>kcfg_PicProgrammerPort</tabstop> +</tabstops> +<layoutdefaults spacing="6" margin="11"/> +<includehints> + <includehint>kpushbutton.h</includehint> + <includehint>kpushbutton.h</includehint> + <includehint>kcombobox.h</includehint> + <includehint>klineedit.h</includehint> + <includehint>klineedit.h</includehint> + <includehint>klineedit.h</includehint> + <includehint>klineedit.h</includehint> + <includehint>klineedit.h</includehint> + <includehint>klineedit.h</includehint> +</includehints> +</UI> diff --git a/src/gui/pieditor.cpp b/src/gui/pieditor.cpp new file mode 100644 index 0000000..b7ca0e5 --- /dev/null +++ b/src/gui/pieditor.cpp @@ -0,0 +1,294 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "colorcombo.h" +#include "doublespinbox.h" +#include "pieditor.h" +#include "variant.h" + +#include <kcombobox.h> +#include <klineedit.h> +#include <klocale.h> +#include <knuminput.h> +#include <kurlrequester.h> +#include <qlayout.h> + +//BEGIN class PIEditor +PIEditor::PIEditor(QString id, Variant *data, QWidget *parent, const char *name) + : QWidget(parent, name) +{ + m_id = id; + m_data = data; + connect(m_data,SIGNAL(valueChanged(QVariant, QVariant )),this,SLOT(valueChanged(QVariant))); + setFocus(); + update(); + //show(); +} + +PIEditor::~PIEditor() +{ +} + +void PIEditor::valueChanged( QVariant /*variant*/ ) +{ +} +//END class PIEditor + + +//BEGIN class PIBool +PIBool::PIBool(QString id, Variant *data, QWidget *parent, const char *name ) + : PIEditor( id, data, parent, name ) +{ + QHBoxLayout *layout = new QHBoxLayout(this); + layout->setAutoAdd(true); + + m_comboBox = new KComboBox(this); + m_comboBox->insertItem( i18n("True"), 0 ); + m_comboBox->insertItem( i18n("False"), 1 ); + m_comboBox->setCurrentItem( m_data->value().toBool() ? 0 : 1 ); + + connect( m_comboBox, SIGNAL(activated(int )), this, SLOT(selectChanged(int )) ); +} + +PIBool::~PIBool() +{ +} + +void PIBool::popup() +{ + m_comboBox->popup(); +} + +void PIBool::selectChanged( int index ) +{ + emit editorDataChanged( m_id, QVariant( index == 0 ) ); +} + +void PIBool::valueChanged( QVariant /*variant*/ ) +{ + m_comboBox->setCurrentItem( m_data->value().toBool() ? 0 : 1 ); +} +//END class PIBool + + +//BEGIN class PIColor +PIColor::PIColor(QString id, Variant *data, QWidget *parent, const char *name ) + : PIEditor(id,data,parent, name) +{ + QHBoxLayout *layout = new QHBoxLayout(this); + layout->setAutoAdd(true); + + m_combo = new ColorCombo( (ColorCombo::ColorScheme)m_data->colorScheme(), this ); + m_combo->setColor(m_data->value().toColor()); + + connect(m_combo,SIGNAL(activated(const QColor&)),this,SLOT(colorChanged(const QColor&))); +// connect(m_combo,SIGNAL(highlighted(const QColor&)),this,SLOT(colorChanged(const QColor&))); +} + +PIColor::~PIColor() +{ +} + +void PIColor::popup() +{ + m_combo->popup(); +} + +void PIColor::colorChanged(const QColor &col) +{ + emit editorDataChanged(m_id,QVariant(col)); +} + +void PIColor::valueChanged( QVariant /*variant*/ ) +{ + m_combo->setColor(m_data->value().toColor()); +} +//END class PIColor + + +//BEGIN class PIDouble +PIDouble::PIDouble(QString id, Variant *data, QWidget *parent, const char *name ) + : PIEditor(id,data,parent, name) +{ + QHBoxLayout *layout = new QHBoxLayout(this); + layout->setAutoAdd(true); + + spin = new DoubleSpinBox(m_data->minValue(),m_data->maxValue(),m_data->minAbsValue(),m_data->value().toDouble(),m_data->unit(),this); + + connect(spin,SIGNAL(valueChanged(double)),this,SLOT(spinValueChanged(double))); +} + +PIDouble::~PIDouble() +{ +} + +void PIDouble::spinValueChanged(double value) +{ + emit editorDataChanged(m_id,QVariant(value)); +} + +void PIDouble::valueChanged( QVariant /*variant*/ ) +{ + spin->setValue(m_data->value().toDouble()); +} +//END class PIDouble + + +//BEGIN class PIFileName +PIFilename::PIFilename(QString id, Variant *data, QWidget *parent, const char *name ) + : PIEditor(id,data,parent, name) +{ + QHBoxLayout *layout = new QHBoxLayout(this); + layout->setAutoAdd(true); + + m_combo = 0L; + if( m_data->allowed().count() == 0 ) + { + m_combo = new KComboBox(this); + m_combo->insertStringList( m_data->allowed() ); + m_urlreq = new KURLRequester(m_combo, this); + } + else m_urlreq = new KURLRequester(this); + + m_urlreq->setURL( m_data->value().toString() ); + m_urlreq->setFilter( m_data->filter() ); + + connect(m_urlreq,SIGNAL(urlSelected(const QString&)),this,SLOT(slotURLChanged(const QString&))); + //connect(m_urlreq,SIGNAL(openFileDialog(KURLRequester*)),this,SLOT(slotOpenFileDialog(KURLRequester* ))); +} + +PIFilename::~PIFilename() +{ +} + +void PIFilename::slotURLChanged(const QString &url) +{ + emit editorDataChanged(m_id,QVariant(url)); +} + +void PIFilename::valueChanged( QVariant /*variant*/ ) +{ + if(m_combo) m_combo->setCurrentItem( m_data->value().toString() ); + m_urlreq->setURL( m_data->value().toString() ); +} + +/* //FIXME Reintroduce this code if deciding not to go with dropping cod files + onto the PIC componenent ?? */ +// +// void PIFilename::slotOpenFileDialog(KURLRequester *kurlreq) +// { +// // If no file has been selected so far then it seems +// // to make most sense to open the dialog at the directory +// // of the current project if open. +// if(kurlreq->url() == m_data->defaultValue().toString() && !ProjectManager::self()->directory().isEmpty() ) kurlreq->setURL(ProjectManager::self()->directory()); +// } +//END class PIFileName + + +//BEGIN class PIInt +PIInt::PIInt( const QString &id, Variant *data, QWidget *parent, const char *name ) + : PIEditor( id, data, parent, name ) +{ + QHBoxLayout *layout = new QHBoxLayout(this); + layout->setAutoAdd(true); + + spin = new KIntSpinBox( (int)m_data->minValue(), (int)m_data->maxValue(), 1, m_data->value().toInt(), 10, this ); + + connect( spin, SIGNAL(valueChanged(int)), this, SLOT(spinValueChanged(int))); +} + +PIInt::~PIInt() +{ +} + +void PIInt::spinValueChanged( int value ) +{ + emit editorDataChanged( m_id, QVariant(value) ); +} + +void PIInt::valueChanged( QVariant /*variant*/ ) +{ + spin->setValue( m_data->value().toInt() ); +} +//END class PIInt + + +//BEGIN class PILineEdit +PILineEdit::PILineEdit(QString id, Variant *data, QWidget *parent, const char *name) + : PIEditor( id, data, parent, name) +{ + QHBoxLayout *layout = new QHBoxLayout(this); + layout->setAutoAdd(true); + m_edit = new KLineEdit( m_data->value().toString() , this ); + connect(m_edit,SIGNAL(textChanged(const QString&)),this,SLOT(slotEditTextChanged())); +} + + +PILineEdit::~PILineEdit() +{ +} + +void PILineEdit::slotEditTextChanged() +{ + emit editorDataChanged(m_id,QVariant(m_edit->text())); +} + +void PILineEdit::valueChanged( QVariant /*variant*/ ) +{ + m_edit->setText(m_data->value().toString()); +} +//END class PILineEdit + + +//BEGIN class PIStringCombo +PIStringCombo::PIStringCombo(QString id, Variant *data, QWidget *parent, const char *name) + : PIEditor( id, data, parent, name) +{ + QHBoxLayout *layout = new QHBoxLayout(this); + layout->setAutoAdd(true); + + m_combo = new KComboBox( this ); + m_combo->insertStringList(m_data->allowed()); + m_combo->setCurrentItem(m_data->value().toString()); + const Variant::Type::Value type = m_data->type(); + m_combo->setEditable( type == Variant::Type::Combo || + type == Variant::Type::FileName || + type == Variant::Type::VarName ); + + connect(m_combo,SIGNAL(highlighted(const QString&)),this,SLOT(slotComboChanged())); + connect(m_combo,SIGNAL(activated(const QString&)),this,SLOT(slotComboChanged())); +} + + +PIStringCombo::~PIStringCombo() +{ +} + +void PIStringCombo::popup() +{ + m_combo->popup(); +} + +void PIStringCombo::slotComboChanged() +{ + emit editorDataChanged(m_id,QVariant(m_combo->currentText())); +} + +void PIStringCombo::valueChanged( QVariant /*variant*/ ) +{ + m_combo->setCurrentItem(m_data->value().toString()); +} +//END class PIStringCombo + + + + +#include "pieditor.moc" + diff --git a/src/gui/pieditor.h b/src/gui/pieditor.h new file mode 100644 index 0000000..8423c7d --- /dev/null +++ b/src/gui/pieditor.h @@ -0,0 +1,188 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef PIEDITOR_H +#define PIEDITOR_H + +#include <qwidget.h> +#include <qstring.h> +#include <qvariant.h> + +class DoubleSpinBox; +class ColorCombo; +class KComboBox; +class KIntSpinBox; +class KLineEdit; +class KURLRequester; +class Variant; + + +/** +@author Daniel Clarke +*/ +class PIEditor : public QWidget +{ + Q_OBJECT + public: + PIEditor(QString id, Variant *data, QWidget *parent = 0, const char *name = 0); + ~PIEditor(); + + public slots: + virtual void valueChanged( QVariant variant ); + + signals: + void editorDataChanged(const QString &id, QVariant data); + + protected: + QString m_id; + Variant *m_data; +}; + + +/** +@author David Saxton + */ +class PIBool : public PIEditor +{ + Q_OBJECT + public: + PIBool( QString id, Variant *data, QWidget *parent = 0, const char *name = 0 ); + ~PIBool(); + + void popup(); + + protected slots: + void selectChanged( int index ); + virtual void valueChanged( QVariant variant ); + + protected: + KComboBox *m_comboBox; +}; + +/** +@author Daniel Clarke + */ +class PIColor : public PIEditor +{ + Q_OBJECT + public: + PIColor(QString id, Variant *data, QWidget *parent = 0, const char *name = 0); + ~PIColor(); + void popup(); + + protected slots: + void colorChanged(const QColor &col); + virtual void valueChanged( QVariant variant ); + + protected: + ColorCombo *m_combo; +}; + + +/** +Allows the editing of double precision numerical values, using the DoubleNum widget +@author Daniel Clarke + */ +class PIDouble : public PIEditor +{ + Q_OBJECT + public: + PIDouble(QString id, Variant *data, QWidget *parent = 0, const char *name = 0); + ~PIDouble(); + + protected slots: + void spinValueChanged(double value); + virtual void valueChanged( QVariant variant ); + + protected: + DoubleSpinBox *spin; +}; + + +/** +@author Daniel Clarke + */ +class PIFilename : public PIEditor +{ + Q_OBJECT + public: + PIFilename(QString id, Variant *data, QWidget *parent = 0, const char *name = 0); + ~PIFilename(); + + protected slots: + void slotURLChanged(const QString &url); + virtual void valueChanged( QVariant variant ); + // see comments in implementation. + //void slotOpenFileDialog(KURLRequester *kurlreq); + + protected: + KURLRequester *m_urlreq; + KComboBox *m_combo; +}; + +/** +@author David Saxton + */ +class PIInt : public PIEditor +{ + Q_OBJECT + public: + PIInt( const QString &id, Variant *data, QWidget *parent = 0, const char *name = 0); + ~PIInt(); + + protected slots: + void spinValueChanged( int value ); + virtual void valueChanged( QVariant variant ); + + protected: + KIntSpinBox *spin; +}; + + +/** +@author Daniel Clarke + */ +class PILineEdit : public PIEditor +{ + Q_OBJECT + public: + PILineEdit(QString id, Variant *data, QWidget *parent = 0, const char *name = 0); + ~PILineEdit(); + + protected slots: + void slotEditTextChanged(); + virtual void valueChanged( QVariant variant ); + + protected: + KLineEdit *m_edit; +}; + + +/** +@author Daniel Clarke + */ +class PIStringCombo : public PIEditor +{ + Q_OBJECT + public: + PIStringCombo(QString id, Variant *data, QWidget *parent, const char *name = 0); + ~PIStringCombo(); + void popup(); + + public slots: + virtual void valueChanged( QVariant variant ); + void slotComboChanged(); + + protected: + KComboBox *m_combo; + +}; + +#endif diff --git a/src/gui/plvitem.cpp b/src/gui/plvitem.cpp new file mode 100644 index 0000000..420259b --- /dev/null +++ b/src/gui/plvitem.cpp @@ -0,0 +1,90 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "plvitem.h" +#include "variant.h" + +#include <klistview.h> +#include <qpainter.h> +#include <qvariant.h> + +//BEGIN class PLVItem +PLVItem::PLVItem(KListView *listview, const QString &id, Variant *data) + : KListViewItem(listview, data->editorCaption()) +{ + p_data = data; + m_id = id; + setText(1,p_data->displayString() ); + + connect(data,SIGNAL(valueChanged(QVariant, QVariant )),this,SLOT(updateData(QVariant ))); + + //setHeight(100); +} + + +PLVItem::~PLVItem() +{ +} + +void PLVItem::updateData(QVariant /*value*/) +{ + if (!p_data) + return; + setText(1,p_data->displayString() ); +} + + +int PLVItem::width ( const QFontMetrics & fm, const QListView * lv, int c ) const +{ + if ( c == 0 ) + return 100; +// return KListViewItem::width( fm, lv, c ); + else + return 200; +} +//END class PLVitem + + + +//BEGIN class PLVColorItem +PLVColorItem::PLVColorItem(KListView *listview, const QString &id, Variant *data) + : PLVItem(listview,id,data) +{ +} + + +PLVColorItem::~PLVColorItem() +{ +} + +void PLVColorItem::paintCell ( QPainter * p, const QColorGroup & cg, int column, int width, int align ) +{ + if (!p_data) + return; + // we only draw column 1 "Data" ourselves, otherwise + // we leave it up to KDE + if (column == 1) + { + p->setBackgroundColor( p_data->value().toColor() ); + QBrush brush( p_data->value().toColor() ); + p->fillRect(QRect(0,0,listView()->columnWidth(1),KListViewItem::height()),brush); + } + else + KListViewItem::paintCell(p,cg,column,width,align); +} + +void PLVColorItem::updateData(QVariant value) +{ + listView()->triggerUpdate(); + PLVItem::updateData(value); +} +//END class PLVColorItem + +#include "plvitem.moc" diff --git a/src/gui/plvitem.h b/src/gui/plvitem.h new file mode 100644 index 0000000..fd48845 --- /dev/null +++ b/src/gui/plvitem.h @@ -0,0 +1,66 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef PLVITEM_H +#define PLVITEM_H + +#include <klistview.h> +#include <qstring.h> +#include <qguardedptr.h> +#include <qobject.h> + +class Variant; + +/** +@author David Saxton + +PropertiesListView Item +Basic item, which holds the Variant data and Id for an item +*/ +class PLVItem : public QObject, public KListViewItem +{ + Q_OBJECT + public: + PLVItem( KListView *listview, const QString &id, Variant * data ); + ~PLVItem(); + + QString id() const { return m_id; } + Variant * data() const { return p_data; } + + virtual int width ( const QFontMetrics & fm, const QListView * lv, int c ) const; + + public slots: + /** + * Call to change the data held by an item, and update the display + * accordingly. + */ + virtual void updateData(QVariant value); + + protected: + QString m_id; + QGuardedPtr<Variant> p_data; +}; + +/** +@author Daniel Clarke + */ +class PLVColorItem : public PLVItem +{ + public: + PLVColorItem( KListView *listview, const QString &id, Variant *data ); + ~PLVColorItem(); + + void updateData(QVariant value); + + protected: + virtual void paintCell( QPainter * p, const QColorGroup & cg, int column, int width, int align ); +}; + +#endif diff --git a/src/gui/probepositioner.cpp b/src/gui/probepositioner.cpp new file mode 100644 index 0000000..1ea2606 --- /dev/null +++ b/src/gui/probepositioner.cpp @@ -0,0 +1,210 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "oscilloscope.h" +#include "oscilloscopedata.h" +#include "oscilloscopeview.h" +#include "probepositioner.h" + +#include <qevent.h> +#include <qpainter.h> +#include <qpointarray.h> + +#include <algorithm> +#include <cmath> + +ProbePositioner::ProbePositioner(QWidget *parent, const char *name) + : QWidget( parent, name, WNoAutoErase ) +{ + m_probePosOffset = 0; + p_draggedProbe = 0l; + setFixedWidth( int(probeArrowWidth) ); + setBackgroundMode(NoBackground); + b_needRedraw = true; + m_pixmap = 0l; +} + + +ProbePositioner::~ProbePositioner() +{ + delete m_pixmap; +} + + +void ProbePositioner::forceRepaint() +{ + b_needRedraw = true; + repaint(false); +} + + +int ProbePositioner::probeOutputHeight() const +{ + int height = int( Oscilloscope::self()->oscilloscopeView->height() - probeArrowHeight ); + int numProbes = Oscilloscope::self()->numberOfProbes(); + if ( numProbes == 0 ) + numProbes = 1; + return height / numProbes; +} + + +int ProbePositioner::probePosition( ProbeData *probeData ) const +{ + if (!probeData) + return -1; + + int spacing = probeOutputHeight(); + int probeNum = Oscilloscope::self()->probeNumber(probeData->id()); + + return int( probeArrowHeight/2 + spacing*( probeNum + probeData->drawPosition() ) ); +} + + +void ProbePositioner::setProbePosition( ProbeData *probeData, int position ) +{ + if (!probeData) + return; + + int height = int( Oscilloscope::self()->oscilloscopeView->height() - probeArrowHeight ); + int numProbes = Oscilloscope::self()->numberOfProbes(); + int spacing = height / numProbes; + int probeNum = Oscilloscope::self()->probeNumber(probeData->id()); + + int minPos = int(probeArrowHeight/2); + int maxPos = int(Oscilloscope::self()->oscilloscopeView->height() - (probeArrowHeight/2)) - 1; + if ( position < minPos ) + position = minPos; + else if ( position > maxPos ) + position = maxPos; + + probeData->setDrawPosition( float(position - probeArrowHeight/2)/float(spacing) - probeNum ); + + forceRepaint(); + Oscilloscope::self()->oscilloscopeView->updateView(); +} + + +ProbeData* ProbePositioner::probeAtPosition( const QPoint &pos ) +{ + int relativeArrowHeight = int( probeArrowHeight * ( 1. - float(pos.x()/probeArrowWidth) ) ); + + const ProbeDataMap::const_iterator end = m_probeDataMap.end(); + for ( ProbeDataMap::const_iterator it = m_probeDataMap.begin(); it != end; ++it ) + { + ProbeData *probeData = it.data(); + int currentPos = probePosition(probeData); + m_probePosOffset = pos.y() - currentPos; + if ( std::abs(m_probePosOffset) <= relativeArrowHeight ) + return probeData; + } + m_probePosOffset = 0; + return 0l; +} + + +void ProbePositioner::slotProbeDataRegistered( int id, ProbeData *probe ) +{ + m_probeDataMap[id] = probe; + connect( probe, SIGNAL(displayAttributeChanged()), this, SLOT(forceRepaint()) ); + // This connect doesn't really belong here, but it save a lot of code + connect( probe, SIGNAL(displayAttributeChanged()), Oscilloscope::self()->oscilloscopeView, SLOT(updateView()) ); + forceRepaint(); + Oscilloscope::self()->oscilloscopeView->updateView(); +} + + +void ProbePositioner::slotProbeDataUnregistered( int id ) +{ + m_probeDataMap.erase(id); + // We "set" the position of each probe to force it into proper bounds + + const ProbeDataMap::const_iterator end = m_probeDataMap.end(); + for ( ProbeDataMap::const_iterator it = m_probeDataMap.begin(); it != end; ++it ) + setProbePosition( it.data(), probePosition( it.data() ) ); + + forceRepaint(); +} + + +void ProbePositioner::resizeEvent( QResizeEvent *e ) +{ + delete m_pixmap; + m_pixmap = new QPixmap( e->size() ); + QWidget::resizeEvent(e); + forceRepaint(); +} + + +void ProbePositioner::mousePressEvent( QMouseEvent * e ) +{ + p_draggedProbe = probeAtPosition(e->pos()); + if (p_draggedProbe) + e->accept(); + else + e->ignore(); +} + + +void ProbePositioner::mouseReleaseEvent( QMouseEvent * e ) +{ + if (p_draggedProbe) + e->accept(); + else + e->ignore(); +} + + +void ProbePositioner::mouseMoveEvent( QMouseEvent * e ) +{ + if (!p_draggedProbe) + { + e->ignore(); + return; + } + e->accept(); + + setProbePosition( p_draggedProbe, e->pos().y() - m_probePosOffset ); + forceRepaint(); +} + + +void ProbePositioner::paintEvent( QPaintEvent *e ) +{ + QRect r = e->rect(); + + if (b_needRedraw) + { + QPainter p; + m_pixmap->fill( paletteBackgroundColor() ); + p.begin(m_pixmap); + p.setClipRegion(e->region()); + + const ProbeDataMap::const_iterator end = m_probeDataMap.end(); + for ( ProbeDataMap::const_iterator it = m_probeDataMap.begin(); it != end; ++it ) + { + ProbeData *probeData = it.data(); + p.setBrush( probeData->color() ); + int currentPos = probePosition(probeData); + + QPointArray pa(3); + pa[0] = QPoint( 0, int(currentPos-(probeArrowHeight/2)) ); + pa[1] = QPoint( int(probeArrowWidth), currentPos ); + pa[2] = QPoint( 0, int(currentPos+(probeArrowHeight/2)) ); + + p.drawPolygon(pa); + } + b_needRedraw = false; + } + + bitBlt( this, r.x(), r.y(), m_pixmap, r.x(), r.y(), r.width(), r.height() ); +} + + +#include "probepositioner.moc" diff --git a/src/gui/probepositioner.h b/src/gui/probepositioner.h new file mode 100644 index 0000000..d106b40 --- /dev/null +++ b/src/gui/probepositioner.h @@ -0,0 +1,77 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef PROBEPOSITIONER_H +#define PROBEPOSITIONER_H + +#include <qwidget.h> + +class ProbeData; +typedef QMap< int, ProbeData* > ProbeDataMap; + +const float probeArrowWidth = 9; +const float probeArrowHeight = 12; + +/** +Widget for positioning the output of Probes in the OscilloscopeView +@author David Saxton +*/ +class ProbePositioner : public QWidget +{ + Q_OBJECT + public: + ProbePositioner(QWidget *parent = 0, const char *name = 0); + ~ProbePositioner(); + /** + * Returns the amount of space (height in pixels) that a probe output + * takes up + */ + int probeOutputHeight() const; + /** + * Returns the probe position (from the top) in pixels that the probe + * with the given id should be displayed at, or -1 if probe with the + * given id couldn't be found + */ + int probePosition( ProbeData *probeData ) const; + /** + * Sets the probe position relative to the top of this widget (and hence + * relative to the top of the oscilloscope view) in pixels + */ + void setProbePosition( ProbeData *probeData, int position ); + /** + * Returns the probe at the given position (plus or minus an an arrow), + * or NULL if none. Records the offset of the position from the mouse + * in m_probePosOffset. + */ + ProbeData* probeAtPosition( const QPoint &pos ); + + public slots: + void forceRepaint(); + + protected slots: + void slotProbeDataRegistered( int id, ProbeData *probe ); + void slotProbeDataUnregistered( int id ); + + protected: + virtual void mousePressEvent( QMouseEvent * e ); + virtual void mouseReleaseEvent( QMouseEvent * e ); + virtual void mouseMoveEvent( QMouseEvent * e ); + virtual void paintEvent( QPaintEvent *e ); + virtual void resizeEvent( QResizeEvent *event ); + + ProbeDataMap m_probeDataMap; + ProbeData *p_draggedProbe; + int m_probePosOffset; + + bool b_needRedraw; + QPixmap *m_pixmap; +}; + +#endif diff --git a/src/gui/processingoptionswidget.ui b/src/gui/processingoptionswidget.ui new file mode 100644 index 0000000..683092a --- /dev/null +++ b/src/gui/processingoptionswidget.ui @@ -0,0 +1,98 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>ProcessingOptionsWidget</class> +<widget class="QWidget"> + <property name="name"> + <cstring>ProcessingOptionsWidget</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>458</width> + <height>40</height> + </rect> + </property> + <property name="caption"> + <string>Processing Options</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <widget class="MicroSelectWidget" row="1" column="0" rowspan="1" colspan="2"> + <property name="name"> + <cstring>m_pMicroSelect</cstring> + </property> + <property name="minimumSize"> + <size> + <width>0</width> + <height>0</height> + </size> + </property> + </widget> + <spacer row="2" column="1"> + <property name="name"> + <cstring>spacer2</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>16</height> + </size> + </property> + </spacer> + <widget class="QLabel" row="0" column="0"> + <property name="name"> + <cstring>textLabel1</cstring> + </property> + <property name="text"> + <string>Output File:</string> + </property> + </widget> + <widget class="KURLRequester" row="0" column="1"> + <property name="name"> + <cstring>m_pOutputURL</cstring> + </property> + </widget> + </grid> +</widget> +<customwidgets> + <customwidget> + <class>MicroSelectWidget</class> + <header location="local">microselectwidget.h</header> + <sizehint> + <width>-1</width> + <height>-1</height> + </sizehint> + <container>0</container> + <sizepolicy> + <hordata>5</hordata> + <verdata>5</verdata> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + <pixmap>image0</pixmap> + </customwidget> +</customwidgets> +<images> + <image name="image0"> + <data format="PNG" length="1122">89504e470d0a1a0a0000000d4948445200000016000000160806000000c4b46c3b0000042949444154388db5954d6c545514c77ff7de37eff1a6a550da994e5ba798868f948fc847a2a2911816c436b0a02ed0083161a1981877ee10d90aa94656063491882c5cd4c4b8103f20cd806909a98604da8482341de80cb69de9bcd799799d79efba980f1da3a80b4f727273939bdff99f9b93f3175a6b6a21849080020c4002a29a8f0a5dcd002803bed63a1035f0d0d0504b6f6fef51d7758fa4d3e98d8ee3fc03af128ee3303939399548242eb8aefb09300f78464de9f0f0f0d148cc7caf3d5424de97a7ec17104220242805520a44557bbd4b21f083801d33e63a6d1bc7bf19c6064e0319a35a58b9ae7ba43d54e4d7c54b64dc097c7f09d30ef04b36736983cc7c11b4456b9b492c5e20dc54a6540e00896a8268a7017010f8bcae1830d2e9f4c69ebe3c197782b2ce60d94ddc9b30f9feeb45ae5d7181c5ead35636ef08d3ffe26a76ee3208b4031a56d80aa00768fda362e9380e25bf80ef3b587613d7afc099533380a4b5dda46fcb5aa42998b9eb7173dce5e6788e434763ec7fc942532008ea436003660d2caa7f8d69c3bd8910674e4d033e83877ad8d36fb0aa4d6058658ac5358c5df6383bf480f31fdda5a3632bbbfb6da4ccd5c00a10b2012cc12fd97cfb5516f0187c25cee0619396480ec3ca909d0f3372d1e5f9fe66de3ad605587c712ec55256a2540d5519d19ae24a29057329839f7ecc138944d833102290f3184ae1e6d6f0e98739c61229e6d21eafbed1c9c8c5558c8f3ee4ceed76946a1c43d97091828585223e2eebfbc295f60d45de89707628cb58224577bc85a79e5d4d786581cddb9b0148259711b20145836221400416e0232d1f6595c9ce4538773ac7b54406f0b15784e8e80aa1ac2594190220d021e49f2437960156b70ba095e95f96f18acd8c263cc6122962b1167a7ba34cdd7ec8c977a699bed546722a8fc2a02b6e2185fff78ab586ce78894ddb9ab9f5738ed14b1e030756929d8ff2e4ae5544632b38f96e89a99b298ebd6950cc2d12ed0eb3618b26f520788462ad09877df60db600f0f1fb49c646f21c7ebd836dcf94e9d9b4c0db27d6d3bd36c6426a96bce731703046d7da129a4670836284a054869d4f1b1c7e2dca67676ef3c1898091ef5ad8bc358c3205c93bf7c9a40ad4c660f4728edd7bbb3142ea2fc1da711cfc200011e08b45f6bd1c261adbce9717928c5f9d63fc6a19f0014567773303073790f8214bd6f5282dfbb8b9fa1f6b40d7c1939393533b92e63ad9240990484aec7e41b27d579cbb531e0f67023492d86316ebb768ba1e0f786e6f37e56550c62237aee7011e54abd7c1412291b8a06de378b4d3c0b215da9708e5a3a442192085404a83d4accfecac067c0c43e13a70e3fa12e96409e0325000966bbb354465339d04a6f9dd15fe6dde07ce030780278088d05ad72cc9a6b2f2daaaa74d75a1f0e8d0d5f60b40868a8364ead6248430000b0857a126ffcdf396abf03ce089ffcb4c7f033046c6b4a995e7a00000000049454e44ae426082</data> + </image> +</images> +<layoutdefaults spacing="6" margin="11"/> +<includehints> + <includehint>microselectwidget.h</includehint> + <includehint>kurlrequester.h</includehint> + <includehint>klineedit.h</includehint> + <includehint>kpushbutton.h</includehint> +</includehints> +</UI> diff --git a/src/gui/programmerdlg.cpp b/src/gui/programmerdlg.cpp new file mode 100644 index 0000000..a2186b4 --- /dev/null +++ b/src/gui/programmerdlg.cpp @@ -0,0 +1,90 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "language.h" +#include "microselectwidget.h" +#include "picprogrammer.h" +#include "port.h" +#include "programmerdlg.h" +#include "programmerwidget.h" +#include "src/core/ktlconfig.h" + +#include <kcombobox.h> +#include <kguiitem.h> +#include <klocale.h> +#include <kstdguiitem.h> + +ProgrammerDlg::ProgrammerDlg( const QString & picID, QWidget *parent, const char *name ) + : KDialogBase( parent, name, true, i18n("PIC Programmer"), Ok|Cancel ) +{ + // Change the "Ok" button to a "Burn" button + KGuiItem burnItem = KStdGuiItem::ok(); + burnItem.setText( i18n("Burn") ); + setButtonOK( burnItem ); + + m_bAccepted = false; + m_pProgrammerWidget = new ProgrammerWidget( this ); + m_pProgrammerSettings = new PicProgrammerSettings; + + // Setup the list of programmers + KComboBox * programmerCombo = m_pProgrammerWidget->m_pProgrammerProgram; + QStringList programmerNames = m_pProgrammerSettings->configNames( false ); + programmerCombo->insertStringList( programmerNames ); + programmerCombo->setSizeLimit( programmerNames.size() ); + programmerCombo->setCurrentText( KTLConfig::picProgrammerProgram() ); + + // Sets up the list of ports + m_pProgrammerWidget->m_pPicProgrammerPort->insertStringList( Port::ports( Port::ExistsAndRW ) ); + m_pProgrammerWidget->m_pPicProgrammerPort->setCurrentText( KTLConfig::picProgrammerPort() ); + + // Set the pic type to the one requested + if ( !picID.isEmpty() ) + m_pProgrammerWidget->m_pMicroSelect->setMicro( picID ); + + setMainWidget( m_pProgrammerWidget ); +} + + +ProgrammerDlg::~ProgrammerDlg() +{ +} + + +void ProgrammerDlg::initOptions( ProcessOptions * options ) +{ + if ( !options ) + return; + + options->m_picID = m_pProgrammerWidget->m_pMicroSelect->micro(); + options->m_port = m_pProgrammerWidget->m_pPicProgrammerPort->currentText(); + options->m_program = m_pProgrammerWidget->m_pProgrammerProgram->currentText(); +} + + +void ProgrammerDlg::accept() +{ + m_bAccepted = true; + hide(); +} + + +void ProgrammerDlg::reject() +{ + m_bAccepted = false; +} + + +MicroSelectWidget * ProgrammerDlg::microSelect( ) const +{ + return m_pProgrammerWidget->m_pMicroSelect; +} + + +#include "programmerdlg.moc" diff --git a/src/gui/programmerdlg.h b/src/gui/programmerdlg.h new file mode 100644 index 0000000..ab64e5a --- /dev/null +++ b/src/gui/programmerdlg.h @@ -0,0 +1,54 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef PROGRAMMERDLG_H +#define PROGRAMMERDLG_H + +#include <kdialogbase.h> + +class MicroSelectWidget; +class PicProgrammerSettings; +class ProcessOptions; +class ProgrammerWidget; + + +/** +@author David Saxton +*/ +class ProgrammerDlg : public KDialogBase +{ + Q_OBJECT + public: + /** + * Create a new ProgrammerDlg with the PIC type set to picID. Other + * options (such as the program to use) will be read in from the + * settings. + */ + ProgrammerDlg( const QString & picID, QWidget * parent = 0, const char * name = 0 ); + ~ProgrammerDlg(); + + virtual void reject(); + virtual void accept(); + bool isAccepted() const { return m_bAccepted; } + /** + * Initialises options with the values that the user has entered into + * the widgets. + */ + void initOptions( ProcessOptions * options ); + + MicroSelectWidget * microSelect() const; + + protected: + bool m_bAccepted; + ProgrammerWidget * m_pProgrammerWidget; + PicProgrammerSettings * m_pProgrammerSettings; +}; + +#endif diff --git a/src/gui/programmerwidget.ui b/src/gui/programmerwidget.ui new file mode 100644 index 0000000..3436ac1 --- /dev/null +++ b/src/gui/programmerwidget.ui @@ -0,0 +1,123 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>ProgrammerWidget</class> +<widget class="QWidget"> + <property name="name"> + <cstring>ProgrammerWidget</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>320</width> + <height>96</height> + </rect> + </property> + <property name="caption"> + <string>Programmer</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <widget class="QLabel" row="0" column="0"> + <property name="name"> + <cstring>textLabel1_3</cstring> + </property> + <property name="text"> + <string>Program:</string> + </property> + </widget> + <widget class="KComboBox" row="0" column="1"> + <property name="name"> + <cstring>m_pProgrammerProgram</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>1</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>1</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="contextMenuEnabled"> + <bool>false</bool> + </property> + </widget> + <widget class="MicroSelectWidget" row="2" column="0" rowspan="1" colspan="2"> + <property name="name"> + <cstring>m_pMicroSelect</cstring> + </property> + <property name="minimumSize"> + <size> + <width>0</width> + <height>0</height> + </size> + </property> + </widget> + <widget class="QLabel" row="1" column="0"> + <property name="name"> + <cstring>textLabel1</cstring> + </property> + <property name="text"> + <string>Port</string> + </property> + </widget> + <widget class="QComboBox" row="1" column="1"> + <property name="name"> + <cstring>m_pPicProgrammerPort</cstring> + </property> + <property name="editable"> + <bool>true</bool> + </property> + </widget> + <spacer row="3" column="1"> + <property name="name"> + <cstring>spacer4</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>0</height> + </size> + </property> + </spacer> + </grid> +</widget> +<customwidgets> + <customwidget> + <class>MicroSelectWidget</class> + <header location="local">microselectwidget.h</header> + <sizehint> + <width>-1</width> + <height>-1</height> + </sizehint> + <container>0</container> + <sizepolicy> + <hordata>5</hordata> + <verdata>5</verdata> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + <pixmap>image0</pixmap> + </customwidget> +</customwidgets> +<images> + <image name="image0"> + <data format="PNG" length="1122">89504e470d0a1a0a0000000d4948445200000016000000160806000000c4b46c3b0000042949444154388db5954d6c545514c77ff7de37eff1a6a550da994e5ba798868f948fc847a2a2911816c436b0a02ed0083161a1981877ee10d90aa94656063491882c5cd4c4b8103f20cd806909a98604da8482341de80cb69de9bcd799799d79efba980f1da3a80b4f727273939bdff99f9b93f3175a6b6a21849080020c4002a29a8f0a5dcd002803bed63a1035f0d0d0504b6f6fef51d7758fa4d3e98d8ee3fc03af128ee3303939399548242eb8aefb09300f78464de9f0f0f0d148cc7caf3d5424de97a7ec17104220242805520a44557bbd4b21f083801d33e63a6d1bc7bf19c6064e0319a35a58b9ae7ba43d54e4d7c54b64dc097c7f09d30ef04b36736983cc7c11b4456b9b492c5e20dc54a6540e00896a8268a7017010f8bcae1830d2e9f4c69ebe3c197782b2ce60d94ddc9b30f9feeb45ae5d7181c5ead35636ef08d3ffe26a76ee3208b4031a56d80aa00768fda362e9380e25bf80ef3b587613d7afc099533380a4b5dda46fcb5aa42998b9eb7173dce5e6788e434763ec7fc942532008ea436003660d2caa7f8d69c3bd8910674e4d033e83877ad8d36fb0aa4d6058658ac5358c5df6383bf480f31fdda5a3632bbbfb6da4ccd5c00a10b2012cc12fd97cfb5516f0187c25cee0619396480ec3ca909d0f3372d1e5f9fe66de3ad605587c712ec55256a2540d5519d19ae24a29057329839f7ecc138944d833102290f3184ae1e6d6f0e98739c61229e6d21eafbed1c9c8c5558c8f3ee4ceed76946a1c43d97091828585223e2eebfbc295f60d45de89707628cb58224577bc85a79e5d4d786581cddb9b0148259711b20145836221400416e0232d1f6595c9ce4538773ac7b54406f0b15784e8e80aa1ac2594190220d021e49f2437960156b70ba095e95f96f18acd8c263cc6122962b1167a7ba34cdd7ec8c977a699bed546722a8fc2a02b6e2185fff78ab586ce78894ddb9ab9f5738ed14b1e030756929d8ff2e4ae5544632b38f96e89a99b298ebd6950cc2d12ed0eb3618b26f520788462ad09877df60db600f0f1fb49c646f21c7ebd836dcf94e9d9b4c0db27d6d3bd36c6426a96bce731703046d7da129a4670836284a054869d4f1b1c7e2dca67676ef3c1898091ef5ad8bc358c3205c93bf7c9a40ad4c660f4728edd7bbb3142ea2fc1da711cfc200011e08b45f6bd1c261adbce9717928c5f9d63fc6a19f0014567773303073790f8214bd6f5282dfbb8b9fa1f6b40d7c1939393533b92e63ad9240990484aec7e41b27d579cbb531e0f67023492d86316ebb768ba1e0f786e6f37e56550c62237aee7011e54abd7c1412291b8a06de378b4d3c0b215da9708e5a3a442192085404a83d4accfecac067c0c43e13a70e3fa12e96409e0325000966bbb354465339d04a6f9dd15fe6dde07ce030780278088d05ad72cc9a6b2f2daaaa74d75a1f0e8d0d5f60b40868a8364ead6248430000b0857a126ffcdf396abf03ce089ffcb4c7f033046c6b4a995e7a00000000049454e44ae426082</data> + </image> +</images> +<layoutdefaults spacing="6" margin="11"/> +<includehints> + <includehint>kcombobox.h</includehint> + <includehint>microselectwidget.h</includehint> +</includehints> +</UI> diff --git a/src/gui/projectdlgs.cpp b/src/gui/projectdlgs.cpp new file mode 100644 index 0000000..4ea18c0 --- /dev/null +++ b/src/gui/projectdlgs.cpp @@ -0,0 +1,275 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "createsubprojectwidget.h" +#include "linkeroptionswidget.h" +#include "microlibrary.h" +#include "microselectwidget.h" +#include "newprojectwidget.h" +#include "processingoptionswidget.h" +#include "projectdlgs.h" +#include "projectmanager.h" + +#include <assert.h> +#include <kcombobox.h> +#include <kdeversion.h> +#include <kfiledialog.h> +#include <klineedit.h> +#include <klocale.h> +#include <kurlrequester.h> +#include <qcheckbox.h> +#include <qcombobox.h> +#include <qlabel.h> +#include <qlayout.h> + +//BEGIN class NewProjectDlg +NewProjectDlg::NewProjectDlg( QWidget * parent ) + : KDialogBase( parent, "newprojectdlg", true, "New Project", KDialogBase::Ok|KDialogBase::Cancel, KDialogBase::Ok, true ) +{ + m_pWidget = new NewProjectWidget(this); + connect( m_pWidget->projectNameEdit, SIGNAL(textChanged(const QString & )), this, SLOT(locationChanged(const QString& )) ); + connect( m_pWidget->projectLocationURL, SIGNAL(textChanged(const QString & )), this, SLOT(locationChanged(const QString& )) ); + + // Check if already valid dir + locationChanged( QString::null ); + + m_pWidget->projectLocationURL->setURL( QDir::homeDirPath() ); + m_pWidget->projectLocationURL->setMode( KFile::Directory ); + + setMainWidget( m_pWidget ); + setInitialSize( m_pWidget->rect().size() ); +} + +void NewProjectDlg::accept() +{ + hide(); + + m_bAccepted = true; + + m_projectName = m_pWidget->projectNameEdit->text(); + m_projectLocation = m_pWidget->projectLocationURL->url(); +} + +void NewProjectDlg::reject() +{ + m_bAccepted = false; +} + +void NewProjectDlg::locationChanged( const QString & ) +{ + m_location = m_pWidget->projectLocationURL->url(); + QDir subDir(m_location); + + if ( !m_location.endsWith("/") ) + m_location.append("/"); + + if ( !m_pWidget->projectNameEdit->text().isEmpty() ) + m_location.append( m_pWidget->projectNameEdit->text().lower() + "/" ); + + m_pWidget->locationLabel->setText( m_location ); + + QDir dir(m_location); + + if ( dir.exists() || !subDir.exists() ) + enableButtonOK(false); + + else + enableButtonOK(true); +} +//END class NewProjectDlg + + + +//BEGIN class CreateSubprojectDlg +CreateSubprojectDlg::CreateSubprojectDlg( QWidget * parent ) + : KDialogBase( parent, "Create Subproject Dialog", true, "Create Subproject", KDialogBase::Ok|KDialogBase::Cancel, KDialogBase::Ok, true ) +{ + m_pWidget = new CreateSubprojectWidget(this); + + if ( ProjectManager::self()->currentProject() ) + m_pWidget->m_targetFile->setURL( ProjectManager::self()->currentProject()->directory() ); + + m_type = ProgramType; + + setMainWidget( m_pWidget ); + setInitialSize( m_pWidget->rect().size() ); +} + + +CreateSubprojectDlg::~CreateSubprojectDlg() +{ +} + + +void CreateSubprojectDlg::accept() +{ + hide(); + + m_bAccepted = true; + + m_targetFile = m_pWidget->m_targetFile->url(); + m_type = (Type)m_pWidget->m_typeCombo->currentItem(); +} + + +void CreateSubprojectDlg::reject() +{ + m_bAccepted = false; +} +//END class CreateSubprojectDlg + + + +//BEGIN class LinkerOptionsDlg +LinkerOptionsDlg::LinkerOptionsDlg( LinkerOptions * linkingOptions, QWidget *parent ) + : KDialogBase( parent, "Linker Options Dialog", true, "Linker Options", KDialogBase::Ok|KDialogBase::Cancel, KDialogBase::Ok, true ) +{ + m_pLinkerOptions = linkingOptions; + m_pWidget = new LinkerOptionsWidget(this); + + ProjectInfo * pi = ProjectManager::self()->currentProject(); + assert(pi); + + + //BEGIN Update gplink options + m_pWidget->m_pHexFormat->setCurrentItem( m_pLinkerOptions->hexFormat() ); + m_pWidget->m_pOutputMap->setChecked( m_pLinkerOptions->outputMapFile() ); + m_pWidget->m_pLibraryDir->setText( m_pLinkerOptions->libraryDir() ); + m_pWidget->m_pLinkerScript->setText( m_pLinkerOptions->linkerScript() ); + m_pWidget->m_pOther->setText( m_pLinkerOptions->linkerOther() ); + //END Update gplink options + + + + //BEGIN Update library widgets + const KURL::List availableInternal = pi->childOutputURLs( ProjectItem::LibraryType ); + const QStringList linkedInternal = m_pLinkerOptions->linkedInternal(); + + KURL::List::const_iterator end = availableInternal.end(); + for ( KURL::List::const_iterator it = availableInternal.begin(); it != end; ++it ) + { + QString relativeURL = KURL::relativeURL( pi->url(), *it ); + QCheckListItem * item = new QCheckListItem( m_pWidget->m_pInternalLibraries, relativeURL, QCheckListItem::CheckBox ); + item->setOn( linkedInternal.contains(relativeURL) ); + } + + m_pExternalLibraryRequester = new KURLRequester( 0l ); + m_pExternalLibraryRequester->fileDialog()->setURL( "/usr/share/sdcc/lib" ); + + delete m_pWidget->m_pExternalLibraries; + m_pWidget->m_pExternalLibraries = new KEditListBox( i18n("Link libraries outside project"), m_pExternalLibraryRequester->customEditor(), m_pWidget ); + m_pWidget->m_pExternalLibraries->layout()->setMargin(11); + (dynamic_cast<QGridLayout*>(m_pWidget->layout()))->addMultiCellWidget( m_pWidget->m_pExternalLibraries, 7, 7, 0, 1 ); + +#if defined(KDE_MAKE_VERSION) +# if KDE_VERSION >= KDE_MAKE_VERSION(3,4,0) + m_pWidget->m_pExternalLibraries->setButtons( KEditListBox::Add | KEditListBox::Remove ); +# endif +#endif + m_pWidget->m_pExternalLibraries->insertStringList( m_pLinkerOptions->linkedExternal() ); + //END Update library widgets + + + setMainWidget( m_pWidget ); + setInitialSize( m_pWidget->rect().size() ); +} + + +LinkerOptionsDlg::~LinkerOptionsDlg() +{ + delete m_pExternalLibraryRequester; +} + + +void LinkerOptionsDlg::accept() +{ + hide(); + + QStringList linkedInternal; + for ( QListViewItemIterator internalIt( m_pWidget->m_pInternalLibraries ); internalIt.current(); ++internalIt ) + { + QCheckListItem * item = static_cast<QCheckListItem*>(internalIt.current()); + if ( item->isOn() ) + linkedInternal << item->text(); + } + m_pLinkerOptions->setLinkedInternal( linkedInternal ); + + m_pLinkerOptions->setLinkedExternal( m_pWidget->m_pExternalLibraries->items() ); + m_pLinkerOptions->setHexFormat( (LinkerOptions::HexFormat::type) m_pWidget->m_pHexFormat->currentItem() ); + m_pLinkerOptions->setOutputMapFile( m_pWidget->m_pOutputMap->isChecked() ); + m_pLinkerOptions->setLibraryDir( m_pWidget->m_pLibraryDir->text() ); + m_pLinkerOptions->setLinkerScript( m_pWidget->m_pLinkerScript->text() ); + m_pLinkerOptions->setLinkerOther( m_pWidget->m_pOther->text() ); +} + + +void LinkerOptionsDlg::reject() +{ +} +//END class LinkerOptionsDlg + + + +//BEGIN class ProcessingOptionsDlg +ProcessingOptionsDlg::ProcessingOptionsDlg( ProjectItem * projectItem, QWidget *parent ) + : KDialogBase( parent, "Processing Options Dialog", true, "Processing Options", KDialogBase::Ok|KDialogBase::Cancel, KDialogBase::Ok, true ) +{ + m_pProjectItem = projectItem; + m_pWidget = new ProcessingOptionsWidget(this); + + m_pWidget->m_pMicroSelect->setEnabled( !projectItem->useParentMicroID() ); + + switch ( projectItem->type() ) + { + case ProjectItem::ProjectType: + m_pWidget->m_pOutputURL->setEnabled(false); + break; + + case ProjectItem::FileType: + m_pWidget->m_pOutputURL->setEnabled(true); + break; + + case ProjectItem::ProgramType: + case ProjectItem::LibraryType: + m_pWidget->m_pOutputURL->setEnabled(false); + break; + } + + m_pWidget->m_pOutputURL->setURL( projectItem->outputURL().path() ); + m_pWidget->m_pMicroSelect->setMicro( projectItem->microID() ); + + setMainWidget( m_pWidget ); + setInitialSize( m_pWidget->rect().size() ); +} + + +ProcessingOptionsDlg::~ProcessingOptionsDlg() +{ +} + + +void ProcessingOptionsDlg::accept() +{ + hide(); + + if ( m_pWidget->m_pOutputURL->isEnabled() ) + m_pProjectItem->setOutputURL( m_pWidget->m_pOutputURL->url() ); + + if ( m_pWidget->m_pMicroSelect->isEnabled() ) + m_pProjectItem->setMicroID( m_pWidget->m_pMicroSelect->micro() ); +} + + +void ProcessingOptionsDlg::reject() +{ +} +//END class ProcessingOptionsDlg + + diff --git a/src/gui/projectdlgs.h b/src/gui/projectdlgs.h new file mode 100644 index 0000000..fe50361 --- /dev/null +++ b/src/gui/projectdlgs.h @@ -0,0 +1,159 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef PROJECTDLGS_H +#define PROJECTDLGS_H + +#include <kdialogbase.h> + +class CreateSubprojectWidget; +class LinkerOptions; +class LinkerOptionsWidget; +class KURLRequester; +class NewProjectWidget; +class ProcessingOptions; +class ProcessingOptionsWidget; +class ProjectItem; + +/** +A standard dialog for getting project details from the user for a new project +@short Dialog for new project details +@author David Saxton +*/ +class NewProjectDlg : public KDialogBase +{ + Q_OBJECT + public: + NewProjectDlg( QWidget * parent ); + + /** + * Called when the 'Cancel' button is pressed. + */ + void reject(); + + /** + * Called when the 'OK' button is pressed. + * User entered values are read in + */ + void accept(); + + bool accepted() const { return m_bAccepted; } + QString projectName() const { return m_projectName; } + QString projectLocation() const { return m_projectLocation; } + QString location() const { return m_location; } + + public slots: + /** + * Called when the projectName or projectLocation edit boxes are edited. + * Checks whether the resultant location combination is a valid path - + * if so, enabels the OK button; otherwise disables it. + */ + void locationChanged( const QString & ); + + protected: + NewProjectWidget * m_pWidget; + bool m_bAccepted; + QString m_projectName; + QString m_projectLocation; + QString m_location; +}; + + +/** +@author David Saxton +*/ +class CreateSubprojectDlg : public KDialogBase +{ + Q_OBJECT + public: + CreateSubprojectDlg( QWidget *parent = 0 ); + ~CreateSubprojectDlg(); + + // The following values should agree with the positions in the combo box + enum Type + { + ProgramType = 0, + LibraryType = 1 + }; + + /** + * Called when the 'Cancel' button is pressed. + */ + void reject(); + /** + * Called when the 'OK' button is pressed. User entered values are read + * in. + */ + void accept(); + + bool accepted() const { return m_bAccepted; } + Type type() const { return m_type; } + QString targetFile() const { return m_targetFile; } + + protected: + CreateSubprojectWidget * m_pWidget; + bool m_bAccepted; + Type m_type; + QString m_targetFile; +}; + + +/** +@author David Saxton +*/ +class LinkerOptionsDlg : public KDialogBase +{ + Q_OBJECT + public: + LinkerOptionsDlg( LinkerOptions * linkingOptions, QWidget *parent = 0 ); + virtual ~LinkerOptionsDlg(); + + /** + * Called when the 'Cancel' button is pressed. + */ + void reject(); + /** + * Called when the 'OK' button is pressed. User entered values are read + * in. + */ + void accept(); + + protected: + LinkerOptions * m_pLinkerOptions; + LinkerOptionsWidget * m_pWidget; + KURLRequester * m_pExternalLibraryRequester; +}; + + +/** +@author David Saxton +*/ +class ProcessingOptionsDlg : public KDialogBase +{ + public: + ProcessingOptionsDlg( ProjectItem * projectItem, QWidget *parent = 0 ); + virtual ~ProcessingOptionsDlg(); + + /** + * Called when the 'Cancel' button is pressed. + */ + void reject(); + /** + * Called when the 'OK' button is pressed. User entered values are read + * in. + */ + void accept(); + + protected: + ProjectItem * m_pProjectItem; + ProcessingOptionsWidget * m_pWidget; +}; + +#endif diff --git a/src/gui/propertieslistview.cpp b/src/gui/propertieslistview.cpp new file mode 100644 index 0000000..5f57aa3 --- /dev/null +++ b/src/gui/propertieslistview.cpp @@ -0,0 +1,328 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "item.h" +#include "cnitemgroup.h" +#include "itemgroup.h" +#include "iteminterface.h" +#include "pieditor.h" +#include "plvitem.h" +#include "propertieslistview.h" +#include "variant.h" + + +#include <kdebug.h> +#include <klocale.h> +#include <qevent.h> +#include <qheader.h> +#include <qpushbutton.h> +#include <qvariant.h> + +PropertiesListView::PropertiesListView(QWidget *parent, const char *name) + : KListView(parent, name) +{ + addColumn(i18n("Property")); + addColumn(i18n("Data")); + setFullWidth(true); + setColumnAlignment(1,Qt::AlignRight); + + p_lastItem = 0l; + m_diffBt = 0L; + + connect(this,SIGNAL(selectionChanged(QListViewItem*)),this,SLOT(slotSelectionChanged(QListViewItem*))); + m_editor = 0L; + connect(header(),SIGNAL(sizeChange(int,int,int)),this,SLOT(headerSizeChanged(int,int,int))); +} + +PropertiesListView::~PropertiesListView() +{ +} + +void PropertiesListView::slotClear() +{ + destroyEditor(); + delete m_diffBt; + m_diffBt = 0L; + clear(); + m_plvItemMap.clear(); +} + +void PropertiesListView::slotCreate( ItemGroup * itemGroup ) +{ + if ( !itemGroup || !itemGroup->activeItem() ) + { + slotClear(); + return; + } + + Item *item = itemGroup->activeItem(); + + VariantDataMap *vmap = item->variantMap(); + // Build the list + for( VariantDataMap::iterator vait = vmap->begin(); vait != vmap->end(); ++vait ) + { + if ( vait.data()->isHidden() ) + continue; + + switch( vait.data()->type() ) + { + case Variant::Type::Int: + case Variant::Type::Double: + case Variant::Type::String: + case Variant::Type::FileName: + case Variant::Type::Port: + case Variant::Type::Pin: + case Variant::Type::VarName: + case Variant::Type::Combo: + case Variant::Type::Select: + case Variant::Type::Bool: + case Variant::Type::PenStyle: + case Variant::Type::PenCapStyle: + case Variant::Type::SevenSegment: + case Variant::Type::KeyPad: + { + m_plvItemMap[vait.key()] = new PLVItem( this, vait.key(), vait.data() ); + break; + } + case Variant::Type::Color: + { + m_plvItemMap[vait.key()] = new PLVColorItem( this, vait.key(), vait.data() ); + break; + } + case Variant::Type::Raw: + case Variant::Type::Multiline: + case Variant::Type::None: + { + break; + } + } + } + + slotUpdate(itemGroup); +} + + +void PropertiesListView::slotUpdate( ItemGroup * itemGroup ) +{ + if ( !itemGroup ) + { + slotClear(); + return; + } + + const PLVItemMap::iterator end = m_plvItemMap.end(); + for ( PLVItemMap::iterator it = m_plvItemMap.begin(); it != end; ++it ) + { + it.data()->setEnabled( itemGroup->itemsHaveSameDataValue( it.key() ) ); + } +} + +void PropertiesListView::slotSelectionChanged(QListViewItem *item) +{ + if (!item) return; + destroyEditor(); + p_lastItem = dynamic_cast<PLVItem*>(item); + if ( !p_lastItem->data() ) return; + + const Variant::Type::Value type = p_lastItem->data()->type(); + switch(type) + { + case Variant::Type::String: + { + m_editor = new PILineEdit(p_lastItem->id(),p_lastItem->data(),this); + break; + } + case Variant::Type::Port: + case Variant::Type::Pin: + case Variant::Type::Combo: + case Variant::Type::VarName: + case Variant::Type::Select: + case Variant::Type::PenStyle: + case Variant::Type::PenCapStyle: + case Variant::Type::SevenSegment: + case Variant::Type::KeyPad: + { + m_editor = new PIStringCombo(p_lastItem->id(),p_lastItem->data(),this); + break; + } + case Variant::Type::FileName: + { + m_editor = new PIFilename(p_lastItem->id(),p_lastItem->data(),this); + break; + } + case Variant::Type::Int: + { + m_editor = new PIInt(p_lastItem->id(),p_lastItem->data(),this); + break; + } + case Variant::Type::Double: + { + m_editor = new PIDouble(p_lastItem->id(),p_lastItem->data(),this); + break; + } + case Variant::Type::Color: + { + m_editor = new PIColor(p_lastItem->id(),p_lastItem->data(),this); + break; + } + case Variant::Type::Bool: + { + m_editor = new PIBool(p_lastItem->id(),p_lastItem->data(),this); + break; + } + case Variant::Type::Raw: + case Variant::Type::Multiline: + case Variant::Type::None: + { + break; + } + } + + connect(p_lastItem->data(),SIGNAL(destroyed()),this,SLOT(destroyEditor())); + // Connect so that changes in the editor change the canvas item data. + connect(m_editor,SIGNAL(editorDataChanged(const QString&,QVariant)),ItemInterface::self(),SLOT(slotSetData(const QString&,QVariant))); + connect(m_editor,SIGNAL(editorDataChanged(const QString&,QVariant)),this,SLOT(slotDataChanged(const QString&,QVariant))); + + int x = columnWidth(0); + int y = viewportToContents(QPoint(0,itemRect(p_lastItem).y())).y(); + addChild(m_editor,x,y); + m_editor->setFocus(); + m_editor->show(); + m_editor->setGeometry(QRect(x,y,columnWidth(1),itemRect(p_lastItem).height())); + + if(p_lastItem->data()->type() == Variant::Type::FileName) + { + // The folder button in the KURLComboBox has a minimum size taller than + // the height of the ListViewItems so this is a temporary kludge to + // make it look slightly acceptable. + m_editor->setGeometry(QRect(x,y,columnWidth(1),itemRect(p_lastItem).height()+7)); + } + + // Active the editor as appropriate + switch(type) + { + case Variant::Type::Port: + case Variant::Type::Pin: + case Variant::Type::Combo: + case Variant::Type::VarName: + case Variant::Type::Select: + case Variant::Type::PenStyle: + case Variant::Type::PenCapStyle: + case Variant::Type::SevenSegment: + case Variant::Type::KeyPad: + { + (static_cast<PIStringCombo*>(m_editor))->popup(); + break; + } + case Variant::Type::Color: + { + (static_cast<PIColor*>(m_editor))->popup(); + break; + } + case Variant::Type::Bool: + { + (static_cast<PIBool*>(m_editor))->popup(); + break; + } + case Variant::Type::FileName: + { + break; + } + case Variant::Type::Int: + { + break; + } + case Variant::Type::Double: + { + break; + } + case Variant::Type::String: + { + break; + } + case Variant::Type::Raw: + case Variant::Type::Multiline: + case Variant::Type::None: + { + break; + } + } +} + +void PropertiesListView::destroyEditor() +{ + if( !m_editor ) return; + + removeChild( m_editor ); + delete m_editor; + m_editor = 0L; +} + +void PropertiesListView::headerSizeChanged(int section, int /*oldSize*/, int newSize) +{ + if( !m_editor || section != 1 ) return; + + // Resize the editor to the new column width + // and move it to the right place. + QRect rect = m_editor->geometry(); + rect.setWidth(newSize); + rect.setX( columnWidth(0) ); + m_editor->setGeometry(rect); +} + +void PropertiesListView::slotDataChanged(const QString &/*id*/, QVariant data) +{ + PLVItem *pItem = static_cast<PLVItem*>(currentItem()); + pItem->updateData(data); +} + +void PropertiesListView::slotMergeProperties() +{ + for( QListViewItemIterator it( this ); it.current(); ++it ) + { + PLVItem * pItem = static_cast<PLVItem*>(it.current()); + if (pItem->isEnabled()) + continue; + + pItem->setEnabled(true); + // manually call the updates on the canvas + // and in the list + pItem->updateData(pItem->data()->defaultValue()); + ItemInterface::self()->slotSetData(pItem->id(),pItem->data()->defaultValue()); + } +} + + +void PropertiesListView::slotSetDefaults() +{ + for( QListViewItemIterator it( this ); it.current(); ++it ) + { + PLVItem *pItem = static_cast<PLVItem*>(it.current()); + ItemInterface::self()->slotSetData(pItem->id(),pItem->data()->defaultValue()); + } +} + + +void PropertiesListView::wheelEvent( QWheelEvent *e ) +{ + QPoint _pos = contentsToViewport(e->pos()); + _pos -= pos(); + _pos.setY( _pos.y()+header()->height() ); + QListViewItem *item = itemAt(_pos); + if ( item && item != dynamic_cast<QListViewItem*>(p_lastItem) ) + { + e->accept(); + if(!item->isSelected()) slotSelectionChanged(item); + } + else KListView::wheelEvent(e); +} + +#include "propertieslistview.moc" + diff --git a/src/gui/propertieslistview.h b/src/gui/propertieslistview.h new file mode 100644 index 0000000..c6ad8f7 --- /dev/null +++ b/src/gui/propertieslistview.h @@ -0,0 +1,75 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef PROPERTIESLISTVIEW_H +#define PROPERTIESLISTVIEW_H + +#include <klistview.h> +#include <qmap.h> + +class CNItemGroup; +class ItemGroup; +class PIEditor; +class PLVItem; +class QPushButton; + +typedef QMap< QString, PLVItem * > PLVItemMap; + +/** +@author Daniel Clarke +@author David Saxton +*/ +class PropertiesListView : public KListView +{ +Q_OBJECT +public: + PropertiesListView( QWidget *parent = 0l, const char *name = 0l ); + ~PropertiesListView(); + +public slots: + /** + * Creates a new set of property items and inserts them into the widget. + */ + void slotCreate( ItemGroup * itemGroup ); + /** + * Updates the set of property items (enables/disables according to whether + * the items have differing values, etc). This will be called by slotCreate, + * and any time new items are selected / unselected. + */ + void slotUpdate( ItemGroup * itemGroup ); + /** + * Removes all property items from the widget. + */ + void slotClear(); + void slotSelectionChanged(QListViewItem *item); + /** + * Whenthe selected items have different values, then the property editor + * for the disagreeing property will have a value taken from one of the + * items, but will also be disabled. This will enable all disabled property + * editors and set the items to the value contained. + */ + void slotMergeProperties(); + void slotSetDefaults(); + +protected slots: + void headerSizeChanged(int section, int oldSize, int newSize); + void slotDataChanged(const QString &id, QVariant data); + void destroyEditor(); + +protected: + virtual void wheelEvent( QWheelEvent *e ); + + PIEditor *m_editor; + QPushButton *m_diffBt; + PLVItem *p_lastItem; + PLVItemMap m_plvItemMap; +}; + +#endif diff --git a/src/gui/sdccoptionswidget.ui b/src/gui/sdccoptionswidget.ui new file mode 100644 index 0000000..218f796 --- /dev/null +++ b/src/gui/sdccoptionswidget.ui @@ -0,0 +1,586 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>SDCCOptionsWidget</class> +<widget class="QWidget"> + <property name="name"> + <cstring>SDCCOptionsWidget</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>441</width> + <height>377</height> + </rect> + </property> + <property name="caption"> + <string>SDCC Options</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <widget class="QTabWidget" row="0" column="0" rowspan="1" colspan="2"> + <property name="name"> + <cstring>tabWidget12</cstring> + </property> + <widget class="QWidget"> + <property name="name"> + <cstring>general</cstring> + </property> + <attribute name="title"> + <string>Ge&neral</string> + </attribute> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QCheckBox"> + <property name="name"> + <cstring>kcfg_SDCC_nostdlib</cstring> + </property> + <property name="text"> + <string>Don't search in the standard librar&y directory (--nostdlib)</string> + </property> + <property name="accel"> + <string>Alt+Y</string> + </property> + </widget> + <widget class="QCheckBox"> + <property name="name"> + <cstring>kcfg_SDCC_nostdinc</cstring> + </property> + <property name="text"> + <string>Don't search in the standard include directory (--nostdinc)</string> + </property> + <property name="accel"> + <string></string> + </property> + </widget> + <widget class="QCheckBox"> + <property name="name"> + <cstring>kcfg_SDCC_less_pedantic</cstring> + </property> + <property name="text"> + <string>Disa&ble pedantic warnings (--less-pedantic)</string> + </property> + <property name="accel"> + <string>Alt+B</string> + </property> + </widget> + <widget class="QCheckBox"> + <property name="name"> + <cstring>kcfg_SDCC_std_c89</cstring> + </property> + <property name="text"> + <string>Use C&89 standard only (--std-c89)</string> + </property> + <property name="accel"> + <string>Alt+8</string> + </property> + <property name="whatsThis" stdset="0"> + <string>Follow the C89 standard and disable SDCC features that conflict with the standard.</string> + </property> + </widget> + <widget class="QCheckBox"> + <property name="name"> + <cstring>kcfg_SDCC_std_c99</cstring> + </property> + <property name="text"> + <string>Use C&99 standard only (--std-c99)</string> + </property> + <property name="accel"> + <string>Alt+9</string> + </property> + <property name="toolTip" stdset="0"> + <string></string> + </property> + <property name="whatsThis" stdset="0"> + <string>Follow the C99 standard and disable SDCC features that conflict with the standard (incomplete support).</string> + </property> + </widget> + <spacer> + <property name="name"> + <cstring>spacer32</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>20</height> + </size> + </property> + </spacer> + </vbox> + </widget> + <widget class="QWidget"> + <property name="name"> + <cstring>code_generation</cstring> + </property> + <attribute name="title"> + <string>Code Gener&ation</string> + </attribute> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QCheckBox"> + <property name="name"> + <cstring>kcfg_SDCC_stack_auto</cstring> + </property> + <property name="text"> + <string>Stack auto&matic variables (--stack-auto)</string> + </property> + <property name="accel"> + <string>Alt+M</string> + </property> + <property name="toolTip" stdset="0"> + <string></string> + </property> + <property name="whatsThis" stdset="0"> + <string>All functions in the source file will be compiled as reentrant, i.e. the parameters and local variables will be allocated on the stack. If this option is used all source files in the project should be compiled with this option. It automatically implies -int-long-reent and -float-reent.</string> + </property> + </widget> + <widget class="QCheckBox"> + <property name="name"> + <cstring>kcfg_SDCC_int_long_reent</cstring> + </property> + <property name="text"> + <string>Integer li&braries were compiled as reentrant (--int-long-reent)</string> + </property> + <property name="accel"> + <string>Alt+B</string> + </property> + <property name="toolTip" stdset="0"> + <string></string> + </property> + <property name="whatsThis" stdset="0"> + <string>Integer (16 bit) and long (32 bit) libraries have been compiled as reentrant. Note by default these libraries are compiled as non-reentrant.</string> + </property> + </widget> + <widget class="QCheckBox"> + <property name="name"> + <cstring>kcfg_SDCC_float_reent</cstring> + </property> + <property name="text"> + <string>Floating point librar&y was compiled as reentrant (--float-reent)</string> + </property> + <property name="accel"> + <string>Alt+Y</string> + </property> + <property name="whatsThis" stdset="0"> + <string>Floating point library is compiled as reentrant.</string> + </property> + </widget> + <widget class="QCheckBox"> + <property name="name"> + <cstring>kcfg_SDCC_fommit_frame_pointer</cstring> + </property> + <property name="text"> + <string>Leave out the frame pointer (--fommit-frame-pointer)</string> + </property> + <property name="accel"> + <string></string> + </property> + </widget> + <widget class="QCheckBox"> + <property name="name"> + <cstring>kcfg_SDCC_no_xinit_opt</cstring> + </property> + <property name="text"> + <string>Don't memcpy initialized &xram from code (--no-xinit-opt)</string> + </property> + <property name="accel"> + <string>Alt+X</string> + </property> + <property name="whatsThis" stdset="0"> + <string>Will not memcpy initialized data from code space into xdata space. This saves a few bytes in code space if you don't have initialized data.</string> + </property> + </widget> + <widget class="QCheckBox"> + <property name="name"> + <cstring>kcfg_SDCC_all_callee_saves</cstring> + </property> + <property name="text"> + <string>Callee will &always save registers used (--all-callee-saves)</string> + </property> + <property name="accel"> + <string>Alt+A</string> + </property> + </widget> + <spacer> + <property name="name"> + <cstring>spacer31</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>20</height> + </size> + </property> + </spacer> + </vbox> + </widget> + <widget class="QWidget"> + <property name="name"> + <cstring>optimization</cstring> + </property> + <attribute name="title"> + <string>&Optimization</string> + </attribute> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QCheckBox"> + <property name="name"> + <cstring>kcfg_SDCC_nooverlay</cstring> + </property> + <property name="text"> + <string>Disable overlaying leaf function &auto variables (--nooverlay)</string> + </property> + <property name="accel"> + <string>Alt+A</string> + </property> + <property name="whatsThis" stdset="0"> + <string>The compiler will not overlay parameters and local variables of any function, see section Parameters and local variables for more details.</string> + </property> + </widget> + <widget class="QCheckBox"> + <property name="name"> + <cstring>kcfg_SDCC_nogcse</cstring> + </property> + <property name="text"> + <string>Disable the GCSE optimization (--nogcse)</string> + </property> + <property name="accel"> + <string></string> + </property> + </widget> + <widget class="QCheckBox"> + <property name="name"> + <cstring>kcfg_SDCC_nolabelopt</cstring> + </property> + <property name="text"> + <string>Disable label optimi&zation (--nolabelopt)</string> + </property> + <property name="accel"> + <string>Alt+Z</string> + </property> + <property name="whatsThis" stdset="0"> + <string>Will not optimize labels (makes the dumpfiles more readable).</string> + </property> + </widget> + <widget class="QCheckBox"> + <property name="name"> + <cstring>kcfg_SDCC_noinvariant</cstring> + </property> + <property name="text"> + <string>Disable optimization of invariants (--noinvariant)</string> + </property> + <property name="accel"> + <string></string> + </property> + </widget> + <widget class="QCheckBox"> + <property name="name"> + <cstring>kcfg_SDCC_noinduction</cstring> + </property> + <property name="text"> + <string>Disable loop variable induction (--noinduction)</string> + </property> + <property name="accel"> + <string></string> + </property> + </widget> + <widget class="QCheckBox"> + <property name="name"> + <cstring>kcfg_SDCC_no_peep</cstring> + </property> + <property name="text"> + <string>Disable peep-hole optimization (--&no-peep)</string> + </property> + <property name="accel"> + <string>Alt+N</string> + </property> + <property name="whatsThis" stdset="0"> + <string>Disable peep-hole optimization.</string> + </property> + </widget> + <widget class="QCheckBox"> + <property name="name"> + <cstring>kcfg_SDCC_noloopreverse</cstring> + </property> + <property name="text"> + <string>Disable loop reverse optimization (--noloopreverse)</string> + </property> + <property name="accel"> + <string></string> + </property> + <property name="whatsThis" stdset="0"> + <string>Will not do loop reversal optimization.</string> + </property> + </widget> + <widget class="QCheckBox"> + <property name="name"> + <cstring>kcfg_SDCC_opt_code_size</cstring> + </property> + <property name="text"> + <string>Opti&mize for compact code (--opt-code-size)</string> + </property> + <property name="accel"> + <string>Alt+M</string> + </property> + <property name="whatsThis" stdset="0"> + <string>The compiler will optimize code generation towards compact code, possibly at the expense of code speed.</string> + </property> + </widget> + <widget class="QCheckBox"> + <property name="name"> + <cstring>kcfg_SDCC_opt_code_speed</cstring> + </property> + <property name="text"> + <string>&Optimize for fast code (--opt-code-speed)</string> + </property> + <property name="accel"> + <string>Alt+O</string> + </property> + <property name="whatsThis" stdset="0"> + <string>The compiler will optimize code generation towards fast code, possibly at the expense of code size.</string> + </property> + </widget> + <widget class="QCheckBox"> + <property name="name"> + <cstring>kcfg_SDCC_peep_asm</cstring> + </property> + <property name="text"> + <string>Ena&ble inline assembly peephole optimization (--peep-asm)</string> + </property> + <property name="accel"> + <string>Alt+B</string> + </property> + <property name="whatsThis" stdset="0"> + <string>Pass the inline assembler code through the peep hole optimizer. This can cause unexpected changes to inline assembler code, please go through the peephole optimizer rules defined in the source file tree '<target>/peeph.def' before using this option.</string> + </property> + </widget> + <widget class="QCheckBox"> + <property name="name"> + <cstring>kcfg_SDCC_nojtbound</cstring> + </property> + <property name="text"> + <string>Don't generate boundary check for &jump tables (--nojtbound)</string> + </property> + <property name="accel"> + <string>Alt+J</string> + </property> + </widget> + <spacer> + <property name="name"> + <cstring>spacer33</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>20</height> + </size> + </property> + </spacer> + </vbox> + </widget> + <widget class="QWidget"> + <property name="name"> + <cstring>pic16_specific</cstring> + </property> + <attribute name="title"> + <string>PIC&16 Specific</string> + </attribute> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QCheckBox"> + <property name="name"> + <cstring>kcfg_SDCC_nodefaultlibs</cstring> + </property> + <property name="text"> + <string>Don't use default libraries (--nodefaultlibs)</string> + </property> + <property name="accel"> + <string></string> + </property> + </widget> + <widget class="QCheckBox"> + <property name="name"> + <cstring>kcfg_SDCC_pno_banksel</cstring> + </property> + <property name="text"> + <string>Don't generate BANKSEL directives (--pno-banksel)</string> + </property> + <property name="accel"> + <string></string> + </property> + </widget> + <widget class="QCheckBox"> + <property name="name"> + <cstring>kcfg_SDCC_pstack_model_large</cstring> + </property> + <property name="text"> + <string>Use large stac&k model (--pstack-model=large)</string> + </property> + <property name="accel"> + <string>Alt+K</string> + </property> + </widget> + <widget class="QCheckBox"> + <property name="name"> + <cstring>kcfg_SDCC_debug_xtra</cstring> + </property> + <property name="text"> + <string>Show more debug info in assembl&y output (--debug-xtra)</string> + </property> + <property name="accel"> + <string>Alt+Y</string> + </property> + </widget> + <widget class="QCheckBox"> + <property name="name"> + <cstring>kcfg_SDCC_denable_peeps</cstring> + </property> + <property name="text"> + <string>E&xplicit enable of peepholes (--denable-peeps)</string> + </property> + <property name="accel"> + <string>Alt+X</string> + </property> + </widget> + <widget class="QCheckBox"> + <property name="name"> + <cstring>kcfg_SDCC_calltree</cstring> + </property> + <property name="text"> + <string>Du&mp call tree in .calltree file (--calltree)</string> + </property> + <property name="accel"> + <string>Alt+M</string> + </property> + </widget> + <widget class="QCheckBox"> + <property name="name"> + <cstring>kcfg_SDCC_fstack</cstring> + </property> + <property name="text"> + <string>Ena&ble stack optimizations (--fstack)</string> + </property> + <property name="accel"> + <string>Alt+B</string> + </property> + </widget> + <widget class="QCheckBox"> + <property name="name"> + <cstring>kcfg_SDCC_optimize_goto</cstring> + </property> + <property name="text"> + <string>Try to use conditional BRA instead of GOTO (--optimi&ze-goto)</string> + </property> + <property name="accel"> + <string>Alt+Z</string> + </property> + </widget> + <widget class="QCheckBox"> + <property name="name"> + <cstring>kcfg_SDCC_optimize_cmp</cstring> + </property> + <property name="text"> + <string>Try to optimize some compares (--optimize-cmp)</string> + </property> + <property name="accel"> + <string></string> + </property> + </widget> + <widget class="QCheckBox"> + <property name="name"> + <cstring>kcfg_SDCC_optimize_df</cstring> + </property> + <property name="text"> + <string>Thorough data flow analyis (resource intensive) (--optimize-df)</string> + </property> + <property name="accel"> + <string></string> + </property> + </widget> + <spacer> + <property name="name"> + <cstring>spacer34</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>20</height> + </size> + </property> + </spacer> + </vbox> + </widget> + </widget> + <widget class="KLineEdit" row="1" column="1"> + <property name="name"> + <cstring>kcfg_MiscSDCCOptions</cstring> + </property> + </widget> + <widget class="QLabel" row="1" column="0"> + <property name="name"> + <cstring>textLabel2</cstring> + </property> + <property name="text"> + <string>Other options:</string> + </property> + </widget> + <spacer row="2" column="1"> + <property name="name"> + <cstring>spacer4</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>16</height> + </size> + </property> + </spacer> + </grid> +</widget> +<layoutdefaults spacing="6" margin="11"/> +<includehints> + <includehint>klineedit.h</includehint> +</includehints> +</UI> diff --git a/src/gui/settingsdlg.cpp b/src/gui/settingsdlg.cpp new file mode 100644 index 0000000..3b42027 --- /dev/null +++ b/src/gui/settingsdlg.cpp @@ -0,0 +1,386 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "asmformattingwidget.h" +#include "generaloptionswidget.h" +#include "gpasmsettingswidget.h" +#include "logicwidget.h" +#include "picprogrammerconfigwidget.h" +#include "picprogrammer.h" +#include "port.h" +#include "sdccoptionswidget.h" +#include "settingsdlg.h" +#include "src/core/ktlconfig.h" + +#include <kapplication.h> +#include <kcolorbutton.h> +#include <kcombobox.h> +#include <kconfig.h> +#include <kinputdialog.h> +#include <klineedit.h> +#include <klocale.h> +#include <kmessagebox.h> +#include <knuminput.h> +#include <kpushbutton.h> +#include <kstandarddirs.h> +#include <qgroupbox.h> +#include <qlabel.h> +#include <qslider.h> +#include <qtimer.h> +#include <qtooltip.h> +#include <qvalidator.h> + +// Make sure that this value is the same as that in ktechlab.kcfg +const int defaultRefreshRate = 50; + +SettingsDlg::SettingsDlg( QWidget *parent, const char *name, KConfigSkeleton *config ) + : KConfigDialog( parent, name, config ) +{ + m_generalOptionsWidget = new GeneralOptionsWidget( this, "generalOptionsWidget" ); + m_gpasmSettingsWidget = new GpasmSettingsWidget( this, "gpasmSettingsWidget" ); + m_sdccOptionsWidget = new SDCCOptionsWidget( this, "sdccOptionsWidget" ); + m_asmFormattingWidget = new AsmFormattingWidget( this, "asmFormattingWidget" ); + m_logicWidget = new LogicWidget( this, "logicWidget" ); + m_picProgrammerConfigWidget = new PicProgrammerConfigWidget( this, "picProgrammerConfigWidget" ); + + m_pPicProgrammerSettings = new PicProgrammerSettings; + + m_logicWidget->kcfg_LogicOutputHighImpedance->setSuffix( QString(" ") + QChar(0x3a9) ); + m_logicWidget->kcfg_LogicOutputLowImpedance->setSuffix( QString(" ") + QChar(0x3a9) ); + + addPage( m_generalOptionsWidget, i18n("General"), "misc", i18n("General Options") ); + addPage( m_picProgrammerConfigWidget, i18n("Programmer"), "memory", i18n("PIC Programmer") ); + addPage( m_asmFormattingWidget, i18n("Formatter"), "indent_asm", i18n("Assembly Formatter") ); + addPage( m_logicWidget, i18n("Logic"), "logic_or", i18n("Electronic Logic Values") ); + addPage( m_gpasmSettingsWidget, "Gpasm", "convert_to_hex", "gpasm" ); + addPage( m_sdccOptionsWidget, "SDCC", "source_c", "SDCC" ); + + connect( m_generalOptionsWidget->refreshRateSlider, SIGNAL(valueChanged(int)), this, SLOT(slotUpdateRefreshRateLabel(int)) ); + connect( m_picProgrammerConfigWidget->kcfg_PicProgrammerProgram, SIGNAL(activated(const QString &)), this, SLOT(slotUpdatePicProgrammerDescription()) ); + connect( m_picProgrammerConfigWidget->removeButton, SIGNAL(clicked()), this, SLOT(slotRemoveProgrammerConfig()) ); + connect( m_picProgrammerConfigWidget->addButton, SIGNAL(clicked()), this, SLOT(slotAddProgrammerConfig()) ); + + + connect( m_picProgrammerConfigWidget->initCommand, SIGNAL(textChanged(const QString &)), this, SLOT(slotSaveCurrentProgrammerConfig()) ); + connect( m_picProgrammerConfigWidget->readCommand, SIGNAL(textChanged(const QString &)), this, SLOT(slotSaveCurrentProgrammerConfig()) ); + connect( m_picProgrammerConfigWidget->writeCommand, SIGNAL(textChanged(const QString &)), this, SLOT(slotSaveCurrentProgrammerConfig()) ); + connect( m_picProgrammerConfigWidget->verifyCommand, SIGNAL(textChanged(const QString &)), this, SLOT(slotSaveCurrentProgrammerConfig()) ); + connect( m_picProgrammerConfigWidget->blankCheckCommand, SIGNAL(textChanged(const QString &)), this, SLOT(slotSaveCurrentProgrammerConfig()) ); + connect( m_picProgrammerConfigWidget->eraseCommand, SIGNAL(textChanged(const QString &)), this, SLOT(slotSaveCurrentProgrammerConfig()) ); + + + m_generalOptionsWidget->kcfg_GridColor->setEnabled( KTLConfig::showGrid() ); + + m_picProgrammerConfigWidget->kcfg_PicProgrammerPort->insertStringList( Port::ports( Port::ExistsAndRW ) ); + slotUpdatePicProgrammerDescription(); +} + + +SettingsDlg::~SettingsDlg() +{ + delete m_pPicProgrammerSettings; +} + + +void SettingsDlg::show() +{ + KComboBox * combo = m_picProgrammerConfigWidget->kcfg_PicProgrammerProgram; + combo->setEditable( true ); + KConfigDialog::show(); + combo->setEditable( false ); +} + + +void SettingsDlg::slotUpdateRefreshRateLabel( int sliderValue ) +{ + const QString number = QString::number( sliderValueToRefreshRate(sliderValue) ); + switch(sliderValue) + { + case 0: + m_generalOptionsWidget->refreshRateLabel->setText( i18n("Lowest (%1 FPS)").arg(number) ); + break; + case 1: + m_generalOptionsWidget->refreshRateLabel->setText( i18n("Low (%1 FPS)").arg(number) ); + break; + case 2: + m_generalOptionsWidget->refreshRateLabel->setText( i18n("Medium (%1 FPS)").arg(number) ); + break; + case 3: + m_generalOptionsWidget->refreshRateLabel->setText( i18n("High (%1 FPS)").arg(number) ); + break; + case 4: + m_generalOptionsWidget->refreshRateLabel->setText( i18n("Highest (%1 FPS)").arg(number) ); + break; + default: + m_generalOptionsWidget->refreshRateLabel->setText( i18n("Unknown value") ); + break; + } + updateButtons(); +} + + +void SettingsDlg::slotUpdatePicProgrammerDescription() +{ + QString program = m_picProgrammerConfigWidget->kcfg_PicProgrammerProgram->currentText(); + + ProgrammerConfig config = m_pPicProgrammerSettings->config( program ); + QString description = config.description; + + bool customProgrammer = ! m_pPicProgrammerSettings->isPredefined( program ); + + QString executable = config.executable; + if ( executable.isEmpty() ) + executable = program.lower(); + + QString programLocation = KStandardDirs::findExe( executable ); + if ( programLocation.isNull() ) + description.prepend( i18n("<b>%1</b> cannot be found.<br>").arg( executable ) ); + else + description.prepend( i18n("<b>%1</b> found: %2<br>").arg( executable ).arg(programLocation) ); + + m_picProgrammerConfigWidget->m_pProgrammerDescription->setText( description ); + m_picProgrammerConfigWidget->removeButton->setEnabled( customProgrammer ); + + KLineEdit * edit; + +#define SETUP_COMMAND( name ) \ + edit = m_picProgrammerConfigWidget->name; \ + edit->setText( config.name ); \ + edit->setEnabled(customProgrammer); \ + QToolTip::add( edit, customProgrammer ? 0 : config.name ) + + SETUP_COMMAND( initCommand ); + SETUP_COMMAND( readCommand ); + SETUP_COMMAND( writeCommand ); + SETUP_COMMAND( verifyCommand ); + SETUP_COMMAND( blankCheckCommand ); + SETUP_COMMAND( eraseCommand ); + +#undef SETUP_COMMAND +} + + +void SettingsDlg::slotSaveCurrentProgrammerConfig() +{ + QString program = m_picProgrammerConfigWidget->kcfg_PicProgrammerProgram->currentText(); + + if ( m_pPicProgrammerSettings->isPredefined( program ) ) + return; + + ProgrammerConfig config; + + config.initCommand = m_picProgrammerConfigWidget->initCommand->text(); + config.readCommand = m_picProgrammerConfigWidget->readCommand->text(); + config.writeCommand = m_picProgrammerConfigWidget->writeCommand->text(); + config.verifyCommand = m_picProgrammerConfigWidget->verifyCommand->text(); + config.blankCheckCommand = m_picProgrammerConfigWidget->blankCheckCommand->text(); + config.eraseCommand = m_picProgrammerConfigWidget->eraseCommand->text(); + + m_pPicProgrammerSettings->saveConfig( program, config ); +} + + +void SettingsDlg::slotRemoveProgrammerConfig() +{ + KComboBox * combo = m_picProgrammerConfigWidget->kcfg_PicProgrammerProgram; + + QString program = combo->currentText(); + + KMessageBox::ButtonCode confirm = (KMessageBox::ButtonCode)KMessageBox::warningContinueCancel( this, i18n("Remove programmer configuration \"%1\"?").arg(program), i18n("Remove \"%1\"").arg(program), i18n("Remove") ); + if ( confirm == KMessageBox::Cancel ) + return; + + m_pPicProgrammerSettings->removeConfig( program ); + combo->removeItem( combo->currentItem() ); + slotUpdatePicProgrammerDescription(); +} + + +class NameValidator : public QValidator +{ + public: + NameValidator( QStringList unallowed ) + : QValidator(0) { + m_unallowed = unallowed; + } + + virtual State validate( QString & input, int & ) const { + return (input.isEmpty() || m_unallowed.contains( input.lower() )) ? Intermediate : Acceptable; + } + + protected: + QStringList m_unallowed; +}; + + +void SettingsDlg::slotAddProgrammerConfig() +{ + KComboBox * combo = m_picProgrammerConfigWidget->kcfg_PicProgrammerProgram; + + QStringList takenNames; + int count = combo->count(); + for ( int i = 0; i < count; ++i ) + takenNames << combo->text(i).lower(); + + NameValidator * nv = new NameValidator( takenNames ); + + bool ok = false; + QString name = KInputDialog::getText( i18n("Configuration Name"), i18n("Name"), 0, &ok, this, 0, nv ); + + delete nv; + + if (!ok) + return; + + ProgrammerConfig config; + config.executable = name.lower(); + + m_pPicProgrammerSettings->saveConfig( name, config ); + + combo->insertItem( name ); + combo->setCurrentItem( count ); + slotUpdatePicProgrammerDescription(); +} + + +int SettingsDlg::refreshRateToSliderValue( int refreshRate ) +{ + switch (refreshRate) + { + case 10: return 0; + case 25: return 1; + case 50: return 2; + case 75: return 3; + case 100: return 4; + default: return -1; + } +} + + +int SettingsDlg::sliderValueToRefreshRate( int sliderValue ) +{ + switch (sliderValue) + { + case 0: return 10; + case 1: return 25; + case 2: return 50; + case 3: return 75; + case 4: return 100; + default: return -1; + } +} + + +void SettingsDlg::updateSettings() +{ + KConfig * config = kapp->config(); + + KConfigSkeleton::ItemInt *item = dynamic_cast<KConfigSkeleton::ItemInt*>(KTLConfig::self()->findItem( "RefreshRate" )); + if ( !item ) + return; + + int newRefreshRate = sliderValueToRefreshRate(m_generalOptionsWidget->refreshRateSlider->value()); + + if ( newRefreshRate != KTLConfig::refreshRate() ) + { + item->setValue(newRefreshRate); + config->setGroup("WorkArea"); + if ( newRefreshRate != defaultRefreshRate ) + config->writeEntry("RefreshRate",newRefreshRate); + else + config->deleteEntry("RefreshRate"); + + emit settingsChanged(); + } + + QTimer::singleShot( 0, this, SLOT(slotUpdateSettings()) ); +} + + +void SettingsDlg::slotUpdateSettings() +{ + KConfig * config = kapp->config(); + + KConfigSkeleton::ItemString * item = dynamic_cast<KConfigSkeleton::ItemString*>(KTLConfig::self()->findItem( "PicProgrammerProgram" )); + if ( !item ) + return; + + KComboBox * combo = m_picProgrammerConfigWidget->kcfg_PicProgrammerProgram; + QString newProgram = combo->currentText(); + + if ( newProgram != KTLConfig::picProgrammerProgram() ) + { + item->setValue( newProgram ); + config->setGroup( "PicProgramming" ); + if ( newProgram != "picp" ) + config->writeEntry( "PicProgrammerProgram", newProgram ); + else + config->deleteEntry( "PicProgrammerProgram" ); + + emit settingsChanged(); + } + + m_pPicProgrammerSettings->save( config ); + + config->sync(); +} + + +void SettingsDlg::updateWidgets() +{ + m_generalOptionsWidget->refreshRateSlider->setValue( refreshRateToSliderValue( KTLConfig::refreshRate() ) ); + + m_pPicProgrammerSettings->load( kapp->config() ); + + QStringList programmerNames = m_pPicProgrammerSettings->configNames( false ); + + KComboBox * combo = m_picProgrammerConfigWidget->kcfg_PicProgrammerProgram; + combo->clear(); + combo->insertStringList( programmerNames ); + combo->setSizeLimit( programmerNames.size() ); + + QTimer::singleShot( 0, this, SLOT(slotUpdateWidgets()) ); +} + + +void SettingsDlg::slotUpdateWidgets() +{ + KComboBox * combo = m_picProgrammerConfigWidget->kcfg_PicProgrammerProgram; + + combo->setCurrentText( KTLConfig::picProgrammerProgram() ); + slotUpdatePicProgrammerDescription(); +} + + +void SettingsDlg::updateWidgetsDefault() +{ + m_generalOptionsWidget->refreshRateSlider->setValue( refreshRateToSliderValue( defaultRefreshRate ) ); + slotUpdatePicProgrammerDescription(); +} + + +bool SettingsDlg::hasChanged() +{ + if ( sliderValueToRefreshRate( m_generalOptionsWidget->refreshRateSlider->value() ) == KTLConfig::refreshRate() ) + return KConfigDialog::hasChanged(); + return true; +} + + +bool SettingsDlg::isDefault() +{ + if ( sliderValueToRefreshRate( m_generalOptionsWidget->refreshRateSlider->value() ) == defaultRefreshRate ) + return KConfigDialog::isDefault(); + + return false; +} + + +#include "settingsdlg.moc" diff --git a/src/gui/settingsdlg.h b/src/gui/settingsdlg.h new file mode 100644 index 0000000..1fbf966 --- /dev/null +++ b/src/gui/settingsdlg.h @@ -0,0 +1,69 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef SETTINGSDLG_H +#define SETTINGSDLG_H + +#include <kconfigdialog.h> +#include <qmap.h> + +class AsmFormattingWidget; +class GeneralOptionsWidget; +class GpasmSettingsWidget; +class LogicWidget; +class PicProgrammerConfigWidget; +class PicProgrammerSettings; +class SDCCOptionsWidget; + + +/** +@author David Saxton +*/ +class SettingsDlg : public KConfigDialog +{ + Q_OBJECT + public: + SettingsDlg( QWidget *parent, const char *name, KConfigSkeleton *config ); + ~SettingsDlg(); + + static int refreshRateToSliderValue( int refreshRate ); + static int sliderValueToRefreshRate( int sliderValue ); + + virtual void show(); + + public slots: + void slotUpdateRefreshRateLabel( int sliderValue ); + void slotUpdatePicProgrammerDescription(); + void slotAddProgrammerConfig(); + void slotRemoveProgrammerConfig(); + void slotSaveCurrentProgrammerConfig(); + + protected slots: + void slotUpdateSettings(); + void slotUpdateWidgets(); + + protected: + virtual void updateSettings(); + virtual void updateWidgets(); + virtual void updateWidgetsDefault(); + virtual bool hasChanged(); + virtual bool isDefault(); + + PicProgrammerSettings * m_pPicProgrammerSettings; + + GeneralOptionsWidget * m_generalOptionsWidget; + GpasmSettingsWidget * m_gpasmSettingsWidget; + SDCCOptionsWidget * m_sdccOptionsWidget; + AsmFormattingWidget * m_asmFormattingWidget; + LogicWidget * m_logicWidget; + PicProgrammerConfigWidget * m_picProgrammerConfigWidget; +}; + +#endif diff --git a/src/gui/symbolviewer.cpp b/src/gui/symbolviewer.cpp new file mode 100644 index 0000000..74d54e1 --- /dev/null +++ b/src/gui/symbolviewer.cpp @@ -0,0 +1,218 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "config.h" +#ifndef NO_GPSIM + +#include "gpsimprocessor.h" +#include "symbolviewer.h" + +#include <kcombobox.h> +#include <kconfig.h> +#include <kdebug.h> +#include <klocale.h> +#include <qlabel.h> +#include <qlayout.h> + +#include <assert.h> + +static const int NAME_COLUMN = 0; +static const int VALUE_COLUMN = 1; + + +//BEGIN class SymbolViewerItem +SymbolViewerItem::SymbolViewerItem( SymbolViewer * symbolViewer, RegisterInfo * registerInfo ) + : KListViewItem( symbolViewer->symbolList() ) +{ + assert(registerInfo); + m_pRegisterInfo = registerInfo; + m_pSymbolViewer = symbolViewer; + + setText( NAME_COLUMN, m_pRegisterInfo->name() ); +// setText( TYPE_COLUMN, RegisterInfo::toString( m_pRegisterInfo->type() ) ); + radixChanged(); // force update of displayed string + + connect( m_pRegisterInfo, SIGNAL(valueChanged(unsigned)), this, SLOT(valueChanged(unsigned)) ); + connect( m_pSymbolViewer, SIGNAL(valueRadixChanged(SymbolViewer::Radix)), this, SLOT(radixChanged()) ); + +} + + +void SymbolViewerItem::valueChanged( unsigned newValue ) +{ + setText( VALUE_COLUMN, m_pSymbolViewer->toDisplayString( newValue ) ); +} + + +void SymbolViewerItem::radixChanged() +{ + valueChanged( m_pRegisterInfo->value() ); +} +//END class SymbolViewerItem + + + +//BEGIN class SymbolView +SymbolViewer * SymbolViewer::m_pSelf = 0l; +SymbolViewer * SymbolViewer::self( KateMDI::ToolView * parent ) +{ + if (!m_pSelf) + { + assert (parent); + m_pSelf = new SymbolViewer(parent); + } + return m_pSelf; +} + +SymbolViewer::SymbolViewer( KateMDI::ToolView * parent ) + : QWidget( (QWidget*)parent ) +{ + QGridLayout * grid = new QGridLayout( this, 1, 1, 0, 6 ); + + m_pSymbolList = new KListView(this); + grid->addMultiCellWidget( m_pSymbolList, 0, 0, 0, 1 ); + + grid->addWidget( new QLabel( i18n("Value radix:"), this ), 1, 0 ); + + m_pRadixCombo = new KComboBox( false, this ); + grid->addWidget( m_pRadixCombo, 1, 1 ); + m_pRadixCombo->insertItem( i18n("Binary") ); + m_pRadixCombo->insertItem( i18n("Octal") ); + m_pRadixCombo->insertItem( i18n("Decimal") ); + m_pRadixCombo->insertItem( i18n("Hexadecimal") ); + m_valueRadix = Decimal; + m_pRadixCombo->setCurrentItem(2); + connect( m_pRadixCombo, SIGNAL(activated(int)), this, SLOT(selectRadix(int)) ); + + m_pGpsim = 0l; + m_pCurrentContext = 0l; + + m_pSymbolList->addColumn( i18n("Name") ); + m_pSymbolList->addColumn( i18n("Value") ); + m_pSymbolList->setFullWidth(true); + m_pSymbolList->setAllColumnsShowFocus( true ); +} + + +SymbolViewer::~SymbolViewer() +{ +} + + +void SymbolViewer::saveProperties( KConfig * config ) +{ + QString oldGroup = config->group(); + + config->setGroup( "SymbolEditor" ); + config->writeEntry( "Radix", m_valueRadix ); + + config->setGroup( oldGroup ); +} + + +void SymbolViewer::readProperties( KConfig * config ) +{ + QString oldGroup = config->group(); + + config->setGroup( "SymbolEditor" ); + m_valueRadix = (SymbolViewer::Radix)config->readNumEntry( "Radix", Decimal ); + + int pos = 4; + switch ( m_valueRadix ) + { + case Binary: + pos--; + case Octal: + pos--; + case Decimal: + pos--; + case Hexadecimal: + pos--; + } + m_pRadixCombo->setCurrentItem( pos ); + + config->setGroup( oldGroup ); +} + + +void SymbolViewer::setContext( GpsimProcessor * gpsim ) +{ + RegisterSet * registerSet = gpsim ? gpsim->registerMemory() : 0l; + + if ( registerSet == m_pCurrentContext ) + return; + + m_pSymbolList->clear(); + m_pGpsim = gpsim; + m_pCurrentContext = registerSet; + + if (!m_pCurrentContext) + return; + + connect( gpsim, SIGNAL(destroyed()), m_pSymbolList, SLOT(clear()) ); + + unsigned count = m_pCurrentContext->size(); + for ( unsigned i = 0; i < count; ++i ) + { + RegisterInfo * reg = m_pCurrentContext->fromAddress(i); + + if ( (reg->type() == RegisterInfo::Generic) || + (reg->type() == RegisterInfo::Invalid) ) + continue; + + new SymbolViewerItem( this, reg ); + } +} + + +void SymbolViewer::selectRadix( int selectIndex ) +{ + if ( (selectIndex<0) || (selectIndex>3) ) + { + kdWarning() << k_funcinfo << "Invalid select position for radix: " << selectIndex << endl; + return; + } + + Radix radii[] = { Binary, Octal, Decimal, Hexadecimal }; + Radix newRadix = radii[selectIndex]; + + if ( newRadix == m_valueRadix ) + return; + + m_valueRadix = newRadix; + + emit valueRadixChanged(m_valueRadix); +} + + +QString SymbolViewer::toDisplayString( unsigned value ) const +{ + switch ( m_valueRadix ) + { + case Binary: + return QString::number( value, 2 ).rightJustify( 8, '0', false ); + + case Octal: + return "0" + QString::number( value, 8 ); + + case Decimal: + return QString::number( value, 10 ); + + case Hexadecimal: + return "0x" + QString::number( value, 16 ); + } + + return "?"; +} +//END class SymbolView + +#include "symbolviewer.moc" + +#endif diff --git a/src/gui/symbolviewer.h b/src/gui/symbolviewer.h new file mode 100644 index 0000000..9a09cb6 --- /dev/null +++ b/src/gui/symbolviewer.h @@ -0,0 +1,103 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "config.h" +#ifndef NO_GPSIM + +#ifndef SYMBOLVIEWER_H +#define SYMBOLVIEWER_H + +#include <klistview.h> +#include <qguardedptr.h> + +class KComboBox; +class RegisterInfo; +class RegisterSet; +class SymbolViewer; +namespace KateMDI { class ToolView; } + + +/** +@author David Saxton +*/ +class SymbolViewer : public QWidget +{ + Q_OBJECT + public: + static SymbolViewer * self( KateMDI::ToolView * parent = 0l ); + static QString toolViewIdentifier() { return "SymbolViewer"; } + ~SymbolViewer(); + + enum Radix + { + Binary = 2, + Octal = 8, + Decimal = 10, + Hexadecimal = 16 + }; + + Radix valueRadix() const { return m_valueRadix; } + + KListView * symbolList() const { return m_pSymbolList; } + /** + * Write the current properties (such as currently selected radix) to + * the config. + */ + void saveProperties( KConfig * config ); + /** + * Reads the properties (such as the last selected radix) from the + * config file. + */ + void readProperties( KConfig * config ); + + void setContext( GpsimProcessor * gpsim ); + /** + * Converts the value to a string for display according to the currently + * selected radix. + */ + QString toDisplayString( unsigned value ) const; + + signals: + void valueRadixChanged( SymbolViewer::Radix newRadix ); + + public slots: + void selectRadix( int selectIndex ); + + protected: + QGuardedPtr<GpsimProcessor> m_pGpsim; + RegisterSet * m_pCurrentContext; + KListView * m_pSymbolList; + Radix m_valueRadix; + + private: + SymbolViewer( KateMDI::ToolView * parent ); + static SymbolViewer * m_pSelf; + KComboBox * m_pRadixCombo; +}; + + +class SymbolViewerItem : public QObject, public KListViewItem +{ + Q_OBJECT + public: + SymbolViewerItem( SymbolViewer * symbolViewer, RegisterInfo * registerInfo ); + + public slots: + void valueChanged( unsigned newValue ); + void radixChanged(); + + protected: + RegisterInfo * m_pRegisterInfo; + SymbolViewer * m_pSymbolViewer; +}; + +#endif + +#endif diff --git a/src/icndocument.cpp b/src/icndocument.cpp new file mode 100644 index 0000000..d220765 --- /dev/null +++ b/src/icndocument.cpp @@ -0,0 +1,1385 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "canvasmanipulator.h" +#include "component.h" +#include "connector.h" +#include "conrouter.h" +#include "cnitemgroup.h" +#include "ecnode.h" +#include "flowcontainer.h" +#include "fpnode.h" +#include "icndocument.h" +#include "icnview.h" +#include "itemdocumentdata.h" +#include "itemlibrary.h" +#include "ktechlab.h" +#include "nodegroup.h" + +#include <kapplication.h> +#include <kdebug.h> +#include <qclipboard.h> +#include <qtimer.h> + +ICNDocument::ICNDocument( const QString &caption, KTechlab *ktechlab, const char *name ) + : ItemDocument( caption, ktechlab, name ), + m_cells(0l) +{ + m_canvas->retune(48); + m_selectList = new CNItemGroup(this); + + createCellMap(); + + m_cmManager->addManipulatorInfo( CMItemMove::manipulatorInfo() ); + m_cmManager->addManipulatorInfo( CMAutoConnector::manipulatorInfo() ); + m_cmManager->addManipulatorInfo( CMManualConnector::manipulatorInfo() ); +} + + +ICNDocument::~ICNDocument() +{ + m_bDeleted = true; + + // Go to hell, QCanvas. I'm in charge of what gets deleted. + QCanvasItemList all = m_canvas->allItems(); + const QCanvasItemList::Iterator end = all.end(); + for ( QCanvasItemList::Iterator it= all.begin(); it != end; ++it ) + (*it)->setCanvas(0l); + + // Remove all items from the canvas + selectAll(); + deleteSelection(); + + // Delete anything that got through the above couple of lines + ConnectorList connectorsToDelete = m_connectorList; + const ConnectorList::iterator connectorListEnd = connectorsToDelete.end(); + for ( ConnectorList::iterator it = connectorsToDelete.begin(); it != connectorListEnd; ++it ) + delete *it; + + NodeList nodesToDelete = m_nodeList; + const NodeList::iterator nodeListEnd = nodesToDelete.end(); + for ( NodeList::iterator it = nodesToDelete.begin(); it != nodeListEnd; ++it ) + delete *it; + + GuardedNodeGroupList ngToDelete = m_nodeGroupList; + const GuardedNodeGroupList::iterator nglEnd = ngToDelete.end(); + for ( GuardedNodeGroupList::iterator it = ngToDelete.begin(); it != nglEnd; ++it ) + delete *it; + + delete m_cells; + m_cells = 0l; + delete m_selectList; + m_selectList = 0l; +} + + +View *ICNDocument::createView( ViewContainer *viewContainer, uint viewAreaId, const char *name ) +{ + ICNView *icnView = new ICNView( this, viewContainer, viewAreaId, name ); + handleNewView(icnView); + return icnView; +} + + +ItemGroup* ICNDocument::selectList() const +{ + return m_selectList; +} + + +void ICNDocument::fillContextMenu( const QPoint &pos ) +{ + ItemDocument::fillContextMenu(pos); + slotInitItemActions( dynamic_cast<CNItem*>(m_selectList->activeItem()) ); +} + + +CNItem* ICNDocument::cnItemWithID( const QString &id ) +{ + return dynamic_cast<CNItem*>(itemWithID(id)); +} + + +Node *ICNDocument::nodeWithID( const QString &id ) +{ + const NodeList::iterator end = m_nodeList.end(); + for ( NodeList::iterator it = m_nodeList.begin(); it != end; ++it ) + { + if ( (*it)->id() == id ) + return *it; + } + return 0L; +} + + +Connector *ICNDocument::connectorWithID( const QString &id ) +{ + const ConnectorList::iterator end = m_connectorList.end(); + for ( ConnectorList::iterator it = m_connectorList.begin(); it != end; ++it ) + { + if ( (*it)->id() == id ) + return *it; + } + return 0L; +} + + +FlowContainer *ICNDocument::flowContainer( const QPoint &pos ) +{ + QCanvasItemList collisions = m_canvas->collisions(pos); + FlowContainer *flowContainer = 0l; + int currentLevel = -1; + const QCanvasItemList::iterator end = collisions.end(); + for ( QCanvasItemList::iterator it = collisions.begin(); it != end; ++it ) + { + if ( FlowContainer *container = dynamic_cast<FlowContainer*>(*it) ) + { + if ( container->level() > currentLevel && !m_selectList->contains(container) ) + { + currentLevel = container->level(); + flowContainer = container; + } + } + } + return flowContainer; +} + + +bool ICNDocument::canConnect( QCanvasItem *qcanvasItem1, QCanvasItem *qcanvasItem2 ) const +{ + // Rough outline of what can and can't connect: + // * At most three connectors to a node + // * Can't have connectors going between different levels (e.g. can't have + // a connector coming outside a FlowContainer from inside). + // * Can't have more than one route between any two nodes + // * In all connections between nodes, must have at least one input and one + // output node at the ends. + + Node *startNode = dynamic_cast<Node*>(qcanvasItem1); + Node *endNode = dynamic_cast<Node*>(qcanvasItem2); + + if ( (startNode && startNode->numCon( true, false ) > 2) || (endNode && endNode->numCon( true, false ) > 2) ) + return false; + + + Connector *startConnector = dynamic_cast<Connector*>(qcanvasItem1); + Connector *endConnector = dynamic_cast<Connector*>(qcanvasItem2); + + // Can't have T- or I- junction in PinMapEditor document + if ( type() == Document::dt_pinMapEditor && (startConnector || endConnector) ) + return false; + + // Can't have I-junction in flowcode document + if ( type() == Document::dt_flowcode && startConnector && endConnector ) + return false; + + + //BEGIN Change connectors to nodes + Node * startNode1 = 0l; + Node * startNode2 = 0l; + if (startConnector) + { + startNode1 = startConnector->startNode(); + startNode2 = startConnector->endNode(); + + if ( !startNode1 || !startNode2 ) + return false; + } + else if (!startNode) + return false; + + Node * endNode1 = 0l; + Node * endNode2 = 0l; + if (endConnector) + { + endNode1 = endConnector->startNode(); + endNode2 = endConnector->endNode(); + + if ( !endNode1 || !endNode2 ) + return false; + } + else if ( !endNode ) + return false; + + Node * start[3]; + start[0] = startNode; + start[1] = startNode1; + start[2] = startNode2; + + Node * end[3]; + end[0] = endNode; + end[1] = endNode1; + end[2] = endNode2; + //END Change connectors to nodes + + + //BEGIN Check nodes aren't already connected + for ( unsigned i = 0; i < 3; i++ ) + { + for ( unsigned j = 0; j < 3; j++ ) + { + if ( start[i] && end[j] && start[i]->isConnected(end[j]) ) + return false; + } + } + //END Check nodes aren't already connected together + + + //BEGIN Check we have appropriate input and output allowance + if ( type() == Document::dt_flowcode ) + { + if ( startNode1 && startNode2 && endNode1 && endNode2 ) + { + // Can't have I-configuration + return false; + } + + if ( startNode && endNode ) + { + // Nice and easy straight line to check + + if ( !startNode->acceptInput() && !endNode->acceptInput() ) + return false; + + if ( !startNode->acceptOutput() && !endNode->acceptOutput() ) + return false; + } + + else + { + // We're in a T-configuration, we can only make this if the base of + // the T is an output + Node * base = startNode ? startNode : endNode; + if ( !base->acceptOutput() ) + return false; + } + } + //END Check we have appropriate input and output allowance + + + //BEGIN Simple level check + for ( unsigned i = 0; i < 3; i++ ) + { + for ( unsigned j = 0; j < 3; j++ ) + { + if ( start[i] && end[j] && start[i]->level() != end[j]->level() ) + return false; + } + } + //END Simple level check + + + //BEGIN Advanced level check + CNItem * startParentItem[3]; + for ( unsigned i = 0; i < 3; i++ ) + startParentItem[i] = start[i] ? start[i]->parentItem() : 0l; + + CNItem * endParentItem[3]; + for ( unsigned i = 0; i < 3; i++ ) + endParentItem[i] = end[i] ? end[i]->parentItem() : 0l; + + Item * container[6] = {0l}; + + for ( unsigned i = 0; i < 3; i++ ) + { + if (startParentItem[i]) + { + int dl = start[i]->level() - startParentItem[i]->level(); + if ( dl == 0 ) + container[i] = startParentItem[i]->parentItem(); + else if ( dl == 1 ) + container[i] = startParentItem[i]; + else + kdError() << k_funcinfo << " start, i="<<i<<" dl="<<dl<<endl; + } + if (endParentItem[i]) + { + int dl = end[i]->level() - endParentItem[i]->level(); + if ( dl == 0 ) + container[i+3] = endParentItem[i]->parentItem(); + else if ( dl == 1 ) + container[i+3] = endParentItem[i]; + else + kdError() << k_funcinfo << " end, i="<<i<<" dl="<<dl<<endl; + } + } + + // Everything better well have the same container... + for ( unsigned i = 0; i < 6; ++i ) + { + for ( unsigned j = 0; j < i; ++j ) + { + Node * n1 = i < 3 ? start[i] : end[i-3]; + Node * n2 = j < 3 ? start[j] : end[j-3]; + if ( n1 && n2 && (container[i] != container[j]) ) + return false; + } + } + //END Advanced level check + + + // Well, it looks like we can, afterall, connect them... + return true; +} + + + +Connector * ICNDocument::createConnector( Node *startNode, Node *endNode, QPointList *pointList ) +{ + if ( !canConnect( startNode, endNode ) ) + return 0l; + + QPointList autoPoints; + if (!pointList) + { + addAllItemConnectorPoints(); + ConRouter cr(this); + cr.mapRoute( int(startNode->x()), int(startNode->y()), int(endNode->x()), int(endNode->y()) ); + autoPoints = cr.pointList(false); + pointList = &autoPoints; + } + + Connector * con = 0l; + + // Check if we need to swap the ends around, and create the connector + if ( endNode->type() == Node::fp_out ) + con = createConnector( endNode->id(), startNode->id(), pointList ); + else + con = createConnector( startNode->id(), endNode->id(), pointList ); + + bool startInGroup = deleteNodeGroup(startNode); + bool endInGroup = deleteNodeGroup(endNode); + if ( startInGroup || endInGroup ) + { + NodeGroup *ng = createNodeGroup(startNode); + ng->addNode( endNode, true ); + ng->init(); + } + + flushDeleteList(); + return con; +} + +Connector * ICNDocument::createConnector( Node *node, Connector *con, const QPoint &pos2, QPointList *pointList ) +{ + if ( !canConnect( node, con ) ) + return 0l; + + Node *conStartNode = con->startNode(); + Node *conEndNode = con->endNode(); + + const bool usedManual = con->usesManualPoints(); + + Node *newNode = 0l; + if ( type() == Document::dt_circuit ) + newNode = new ECNode( this, Node::ec_junction, Node::dir_right, pos2 ); + + else if ( type() == Document::dt_flowcode ) + newNode = new FPNode( this, Node::fp_junction, Node::dir_right, pos2 ); + + else + return 0l; + + QPointList autoPoints; + if (!pointList) + { + addAllItemConnectorPoints(); + ConRouter cr(this); + cr.mapRoute( int(node->x()), int(node->y()), pos2.x(), pos2.y() ); + autoPoints = cr.pointList(false); + pointList = &autoPoints; + } + + QValueList<QPointList> oldConPoints = con->splitConnectorPoints(pos2); + con->hide(); + + // The actual new connector + Connector *new1 = newNode->createInputConnector(node); + node->addOutputConnector(new1); + new1->setRoutePoints(*pointList,usedManual); + + // The two connectors formed from the original one when split + Connector *new2 = newNode->createInputConnector(conStartNode); + conStartNode->addOutputConnector(new2); + new2->setRoutePoints( *oldConPoints.at(0), usedManual ); + + Connector *new3 = conEndNode->createInputConnector(newNode); + newNode->addOutputConnector(new3); + new3->setRoutePoints( *oldConPoints.at(1), usedManual ); + + // Avoid flicker: tell them to update their draw lists now + con->updateConnectorPoints(false); + new1->updateDrawList(); + new2->updateDrawList(); + new3->updateDrawList(); + + // Now it's safe to remove the connector... + con->removeConnector(); + flushDeleteList(); + + deleteNodeGroup(conStartNode); + deleteNodeGroup(conEndNode); + createNodeGroup(newNode)->init(); + + return new1; +} + +Connector * ICNDocument::createConnector( Connector *con1, Connector *con2, const QPoint &pos1, const QPoint &pos2, QPointList *pointList ) +{ + if ( !canConnect( con1, con2 ) ) + return 0l; + + const bool con1UsedManual = con1->usesManualPoints(); + const bool con2UsedManual = con2->usesManualPoints(); + + QValueList<QPointList> oldCon1Points = con1->splitConnectorPoints(pos1); + QValueList<QPointList> oldCon2Points = con2->splitConnectorPoints(pos2); + + Node *node1a = con1->startNode(); + Node *node1b = con1->endNode(); + + Node *node2a = con2->startNode(); + Node *node2b = con2->endNode(); + + if ( !node1a || !node1b || !node2a || !node2b ) + return 0l; + + con1->hide(); + con2->hide(); + + if ( type() != Document::dt_circuit ) + return 0l; + + ECNode * newNode1 = new ECNode( this, Node::ec_junction, Node::dir_right, pos1 ); + ECNode * newNode2 = new ECNode( this, Node::ec_junction, Node::dir_right, pos2 ); + + Connector *con1a = newNode1->createInputConnector(node1a); + node1a->addOutputConnector(con1a); + Connector *con1b = newNode1->createInputConnector(node1b); + node1b->addOutputConnector(con1b); + + Connector *newCon = newNode1->createInputConnector(newNode2); + newNode2->addOutputConnector(newCon); + + Connector *con2a = node2a->createInputConnector(newNode2); + newNode2->addOutputConnector(con2a); + Connector *con2b = node2b->createInputConnector(newNode2); + newNode2->addOutputConnector(con2b); + + if ( !con1a || !con1b || !con2a || !con2b ) + { + // This should never happen, as the canConnect function should strictly + // determine whether the connectors could be created before hand. + kdWarning() << k_funcinfo << "Not all the connectors were created, this should never happen" << endl; + + if (con1a) + con1a->removeConnector(); + + if (con1b) + con1b->removeConnector(); + + if (con2a) + con2a->removeConnector(); + + if (con2b) + con2b->removeConnector(); + + newNode1->removeNode(); + newNode2->removeNode(); + + flushDeleteList(); + return 0l; + } + + con1a->setRoutePoints( *oldCon1Points.at(0), con1UsedManual ); + con1b->setRoutePoints( *oldCon1Points.at(1), con1UsedManual ); + + con2a->setRoutePoints( *oldCon2Points.at(0), con2UsedManual ); + con2b->setRoutePoints( *oldCon2Points.at(1), con2UsedManual ); + + QPointList autoPoints; + if (!pointList) + { + addAllItemConnectorPoints(); + ConRouter cr(this); + cr.mapRoute( pos1.x(), pos1.y(), pos2.x(), pos2.y() ); + autoPoints = cr.pointList(false); + pointList = &autoPoints; + } + newCon->setRoutePoints(*pointList,true); + + + // Avoid flicker: tell them to update their draw lists now + con1->updateConnectorPoints(false); + con2->updateConnectorPoints(false); + newCon->updateDrawList(); + con1a->updateDrawList(); + con1b->updateDrawList(); + con2a->updateDrawList(); + con2b->updateDrawList(); + + + // Now it's safe to remove the connectors + con1->removeConnector(); + con2->removeConnector(); + + flushDeleteList(); + + deleteNodeGroup(node1a); + deleteNodeGroup(node1b); + deleteNodeGroup(node2a); + deleteNodeGroup(node2b); + NodeGroup *ng = createNodeGroup(newNode1); + ng->addNode( newNode2, true ); + ng->init(); + + return newCon; +} + + +NodeGroup* ICNDocument::createNodeGroup( Node *node ) +{ + if ( !node || node->isChildNode() ) { + return 0l; + } + + const GuardedNodeGroupList::iterator end = m_nodeGroupList.end(); + for ( GuardedNodeGroupList::iterator it = m_nodeGroupList.begin(); it != end; ++it ) + { + if ( *it && (*it)->contains(node) ) { + return *it; + } + } + + NodeGroup *group = new NodeGroup(this); + m_nodeGroupList += group; + group->addNode( node, true ); + + return group; +} + + +bool ICNDocument::deleteNodeGroup( Node *node ) +{ + if ( !node || node->isChildNode() ) { + return false; + } + + const GuardedNodeGroupList::iterator end = m_nodeGroupList.end(); + for ( GuardedNodeGroupList::iterator it = m_nodeGroupList.begin(); it != end; ++it ) + { + if ( *it && (*it)->contains(node) ) + { + delete *it; + m_nodeGroupList.remove(it); + return true; + } + } + + return false; +} + + +void ICNDocument::slotRequestAssignNG() +{ + requestEvent( ItemDocumentEvent::UpdateNodeGroups ); +} + + +void ICNDocument::slotAssignNodeGroups() +{ + const GuardedNodeGroupList::iterator nglEnd = m_nodeGroupList.end(); + for ( GuardedNodeGroupList::iterator it = m_nodeGroupList.begin(); it != nglEnd; ++it ) + delete *it; + m_nodeGroupList.clear(); + + const NodeList::iterator end = m_nodeList.end(); + for ( NodeList::iterator it = m_nodeList.begin(); it != end; ++it ) + { + NodeGroup *ng = createNodeGroup(*it); + if (ng) + ng->init(); + } + + // We've destroyed the old node groups, so any collapsed flowcontainers + // containing new node groups need to update them to make them invisible. + const ItemList::const_iterator itemListEnd = m_itemList.end(); + for ( ItemList::const_iterator it = m_itemList.begin(); it != itemListEnd; ++it ) + { + if ( FlowContainer * fc = dynamic_cast<FlowContainer*>((Item*)*it) ) + fc->updateContainedVisibility(); + } +} + + +void ICNDocument::getTranslatable( const ItemList & itemList, ConnectorList * fixedConnectors, ConnectorList * translatableConnectors, NodeGroupList * translatableNodeGroups ) +{ + ConnectorList tempCL1; + if ( !fixedConnectors ) + fixedConnectors = &tempCL1; + + ConnectorList tempCL2; + if ( !translatableConnectors ) + translatableConnectors = &tempCL2; + + NodeGroupList tempNGL; + if ( !translatableNodeGroups ) + translatableNodeGroups = &tempNGL; + + // We record the connectors attached to the items, and + // the number of times an item in the list is connected to + // it - i.e. 1 or 2. For those with 2, it is safe to update their + // route as it simply involves shifting the route + typedef QMap< Connector*, int > ConnectorMap; + ConnectorMap fixedConnectorMap; + + // This list of nodes is built up, used for later in determining fixed NodeGroups + NodeList itemNodeList; + { + const ItemList::const_iterator itemListEnd = itemList.end(); + for ( ItemList::const_iterator it = itemList.begin(); it != itemListEnd; ++it ) + { + CNItem *cnItem = dynamic_cast<CNItem*>((Item*)*it); + if ( !cnItem || !cnItem->canvas() ) + continue; + + NodeMap nodeMap = cnItem->nodeMap(); + const NodeMap::iterator nlEnd = nodeMap.end(); + for ( NodeMap::iterator nlIt = nodeMap.begin(); nlIt != nlEnd; ++nlIt ) + { + itemNodeList.append(nlIt.data().node); + } + + ConnectorList conList = cnItem->connectorList(); + conList.remove((Connector*)0l); + const ConnectorList::iterator clEnd = conList.end(); + for ( ConnectorList::iterator clit = conList.begin(); clit != clEnd; ++clit ) + { + ConnectorMap::iterator cit = fixedConnectorMap.find(*clit); + if ( cit != fixedConnectorMap.end() ) { + cit.data()++; + } else { + fixedConnectorMap[*clit] = 1; + } + } + } + } + + // We now look through the NodeGroups to see if we have all the external + // nodes for a given nodeGroup - if so, then the connectors in the fixed + // connectors are ok to be moved + ConnectorList fixedNGConnectors; + { + translatableNodeGroups->clear(); + const GuardedNodeGroupList::const_iterator end = m_nodeGroupList.end(); + for ( GuardedNodeGroupList::const_iterator it = m_nodeGroupList.begin(); it != end; ++it ) + { + NodeGroup *ng = *it; + if (!ng) + continue; + + NodeList externalNodeList = ng->externalNodeList(); + const NodeList::iterator itemNodeListEnd = itemNodeList.end(); + for ( NodeList::iterator inlIt = itemNodeList.begin(); inlIt != itemNodeListEnd; ++inlIt ) + externalNodeList.remove(*inlIt); + + if ( externalNodeList.isEmpty() ) + { + translatableNodeGroups->append(ng); + + const ConnectorList ngConnectorList = ng->connectorList(); + const ConnectorList::const_iterator ngConnectorListEnd = ngConnectorList.end(); + for ( ConnectorList::const_iterator ngclIt = ngConnectorList.begin(); ngclIt != ngConnectorListEnd; ++ngclIt ) + { + if (*ngclIt) + fixedNGConnectors += *ngclIt; + } + } + } + } + + translatableConnectors->clear(); + const ConnectorMap::iterator fcEnd = fixedConnectorMap.end(); + for ( ConnectorMap::iterator it = fixedConnectorMap.begin(); it != fcEnd; ++it ) + { + // We allow it to be fixed if it is connected to two of the CNItems in the + // select list, or is connected to itself (hence only appears to be connected to one, + // but is fixed anyway + Node *startNode = it.key()->endNode(); + Node *endNode = it.key()->startNode(); + if ( (it.data() > 1) || + (startNode && endNode && startNode->parentItem() == endNode->parentItem()) ) + { + translatableConnectors->append( const_cast<Connector*>(it.key()) ); + } + else if ( !fixedNGConnectors.contains(it.key()) && !fixedConnectors->contains(it.key()) ) + { + fixedConnectors->append(it.key()); + } + } +} + + +void ICNDocument::addCPenalty( int x, int y, int score ) +{ + if ( isValidCellReference(x,y) ) + { + (*m_cells)[x][y].Cpenalty += score; + } +} + + +void ICNDocument::createCellMap() +{ + unsigned newCellsX = QMAX( canvas()->width()/cellSize, 1 ); + unsigned newCellsY = QMAX( canvas()->height()/cellSize, 1 ); + + if ( m_cells && newCellsX == m_cellsX && newCellsY == m_cellsY ) + return; + + const ItemList::iterator ciEnd = m_itemList.end(); + for ( ItemList::iterator it = m_itemList.begin(); it != ciEnd; ++it ) + { + CNItem *cnItem = dynamic_cast<CNItem*>((Item*)(*it)); + if (cnItem) + cnItem->updateConnectorPoints(false); + } + const ConnectorList::iterator conEnd = m_connectorList.end(); + for ( ConnectorList::iterator it = m_connectorList.begin(); it != conEnd; ++it ) + { + (*it)->updateConnectorPoints(false); + } + + delete m_cells; + m_cellsX = newCellsX; + m_cellsY = newCellsY; + m_cells = new Cells( m_cellsX, m_cellsY ); + + for ( ConnectorList::iterator it = m_connectorList.begin(); it != conEnd; ++it ) + (*it)->updateConnectorPoints(true); +} + + +int ICNDocument::gridSnap( int pos ) +{ + return pos-(pos%8)+4; +// return int((floor(pos/8))*8)+4; +} + +QPoint ICNDocument::gridSnap( const QPoint &pos ) +{ + return QPoint( gridSnap( pos.x() ), gridSnap( pos.y() ) ); +} + + +void ICNDocument::appendDeleteList( QCanvasItem *qcanvasItem ) +{ + if ( !qcanvasItem || m_itemDeleteList.findIndex(qcanvasItem) != -1 ) { + return; + } + + m_itemDeleteList.append(qcanvasItem); + + if ( qcanvasItem->rtti() == ItemDocument::RTTI::Node ) + { + Node *node = dynamic_cast<Node*>(qcanvasItem); + node->removeNode(); + } + else if ( qcanvasItem->rtti() == ItemDocument::RTTI::CNItem || + qcanvasItem->rtti() == ItemDocument::RTTI::DrawPart ) + { + Item *item = dynamic_cast<Item*>(qcanvasItem); + item->removeItem(); + } + else if ( qcanvasItem->rtti() == ItemDocument::RTTI::Connector || + qcanvasItem->rtti() == ItemDocument::RTTI::ConnectorLine ) + { + Connector *connector = dynamic_cast<Connector*>(qcanvasItem); + if (!connector) + connector = (dynamic_cast<ConnectorLine*>(qcanvasItem))->parent(); + connector->removeConnector(); + } + else + { + kdDebug() << k_funcinfo << "unrecognised QCanvasItem rtti " << QString::number(qcanvasItem->rtti()) << endl; + } +} + +void ICNDocument::flushDeleteList() +{ + // Remove duplicate items in the delete list + QCanvasItemList::iterator end = m_itemDeleteList.end(); + for ( QCanvasItemList::iterator it = m_itemDeleteList.begin(); it != end; ++it ) + { + if ( *it && m_itemDeleteList.contains(*it) > 1 ) { + *it = 0l; + } + } + m_itemDeleteList.remove(0l); + + end = m_itemDeleteList.end(); + for ( QCanvasItemList::iterator it = m_itemDeleteList.begin(); it != end; ++it ) + { + QCanvasItem *qcanvasItem = *it; + m_selectList->removeQCanvasItem(*it); + + if ( Item *item = dynamic_cast<Item*>(qcanvasItem) ) + m_itemList.remove(item); + + else if ( qcanvasItem->rtti() == ItemDocument::RTTI::Node ) + m_nodeList.remove( dynamic_cast<Node*>(qcanvasItem) ); + + else if ( qcanvasItem->rtti() == ItemDocument::RTTI::Connector ) + m_connectorList.remove( dynamic_cast<Connector*>(qcanvasItem) ); + + else + kdError() << k_funcinfo << "Unknown qcanvasItem! "<<qcanvasItem << endl; + + qcanvasItem->setCanvas(0l); + + delete qcanvasItem; + *it = 0l; + } + +// // Check connectors for merging + bool doneJoin = false; + const NodeList::iterator nlEnd = m_nodeList.end(); + for ( NodeList::iterator it = m_nodeList.begin(); it != nlEnd; ++it ) + { + (*it)->removeNullConnectors(); + int conCount = (*it)->inputConnectorList().count() + (*it)->outputConnectorList().count(); + if ( conCount == 2 && !(*it)->parentItem() ) + { + if ( joinConnectors(*it) ) + doneJoin = true; + } + } + + if (doneJoin) + flushDeleteList(); + + requestRerouteInvalidatedConnectors(); +} + + + +bool ICNDocument::joinConnectors( Node *node ) +{ + // We don't want to destroy the node if it has a parent + if ( node->parentItem() ) + return false; + + node->removeNullConnectors(); + + int conCount = node->inputConnectorList().count() + node->outputConnectorList().count(); + if ( conCount != 2 ) + return false; + + Connector *con1, *con2; + Node *startNode, *endNode; + QPointList conPoints; + + + if ( node->inputConnectorList().count() == 0 ) + { + // Both connectors emerge from node - output - i.e. node is pure start node + con1 = *node->outputConnectorList().at(0); + con2 = *node->outputConnectorList().at(1); + if ( con1 == con2 ) { + return false; + } + + startNode = con1->endNode(); + endNode = con2->endNode(); + conPoints = con1->connectorPoints(true) + con2->connectorPoints(false); + } + else if ( node->inputConnectorList().count() == 1 ) + { + // Ont input, one output + con1 = *node->inputConnectorList().at(0); + con2 = *node->outputConnectorList().at(0); + if ( con1 == con2 ) { + return false; + } + + startNode = con1->startNode(); + endNode = con2->endNode(); + conPoints = con1->connectorPoints(false) + con2->connectorPoints(false); + } + else + { + // Both input - i.e. node is pure end node + con1 = *node->inputConnectorList().at(0); + con2 = *node->inputConnectorList().at(1); + if ( con1 == con2 ) { + return false; + } + + startNode = con1->startNode(); + endNode = con2->startNode(); + conPoints = con1->connectorPoints(false) + con2->connectorPoints(true); + } + + if ( !startNode || !endNode ) + return false; + + Connector *newCon = endNode->createInputConnector(startNode); + if (!newCon) + return false; + + startNode->addOutputConnector(newCon); + newCon->setRoutePoints( conPoints, con1->usesManualPoints() || con2->usesManualPoints() ); + + // Avoid flicker: update draw lists now + con1->updateConnectorPoints(false); + con2->updateConnectorPoints(false); + newCon->updateDrawList(); + + node->removeNode(); + con1->removeConnector(); + con2->removeConnector(); + + return true; +} + + +bool ICNDocument::registerItem( QCanvasItem *qcanvasItem ) +{ + if (!qcanvasItem) + return false; + + if ( !ItemDocument::registerItem(qcanvasItem) ) + { + switch (qcanvasItem->rtti()) + { + case ItemDocument::RTTI::Node: + { + Node *node = (Node*)qcanvasItem; + m_nodeList.append(node); + connect( node, SIGNAL(removed(Node*)), this, SLOT(requestRerouteInvalidatedConnectors()) ); + emit nodeAdded(node); + break; + } + case ItemDocument::RTTI::Connector: + { + Connector *connector = dynamic_cast<Connector*>(qcanvasItem); + m_connectorList.append(connector); + connect( connector, SIGNAL(removed(Connector*)), this, SLOT(requestRerouteInvalidatedConnectors()) ); + emit connectorAdded(connector); + break; + } + default: + { + kdError() << k_funcinfo << "Unrecognised item rtti"<<endl; + return false; + } + } + } + + requestRerouteInvalidatedConnectors(); + + return true; +} + + +void ICNDocument::copy() +{ + if ( m_selectList->isEmpty() ) + return; + + ItemDocumentData data( type() ); + + // We only want to copy the connectors who have all ends attached to something in the selection + ConnectorList connectorList = m_selectList->connectors(false); + + typedef QMap< Node*, ConnectorList > NCLMap; + NCLMap nclMap; + + ConnectorList::iterator end = connectorList.end(); + for ( ConnectorList::iterator it = connectorList.begin(); it != end; ++it ) + { + Node *startNode = (*it)->startNode(); + if ( startNode && !startNode->isChildNode() ) + nclMap[startNode].append(*it); + + Node *endNode = (*it)->endNode(); + if ( endNode && !endNode->isChildNode() ) + nclMap[endNode].append(*it); + } + + NodeList nodeList; + // Remove those connectors (and nodes) which are dangling on an orphan node + NCLMap::iterator nclEnd = nclMap.end(); + for ( NCLMap::iterator it = nclMap.begin(); it != nclEnd; ++it ) + { + if ( it.data().size() > 1 ) + nodeList.append(it.key()); + + else if ( it.data().size() > 0 ) + connectorList.remove( it.data().at(0) ); + } + + data.addItems( m_selectList->items(false) ); + data.addNodes( nodeList ); + data.addConnectors( connectorList ); + + KApplication::clipboard()->setText( data.toXML(), QClipboard::Clipboard ); +} + +void ICNDocument::selectAll() +{ + const NodeList::iterator nodeEnd = m_nodeList.end(); + for ( NodeList::iterator nodeIt = m_nodeList.begin(); nodeIt != nodeEnd; ++nodeIt ) + { + if (*nodeIt) + select(*nodeIt); + } + const ItemList::iterator itemEnd = m_itemList.end(); + for ( ItemList::iterator itemIt = m_itemList.begin(); itemIt != itemEnd; ++itemIt ) + { + if (*itemIt) + select(*itemIt); + } + const ConnectorList::iterator conEnd = m_connectorList.end(); + for ( ConnectorList::iterator connectorIt = m_connectorList.begin(); connectorIt != conEnd; ++connectorIt ) + { + if (*connectorIt) + select(*connectorIt); + } +} + + +Item* ICNDocument::addItem( const QString &id, const QPoint &p, bool newItem ) +{ + if ( !isValidItem(id) ) { + return 0l; + } + + // First, we need to tell all containers to go to full bounding so that + // we can detect a "collision" with them + const ItemList::iterator end = m_itemList.end(); + for ( ItemList::iterator it = m_itemList.begin(); it != end; ++it ) + { + if ( FlowContainer *flowContainer = dynamic_cast<FlowContainer*>((Item*)(*it)) ) + flowContainer->setFullBounds(true); + } + QCanvasItemList preCollisions = canvas()->collisions(p); + for ( ItemList::iterator it = m_itemList.begin(); it != end; ++it ) + { + if ( FlowContainer *flowContainer = dynamic_cast<FlowContainer*>((Item*)(*it)) ) + flowContainer->setFullBounds(false); + } + + + Item *item = itemLibrary()->createItem( id, this, newItem ); + if (!item) return 0L; + + // Look through the CNItems at the given point (sorted by z-coordinate) for + // a container item. + FlowContainer *container = 0l; + const QCanvasItemList::iterator pcEnd = preCollisions.end(); + for ( QCanvasItemList::iterator it = preCollisions.begin(); it != pcEnd && !container; ++it ) + { + if ( FlowContainer *flowContainer = dynamic_cast<FlowContainer*>(*it) ) + container = flowContainer; + } + + // We want to check it is not a special item first as + // isValidItem may prompt the user about his bad choice + if ( !isValidItem(item) ) + { + item->removeItem(); + flushDeleteList(); + return 0L; + } + + int x = int(p.x()); + int y = int(p.y()); + + if ( x < 16 || x > canvas()->width() ) + x = 16; + if ( y < 16 || y > canvas()->height() ) + y = 16; + + if ( CNItem *cnItem = dynamic_cast<CNItem*>(item) ) + { + cnItem->snap( x, y ); + + if (container) + container->addChild(cnItem); + } + else + item->move( x, y ); + + item->show(); + requestStateSave(); + return item; +} + + +void ICNDocument::addAllItemConnectorPoints() +{ + // FIXME The next line crashes sometimes??! + const ItemList::iterator ciEnd = m_itemList.end(); + for ( ItemList::iterator it = m_itemList.begin(); it != ciEnd; ++it ) + { + if ( CNItem *cnItem = dynamic_cast<CNItem*>((Item*)(*it)) ) + cnItem->updateConnectorPoints(true); + } +} + + +void ICNDocument::requestRerouteInvalidatedConnectors() +{ + requestEvent( ItemDocumentEvent::RerouteInvalidatedConnectors ); +} +void ICNDocument::rerouteInvalidatedConnectors() +{ + qApp->processEvents(300); + + // We only ever need to add the connector points for CNItem's when we're about to reroute... + addAllItemConnectorPoints(); + + // List of connectors which are to be determined to need rerouting (and whose routes aren't controlled by NodeGroups) + ConnectorList connectorRerouteList; + + // For those connectors that are controlled by node groups + NodeGroupList nodeGroupRerouteList; + + const ConnectorList::iterator connectorListEnd = m_connectorList.end(); + for ( ConnectorList::iterator it = m_connectorList.begin(); it != connectorListEnd; ++it ) + { + Connector *connector = *it; + if ( connector && connector->isVisible() && connector->startNode() && connector->endNode() ) + { + // Perform a series of tests to see if the connector needs rerouting + bool needsRerouting = false; + + // Test to see if we actually have any points + const QPointList pointList = connector->connectorPoints(); + if ( pointList.isEmpty() ) + needsRerouting = true; + + // Test to see if the route doesn't match up with the node positions at either end + if (!needsRerouting) + { + const QPoint listStart = pointList.first(); + const QPoint listEnd = pointList.last(); + const QPoint nodeStart = QPoint( int(connector->startNode()->x()), int(connector->startNode()->y()) ); + const QPoint nodeEnd = QPoint( int(connector->endNode()->x()), int(connector->endNode()->y()) ); + + if ( ((listStart != nodeStart) || (listEnd != nodeEnd)) && + ((listStart != nodeEnd) || (listEnd != nodeStart)) ) + { + needsRerouting = true; +// kdDebug() << "listStart=("<<listStart.x()<<","<<listStart.y()<<") nodeStart=("<<nodeStart.x()<<","<<nodeStart.y()<<") listEnd=("<<listEnd.x()<<","<<listEnd.y()<<") nodeEnd=("<<nodeEnd.x()<<","<<nodeEnd.y()<<")"<<endl; + } + } + + // Test to see if the route intersects any Items (we ignore if it is a manual route) + if ( !needsRerouting && !connector->usesManualPoints() ) + { + const QCanvasItemList collisions = connector->collisions(true); + const QCanvasItemList::const_iterator collisionsEnd = collisions.end(); + for ( QCanvasItemList::const_iterator collisionsIt = collisions.begin(); (collisionsIt != collisionsEnd) && !needsRerouting; ++collisionsIt ) + { + if ( dynamic_cast<Item*>(*collisionsIt) ) + needsRerouting = true; + } + } + + if (needsRerouting) + { + NodeGroup *nodeGroup = connector->nodeGroup(); + + if ( !nodeGroup && !connectorRerouteList.contains(connector) ) + connectorRerouteList.append(connector); + + else if ( nodeGroup && !nodeGroupRerouteList.contains(nodeGroup) ) + nodeGroupRerouteList.append(nodeGroup); + } + } + } + + // To allow proper rerouting, we want to start with clean routes for all of the invalidated connectors + const NodeGroupList::iterator nodeGroupRerouteEnd = nodeGroupRerouteList.end(); + for ( NodeGroupList::iterator it = nodeGroupRerouteList.begin(); it != nodeGroupRerouteEnd; ++it ) + { + const ConnectorList contained = (*it)->connectorList(); + const ConnectorList::const_iterator end = contained.end(); + for ( ConnectorList::const_iterator it = contained.begin(); it != end; ++it ) + (*it)->updateConnectorPoints(false); + } + + const ConnectorList::iterator connectorRerouteEnd = connectorRerouteList.end(); + for ( ConnectorList::iterator it = connectorRerouteList.begin(); it != connectorRerouteEnd; ++it ) + (*it)->updateConnectorPoints(false); + + // And finally, reroute the connectors + for ( NodeGroupList::iterator it = nodeGroupRerouteList.begin(); it != nodeGroupRerouteEnd; ++it ) + (*it)->updateRoutes(); + + for ( ConnectorList::iterator it = connectorRerouteList.begin(); it != connectorRerouteEnd; ++it ) + (*it)->rerouteConnector(); + + for ( ConnectorList::iterator it = m_connectorList.begin(); it != connectorListEnd; ++it ) + { + if (*it) + (*it)->updateDrawList(); + } +} + + +Connector* ICNDocument::createConnector( const QString &startNodeId, const QString &endNodeId, QPointList *pointList ) +{ + Node *startNode = nodeWithID(startNodeId); + Node *endNode = nodeWithID(endNodeId); + + if ( !startNode || !endNode ) + { + kdDebug() << "Either/both the connector start node and end node could not be found" << endl; + return 0L; + } + + Connector *connector = endNode->createInputConnector(startNode); + if (!connector) + { + kdError() << k_funcinfo << "End node did not create the connector" << endl; + return 0l; + } + startNode->addOutputConnector(connector); + flushDeleteList(); // Delete any connectors that might have been removed by the nodes + + // Set the route to the manual created one if the user created such a route + if (pointList) + connector->setRoutePoints(*pointList,true); + + ConnectorList connectorList; + connectorList.append(connector); + + setModified(true); + + requestRerouteInvalidatedConnectors(); + return connector; +} + +void ICNDocument::deleteSelection() +{ + // End whatever editing mode we are in, as we don't want to start editing + // something that is about to no longer exist... + m_cmManager->cancelCurrentManipulation(); + + if ( m_selectList->isEmpty() ) + return; + + m_selectList->deleteAllItems(); + flushDeleteList(); + setModified(true); + + // We need to emit this so that property widgets etc... + // can clear themselves. + emit itemUnselected(0L); + + requestRerouteInvalidatedConnectors(); + requestStateSave(); +} + + +ConnectorList ICNDocument::getCommonConnectors( const ItemList &list ) +{ + NodeList nodeList = getCommonNodes(list); + + // Now, get all the connectors, and remove the ones that don't have both end + // nodes in the above generated list + ConnectorList connectorList = m_connectorList; + const ConnectorList::iterator connectorListEnd = connectorList.end(); + for ( ConnectorList::iterator it = connectorList.begin(); it != connectorListEnd; ++it ) + { + Connector *con = *it; + if ( !con || !nodeList.contains(con->startNode()) || !nodeList.contains(con->endNode()) ) { + *it = 0l; + } + } + connectorList.remove((Connector*)0l); + return connectorList; +} + + +NodeList ICNDocument::getCommonNodes( const ItemList &list ) +{ + NodeList nodeList; + + const ItemList::const_iterator listEnd = list.end(); + for ( ItemList::const_iterator it = list.begin(); it != listEnd; ++it ) + { + NodeMap nodeMap; + CNItem *cnItem = dynamic_cast<CNItem*>((Item*)*it); + if (cnItem) + nodeMap = cnItem->nodeMap(); + const NodeMap::iterator nodeMapEnd = nodeMap.end(); + for ( NodeMap::iterator it = nodeMap.begin(); it != nodeMapEnd; ++it ) + { + Node *node = it.data().node; + + if ( !nodeList.contains(node) ) { + nodeList += node; + } + + NodeGroup *ng = node->nodeGroup(); + if (ng) + { + NodeList intNodeList = ng->internalNodeList(); + const NodeList::iterator intNodeListEnd = intNodeList.end(); + for ( NodeList::iterator it = intNodeList.begin(); it != intNodeListEnd; ++it ) + { + Node *intNode = *it; + if ( !nodeList.contains(intNode) ) { + nodeList += intNode; + } + } + } + } + } + + return nodeList; +} + + + +DirCursor *DirCursor::m_self = 0l; + +DirCursor::DirCursor() +{ + initCursors(); +} + +DirCursor::~DirCursor() +{ +} + +DirCursor* DirCursor::self() +{ + if (!m_self) m_self = new DirCursor; + return m_self; +} + +void DirCursor::initCursors() +{ +// QCursor c(Qt::ArrowCursor); +// QBitmap bitmap = *c.bitmap(); +// QBitmap mask = *c.mask(); +// QPixmap pm( bitmap->width(), bitmap->height() ); +// pm.setMask(mask); +// pm = c.pi + // @todo finish +} + + +#include "icndocument.moc" diff --git a/src/icndocument.h b/src/icndocument.h new file mode 100644 index 0000000..7a5b126 --- /dev/null +++ b/src/icndocument.h @@ -0,0 +1,280 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef ICNDOCUMENT_H +#define ICNDOCUMENT_H + +#include "itemdocument.h" + + +class Cells; +class CNItem; +class CNItemGroup; +class Connector; +class ECNode; +class FlowContainer; +class Node; +class NodeGroup; + +typedef QValueList<QGuardedPtr<Connector> > ConnectorList; +typedef QValueList<QGuardedPtr<Node> > NodeList; +typedef QValueList<NodeGroup*> NodeGroupList; +typedef QValueList<QGuardedPtr<NodeGroup> > GuardedNodeGroupList; + +/** +@author David Saxton +*/ +class ICNDocument : public ItemDocument +{ +Q_OBJECT +public: + ICNDocument( const QString &caption, KTechlab *ktechlab, const char *name ); + virtual ~ICNDocument(); + + enum hit_score + { + hs_none = 0, + hs_connector = 4, + hs_item = 1000 + }; + + virtual View *createView( ViewContainer *viewContainer, uint viewAreaId, const char *name = 0l ); + + /** + * Will attempt to create an item with the given id at position p. Some item + * (such as PIC/START) have restrictions, and can only have one instance of + * themselves on the canvas, and adds the operation to the undo list + */ + virtual Item* addItem( const QString &id, const QPoint &p, bool newItem ); + /** + * Creates a connector between two nodes, and returns a pointer to it + * and adds the operation to the undo list + */ + Connector* createConnector( const QString &startNodeId, const QString &endNodeId, QPointList *pointList = 0L ); + /** + * short for casting whatever itemWithID(id) returns + */ + CNItem* cnItemWithID( const QString &id ); + /** + * Returns a pointer to a node on the canvas with the given id, + * or NULL if no such node exists + */ + Node* nodeWithID( const QString &id ); + /** + * Returns a pointer to a Connector on the canvas with the given id, + * or NULL if no such Connector exists + */ + Connector* connectorWithID( const QString &id ); + /** + * Adds a QCanvasItem to the delete list to be deleted, + * when flushDeleteList() is called + */ + virtual void appendDeleteList( QCanvasItem *qcanvasItem ); + /** + * Permantly deletes all items that have been added to the delete list with + * the appendDeleteList( QCanvasItem *qcanvasItem ) function. + */ + virtual void flushDeleteList(); + /** + * Reinherit this function to perform special checks on whether the two + * given QCanvasItems (either nodes or connectors or both) can be + * connected together. + */ + virtual bool canConnect( QCanvasItem *qcanvasItem1, QCanvasItem *qcanvasItem2 ) const; + virtual void copy(); + virtual void selectAll(); + + + virtual bool registerItem( QCanvasItem *qcanvasItem ); + /** + * Returns a pointer to the 2-dimension array of ICNDocument cells. + */ + Cells *cells() const { return m_cells; } + /** + * Returns true if the cell-reference given by x and y is valid (i.e. + * greater than 0, but within the m_cells boundary) + */ + inline bool isValidCellReference( const uint x, const uint y ) const + { + return ( x<m_cellsX && y<m_cellsY ); + } + /** + * Adds score to the cells at the given cell referece + */ + void addCPenalty( int x, int y, int score ); + /** + * If there are two connectors joined to a node, then they can be merged + * into one connector. The node will not be removed. + * @param node The node between the two connectors + * @param noCreate If true, no new connectors will be created + * @returns true if it was successful in merging the connectors + */ + bool joinConnectors( Node *node ); + static int gridSnap( int pos ); /// Returns 'pos' when snapped to grid + static QPoint gridSnap( const QPoint &pos ); + /** + * Returns true if the CNItem is valid - e.g. will return true for a + * component in a circuit, but not in a pic program + */ + virtual bool isValidItem( Item *item ) = 0; + virtual bool isValidItem( const QString &itemId ) = 0; + ConnectorList getCommonConnectors( const ItemList &list ); + NodeList getCommonNodes( const ItemList &list ); + const NodeList & nodeList() const { return m_nodeList; } + const ConnectorList & connectorList() const { return m_connectorList; } + const GuardedNodeGroupList & nodeGroupList() const { return m_nodeGroupList; } + virtual ItemGroup *selectList() const; + /** + * Creates a connector from node1 to node2. If pointList is non-null, then the + * connector will be assigned those points + */ + Connector * createConnector( Node *node1, Node *node2, QPointList *pointList = 0L ); + /** + * Splits Connector con into two connectors at point pos2, and creates a connector from the node + * to the intersection of the two new connectors. If pointList is non-null, then the new connector + * from the node will be assigned those points + */ + Connector * createConnector( Node *node, Connector *con, const QPoint &pos2, QPointList *pointList = 0L ); + /** + * Splits con1 and con2 into two new connectors each at points pos1 and pos2, and creates a new connector + * between the two points of intersection given by pos1 and pos2. If pointList is non-null, then the new + * connector between the two points will be assigned those points + */ + Connector * createConnector( Connector *con1, Connector *con2, const QPoint &pos1, const QPoint &pos2, QPointList *pointList = 0L ); + /** + * Returns the flowcontainer at the given position at the highest level that + * is not in the current select list, or 0l if there isn't one + */ + FlowContainer *flowContainer( const QPoint &pos ); + /** + * Sets the drag (e.g. horizontal arrow) cursor for resizing a CNItem, depending on the corner clicked on + */ + void setItemResizeCursor( int cornerType ); + + void getTranslatable( const ItemList & itemList, ConnectorList * fixedConnectors = 0l, ConnectorList * translatableConnectors = 0l, NodeGroupList * translatableNodeGroups = 0l ); + /** + * Reroutes invalidated directors. You shouldn't call this function + * directly - instead use ItemDocument::requestEvent. + */ + void rerouteInvalidatedConnectors(); + /** + * Assigns the orphan nodes into NodeGroups. You shouldn't call this + * function directly - instead use ItemDocument::requestEvent. + */ + void slotAssignNodeGroups(); + +public slots: + /** + * Deletes all items in the selected item list, along with associated + * connectors, etc, and adds the operation to the undo list + */ + virtual void deleteSelection(); + /** + * This function looks at all the connectors and the nodes, determines + * which ones need rerouting, and then reroutes them + */ + void requestRerouteInvalidatedConnectors(); + /** + * Remaps the 2-dimension array of ICNDocument cells, and the various + * hitscores / etc associated with them. This is used for connector + * routing, and should be called after e.g. items have been moved + */ + void createCellMap(); + /** + * Call this to request NodeGroup reassignment. + */ + void slotRequestAssignNG(); + +signals: + /** + * Emitted when a Connector is added + */ + void connectorAdded( Connector *connector ); + /** + * Emitted when a Node is added + */ + void nodeAdded( Node *node ); + +protected: + /** + * Adds all connector points from the items (used in connector routing). + * This only needs to be called when connector(s) need routing. + */ + void addAllItemConnectorPoints(); + virtual void fillContextMenu( const QPoint &pos ); + /** + * Creates a new NodeGroup to control the node, if there does not already + * exist a NodeGroup containing the given node. The associated nodes will + * also be added to the NodeGroup. + * @returns a pointer to the NodeGroup if one was created, or a pointer to the existing one containing that node + */ + NodeGroup* createNodeGroup( Node *node ); + /** + * Finds (and deletes if found) the NodeGroup containing the given node. + * @returns true if the NodeGroup was found and deleted + */ + bool deleteNodeGroup( Node *node ); + + friend class CanvasEditor; + + Cells *m_cells; + uint m_cellsX, m_cellsY; + NodeList m_nodeList; + ConnectorList m_connectorList; + CNItemGroup *m_selectList; // Selected objects + GuardedNodeGroupList m_nodeGroupList; + +private: + QCanvasItemList m_itemDeleteList; // List of canvas items to be deleted +}; + + +/** +@author David Saxton +*/ +class DirCursor +{ +public: + static DirCursor* self(); + ~DirCursor(); + + static QPixmap leftArrow() + { + return self()->m_leftArrow; + } + + static QPixmap rightArrow() + { + return self()->m_rightArrow; + } + + static QPixmap upArrow() + { + return self()->m_upArrow; + } + + static QPixmap downArrow() + { + return self()->m_downArrow; + } + +protected: + DirCursor(); + void initCursors(); + + static DirCursor *m_self; + QPixmap m_leftArrow; + QPixmap m_rightArrow; + QPixmap m_upArrow; + QPixmap m_downArrow; +}; + + +#endif diff --git a/src/icnview.cpp b/src/icnview.cpp new file mode 100644 index 0000000..cff8a2c --- /dev/null +++ b/src/icnview.cpp @@ -0,0 +1,114 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "canvasmanipulator.h" +#include "icndocument.h" +#include "icnview.h" +#include "ktechlab.h" + +#include <kconfig.h> +#include <kglobal.h> +#include <kiconloader.h> +#include <klocale.h> +#include <kpopupmenu.h> + +ICNView::ICNView( ICNDocument *icnDocument, ViewContainer *viewContainer, uint viewAreaId, const char *name ) + : ItemView( icnDocument, viewContainer, viewAreaId, name ) +{ + bool manualRouting = (icnDocument->m_cmManager->cmState() & CMManager::cms_manual_route); + + KActionCollection * ac = actionCollection(); + + //BEGIN Routing Actions + // These actions get inserted into the main menu + m_pAutoRoutingAction = new KRadioAction( i18n("Automatic"), "", 0, this, SLOT(slotSetRoutingAuto()), ac, "routing_mode_auto" ); + m_pAutoRoutingAction->setExclusiveGroup("routing_mode"); + if ( !manualRouting ) + m_pAutoRoutingAction->setChecked( true ); + + m_pManualRoutingAction = new KRadioAction( i18n("Manual"), "", 0, this, SLOT(slotSetRoutingManual()), ac, "routing_mode_manual" ); + m_pManualRoutingAction->setExclusiveGroup("routing_mode"); + if ( manualRouting ) + m_pManualRoutingAction->setChecked( true ); + + + // This popup gets inserted into the toolbar + m_pRoutingModeToolbarPopup = new KToolBarPopupAction( i18n("Connection Routing Mode"), "pencil", 0, 0, 0, ac, "routing_mode" ); + m_pRoutingModeToolbarPopup->setDelayed(false); + + KPopupMenu * m = m_pRoutingModeToolbarPopup->popupMenu(); + m->insertTitle( i18n("Connection Routing Mode") ); + + m->insertItem( /*KGlobal::iconLoader()->loadIcon( "routing_mode_auto", KIcon::Small ), */i18n("Automatic"), 0 ); + m->insertItem( /*KGlobal::iconLoader()->loadIcon( "routing_mode_manual", KIcon::Small ),*/ i18n("Manual"), 1 ); + + m->setCheckable(true); + m->setItemChecked( manualRouting ? 1 : 0, true ); + + connect( m, SIGNAL(activated(int)), this, SLOT(slotSetRoutingMode(int)) ); + //END Routing Actions + + connect( icnDocument->m_cmManager, SIGNAL(manualRoutingChanged(bool )), this, SLOT(slotUpdateRoutingToggles(bool )) ); +} + + +ICNView::~ICNView() +{ +} + + +void ICNView::slotSetRoutingMode( int mode ) +{ + // This function is called when the user selects a mode from the toolbar drop-down menu + bool manualEnabled = (mode == 1); + + if ( bool(p_itemDocument->m_cmManager->cmState() & CMManager::cms_manual_route) == manualEnabled ) + return; + + slotUpdateRoutingMode( manualEnabled ); + slotUpdateRoutingToggles( manualEnabled ); +} + + +void ICNView::slotSetRoutingManual() +{ + slotUpdateRoutingMode( true ); + slotUpdateRoutingToggles( true ); +} + + +void ICNView::slotSetRoutingAuto() +{ + slotUpdateRoutingMode( false ); + slotUpdateRoutingToggles( false ); +} + + +void ICNView::slotUpdateRoutingMode( bool manualRouting ) +{ + p_itemDocument->m_cmManager->slotSetManualRoute( manualRouting ); + p_itemDocument->canvas()->setMessage( manualRouting ? i18n("<b>Manual</b> connection routing enabled.") : i18n("<b>Automatic</b> connection routing enabled.") ); +} + + +void ICNView::slotUpdateRoutingToggles( bool manualRouting ) +{ + m_pRoutingModeToolbarPopup->popupMenu()->setItemChecked( !manualRouting, 0 ); + m_pRoutingModeToolbarPopup->popupMenu()->setItemChecked( manualRouting, 1 ); + + if ( manualRouting ) + m_pManualRoutingAction->setChecked(true); + + else + m_pAutoRoutingAction->setChecked(true); +} + + +#include "icnview.moc" diff --git a/src/icnview.h b/src/icnview.h new file mode 100644 index 0000000..4f4d0bc --- /dev/null +++ b/src/icnview.h @@ -0,0 +1,43 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef ICNVIEW_H +#define ICNVIEW_H + +#include <itemview.h> + +class ICNDocument; +class KRadioAction; +class KToolBarPopupAction; + +/** +@author David Saxton +*/ +class ICNView : public ItemView +{ + Q_OBJECT + public: + ICNView( ICNDocument * icnDocument, ViewContainer *viewContainer, uint viewAreaId, const char * name = 0l ); + ~ICNView(); + + protected slots: + void slotSetRoutingMode( int mode ); // 0 = auto, 1 = manual + void slotSetRoutingAuto(); + void slotSetRoutingManual(); + void slotUpdateRoutingMode( bool manualRouting ); + void slotUpdateRoutingToggles( bool manualRouting ); + + protected: + KToolBarPopupAction * m_pRoutingModeToolbarPopup; + KRadioAction * m_pManualRoutingAction; + KRadioAction * m_pAutoRoutingAction; +}; + +#endif diff --git a/src/item.cpp b/src/item.cpp new file mode 100644 index 0000000..1cbf7e3 --- /dev/null +++ b/src/item.cpp @@ -0,0 +1,599 @@ +/*************************************************************************** + * Copyright (C) 2004-2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "itemdocument.h" +#include "itemdocumentdata.h" +#include "core/ktlconfig.h" + +#include <cmath> +#include <kdebug.h> +#include <kdialogbase.h> +#include <ktextedit.h> +#include <qbitarray.h> +#include <qlayout.h> +#include <qtimer.h> + +const int minPrefixExp = -24; +const int maxPrefixExp = 24; +const int numPrefix = int((maxPrefixExp-minPrefixExp)/3)+1; +const QString SIprefix[] = {"y","z","a","f","p","n",QChar(0xB5),"m","","k","M","G","T","P","E","Z","Y"}; + + +Item::Item( ItemDocument *itemDocument, bool newItem, const QString &id ) + : QObject(), QCanvasPolygon( itemDocument->canvas() ) +{ + m_bDynamicContent = false; + m_bIsRaised = false; + m_bDoneCreation = false; + p_parentItem = 0l; + b_deleted = false; + p_itemDocument = itemDocument; + m_baseZ = -1; + + if ( QFontInfo(m_font).pixelSize() > 11 ) // It has to be > 11, not > 12, as (I think) pixelSize() rounds off the actual size + m_font.setPixelSize(12); + + if (newItem) + m_id = p_itemDocument->generateUID(id); + + else + { + m_id = id; + p_itemDocument->registerUID(id); + } +} + + +Item::~Item() +{ + p_itemDocument->requestEvent( ItemDocument::ItemDocumentEvent::ResizeCanvasToItems ); + p_itemDocument->unregisterUID( id() ); + + QCanvasPolygon::hide(); + + const VariantDataMap::iterator variantDataEnd = m_variantData.end(); + for ( VariantDataMap::iterator it = m_variantData.begin(); it != variantDataEnd; ++it ) + delete it.data(); + m_variantData.clear(); +} + + +void Item::removeItem() +{ + if (b_deleted) + return; + b_deleted = true; + + hide(); + setCanvas(0l); + emit removed(this); + p_itemDocument->appendDeleteList(this); +} + + +void Item::moveBy( double dx, double dy ) +{ + QCanvasPolygon::moveBy(dx,dy); + emit movedBy( dx, dy ); +} + + +void Item::setChanged() +{ + if (b_deleted) + return; + + if (canvas()) + canvas()->setChanged(boundingRect()); +} + + +void Item::setItemPoints( const QPointArray & pa, bool setSizeFromPoints ) +{ + m_itemPoints = pa; + if (setSizeFromPoints) + setSize( m_itemPoints.boundingRect() ); + itemPointsChanged(); +} + + +void Item::itemPointsChanged() +{ + setPoints(m_itemPoints); +} + + +void Item::setSize( QRect sizeRect, bool forceItemPoints ) +{ + if ( m_sizeRect == sizeRect && !forceItemPoints ) + return; + + if ( !preResize(sizeRect) ) + return; + + canvas()->setChanged(areaPoints().boundingRect()); + m_sizeRect = sizeRect; + if ( m_itemPoints.isEmpty() || forceItemPoints ) + { + setItemPoints( QPointArray( m_sizeRect ), false ); + } + canvas()->setChanged(areaPoints().boundingRect()); + postResize(); + emit resized(); +} + + +ItemData Item::itemData() const +{ + ItemData itemData; + + itemData.type = m_type; + itemData.x = x(); + itemData.y = y(); + + if ( !parentItem() ) + itemData.z = m_baseZ; + + itemData.size = m_sizeRect; + itemData.setSize = canResize(); + + if (p_parentItem) + itemData.parentId = p_parentItem->id(); + + const VariantDataMap::const_iterator end = m_variantData.end(); + for ( VariantDataMap::const_iterator it = m_variantData.begin(); it != end; ++it ) + { + switch( it.data()->type() ) + { + case Variant::Type::String: + case Variant::Type::FileName: + case Variant::Type::Port: + case Variant::Type::Pin: + case Variant::Type::VarName: + case Variant::Type::Combo: + case Variant::Type::Select: + case Variant::Type::Multiline: + case Variant::Type::SevenSegment: + case Variant::Type::KeyPad: + { + itemData.dataString[it.key()] = it.data()->value().toString(); + break; + } + case Variant::Type::Int: + case Variant::Type::Double: + { + itemData.dataNumber[it.key()] = it.data()->value().toDouble(); + break; + } + case Variant::Type::Color: + { + itemData.dataColor[it.key()] = it.data()->value().toColor(); + break; + } + case Variant::Type::Bool: + { + itemData.dataBool[it.key()] = it.data()->value().toBool(); + break; + } + case Variant::Type::Raw: + { + itemData.dataRaw[it.key()] = it.data()->value().toBitArray(); + break; + } + case Variant::Type::PenStyle: + case Variant::Type::PenCapStyle: + { + // These types are only created from DrawPart, and that class + // deals with these, so we can ignore them + break; + } + case Variant::Type::None: + { + // ? Maybe obsoleted data... + break; + } + } + } + + return itemData; +} + + +void Item::restoreFromItemData( const ItemData &itemData ) +{ + move( itemData.x, itemData.y ); + if ( canResize() ) + setSize( itemData.size ); + + Item *parentItem = p_itemDocument->itemWithID( itemData.parentId ); + if (parentItem) + setParentItem(parentItem); + else + m_baseZ = itemData.z; + + //BEGIN Restore data + const QStringMap::const_iterator stringEnd = itemData.dataString.end(); + for ( QStringMap::const_iterator it = itemData.dataString.begin(); it != stringEnd; ++it ) + { + if ( hasProperty(it.key()) ) + property( it.key() )->setValue( it.data() ); + } + + const DoubleMap::const_iterator numberEnd = itemData.dataNumber.end(); + for ( DoubleMap::const_iterator it = itemData.dataNumber.begin(); it != numberEnd; ++it ) + { + if ( hasProperty(it.key()) ) + property( it.key() )->setValue( it.data() ); + } + + const QColorMap::const_iterator colorEnd = itemData.dataColor.end(); + for ( QColorMap::const_iterator it = itemData.dataColor.begin(); it != colorEnd; ++it ) + { + if ( hasProperty(it.key()) ) + property( it.key() )->setValue( it.data() ); + } + + const BoolMap::const_iterator boolEnd = itemData.dataBool.end(); + for ( BoolMap::const_iterator it = itemData.dataBool.begin(); it != boolEnd; ++it ) + { + if ( hasProperty(it.key()) ) + property( it.key() )->setValue( QVariant( it.data(), 0 ) ); + } + + const QBitArrayMap::const_iterator rawEnd = itemData.dataRaw.end(); + for ( QBitArrayMap::const_iterator it = itemData.dataRaw.begin(); it != rawEnd; ++it ) + { + if ( hasProperty(it.key()) ) + property( it.key() )->setValue( it.data() ); + } + //END Restore Data +} + + +bool Item::mousePressEvent( const EventInfo &eventInfo ) +{ + Q_UNUSED(eventInfo); + return false; +} +bool Item::mouseReleaseEvent( const EventInfo &eventInfo ) +{ + Q_UNUSED(eventInfo); + return false; +} +bool Item::mouseMoveEvent( const EventInfo &eventInfo ) +{ + Q_UNUSED(eventInfo); + return false; +} +bool Item::wheelEvent( const EventInfo &eventInfo ) +{ + Q_UNUSED(eventInfo); + return false; +} +void Item::enterEvent() +{ +} +void Item::leaveEvent() +{ +} + +bool Item::mouseDoubleClickEvent( const EventInfo &eventInfo ) +{ + Q_UNUSED(eventInfo); + + typedef QValueList<Variant*> VarPtrLst; + VarPtrLst list; + const VariantDataMap::iterator variantDataEnd = m_variantData.end(); + for ( VariantDataMap::iterator it = m_variantData.begin(); it != variantDataEnd; ++it ) + { + if ( it.data()->type() == Variant::Type::Multiline ) { + list.append(it.data()); + } + } + if ( list.count() > 1 ) + { + kdWarning() << "Item::mouseDoubleClickEvent: Can't handle more than one multiline data"<<endl; + return false; + } + else if ( list.isEmpty() ) + return false; + + Variant *v = *(list.at(0)); + + /// @todo Replace this with KInputDialog::getMultiLineText for KDE 3.3 +// bool ok; +// QString text = KInputDialog::getMultiLineText( v->caption(), "", v->getValue(), ok ); + + KDialogBase *dlg = new KDialogBase( 0l, "", true, v->editorCaption(), KDialogBase::Ok|KDialogBase::Cancel|KDialogBase::User1, KDialogBase::Ok, false, KStdGuiItem::clear() ); + QFrame *frame = dlg->makeMainWidget(); + QVBoxLayout *layout = new QVBoxLayout( frame, 0, dlg->spacingHint() ); + KTextEdit *textEdit = new KTextEdit( frame ); + textEdit->setTextFormat( PlainText ); + textEdit->setText( v->value().toString() ); + layout->addWidget( textEdit, 10 ); + textEdit->setFocus(); + connect( dlg, SIGNAL( user1Clicked() ), textEdit, SLOT( clear() ) ); + dlg->setMinimumWidth( 600 ); + if ( dlg->exec() == KDialogBase::Accepted ) + { + v->setValue( textEdit->text() ); + dataChanged(); + p_itemDocument->setModified(true); + } + delete dlg; + + return true; +} + + +void Item::setSelected( bool yes ) +{ + if ( isSelected() == yes ) { + return; + } + QCanvasPolygon::setSelected(yes); + yes ? (emit selected(this)) : (emit unselected(this)); +} + + +void Item::setParentItem( Item *newParentItem ) +{ +// kdDebug() << k_funcinfo << "this = "<<this<<" newParentItem = "<<newParentItem<<endl; + if ( newParentItem == p_parentItem ) + return; + + Item *oldParentItem = p_parentItem; + + if (oldParentItem) + { + disconnect( oldParentItem, SIGNAL(removed(Item*)), this, SLOT(removeItem()) ); + oldParentItem->removeChild(this); + } + + if (newParentItem) + { + if ( newParentItem->contains(this) ); +// kdError() << k_funcinfo << "Already a child of " << newParentItem << endl; + else + { + connect( newParentItem, SIGNAL(removed(Item*)), this, SLOT(removeItem()) ); + newParentItem->addChild(this); + } + } + + p_parentItem = newParentItem; + (void)level(); + reparented( oldParentItem, newParentItem ); + p_itemDocument->slotUpdateZOrdering(); +} + + +int Item::level() const +{ + return p_parentItem ? p_parentItem->level()+1 : 0; +} + + +ItemList Item::children( bool includeGrandChildren ) const +{ + if (!includeGrandChildren) + return m_children; + + ItemList children = m_children; + ItemList::const_iterator end = m_children.end(); + for ( ItemList::const_iterator it = m_children.begin(); it != end; ++it ) + { + if (!*it) + continue; + + children += (*it)->children(true); + } + + return children; +} + + +void Item::addChild( Item *child ) +{ + if ( !child ) + return; + + if ( child->contains(this) ) + { +// kdError() << k_funcinfo << "Attempting to add a child to this item that is already a parent of this item. Incest results in stack overflow." << endl; + return; + } + + if ( contains( child, true ) ) + { +// kdError() << k_funcinfo << "Already have child " << child << endl; + return; + } + + m_children.append(child); + connect( child, SIGNAL(removed(Item* )), this, SLOT(removeChild(Item* )) ); + + child->setParentItem(this); + childAdded(child); + p_itemDocument->slotUpdateZOrdering(); +} + + +void Item::removeChild( Item *child ) +{ + if ( !child || !m_children.contains(child) ) + return; + + m_children.remove(child); + disconnect( child, SIGNAL(removed(Item* )), this, SLOT(removeChild(Item* )) ); + + childRemoved(child); + p_itemDocument->slotUpdateZOrdering(); +} + + +bool Item::contains( Item *item, bool direct ) const +{ + const ItemList::const_iterator end = m_children.end(); + for ( ItemList::const_iterator it = m_children.begin(); it != end; ++it ) + { + if ( (Item*)*it == item || ( !direct && (*it)->contains( item, false ) ) ) + return true; + } + return false; +} + + +void Item::setRaised( bool isRaised ) +{ + m_bIsRaised = isRaised; + // We'll get called later to update our Z +} + + +void Item::updateZ( int baseZ ) +{ + m_baseZ = baseZ; + double z = ItemDocument::Z::Item + (ItemDocument::Z::DeltaItem)*baseZ; + + if ( isRaised() ) + z += ItemDocument::Z::RaisedItem - ItemDocument::Z::Item; + + setZ(z); + + const ItemList::const_iterator end = m_children.end(); + for ( ItemList::const_iterator it = m_children.begin(); it != end; ++it ) + { + if (*it) + (*it)->updateZ(baseZ+1); + } +} + + +int Item::getNumberPre( double num ) +{ + return (int)(num/getMultiplier(num)); +} + +QString Item::getNumberMag( double num ) +{ + if ( num == 0. ) return ""; + const double exp_n = std::log10(std::abs(num)); + if ( exp_n < minPrefixExp+3 ) return SIprefix[0]; + else if ( exp_n >= maxPrefixExp ) return SIprefix[numPrefix-1]; + else return SIprefix[(int)std::floor((double)(exp_n/3))-(int)floor(double(minPrefixExp/3))]; +} + +double Item::getMultiplier( double num ) +{ + if ( num == 0. ) return 1.; + else return std::pow( 10, 3*std::floor(std::log10(std::abs(num))/3) ); +} + +double Item::getMultiplier( const QString &_mag ) +{ + QString mag; + // Allow the user to enter in "u" instead of mu, as unfortunately many keyboards don't have the mu key + if ( _mag == "u" ) + mag = QChar(0xB5); + else + mag = _mag; + + for ( int i=0; i<numPrefix; ++i ) + { + if ( mag == SIprefix[i] ) + { + return std::pow( 10., (i*3)+minPrefixExp ); + } + } + + // I think it is safer to return '1' if the unit is unknown + return 1.; +// return pow( 10., maxPrefixExp+3. ); +} + + + +//BEGIN Data stuff +double Item::dataDouble( const QString & id ) const +{ + Variant * variant = property(id); + return variant ? variant->value().toDouble() : 0.0; +} + + +int Item::dataInt( const QString & id ) const +{ + Variant * variant = property(id); + return variant ? variant->value().toInt() : 0; +} + + +bool Item::dataBool( const QString & id ) const +{ + Variant * variant = property(id); + return variant ? variant->value().toBool() : false; +} + + +QString Item::dataString( const QString & id ) const +{ + Variant * variant = property(id); + return variant ? variant->value().toString() : QString::null; +} + + +QColor Item::dataColor( const QString & id ) const +{ + Variant * variant = property(id); + return variant ? variant->value().toColor() : Qt::black; +} + + +Variant * Item::createProperty( const QString & id, Variant::Type::Value type ) +{ + if ( !m_variantData.contains(id) ) + { + m_variantData[id] = new Variant(type); + if (m_bDoneCreation) + connect( m_variantData[id], SIGNAL(valueChanged(QVariant,QVariant)), this, SLOT(dataChanged()) ); + } + + return m_variantData[id]; +} + + +Variant * Item::property( const QString & id ) const +{ + if ( m_variantData.contains(id) ) + return m_variantData[id]; + + kdError() << k_funcinfo << " No such property with id " << id << endl; + return 0l; +} + + +bool Item::hasProperty( const QString & id ) const +{ + return m_variantData.contains(id); +} + + +void Item::finishedCreation( ) +{ + m_bDoneCreation = true; + const VariantDataMap::iterator end = m_variantData.end(); + for ( VariantDataMap::iterator it = m_variantData.begin(); it != end; ++it ) + connect( it.data(), SIGNAL(valueChanged(QVariant,QVariant)), this, SLOT(dataChanged()) ); + dataChanged(); +} +//END Data stuff + +#include "item.moc" diff --git a/src/item.h b/src/item.h new file mode 100644 index 0000000..f1968d2 --- /dev/null +++ b/src/item.h @@ -0,0 +1,308 @@ +/*************************************************************************** + * Copyright (C) 2004-2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef ITEM_H +#define ITEM_H + +#include "variant.h" + +#include <qcanvas.h> +#include <qfont.h> +#include <qguardedptr.h> + + +class Document; +class EventInfo; +class Item; +class ItemData; +class ItemDocument; +class ItemView; +class DoubleSpinBox; +class Document; +class Variant; +class QBitArray; + +typedef Item*(*createItemPtr)( ItemDocument *itemDocument, bool newItem, const char *id ); +typedef QGuardedPtr<Item> GuardedItem; +typedef QMap<QString, Variant*> VariantDataMap; +typedef QValueList<GuardedItem> ItemList; + +/** +@author David Saxton +@author Daniel Clarke +*/ +class Item : public QObject, public QCanvasPolygon +{ +Q_OBJECT +public: + Item( ItemDocument *itemDocument, bool newItem, const QString &id ); + virtual ~Item(); + + /** + * @return Pointer to the VariantMap used for internal data storage + */ + VariantDataMap *variantMap() { return &m_variantData; } + + double dataDouble( const QString & id ) const; + int dataInt( const QString & id ) const; + bool dataBool( const QString & id ) const; + QString dataString( const QString & id ) const; + QColor dataColor( const QString & id ) const; + + virtual Variant * createProperty( const QString & id, Variant::Type::Value type ); + Variant * property( const QString & id ) const; + bool hasProperty( const QString & id ) const; + + /** + * Whether or not we can rotate the item + */ + virtual bool canRotate() const { return false; } + /** + * Whether or not we can flip the item + */ + virtual bool canFlip() const { return false; } + /** + * Whether or not we can resize the item + */ + virtual bool canResize() const { return false; } + /** + * Returns whether the CNItem allows itself to be moved on the canvas. + * Most do, but some (such as the PicItem) don't allow this + */ + virtual bool isMovable() const { return true; } + /** + * If your item doesn't move, yet still continously changes what is being + * displayed (such as a seven segment display or a lamp), then this should + * return true (set m_bDynamicContent to be true in your constructor). + */ + bool hasDynamicContent() const { return m_bDynamicContent; } + /** + * Returns a identifier for the CNItem, which is unique on the ICNDocument + */ + QString id() const { return m_id; } + QString type() const { return m_type; } + /** + * Called from ItemLibrary after this class and subclasses have finished + * constructing themselves. + */ + virtual void finishedCreation(); + /** + * Sets the selected flag of the item to yes. selected or unselected will be + * emitted as appropriate + */ + virtual void setSelected( bool yes ); + /** + * Convenience function for setting the item bounding area as changed on the + * canvas + */ + void setChanged(); + /** + * Sets this item as a child of the given item. Calls reparented with the + * old and the new parent. + */ + void setParentItem( Item *parentItem ); + /** + * The parent item for this item, or NULL if none + */ + Item *parentItem() const { return p_parentItem; } + ItemDocument *itemDocument() const { return p_itemDocument; } + /** + * Returns the number of items away from the top item this is + * (parent-wise). Returns 0 if has no parent. + */ + int level() const; + /** + * If true, then adds ItemDocument::Z::(RaisedItem-Item) to the z value of + * the item. + */ + void setRaised( bool isRaised ); + /** + * @Returns whether raised or not + */ + bool isRaised() const { return m_bIsRaised; } + /** + * Sets this item to the given baseZ level, and calls this function for the + * children with baseZ incremented by one. Reinherit this function to set + * the Z of attached stuff (such as nodes). + */ + virtual void updateZ( int baseZ ); + /** + * Returns the item's position in the overall z-stack of items. + */ + int baseZ() const { return m_baseZ; } + /** + * Adds a child. Calls the virtual function childAdded. + */ + void addChild( Item *child ); + /** + * Returns the list of children. + * @param if includeGrandChildren is true then this list will also contain + * the children's children, and so on recursively, instead of just the + * immediate children. + */ + ItemList children( bool includeGrandChildren = false ) const; + /** + * Returns whether we have the given child as either a direct child, or as + * either a direct or indirect child + */ + bool contains( Item *item, bool direct = false ) const; + /** + * Calls prePresize with the bounds, and if that returns true, sets + * m_sizeRect to the given rect, and then calls postResize. + * @param forceItemPoints if true, will set the item points to a rectangle of the given size + */ + void setSize( QRect sizeRect, bool forceItemPoints = false ); + /** + * Convenience function. + * @see setSize( QRect sizeRect, bool forceItemPoints ); + */ + void setSize( int x, int y, int w, int h, bool forceItemPoints = false ) { setSize( QRect(x,y,w,h), forceItemPoints ); } + /** + * @returns the m_sizeRect rectangble that contains the item points + */ + QRect sizeRect() const { return m_sizeRect; } + /** + * Reinherit this function if you want to determine what the minimum size is + * that this item can be resized to. + */ + virtual QSize minimumSize() const { return QSize(0,0); } + int offsetX() const { return m_sizeRect.x(); } + int offsetY() const { return m_sizeRect.y(); } + int width() const { return m_sizeRect.width(); } + int height() const { return m_sizeRect.height(); } + virtual bool mousePressEvent( const EventInfo &eventInfo ); + virtual bool mouseReleaseEvent( const EventInfo &eventInfo ); + virtual bool mouseDoubleClickEvent ( const EventInfo &eventInfo ); + virtual bool mouseMoveEvent( const EventInfo &eventInfo ); + virtual bool wheelEvent( const EventInfo &eventInfo ); + virtual void enterEvent(); + virtual void leaveEvent(); + /** + * Returns the name of the CNItem, e.g. "Resistor" + */ + QString name() const { return m_name; } + /** + * Returns a description of the CNItem, with html tags if appropriate. + */ + QString description() const { return m_desc; } + /** + * Modifies the exponent of the number so that it appears readable: + * eg 10000->10, 174822->175, 0.6->600, etc + */ + static int getNumberPre( double num ); + /** + * Returns the SI exponent of the number as a letter: + * eg 10000 returns 'k', 0.6 returns 'm', etc + */ + static QString getNumberMag( double num ); + /** + * Returns the multiplier required to get the num up to human readable form: + * eg 10000 returns 0.001, etc + */ + static double getMultiplier( double num ); + /** + * Returns the multiplier required to get the num from human readable form + * to its actual value based on the SI exponent: + * eg 'm' returns 0.001, etc + */ + static double getMultiplier( const QString &mag ); + + virtual ItemData itemData() const; + virtual void restoreFromItemData( const ItemData &itemData ); + + const QFont & font() const { return m_font; } + +public slots: + virtual void removeItem(); + /** + * Moves item - use this instead of moveBy() so that associated Nodes also get moved + */ + virtual void moveBy( double dx, double dy ); + /** + * Removes a child. Calls the virtual function childRemoved + */ + void removeChild( Item *child ); + +signals: + /** + * Emitted when the CNItem is removed. Normally, this signal is caught by associated + * nodes, who will remove themselves as well. + */ + void removed( Item *item ); + /** + * Emitted when the item is selected + */ + void selected( Item *item, bool isSelected = true ); + /** + * Emitted when the item is unselected + */ + void unselected( Item *item, bool isSelected = false ); + /** + * Emitted when the item is resized (after calling postResize) + */ + void resized(); + /** + * Emitted when the item is moved (by dx, dy). + */ + void movedBy( double dx, double dy ); + +protected slots: + virtual void dataChanged() {}; + +protected: + /** + * Reinherit this function if you want to do anything with children. Called + * after the parent is changed, with the old parent and the new parent. + */ + virtual void reparented( Item */*oldParent*/, Item */*newParent*/ ) {}; + /** + * Reinherit this function if you want to do anything with children. Called + * after a child has been added. + */ + virtual void childAdded( Item * ) {}; + /** + * Reinherit this function if you want to do anything with children. Called + * after a child has been removed. + */ + virtual void childRemoved( Item * ) {}; + /** + * Set the rough bounding points for this item. Calls itemPointsChanged + * after setting the points + */ + void setItemPoints( const QPointArray &pa, bool setSizeFromPoints = true ); + /** + * Reinherit this function if you want to apply any sort of transformation + * to the item points + */ + virtual void itemPointsChanged(); + virtual bool preResize( QRect sizeRect ) { Q_UNUSED(sizeRect); return true; } + virtual void postResize() {}; + + QString m_id; + QString m_name, m_desc; // Name and description + QString m_type; + GuardedItem p_parentItem; // If attached to a parent item + ItemList m_children; + QGuardedPtr<ItemDocument> p_itemDocument; + QPointArray m_itemPoints; // The unorientated and unsized item points + + friend class ItemLibrary; + + int m_baseZ; + bool m_bIsRaised; + bool m_bDoneCreation; + bool b_deleted; + bool m_bDynamicContent; + QFont m_font; + QRect m_sizeRect; + VariantDataMap m_variantData; +}; + +#endif diff --git a/src/itemdocument.cpp b/src/itemdocument.cpp new file mode 100644 index 0000000..cfe3230 --- /dev/null +++ b/src/itemdocument.cpp @@ -0,0 +1,1323 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "canvasmanipulator.h" +#include "circuitdocument.h" +#include "connector.h" +#include "cnitem.h" +#include "drawpart.h" +#include "ecnode.h" +#include "flowcodedocument.h" +#include "icnview.h" +#include "itemdocumentdata.h" +#include "itemgroup.h" +#include "itemselector.h" +#include "ktechlab.h" +#include "core/ktlconfig.h" +#include "pin.h" +#include "simulator.h" + +#include <kapplication.h> +#include <kdebug.h> +#include <kfiledialog.h> +#include <kiconloader.h> +#include <klocale.h> +#include <kmessagebox.h> +#include <kpopupmenu.h> +#include <kprinter.h> +#include <qcheckbox.h> +#include <qclipboard.h> +#include <qcursor.h> +#include <qimage.h> +#include <qpaintdevicemetrics.h> +#include <qpainter.h> +#include <qpicture.h> +#include <qregexp.h> +#include <qsimplerichtext.h> +#include <qtimer.h> + +#include <cmath> + + +//BEGIN class ItemDocument +int ItemDocument::m_nextActionTicket = 0; + +ItemDocument::ItemDocument( const QString &caption, KTechlab *ktechlab, const char *name) + : Document( caption, ktechlab, name ) +{ + m_queuedEvents = 0; + p_ktechlab = ktechlab; + m_nextIdNum = 1; + m_savedState = 0l; + m_currentState = 0l; + m_bIsLoading = false; + + m_canvas = new Canvas( this, "canvas" ); + m_canvasTip = new CanvasTip(this,m_canvas); + m_cmManager = new CMManager(this); + m_undoStack.setAutoDelete(true); + m_redoStack.setAutoDelete(true); + + updateBackground(); + m_canvas->resize( 0, 0 ); + m_canvas->setDoubleBuffering(true); + + m_pEventTimer = new QTimer(this); + connect( m_pEventTimer, SIGNAL(timeout()), this, SLOT(processItemDocumentEvents()) ); + + connect( this, SIGNAL(itemSelected(Item*)), this, SLOT(slotInitItemActions(Item*)) ); + connect( this, SIGNAL(itemUnselected(Item*)), this, SLOT(slotInitItemActions(Item*)) ); + + connect( ComponentSelector::self(), SIGNAL(itemClicked(const QString& )), this, SLOT(slotUnsetRepeatedItemId()) ); + connect( FlowPartSelector::self(), SIGNAL(itemClicked(const QString& )), this, SLOT(slotUnsetRepeatedItemId()) ); +#ifdef MECHANICS + connect( MechanicsSelector::self(), SIGNAL(itemClicked(const QString& )), this, SLOT(slotUnsetRepeatedItemId()) ); +#endif + + m_pAlignmentAction = new KActionMenu( i18n("Alignment"), "rightjust", this ); + + slotUpdateConfiguration(); +} + + +ItemDocument::~ItemDocument() +{ + m_bDeleted = true; + + ItemList toDelete = m_itemList; + const ItemList::iterator end = toDelete.end(); + for ( ItemList::iterator it = toDelete.begin(); it != end; ++it ) + delete *it; + + delete m_cmManager; + m_cmManager = 0l; + delete m_currentState; + m_currentState = 0l; + delete m_canvasTip; + m_canvasTip = 0l; +} + + +void ItemDocument::handleNewView( View * view ) +{ + Document::handleNewView(view); + requestEvent( ItemDocument::ItemDocumentEvent::ResizeCanvasToItems ); +} + + +bool ItemDocument::registerItem( QCanvasItem *qcanvasItem ) +{ + if (!qcanvasItem) + return false; + + requestEvent( ItemDocument::ItemDocumentEvent::ResizeCanvasToItems ); + + switch (qcanvasItem->rtti()) + { + case ItemDocument::RTTI::DrawPart: + case ItemDocument::RTTI::CNItem: + { + Item *item = dynamic_cast<Item*>(qcanvasItem); + m_itemList.append(item); + connect( item, SIGNAL(removed(Item*)), this, SLOT(requestRerouteInvalidatedConnectors()) ); + connect( item, SIGNAL(selected(Item*,bool)), this, SIGNAL(itemSelected(Item*)) ); + connect( item, SIGNAL(unselected(Item*,bool)), this, SIGNAL(itemUnselected(Item*)) ); + itemAdded(item); + return true; + } + default: + return false; + } +} + + +void ItemDocument::slotSetDrawAction( int drawAction ) +{ + m_cmManager->setDrawAction(drawAction); +} + + +void ItemDocument::cancelCurrentOperation() +{ + m_cmManager->cancelCurrentManipulation(); +} + + +void ItemDocument::slotSetRepeatedItemId( const QString &id ) +{ + m_cmManager->setCMState( CMManager::cms_repeated_add, true ); + m_cmManager->setRepeatedAddId(id); +} + + +void ItemDocument::slotUnsetRepeatedItemId() +{ + m_cmManager->setCMState( CMManager::cms_repeated_add, false ); +} + + +void ItemDocument::fileSave() +{ + if ( url().isEmpty() && !getURL(m_fileExtensionInfo) ) return; + writeFile(); +} + + +void ItemDocument::fileSaveAs() +{ + if ( !getURL(m_fileExtensionInfo) ) return; + writeFile(); + + // Our modified state may not have changed, but we emit this to force the + // main window to update our caption. + emit modifiedStateChanged(); +} + + +void ItemDocument::writeFile() +{ + ItemDocumentData data( type() ); + data.saveDocumentState(this); + + if ( data.saveData(url()) ) + { + m_savedState = m_currentState; + setModified(false); + } +} + + +bool ItemDocument::openURL( const KURL &url ) +{ + ItemDocumentData data( type() ); + + if ( !data.loadData(url) ) + return false; + + // Why do we stop simulating while loading a document? + // Crash possible when loading a circuit document, and the Qt event loop is + // reentered (such as when a PIC component pops-up a message box), which + // will then call the Simulator::step function, which might use components + // that have not fully initialized themselves. + + m_bIsLoading = true; + bool wasSimulating = Simulator::self()->isSimulating(); + Simulator::self()->slotSetSimulating( false ); + data.restoreDocument(this); + Simulator::self()->slotSetSimulating( wasSimulating ); + m_bIsLoading = false; + + setURL(url); + clearHistory(); + m_savedState = m_currentState; + setModified(false); + + if ( FlowCodeDocument *fcd = dynamic_cast<FlowCodeDocument*>(this) ) + { + // We need to tell all pic-depedent components about what pic type is in use + emit fcd->picTypeChanged(); + } + + requestEvent( ItemDocument::ItemDocumentEvent::ResizeCanvasToItems ); + + // Load Z-position info + m_zOrder.clear(); + ItemList::iterator end = m_itemList.end(); + for ( ItemList::iterator it = m_itemList.begin(); it != end; ++it ) + { + if ( !*it || (*it)->parentItem() ) + continue; + + m_zOrder[(*it)->baseZ()] = *it; + } + slotUpdateZOrdering(); + + return true; +} + +void ItemDocument::print() +{ + static KPrinter * printer = new KPrinter; + + if ( ! printer->setup( p_ktechlab ) ) + return; + + // setup the printer. with Qt, you always "print" to a + // QPainter.. whether the output medium is a pixmap, a screen, + // or paper + QPainter p; + p.begin( printer ); + + // we let our view do the actual printing + QPaintDeviceMetrics metrics( printer ); + + // Round to 16 (= 2 * 8) so that we cut in the middle of squares + int w = 16*int(metrics.width()/16); + int h = 16*int(metrics.height()/16); + + p.setClipping( true ); + p.setClipRect( 0, 0, w, h, QPainter::CoordPainter ); + + // Send off the painter for drawing + m_canvas->setBackgroundPixmap( 0 ); + + QRect bounding = canvasBoundingRect(); + unsigned int rows = (unsigned) std::ceil( double( bounding.height() ) / double( h ) ); + unsigned int cols = (unsigned) std::ceil( double( bounding.width() ) / double( w ) ); + int offset_x = bounding.x(); + int offset_y = bounding.y(); + + for ( unsigned row = 0; row < rows; ++row ) + { + for ( unsigned col = 0; col < cols; ++col ) + { + if ( row != 0 || col != 0 ) + printer->newPage(); + + QRect drawArea( offset_x + (col * w), offset_y + (row * h), w, h ); + m_canvas->drawArea( drawArea, & p ); + + p.translate( -w, 0 ); + } + p.translate( w * cols, -h ); + } + + updateBackground(); + + // and send the result to the printer + p.end(); +} + + +void ItemDocument::requestStateSave( int actionTicket ) +{ + if ( m_bIsLoading ) + return; + + m_redoStack.clear(); + + if ( (actionTicket >= 0) && (actionTicket == m_currentActionTicket) ) + { + delete m_currentState; + m_currentState = 0l; + } + + m_currentActionTicket = actionTicket; + + if (m_currentState) + m_undoStack.push(m_currentState); + + m_currentState = new ItemDocumentData( type() ); + m_currentState->saveDocumentState(this); + + if (!m_savedState) + m_savedState = m_currentState; + + setModified( m_savedState != m_currentState ); + + emit undoRedoStateChanged(); + + //FIXME To resize undo queue, have to pop and push everything + int maxUndo = KTLConfig::maxUndo(); + if ( maxUndo <= 0 || m_undoStack.count() < (unsigned)maxUndo ) + return; + IDDStack tempStack; + int pushed = 0; + while ( !m_undoStack.isEmpty() && pushed < maxUndo ) + { + tempStack.push( m_undoStack.pop() ); + pushed++; + } + m_undoStack.clear(); + while ( !tempStack.isEmpty() ) + m_undoStack.push( tempStack.pop() ); +} + + +void ItemDocument::clearHistory() +{ + m_undoStack.clear(); + m_redoStack.clear(); + delete m_currentState; + m_currentState = 0l; + requestStateSave(); +} + + +bool ItemDocument::isUndoAvailable() const +{ + return !m_undoStack.isEmpty(); +} + + +bool ItemDocument::isRedoAvailable() const +{ + return !m_redoStack.isEmpty(); +} + + +void ItemDocument::undo() +{ + ItemDocumentData *idd = m_undoStack.pop(); + if (!idd) + return; + + if (m_currentState) + m_redoStack.push(m_currentState); + + idd->restoreDocument(this); + m_currentState = idd; + + setModified( m_savedState != m_currentState ); + emit undoRedoStateChanged(); +} + + +void ItemDocument::redo() +{ + ItemDocumentData *idd = m_redoStack.pop(); + if (!idd) + return; + + if (m_currentState) + m_undoStack.push(m_currentState); + + idd->restoreDocument(this); + m_currentState = idd; + + setModified( m_savedState != m_currentState ); + emit undoRedoStateChanged(); +} + + +void ItemDocument::cut() +{ + copy(); + deleteSelection(); +} + + +void ItemDocument::paste() +{ + QString xml = KApplication::clipboard()->text( QClipboard::Clipboard ); + if ( xml.isEmpty() ) + return; + + unselectAll(); + + ItemDocumentData data( type() ); + data.fromXML(xml); + data.generateUniqueIDs(this); + data.translateContents( 64, 64 ); + data.mergeWithDocument( this, true ); + + // Get rid of any garbage that shouldn't be around / merge connectors / etc + flushDeleteList(); + + requestStateSave(); +} + + +Item *ItemDocument::itemWithID( const QString &id ) +{ + const ItemList::iterator end = m_itemList.end(); + for ( ItemList::iterator it = m_itemList.begin(); it != end; ++it ) + { + if ( (*it)->id() == id ) + return *it; + } + return 0L; +} + + +void ItemDocument::unselectAll() +{ + selectList()->removeAllItems(); +} + + +void ItemDocument::select( QCanvasItem * item ) +{ + if (!item) + return; + item->setSelected( selectList()->contains( item ) || selectList()->addQCanvasItem( item ) ); +} + + +void ItemDocument::select( const QCanvasItemList & list ) +{ + const QCanvasItemList::const_iterator end = list.end(); + for ( QCanvasItemList::const_iterator it = list.begin(); it != end; ++it ) + selectList()->addQCanvasItem(*it); + + selectList()->setSelected(true); +} + + +void ItemDocument::unselect( QCanvasItem *qcanvasItem ) +{ + selectList()->removeQCanvasItem(qcanvasItem); + qcanvasItem->setSelected(false); +} + + +void ItemDocument::slotUpdateConfiguration() +{ + updateBackground(); + m_canvas->setUpdatePeriod( int(1000./KTLConfig::refreshRate()) ); +} + + +QCanvasItem* ItemDocument::itemAtTop( const QPoint &pos ) const +{ + QCanvasItemList list = m_canvas->collisions( QRect( pos.x()-1, pos.y()-1, 3, 3 ) ); + + QCanvasItemList::const_iterator it = list.begin(); + const QCanvasItemList::const_iterator end = list.end(); + while ( it != end ) + { + QCanvasItem *item = *it; + if ( item == m_canvasTip || + item->rtti() == QCanvasItem::Rtti_Line || + item->rtti() == QCanvasItem::Rtti_Text || + item->rtti() == QCanvasItem::Rtti_Rectangle ) + { + ++it; + } + else + { + if ( item->rtti() == ItemDocument::RTTI::ConnectorLine ) + return (static_cast<ConnectorLine*>(item))->parent(); + + return item; + } + } + + return 0L; +} + + + +void ItemDocument::alignHorizontally( ) +{ + selectList()->slotAlignHorizontally(); + if ( ICNDocument *icnd = dynamic_cast<ICNDocument*>(this) ) + icnd->requestRerouteInvalidatedConnectors(); +} +void ItemDocument::alignVertically( ) +{ + selectList()->slotAlignVertically(); + if ( ICNDocument *icnd = dynamic_cast<ICNDocument*>(this) ) + icnd->requestRerouteInvalidatedConnectors(); +} +void ItemDocument::distributeHorizontally( ) +{ + selectList()->slotDistributeHorizontally(); + if ( ICNDocument *icnd = dynamic_cast<ICNDocument*>(this) ) + icnd->requestRerouteInvalidatedConnectors(); +} +void ItemDocument::distributeVertically( ) +{ + selectList()->slotDistributeVertically(); + if ( ICNDocument *icnd = dynamic_cast<ICNDocument*>(this) ) + icnd->requestRerouteInvalidatedConnectors(); +} + + +bool ItemDocument::registerUID( const QString &UID ) +{ + if ( m_idList.findIndex(UID) == -1 ) + { + m_idList.append(UID); + return true; + } + + return false; +} + + +void ItemDocument::unregisterUID( const QString & uid ) +{ + m_idList.remove(uid); +} + + +QString ItemDocument::generateUID( QString name ) +{ + name.remove( QRegExp("__.*") ); // Change 'node__13' to 'node', for example + QString idAttempt = name; +// if ( idAttempt.find("__") != -1 ) idAttempt.truncate( idAttempt.find("__") ); + while ( !registerUID(idAttempt) ) { idAttempt = name + "__" + QString::number(m_nextIdNum++); } + + return idAttempt; +} + + +void ItemDocument::canvasRightClick( const QPoint &pos, QCanvasItem* item ) +{ + if (item) + { + if ( item->rtti() == ItemDocument::RTTI::CNItem && + !item->isSelected() ) + { + unselectAll(); + select(item); + } + } + + p_ktechlab->unplugActionList("alignment_actionlist"); + p_ktechlab->unplugActionList("orientation_actionlist"); + p_ktechlab->unplugActionList("component_actionlist"); + fillContextMenu(pos); + + QPopupMenu *pop = static_cast<QPopupMenu*>(p_ktechlab->factory()->container("item_popup", p_ktechlab)); + + if (!pop) + return; + + pop->popup(pos); +} + + +void ItemDocument::fillContextMenu( const QPoint & pos ) +{ + Q_UNUSED(pos); + + ItemView * activeItemView = dynamic_cast<ItemView*>(activeView()); + if ( !p_ktechlab || !activeItemView ) + return; + + KAction * align_actions[] = { + activeItemView->action("align_horizontally"), + activeItemView->action("align_vertically"), + activeItemView->action("distribute_horizontally"), + activeItemView->action("distribute_vertically") }; + + bool enableAlignment = selectList()->itemCount() > 1; + + if ( !enableAlignment ) + return; + + for ( unsigned i = 0; i < 4; ++i ) + { + align_actions[i]->setEnabled(true); + m_pAlignmentAction->remove( align_actions[i] ); + m_pAlignmentAction->insert( align_actions[i] ); + } + QPtrList<KAction> alignment_actions; + alignment_actions.append( m_pAlignmentAction ); + p_ktechlab->plugActionList( "alignment_actionlist", alignment_actions ); +} + + +void ItemDocument::slotInitItemActions( Item *item ) +{ + Q_UNUSED(item); + + ItemView * activeItemView = dynamic_cast<ItemView*>(activeView()); + if ( !p_ktechlab || !activeItemView ) + return; + + KAction * align_actions[] = { + activeItemView->action("align_horizontally"), + activeItemView->action("align_vertically"), + activeItemView->action("distribute_horizontally"), + activeItemView->action("distribute_vertically") }; + + bool enableAlignment = selectList()->itemCount() > 1; + for ( unsigned i = 0; i < 4; ++i ) + align_actions[i]->setEnabled(enableAlignment); +} + + +void ItemDocument::updateBackground() +{ + // Also used in the constructor to make the background initially. + + // Thoughts. + // ~The pixmap could be done somehow with 1bpp. It might save some waste + // I expect it won't hurt for now. + // ~This is all rather static, only works with square etc... should be no prob. for most uses. IMO. + // ~If you want, decide what maximum and minimum spacing should be, then enforce them + // in the Config (I suppose you can use <max></max> tags?) + // ~Defaults based on the existing grid background png. It should produce identical results, to your + // original png. + + // **** Below where it says "interval * 10", that decides how big the pixmap will be (always square) + // Originally I set this to 32, which give 256x256 with 8 spacing, as that was the size of your pixmap + // Are there any good reasons to make the a certain size? (i.e. big or small ?). + + int interval = 8; + int bigness = interval * 10; + QPixmap pm( bigness, bigness ); +// pm.fill( KTLConfig::bgColor() ); // first fill the background colour in + pm.fill( Qt::white ); + + if( KTLConfig::showGrid() ){ + QPainter p(&pm); // setup painter to draw on pixmap + p.setPen( KTLConfig::gridColor() ); // set forecolour + // note: anything other than 8 borks this + for( int i = (interval / 2); i < bigness; i+=interval ){ + p.drawLine( 0, i, bigness, i ); // horizontal + p.drawLine( i, 0, i, bigness ); // vertical + } + p.end(); // all done + } + + pm.setDefaultOptimization( QPixmap::BestOptim ); + m_canvas->setBackgroundPixmap(pm); // and the finale. +} + + +void ItemDocument::requestEvent( ItemDocumentEvent::type type ) +{ + m_queuedEvents |= type; + m_pEventTimer->stop(); + m_pEventTimer->start( 0, true ); +} + + +void ItemDocument::processItemDocumentEvents() +{ + // Copy it incase we have new events requested while doing this... + unsigned queuedEvents = m_queuedEvents; + m_queuedEvents = 0; + + if ( queuedEvents & ItemDocumentEvent::ResizeCanvasToItems ) + resizeCanvasToItems(); + + if ( queuedEvents & ItemDocumentEvent::UpdateZOrdering ) + slotUpdateZOrdering(); + + ICNDocument * icnd = dynamic_cast<ICNDocument*>(this); + + if ( icnd && (queuedEvents & ItemDocumentEvent::UpdateNodeGroups) ) + icnd->slotAssignNodeGroups(); + + if ( icnd && (queuedEvents & ItemDocumentEvent::RerouteInvalidatedConnectors) ) + icnd->rerouteInvalidatedConnectors(); +} + + +void ItemDocument::resizeCanvasToItems() +{ + const ViewList::iterator end = m_viewList.end(); + + QRect bound = canvasBoundingRect(); + QSize size( bound.right(), bound.bottom() ); + + m_viewList.remove((View*)0); + + for ( ViewList::iterator it = m_viewList.begin(); it != end; ++it ) + { + CVBEditor * cvbEditor = (static_cast<ItemView*>((View*)*it))->cvbEditor(); + + int contentsX, contentsY; + int contentsWMX, contentsWMY; + + cvbEditor->viewportToContents( cvbEditor->viewport()->width(), cvbEditor->viewport()->height(), contentsX, contentsY ); + cvbEditor->inverseWorldMatrix().map( contentsX, contentsY, &contentsWMX, &contentsWMY ); + + // Hack to fix a bug whereby when scrolled, but emoty gap before scrollbars, + // size slowly decreases one pixel at a time + if ( (contentsX - contentsWMX) == 1 ) + contentsWMX = contentsX; + if ( (contentsY - contentsWMY) == 1 ) + contentsWMY = contentsY; + + size = size.expandedTo( QSize( contentsWMX, contentsWMY ) ); + } + + // We want to avoid flicker.... + for ( ViewList::iterator it = m_viewList.begin(); it != end; ++it ) + { + ItemView * itemView = static_cast<ItemView*>((View*)*it); + CVBEditor * cvbEditor = itemView->cvbEditor(); + + cvbEditor->setVScrollBarMode( ((size.height()*itemView->zoomLevel()) > cvbEditor->visibleHeight()) ? QScrollView::AlwaysOn : QScrollView::AlwaysOff ); + cvbEditor->setHScrollBarMode( ((size.width()*itemView->zoomLevel()) > cvbEditor->visibleWidth()) ? QScrollView::AlwaysOn : QScrollView::AlwaysOff ); + } + + bool changedSize = canvas()->size() != size; + canvas()->resize( size.width(), size.height() ); + + if (changedSize) + requestEvent( ItemDocumentEvent::ResizeCanvasToItems ); + else if ( ICNDocument * icnd = dynamic_cast<ICNDocument*>(this) ) + icnd->createCellMap(); +} + + +QRect ItemDocument::canvasBoundingRect() const +{ + QRect bound; + + const QCanvasItemList allItems = canvas()->allItems(); + const QCanvasItemList::const_iterator end = allItems.end(); + for ( QCanvasItemList::const_iterator it = allItems.begin(); it != end; ++it ) + { + if ( !(*it)->isVisible() ) + continue; + bound |= (*it)->boundingRect(); + } + + if ( !bound.isNull() ) + { + bound.setLeft( bound.left() - 16 ); + bound.setTop( bound.top() - 16 ); + bound.setRight( bound.right() + 16 ); + bound.setBottom( bound.bottom() + 16 ); + } + + return bound; +} + + +void ItemDocument::exportToImage() +{ + // scaralously copied from print. + // this slot is called whenever the File->Export menu is selected, + // the Export shortcut is pressed or the Export toolbar + // button is clicked + + // widget for the kfiledialog + // It is the bit that says "Crop circuit?" + // Okay need to think of something way better to say here. + // gotme here, KFileDialog makes itself parent so tries to destroy cropCheck when it is deleted. + // therefore we use a pointer. + QString cropMessage; + if ( type() == Document::dt_flowcode ) + cropMessage = i18n("Crop image to program parts"); + + else if ( type() == Document::dt_circuit ) + cropMessage = i18n("Crop image to circuit components"); + + else + cropMessage = i18n("Crop image"); + + QCheckBox *cropCheck = new QCheckBox( cropMessage, p_ktechlab, "cropCheck" ); + cropCheck->setChecked(true); // yes by default? + + // we need an object so we can retrieve which image type was selected by the user + // so setup the filedialog. + KFileDialog exportDialog(QString::null, "*.png|PNG Image\n*.bmp|BMP Image\n*.svg|SVG Image" , p_ktechlab, i18n("Export As Image"), true, cropCheck); + + exportDialog.setOperationMode( KFileDialog::Saving ); + // now actually show it + if ( exportDialog.exec() == QDialog::Rejected ) + return; + KURL url = exportDialog.selectedURL(); + + if ( url.isEmpty() ) + return; + + if ( QFile::exists( url.path() ) ) + { + int query = KMessageBox::warningYesNo( p_ktechlab, i18n( "A file named \"%1\" already exists. " "Are you sure you want to overwrite it?" ).arg( url.fileName() ), i18n( "Overwrite File?" ), i18n( "Overwrite" ), KStdGuiItem::cancel() ); + if ( query == KMessageBox::No ) return; + } + + // with Qt, you always "print" to a + // QPainter.. whether the output medium is a pixmap, a screen, + // or paper + + // needs to be something like QPicture to do SVG etc... + // at the moment the pixmap is just as big as the canvas, + // intend to make some kind of cropping thing so it just + // takes the bit with the circuit on. + + QRect saveArea; + QString type; + QRect cropArea; + QPaintDevice *outputImage; + QString filter = exportDialog.currentFilter(); + filter = filter.lower(); // gently soften the appearance of the letters. + + // did have a switch here but seems you can't use that on strings + if ( filter == "*.png" ) + type = "PNG"; + + else if ( filter == "*.bmp" ) + type = "BMP"; + + else if ( filter == "*.svg" ) + { + KMessageBox::information( NULL, i18n("SVG export is sub-functional"), i18n("Export As Image") ); + type = "SVG"; + } + // I don't like forcing people to use the right extension (personally) + // but it is the easiest way to decide image type. + else + { + KMessageBox::sorry( NULL, i18n("Unknown extension, please select one from the filter list."), i18n("Export As Image") ); + return; + } + + if ( cropCheck->isChecked() ) + { + cropArea = canvasBoundingRect(); + if ( cropArea.isNull() ) + { + KMessageBox::sorry( 0l, i18n("There is nothing to crop"), i18n("Export As Image") ); + return; + } + else + { + cropArea &= canvas()->rect(); + } + } + + saveArea = m_canvas->rect(); + + if ( type == "PNG" || type == "BMP" ) + outputImage = new QPixmap( saveArea.size() ); + + else if ( type == "SVG" ) + { + setSVGExport(true); + outputImage = new QPicture(); + // svg can't be cropped using the qimage method. + saveArea = cropArea; + } + else + { + kdWarning() << "Unknown type!" << endl; + return; + } + + QPainter p(outputImage); + + m_canvas->setBackgroundPixmap(QPixmap()); + m_canvas->drawArea( saveArea, &p ); + updateBackground(); + + p.end(); + + bool saveResult; + + // if cropping we need to convert to an image, + // crop, then save. + if ( cropCheck->isChecked() ) + { + if( type == "SVG" ) + saveResult = dynamic_cast<QPicture*>(outputImage)->save( url.path(), type); + + else + { + QImage img = dynamic_cast<QPixmap*>(outputImage)->convertToImage(); + img = img.copy(cropArea); + saveResult = img.save(url.path(),type); + } + } + else + { + if ( type=="SVG" ) + saveResult = dynamic_cast<QPicture*>(outputImage)->save( url.path(), type ); + else + saveResult = dynamic_cast<QPixmap*>(outputImage)->save( url.path(), type ); + } + + //if(saveResult == true) KMessageBox::information( this, i18n("Sucessfully exported to \"%1\"").arg( url.filename() ), i18n("Image Export") ); + //else KMessageBox::information( this, i18n("Export failed"), i18n("Image Export") ); + + if ( type == "SVG" ) + setSVGExport(false); + + if (saveResult == false) + KMessageBox::information( p_ktechlab, i18n("Export failed"), i18n("Image Export") ); + + delete outputImage; +} + + +void ItemDocument::setSVGExport( bool svgExport ) +{ + // Find any items and tell them not to draw buttons or sliders + QCanvasItemList items = m_canvas->allItems(); + const QCanvasItemList::iterator end = items.end(); + for ( QCanvasItemList::Iterator it = items.begin(); it != end; ++it ) + { + if ( CNItem * cnItem = dynamic_cast<CNItem*>(*it) ) + cnItem->setDrawWidgets(!svgExport); + } +} + +void ItemDocument::raiseZ() +{ + raiseZ( selectList()->items(true) ); +} +void ItemDocument::raiseZ( const ItemList & itemList ) +{ + if ( m_zOrder.isEmpty() ) + slotUpdateZOrdering(); + + if ( m_zOrder.isEmpty() ) + return; + + IntItemMap::iterator begin = m_zOrder.begin(); + IntItemMap::iterator previous = m_zOrder.end(); + IntItemMap::iterator it = --m_zOrder.end(); + do + { + Item * previousData = (previous == m_zOrder.end()) ? 0l : previous.data(); + Item * currentData = it.data(); + + if ( currentData && previousData && itemList.contains(currentData) && !itemList.contains(previousData) ) + { + previous.data() = currentData; + it.data() = previousData; + } + + previous = it; + --it; + } + while ( previous != begin ); + + slotUpdateZOrdering(); +} + + +void ItemDocument::lowerZ() +{ + lowerZ( selectList()->items(true) ); +} +void ItemDocument::lowerZ( const ItemList & itemList ) +{ + if ( m_zOrder.isEmpty() ) + slotUpdateZOrdering(); + + if ( m_zOrder.isEmpty() ) + return; + + IntItemMap::iterator previous = m_zOrder.begin(); + IntItemMap::iterator end = m_zOrder.end(); + for ( IntItemMap::iterator it = m_zOrder.begin(); it != end; ++it ) + { + Item * previousData = previous.data(); + Item * currentData = it.data(); + + if ( currentData && previousData && itemList.contains(currentData) && !itemList.contains(previousData) ) + { + previous.data() = currentData; + it.data() = previousData; + } + + previous = it; + } + + slotUpdateZOrdering(); +} + + +void ItemDocument::itemAdded( Item * ) +{ + requestEvent( ItemDocument::ItemDocumentEvent::UpdateZOrdering ); +} + + +void ItemDocument::slotUpdateZOrdering() +{ + ItemList toAdd = m_itemList; + toAdd.remove((Item*)0l); + + IntItemMap newZOrder; + int atLevel = 0; + + IntItemMap::iterator zEnd = m_zOrder.end(); + for ( IntItemMap::iterator it = m_zOrder.begin(); it != zEnd; ++it ) + { + Item * item = it.data(); + if (!item) + continue; + + toAdd.remove(item); + + if ( !item->parentItem() && item->isMovable() ) + newZOrder[atLevel++] = item; + } + + ItemList::iterator addEnd = toAdd.end(); + for ( ItemList::iterator it = toAdd.begin(); it != addEnd; ++it ) + { + Item * item = *it; + if ( item->parentItem() || !item->isMovable() ) + continue; + + newZOrder[atLevel++] = item; + } + + m_zOrder = newZOrder; + + zEnd = m_zOrder.end(); + for ( IntItemMap::iterator it = m_zOrder.begin(); it != zEnd; ++it ) + it.data()->updateZ( it.key() ); +} + + +void ItemDocument::update( ) +{ + ItemList::iterator end = m_itemList.end(); + for ( ItemList::iterator it = m_itemList.begin(); it != end; ++it ) + { + if ( (*it)->hasDynamicContent() ) + (*it)->setChanged(); + } +} +//END class ItemDocument + + + +//BEGIN class CanvasTip +CanvasTip::CanvasTip( ItemDocument *itemDocument, QCanvas *qcanvas ) + : QCanvasText(qcanvas) +{ + p_itemDocument = itemDocument; + + setColor( Qt::black ); + setZ( ICNDocument::Z::Tip ); +} + +CanvasTip::~CanvasTip() +{ +} + +void CanvasTip::displayVI( ECNode *node, const QPoint &pos ) +{ + if ( !node || !updateVI() ) + return; + + unsigned num = node->numPins(); + + m_v.resize(num); + m_i.resize(num); + + for ( unsigned i = 0; i < num; i++ ) + { + if ( Pin * pin = node->pin(i) ) + { + m_v[i] = pin->voltage(); + m_i[i] = pin->current(); + } + } + + display(pos); +} + + +void CanvasTip::displayVI( Connector *connector, const QPoint &pos ) +{ + if ( !connector || !updateVI()) + return; + + unsigned num = connector->numWires(); + + m_v.resize(num); + m_i.resize(num); + + for ( unsigned i = 0; i < num; i++ ) + { + if ( Wire * wire = connector->wire(i) ) + { + m_v[i] = wire->voltage(); + m_i[i] = std::abs(wire->current()); + } + } + + display(pos); +} + + +bool CanvasTip::updateVI() +{ + CircuitDocument *circuitDocument = dynamic_cast<CircuitDocument*>(p_itemDocument); + if ( !circuitDocument || !Simulator::self()->isSimulating() ) + return false; + + circuitDocument->calculateConnectorCurrents(); + return true; +} + + +void CanvasTip::display( const QPoint &pos ) +{ + unsigned num = m_v.size(); + + for ( unsigned i = 0; i < num; i++ ) + { + if ( !std::isfinite(m_v[i]) || std::abs(m_v[i]) < 1e-9 ) + m_v[i] = 0.; + + if ( !std::isfinite(m_i[i]) || std::abs(m_i[i]) < 1e-9 ) + m_i[i] = 0.; + } + + move( pos.x()+20, pos.y()+4 ); + + if ( num == 0 ) + return; + + if ( num == 1 ) + setText( displayText(0) ); + + else + { + QString text; + for ( unsigned i = 0; i < num; i++ ) + text += QString(" %1: %2\n").arg( QString::number(i) ).arg( displayText(i) ); + setText(text); + } +} + + +QString CanvasTip::displayText( unsigned num ) const +{ + if ( m_v.size() <= num ) + return QString::null; + + return QString(" %1%2V %3%4A ") + .arg( QString::number( m_v[num] / CNItem::getMultiplier(m_v[num]), 'g', 3 ) ) + .arg( CNItem::getNumberMag( m_v[num] ) ) + .arg( QString::number( m_i[num] / CNItem::getMultiplier(m_i[num]), 'g', 3 ) ) + .arg( CNItem::getNumberMag( m_i[num] ) ); +} + + +void CanvasTip::draw( QPainter &p ) +{ + CircuitDocument *circuitDocument = dynamic_cast<CircuitDocument*>(p_itemDocument); + if ( !circuitDocument || !Simulator::self()->isSimulating() ) + return; + + p.setBrush( QColor( 0xff, 0xff, 0xdc ) ); + p.setPen( Qt::black ); + p.drawRect( boundingRect() ); + QCanvasText::draw(p); +} +//END class CanvasTip + + + + +//BEGIN class Canvas +Canvas::Canvas( ItemDocument *itemDocument, const char * name ) + : QCanvas( itemDocument, name ) +{ + p_itemDocument = itemDocument; + m_pMessageTimeout = new QTimer(this); + connect( m_pMessageTimeout, SIGNAL(timeout()), this, SLOT(slotSetAllChanged()) ); +} + + +void Canvas::setMessage( const QString & message ) +{ + m_message = message; + + if ( message.isEmpty() ) + m_pMessageTimeout->stop(); + + else + m_pMessageTimeout->start( 2000, true ); + + setAllChanged(); +} + + +void Canvas::drawBackground ( QPainter &p, const QRect & clip ) +{ + QCanvas::drawBackground( p, clip ); +#if 0 + const int scx = (int)((clip.left()-4)/8); + const int ecx = (int)((clip.right()+4)/8); + const int scy = (int)((clip.top()-4)/8); + const int ecy = (int)((clip.bottom()+4)/8); + if ( !((ICNDocument*)(p_itemDocument))->isValidCellReference( scx, scy ) || + !((ICNDocument*)(p_itemDocument))->isValidCellReference( ecx, ecy ) ) return; + Cells *c = ((ICNDocument*)(p_itemDocument))->cells(); + for ( int x=scx; x<=ecx; x++ ) + { + for ( int y=scy; y<=ecy; y++ ) + { + const double score = (*c)[x][y].CIpenalty+(*c)[x][y].Cpenalty; + int value = (int)std::log(score)*20; + if ( value>255 ) value=255; + else if (value<0 ) value=0; + p.setBrush( QColor( 255, (255-value), (255-value) ) ); + p.setPen( Qt::NoPen ); + p.drawRect( (x*8), (y*8), 8, 8 ); + } + } +#endif +} + + +void Canvas::drawForeground ( QPainter &p, const QRect & clip ) +{ + QCanvas::drawForeground( p, clip ); + + if ( !m_pMessageTimeout->isActive() ) + return; + + + + // Following code stolen and adapted from amarok/src/playlist.cpp :) + + // Find out width of smallest view + QSize minSize; + const ViewList viewList = p_itemDocument->viewList(); + ViewList::const_iterator end = viewList.end(); + View * firstView = 0l; + for ( ViewList::const_iterator it = viewList.begin(); it != end; ++it ) + { + if ( !*it ) + continue; + + if ( !firstView ) + { + firstView = *it; + minSize = (*it)->size(); + } + else + minSize = minSize.boundedTo( (*it)->size() ); + } + + if ( !firstView ) + return; + + QSimpleRichText * t = new QSimpleRichText( m_message, QApplication::font() ); + + int w = t->width(); + int h = t->height(); + int x = 15; + int y = 15; + int b = 10; // text padding + + if ( w+2*b >= minSize.width() || h+2*b >= minSize.height() ) + { + delete t; + return; + } + + p.setBrush( firstView->colorGroup().background() ); + p.drawRoundRect( x, y, w+2*b, h+2*b, (8*200)/(w+2*b), (8*200)/(h+2*b) ); + t->draw( &p, x+b, y+b, QRect(), firstView->colorGroup() ); + delete t; +} + + +void Canvas::update() +{ + p_itemDocument->update(); + QCanvas::update(); +} +//END class Canvas + +#include "itemdocument.moc" + + diff --git a/src/itemdocument.h b/src/itemdocument.h new file mode 100644 index 0000000..e9e9f6f --- /dev/null +++ b/src/itemdocument.h @@ -0,0 +1,449 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef ITEMDOCUMENT_H +#define ITEMDOCUMENT_H + +#include <document.h> +#include <qcanvas.h> +#include <qptrstack.h> +#include <qvaluevector.h> + +class Canvas; +class CanvasTip; +class Connector; +class CMManager; +class ECNode; +class Item; +class ItemDocumentData; +class ItemGroup; +class KTechlab; +class Operation; + +class KActionMenu; +class QCanvasItem; + +typedef QPtrStack<ItemDocumentData> IDDStack; +typedef QGuardedPtr<Item> GuardedItem; +typedef QMap< int, GuardedItem > IntItemMap; +typedef QValueList<GuardedItem> ItemList; +typedef QValueList<QPoint> QPointList; + +/** +@author David Saxton +*/ +class ItemDocument : public Document +{ + Q_OBJECT + public: + ItemDocument( const QString &caption, KTechlab *ktechlab, const char *name = 0 ); + ~ItemDocument(); + + class Z + { + public: + enum + { + Select = 10000000, + Connector = 20000000, + Item = 30000000, + RaisedItem = 40000000, + ResizeHandle = 50000000, + Tip = 60000000, + ConnectorCreateLine = 70000000, + + // How much "Z" separates items stacked on each other + DeltaItem = 10000 + }; + }; + + class RTTI + { + public: + enum + { + None = 1000, + CNItem = 1001, + Node = 1002, + Connector = 1003, + Pin = 1004, + Widget = 1005, + MechanicsItem = 1006, + ResizeHandle = 1007, + DrawPart = 1008, + ConnectorLine = 1009 + }; + }; + + /** + * Some things (such as the canvas getting resized, connectors being + * invalidated, need to be done after editing operations have finished, + * etc, and they also need to be done in the order given in the + * enumeration below. + */ + class ItemDocumentEvent + { + public: enum type + { + ResizeCanvasToItems = 1 << 0, + UpdateNodeGroups = 1 << 1, + RerouteInvalidatedConnectors = 1 << 2, + UpdateZOrdering = 1 << 3, + }; + }; + + virtual void fileSave(); + virtual void fileSaveAs(); + virtual void print(); + virtual bool openURL( const KURL &url ); + /** + * Attempt to register the item, returning true iff successful + */ + virtual bool registerItem( QCanvasItem *qcanvasItem ); + /** + * Will attempt to create an item with the given id at position p. Some item + * (such as PIC/START) have restrictions, and can only have one instance of + * themselves on the canvas, and adds the operation to the undo list + */ + virtual Item* addItem( const QString &id, const QPoint &p, bool newItem ) = 0; + /** + * @returns A pointer to the canvas + */ + Canvas *canvas() const { return m_canvas; } + /** + * Attemtps to register a unique id for the canvas view of an item on the + * canvas. If the id does not already exist, will return true; otherwise + * the function will return false. + */ + bool registerUID( const QString & uid ); + /** + * Generates a unique id based on a possibly unique component name. + */ + QString generateUID( QString name ); + /** + * Unlists the given id as one that is used. + * @see registerUID + */ + void unregisterUID( const QString & uid ); + /** + * @return Whether or not the item is valid; i.e. is appropriate to the + * document being edited, and does not have other special restrictions + * on it (such as only allowing one instance of the Start part in + * FlowCode). + */ + virtual bool isValidItem( Item *item ) = 0; + /** + * @return Whether or not the item is valid; i.e. is appropriate to the + * document being edited, and does not have other special restrictions + * on it (such as only allowing one instance of the Start part in + * FlowCode). + */ + virtual bool isValidItem( const QString &itemId ) = 0; + /** + * Increases the "height" of the given list of items by "one". + */ + void raiseZ( const ItemList & itemList ); + /** + * Decreases the "height" of the given list of items by "one". + */ + void lowerZ( const ItemList & itemList ); + /** + * @return ItemGroup that is used as the select list for this document. + */ + virtual ItemGroup *selectList() const = 0; + /** + * Deselects any currently selected items + */ + void unselectAll(); + /** + * Select a list of QCanvasItem's + */ + void select( const QCanvasItemList & list ); + /** + * Select a QCanvasItem + */ + void select( QCanvasItem * item ); + /** + * Unselects the item + */ + void unselect( QCanvasItem *qcanvasItem ); + /** + * Deletes anything waiting to be deleted. + */ + virtual void flushDeleteList() = 0; + /** + * Returns a rubber-band rectangle that contains all of the items on the + * canvas, padded out by a small border. + */ + QRect canvasBoundingRect() const; + /** + * Returns a pointer to a Item on the canvas with the given id, + * or NULL if no such Item exists. + */ + Item* itemWithID( const QString & ); + /** + * Returns true if the the user can perform an undo action + * (i.e. the undo stack is not empty) + */ + virtual bool isUndoAvailable() const; + /** + * Returns true if the the user can perform an redo action + * (i.e. the redo stack is not empty) + */ + virtual bool isRedoAvailable() const; + /** + * Returns the top item at point (x, y), or NULL if there is no item there + */ + QCanvasItem* itemAtTop( const QPoint &pos ) const; + /** + * Called when the canvas is clicked on with the right mouse button. + * Popups up a menu for editing operations + */ + virtual void canvasRightClick( const QPoint &pos, QCanvasItem* item ); + /** + * List of items in the ItemDocument + */ + ItemList itemList() const { return m_itemList; } + /** + * Set the given QCanvasItem (which will attempt to be casted to known + * items to be deleted. + */ + virtual void appendDeleteList( QCanvasItem * ) = 0; + /** + * Save the current state of the document to the undo/redo history. + * @param actionTicket if this is non-negative, and the last state save + * also had the same actionTicket, then the next state save will + * overwrite the previous state save. + * @see getActionTicket + */ + void requestStateSave( int actionTicket = -1 ); + /** + * Returns a unique id, for use in requestStateSave + */ + int getActionTicket() const { return m_nextActionTicket++; } + /** + * Clears the undo / redo history + */ + void clearHistory(); + /** + * Requests an event to be done after other stuff (editing, etc) is finished. + */ + void requestEvent( ItemDocumentEvent::type type ); + /** + * Called from Canvas (when QCanvas::advance is called). + */ + virtual void update(); + + public slots: + virtual void undo(); + virtual void redo(); + virtual void cut(); + virtual void paste(); + /** + * Selects everything in the view. + */ + virtual void selectAll() = 0; + /** + * Increases the "height" of the selected items. + */ + void raiseZ(); + /** + * Decreases the "height" of the selected items. + */ + void lowerZ(); + /** + * Brings up a file dialog requesting the location of the file to export + * to, and then exports an image of the canvas. + */ + void exportToImage(); + /** + * Deletes whatever is selected. + */ + virtual void deleteSelection() {}; + /** + * Called when the user presses Escape (or similar) + */ + void cancelCurrentOperation(); + /** + * Sets the y-positions of the selected items to the average of the + * initial y-positions. + */ + void alignHorizontally(); + /** + * Sets the x-positions of the selected items to the average of the + * initial x-positions. + */ + void alignVertically(); + /** + * Averages out the horizontal spacing between the selected items. + */ + void distributeHorizontally(); + /** + * Averages out the vertical spacing between the selected items. + */ + void distributeVertically(); + /** + * Adds an items not in the Z ordering to the ordering, and removes any + * items from the Z ordering if they have parents. Then, calls all items + * found in the ordering to tell them their Z position. + */ + void slotUpdateZOrdering(); + /** + * Call this with ItemDocument::DrawAction to start drawing the given thing + */ + void slotSetDrawAction( int da ); + /** + * Sets the editing mode to repeatedly creating a CNItem + * with the given id. Usually called when the user double-clicks on + * the component box. + */ + void slotSetRepeatedItemId( const QString &id ); + /** + * Unsets the editing mode from repeatedly creating a CNItem + */ + void slotUnsetRepeatedItemId(); + /** + * Called when the user changes the configuration. + * This, for example, will tell the CNItems on the canvas to update + * their configuration. + */ + virtual void slotUpdateConfiguration(); + /** + * Enables / disables / selects various actions depending on + * what is selected or not. + */ + virtual void slotInitItemActions( Item *item = 0 ); + /** + * Process queued events (see ItemDocument::ItemDocumentEvent). + */ + void processItemDocumentEvents(); + + signals: + /** + * Emitted when a Item is selected + */ + void itemSelected( Item *item ); + /** + * Emitted when a Item is unselected + */ + void itemUnselected( Item *item = 0 ); + + protected: + /** + * Called from registerItem when a new item is added. + */ + virtual void itemAdded( Item * item ); + virtual void handleNewView( View *view ); + /** + * Set to true to remove buttons and grid and so on from the canvas, set false to put them back + */ + void setSVGExport( bool svgExport ); + void writeFile(); + /** + * Reinherit this if you want to add any options to the right-click context + */ + virtual void fillContextMenu( const QPoint & pos ); + /** + * Reads the background settings (grid-colour, underlying colour) from the Config settings, + * and generates the background pixmap from those settings + */ + void updateBackground(); + /** + * Sets the canvas size to both (a) containing all items present on the + * canvas, and (b) no smaller than the smallest view of the canvas. This + * function should only be called by processItemDocumentEvents - a resize + * request must be made with requestEvent. + */ + void resizeCanvasToItems(); + + Canvas *m_canvas; + KTechlab *p_ktechlab; + QStringList m_idList; + static int m_nextActionTicket; + uint m_nextIdNum; + bool m_bIsLoading; + QSize m_oldCanvasSize; + CMManager *m_cmManager; + ItemList m_itemDeleteList; + ItemList m_itemList; + IDDStack m_undoStack; + IDDStack m_redoStack; + ItemDocumentData * m_currentState; + int m_currentActionTicket; + ItemDocumentData * m_savedState; // Pointer to the document data that holds the state when it saved + QString m_fileExtensionInfo; // For displaying in the save file dialog + CanvasTip * m_canvasTip; + IntItemMap m_zOrder; + KActionMenu * m_pAlignmentAction; + QTimer * m_pEventTimer; + unsigned m_queuedEvents; // OR'ed together list of ItemDocumentEvent::type + + friend class ICNView; + friend class ItemView; +}; + + +/** +@author David Saxton +*/ +class Canvas : public QCanvas +{ + Q_OBJECT + public: + Canvas( ItemDocument *itemDocument, const char * name = 0 ); + + /** + * Sets a message to be displayed on the canvas for a brief period of + * time. If this is called with an empty message, then any existing + * message will be removed. + */ + void setMessage( const QString & message ); + + virtual void update(); + + public slots: + void slotSetAllChanged() { setAllChanged(); } + + protected: + virtual void drawBackground ( QPainter & painter, const QRect & clip ); + virtual void drawForeground ( QPainter & painter, const QRect & clip ); + + ItemDocument *p_itemDocument; + + QString m_message; + QTimer * m_pMessageTimeout; +}; + + +/** +@author David Saxton +*/ +class CanvasTip : public QCanvasText +{ +public: + CanvasTip( ItemDocument *itemDocument, QCanvas *qcanvas ); + virtual ~CanvasTip(); + + void displayVI( ECNode *node, const QPoint &pos ); + void displayVI( Connector *connector, const QPoint &pos ); + +protected: + virtual void draw( QPainter &p ); + bool updateVI(); + void display( const QPoint &pos ); + QString displayText( unsigned num ) const; + + QValueVector<double> m_v; + QValueVector<double> m_i; + ItemDocument *p_itemDocument; +}; + + +#endif diff --git a/src/itemdocumentdata.cpp b/src/itemdocumentdata.cpp new file mode 100644 index 0000000..a582231 --- /dev/null +++ b/src/itemdocumentdata.cpp @@ -0,0 +1,1340 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "connector.h" +#include "ecnode.h" +#include "ecsubcircuit.h" +#include "flowcodedocument.h" +#include "flowcontainer.h" +#include "fpnode.h" +#include "itemdocumentdata.h" +#include "itemlibrary.h" +#include "picitem.h" +#include "pinmapping.h" + +#include <kdebug.h> +#include <kio/netaccess.h> +#include <klocale.h> +#include <kmessagebox.h> +#include <ktempfile.h> +#include <qbitarray.h> +#include <qfile.h> + + +// Converts the QBitArray into a string (e.g. "F289A9E") that can be stored in an xml file +static QString toAsciiHex( QBitArray _data ) +{ + QBitArray data = _data; +// data = qCompress(data); + + // Pad out the data to a nice size + if ( (data.size() % 4) != 0 ) + { + data.detach(); + data.resize( data.size() + 4 - (data.size()%4) ); + } + + QString text; + for ( unsigned i = 0; i < data.size()/4; ++i ) + { + unsigned val = 0; + for ( unsigned j = 0; j < 4; ++j ) + val += (data[4*i+j] ? 1:0) << j; + + text += QString::number( val, 16 ); + } + return text; +} + +// Converts a string (e.g. "F289A9E") into a QBitArray, the opposite of the above function +static QBitArray toQBitArray( QString text ) +{ + unsigned size = text.length(); + QBitArray data(size*4); + + for ( unsigned i = 0; i < size; ++i ) + { + unsigned val = QString(text[i]).toInt( 0l, 16 ); + for ( unsigned j = 0; j < 4; ++j ) + data[4*i+j] = val & (1 << j); + } + +// data = qUncompress(data); + + return data; +} + + +//BEGIN class ItemDocumentData +ItemDocumentData::ItemDocumentData( uint documentType ) +{ + reset(); + m_documentType = documentType; +} + + +ItemDocumentData::~ItemDocumentData() +{ +} + + +void ItemDocumentData::reset() +{ + m_itemDataMap.clear(); + m_connectorDataMap.clear(); + m_nodeDataMap.clear(); + m_microData.reset(); + m_documentType = Document::dt_none; +} + + +bool ItemDocumentData::loadData( const KURL &url ) +{ + QString target; + if ( !KIO::NetAccess::download( url, target, 0l ) ) + { + // If the file could not be downloaded, for example does not + // exist on disk, NetAccess will tell us what error to use + KMessageBox::error( 0l, KIO::NetAccess::lastErrorString() ); + + return false; + } + + QFile file(target); + if ( !file.open( IO_ReadOnly ) ) + { + KMessageBox::sorry( 0l, i18n("Could not open %1 for reading").arg(target) ); + return false; + } + + QString xml; + QTextStream textStream( &file ); + while ( !textStream.eof() ) + xml += textStream.readLine() + '\n'; + + file.close(); + return fromXML(xml); +} + + +bool ItemDocumentData::fromXML( const QString &xml ) +{ + reset(); + + QDomDocument doc( "KTechlab" ); + QString errorMessage; + if ( !doc.setContent( xml, &errorMessage ) ) + { + KMessageBox::sorry( 0l, i18n("Couldn't parse xml:\n%1").arg(errorMessage) ); + return false; + } + + QDomElement root = doc.documentElement(); + + QDomNode node = root.firstChild(); + while ( !node.isNull() ) + { + QDomElement element = node.toElement(); + if ( !element.isNull() ) + { + const QString tagName = element.tagName(); + + if ( tagName == "item" ) + elementToItemData(element); + + else if ( tagName == "node" ) + elementToNodeData(element); + + else if ( tagName == "connector" ) + elementToConnectorData(element); + + else if ( tagName == "pic-settings" || tagName == "micro" ) + elementToMicroData(element); + + else if ( tagName == "code" ) + ; // do nothing - we no longer use this tag + + else + kdWarning() << k_funcinfo << "Unrecognised element tag name: "<<tagName<<endl; + } + + node = node.nextSibling(); + } + + return true; +} + + +bool ItemDocumentData::saveData( const KURL &url ) +{ + + if ( url.isLocalFile() ) + { + QFile file( url.path() ); + if ( !file.open(IO_WriteOnly) ) + { + KMessageBox::sorry( 0l, i18n("Could not open '%1' for writing. Check that you have write permissions").arg(url.path()), i18n("Saving File") ); + return false; + } + + QTextStream stream(&file); + stream << toXML(); + file.close(); + } + else + { + KTempFile file; + *file.textStream() << toXML(); + file.close(); + + if ( !KIO::NetAccess::upload( file.name(), url, 0l ) ) + { + KMessageBox::error( 0l, KIO::NetAccess::lastErrorString() ); + return false; + } + } + + return true; +} + + +QString ItemDocumentData::toXML() +{ + QDomDocument doc("KTechlab"); + //TODO Add revision information to save file + + QDomElement root = doc.createElement("document"); + root.setAttribute( "type", documentTypeString() ); + doc.appendChild(root); + + { + const ItemDataMap::iterator end = m_itemDataMap.end(); + for ( ItemDataMap::iterator it = m_itemDataMap.begin(); it != end; ++it ) + { + QDomElement node = itemDataToElement( doc, it.data() ); + node.setAttribute( "id", it.key() ); + root.appendChild(node); + } + } + { + const ConnectorDataMap::iterator end = m_connectorDataMap.end(); + for ( ConnectorDataMap::iterator it = m_connectorDataMap.begin(); it != end; ++it ) + { + QDomElement node = connectorDataToElement( doc, it.data() ); + node.setAttribute( "id", it.key() ); + root.appendChild(node); + } + } + { + const NodeDataMap::iterator end = m_nodeDataMap.end(); + for ( NodeDataMap::iterator it = m_nodeDataMap.begin(); it != end; ++it ) + { + QDomElement node = nodeDataToElement( doc, it.data() ); + node.setAttribute( "id", it.key() ); + root.appendChild(node); + } + } + if ( m_documentType == Document::dt_flowcode ) + { + QDomElement node = microDataToElement(doc); + root.appendChild(node); + } + + return doc.toString(); +} + + + +//BEGIN functions for generating / reading QDomElements +QDomElement ItemDocumentData::microDataToElement( QDomDocument &doc ) +{ + QDomElement node = doc.createElement("micro"); + node.setAttribute( "id", m_microData.id ); + + { + const PinMappingMap::iterator end = m_microData.pinMappings.end(); + for ( PinMappingMap::iterator it = m_microData.pinMappings.begin(); it != end; ++it ) + { + QDomElement pinMapNode = doc.createElement("pinmap"); + + QString type; + switch ( it.data().type() ) + { + case PinMapping::SevenSegment: + type = "sevensegment"; + break; + + case PinMapping::Keypad_4x3: + type = "keypad_4x3"; + break; + + case PinMapping::Keypad_4x4: + type = "keypad_4x4"; + break; + + case PinMapping::Invalid: + break; + } + + pinMapNode.setAttribute( "id", it.key() ); + pinMapNode.setAttribute( "type", type ); + pinMapNode.setAttribute( "map", it.data().pins().join(" ") ); + + node.appendChild(pinMapNode); + } + } + + { + const PinDataMap::iterator end = m_microData.pinMap.end(); + for ( PinDataMap::iterator it = m_microData.pinMap.begin(); it != end; ++it ) + { + QDomElement pinNode = doc.createElement("pin"); + + pinNode.setAttribute( "id", it.key() ); + pinNode.setAttribute( "type", (it.data().type == PinSettings::pt_input) ? "input" : "output" ); + pinNode.setAttribute( "state", (it.data().state == PinSettings::ps_off) ? "off" : "on" ); + + node.appendChild(pinNode); + } + } + + { + const QStringMap::iterator end = m_microData.variableMap.end(); + for ( QStringMap::iterator it = m_microData.variableMap.begin(); it != end; ++it ) + { + QDomElement variableNode = doc.createElement("variable"); + + variableNode.setAttribute( "name", it.key() ); + variableNode.setAttribute( "value", it.data() ); + + node.appendChild(variableNode); + } + } + + return node; +} + + +void ItemDocumentData::elementToMicroData( QDomElement element ) +{ + QString id = element.attribute( "id", QString::null ); + + if ( id.isNull() ) + id = element.attribute( "pic", QString::null ); + + if ( id.isNull() ) + { + kdError() << k_funcinfo << "Could not find id in element" << endl; + return; + } + + m_microData.reset(); + m_microData.id = id; + + QDomNode node = element.firstChild(); + while ( !node.isNull() ) + { + QDomElement childElement = node.toElement(); + if ( !childElement.isNull() ) + { + const QString tagName = childElement.tagName(); + + if ( tagName == "pinmap" ) + { + QString id = childElement.attribute( "id", QString::null ); + QString typeString = childElement.attribute( "type", QString::null ); + + if ( !id.isEmpty() && !typeString.isEmpty() ) + { + PinMapping::Type type = PinMapping::Invalid; + + if ( typeString == "sevensegment" ) + type = PinMapping::SevenSegment; + + else if ( typeString == "keypad_4x3" ) + type = PinMapping::Keypad_4x3; + + else if ( typeString == "keypad_4x4" ) + type = PinMapping::Keypad_4x4; + + PinMapping pinMapping( type ); + pinMapping.setPins( QStringList::split( " ", childElement.attribute( "map", 0 ) ) ); + + m_microData.pinMappings[id] = pinMapping; + } + } + + else if ( tagName == "pin" ) + { + QString pinID = childElement.attribute( "id", QString::null ); + if ( !pinID.isEmpty() ) + { + m_microData.pinMap[pinID].type = (childElement.attribute( "type", "input" ) == "input" ) ? PinSettings::pt_input : PinSettings::pt_output; + m_microData.pinMap[pinID].state = (childElement.attribute( "state", "off" ) == "off" ) ? PinSettings::ps_off : PinSettings::ps_on; + } + } + + else if ( tagName == "variable" ) + { + QString variableId = childElement.attribute( "name", QString::null ); + m_microData.variableMap[variableId] = childElement.attribute( "value", QString::null ); + } + + else + kdError() << k_funcinfo << "Unrecognised element tag name: "<<tagName<<endl; + } + + node = node.nextSibling(); + } +} + + +QDomElement ItemDocumentData::itemDataToElement( QDomDocument &doc, const ItemData &itemData ) +{ + QDomElement node = doc.createElement("item"); + node.setAttribute( "type", itemData.type ); + node.setAttribute( "x", itemData.x ); + node.setAttribute( "y", itemData.y ); + if ( itemData.z != -1 ) + node.setAttribute( "z", itemData.z ); + if ( itemData.setSize ) + { + node.setAttribute( "offset-x", itemData.size.x() ); + node.setAttribute( "offset-y", itemData.size.y() ); + node.setAttribute( "width", itemData.size.width() ); + node.setAttribute( "height", itemData.size.height() ); + } + + // If the "orientation" is >= 0, then set by a FlowPart, so we don't need to worry about the angle / flip + if ( itemData.orientation >= 0 ) + { + node.setAttribute( "orientation", itemData.orientation ); + } + else + { + node.setAttribute( "angle", itemData.angleDegrees ); + node.setAttribute( "flip", itemData.flipped ); + } + + if ( !itemData.parentId.isEmpty() ) + node.setAttribute( "parent", itemData.parentId ); + + const QStringMap::const_iterator stringEnd = itemData.dataString.end(); + for ( QStringMap::const_iterator it = itemData.dataString.begin(); it != stringEnd; ++it ) + { + QDomElement e = doc.createElement("data"); + node.appendChild(e); + e.setAttribute( "id", it.key() ); + e.setAttribute( "type", "string" ); + e.setAttribute( "value", it.data() ); + } + + const DoubleMap::const_iterator numberEnd = itemData.dataNumber.end(); + for ( DoubleMap::const_iterator it = itemData.dataNumber.begin(); it != numberEnd; ++it ) + { + QDomElement e = doc.createElement("data"); + node.appendChild(e); + e.setAttribute( "id", it.key() ); + e.setAttribute( "type", "number" ); + e.setAttribute( "value", QString::number(it.data()) ); + } + + const QColorMap::const_iterator colorEnd = itemData.dataColor.end(); + for ( QColorMap::const_iterator it = itemData.dataColor.begin(); it != colorEnd; ++it ) + { + QDomElement e = doc.createElement("data"); + node.appendChild(e); + e.setAttribute( "id", it.key() ); + e.setAttribute( "type", "color" ); + e.setAttribute( "value", it.data().name() ); + } + + const QBitArrayMap::const_iterator rawEnd = itemData.dataRaw.end(); + for ( QBitArrayMap::const_iterator it = itemData.dataRaw.begin(); it != rawEnd; ++it ) + { + QDomElement e = doc.createElement("data"); + node.appendChild(e); + e.setAttribute( "id", it.key() ); + e.setAttribute( "type", "raw" ); + e.setAttribute( "value", toAsciiHex(it.data()) ); + } + + const BoolMap::const_iterator boolEnd = itemData.dataBool.end(); + for ( BoolMap::const_iterator it = itemData.dataBool.begin(); it != boolEnd; ++it ) + { + QDomElement e = doc.createElement("data"); + node.appendChild(e); + e.setAttribute( "id", it.key() ); + e.setAttribute( "type", "bool" ); + e.setAttribute( "value", QString::number(it.data()) ); + } + + const BoolMap::const_iterator buttonEnd = itemData.buttonMap.end(); + for ( BoolMap::const_iterator it = itemData.buttonMap.begin(); it != buttonEnd; ++it ) + { + QDomElement e = doc.createElement("button"); + node.appendChild(e); + e.setAttribute( "id", it.key() ); + e.setAttribute( "state", QString::number(it.data()) ); + } + + const IntMap::const_iterator sliderEnd = itemData.sliderMap.end(); + for ( IntMap::const_iterator it = itemData.sliderMap.begin(); it != sliderEnd; ++it ) + { + QDomElement e = doc.createElement("slider"); + node.appendChild(e); + e.setAttribute( "id", it.key() ); + e.setAttribute( "value", QString::number(it.data()) ); + } + + return node; +} + + +void ItemDocumentData::elementToItemData( QDomElement element ) +{ + QString id = element.attribute( "id", QString::null ); + if ( id.isNull() ) + { + kdError() << k_funcinfo << "Could not find id in element" << endl; + return; + } + + ItemData itemData; + itemData.type = element.attribute( "type", QString::null ); + itemData.x = element.attribute( "x", "120" ).toInt(); + itemData.y = element.attribute( "y", "120" ).toInt(); + itemData.z = element.attribute( "z", "-1" ).toInt(); + + if ( element.hasAttribute("width") && + element.hasAttribute("height") ) + { + itemData.setSize = true; + itemData.size = QRect( element.attribute( "offset-x", "0" ).toInt(), + element.attribute( "offset-y", "0" ).toInt(), + element.attribute( "width", "120" ).toInt(), + element.attribute( "height", "120" ).toInt() ); + } + else + itemData.setSize = false; + + itemData.angleDegrees = element.attribute( "angle", "0" ).toInt(); + itemData.flipped = element.attribute( "flip", "0" ).toInt(); + itemData.orientation = element.attribute( "orientation", "-1" ).toInt(); + itemData.parentId = element.attribute( "parent", QString::null ); + + m_itemDataMap[id] = itemData; + + QDomNode node = element.firstChild(); + while ( !node.isNull() ) + { + QDomElement childElement = node.toElement(); + if ( !childElement.isNull() ) + { + const QString tagName = childElement.tagName(); + + if ( tagName == "item" ) + { + // We're reading in a file saved in the older format, with + // child items nestled, so we must specify that the new item + // has the currently parsed item as its parent. + elementToItemData(childElement); + QString childId = childElement.attribute( "id", QString::null ); + if ( !childId.isNull() ) + m_itemDataMap[childId].parentId = id; + } + + else if ( tagName == "data" ) + { + QString dataId = childElement.attribute( "id", QString::null ); + if ( !dataId.isNull() ) + { + QString dataType = childElement.attribute( "type", QString::null ); + QString value = childElement.attribute( "value", QString::null ); + + if ( dataType == "string" || dataType == "multiline" ) + m_itemDataMap[id].dataString[dataId] = value; + else if ( dataType == "number" ) + m_itemDataMap[id].dataNumber[dataId] = value.toDouble(); + else if ( dataType == "color" ) + m_itemDataMap[id].dataColor[dataId] = QColor(value); + else if ( dataType == "raw" ) + m_itemDataMap[id].dataRaw[dataId] = toQBitArray(value); + else if ( dataType == "bool" ) + m_itemDataMap[id].dataBool[dataId] = bool(value.toInt()); + else + kdError() << k_funcinfo << "Unknown data type of \""<<dataType<<"\" with id \""<<dataId<<"\""<<endl; + } + } + + else if ( tagName == "button" ) + { + QString buttonId = childElement.attribute( "id", QString::null ); + if ( !buttonId.isNull() ) + m_itemDataMap[id].buttonMap[buttonId] = childElement.attribute( "state", "0" ).toInt(); + } + + else if ( tagName == "slider" ) + { + QString sliderId = childElement.attribute( "id", QString::null ); + if ( !sliderId.isNull() ) + m_itemDataMap[id].sliderMap[sliderId] = childElement.attribute( "value", "0" ).toInt(); + } + + else if ( tagName == "child-node" ) + ; // Tag name was used in 0.1 file save format + + else + kdError() << k_funcinfo << "Unrecognised element tag name: "<<tagName<<endl; + } + + node = node.nextSibling(); + } +} + + +QDomElement ItemDocumentData::nodeDataToElement( QDomDocument &doc, const NodeData &nodeData ) +{ + QDomElement node = doc.createElement("node"); + node.setAttribute( "x", nodeData.x ); + node.setAttribute( "y", nodeData.y ); + return node; +} + + +void ItemDocumentData::elementToNodeData( QDomElement element ) +{ + QString id = element.attribute( "id", QString::null ); + if ( id.isNull() ) + { + kdError() << k_funcinfo << "Could not find id in element" << endl; + return; + } + + NodeData nodeData; + nodeData.x = element.attribute( "x", "120" ).toInt(); + nodeData.y = element.attribute( "y", "120" ).toInt(); + + m_nodeDataMap[id] = nodeData; +} + + +QDomElement ItemDocumentData::connectorDataToElement( QDomDocument &doc, const ConnectorData &connectorData ) +{ + QDomElement node = doc.createElement("connector"); + + node.setAttribute( "manual-route", connectorData.manualRoute ); + + QString route; + const QPointList::const_iterator end = connectorData.route.end(); + for ( QPointList::const_iterator it = connectorData.route.begin(); it != end; ++it ) + { + route.append( QString::number((*it).x())+"," ); + route.append( QString::number((*it).y())+"," ); + } + node.setAttribute( "route", route ); + + if ( connectorData.startNodeIsChild ) + { + node.setAttribute( "start-node-is-child", 1 ); + node.setAttribute( "start-node-cid", connectorData.startNodeCId ); + node.setAttribute( "start-node-parent", connectorData.startNodeParent ); + } + else + { + node.setAttribute( "start-node-is-child", 0 ); + node.setAttribute( "start-node-id", connectorData.startNodeId ); + } + + + if ( connectorData.endNodeIsChild ) + { + node.setAttribute( "end-node-is-child", 1 ); + node.setAttribute( "end-node-cid", connectorData.endNodeCId ); + node.setAttribute( "end-node-parent", connectorData.endNodeParent ); + } + else + { + node.setAttribute( "end-node-is-child", 0 ); + node.setAttribute( "end-node-id", connectorData.endNodeId ); + } + + return node; +} + + +void ItemDocumentData::elementToConnectorData( QDomElement element ) +{ + QString id = element.attribute( "id", QString::null ); + if ( id.isNull() ) + { + kdError() << k_funcinfo << "Could not find id in element" << endl; + return; + } + + ConnectorData connectorData; + + connectorData.manualRoute = element.attribute( "manual-route", "0" ); + QString route = element.attribute( "route", "" ); + + QStringList points = QStringList::split( ",", route ); + const QStringList::iterator end = points.end(); + for ( QStringList::iterator it = points.begin(); it != end; ++it ) + { + int x = (*it).toInt(); + it++; + if ( it != end ) + { + int y = (*it).toInt(); + connectorData.route.append( QPoint(x,y) ); + } + } + + connectorData.startNodeIsChild = element.attribute( "start-node-is-child", "0" ).toInt(); + if ( connectorData.startNodeIsChild ) + { + connectorData.startNodeCId = element.attribute( "start-node-cid", QString::null ); + connectorData.startNodeParent = element.attribute( "start-node-parent", QString::null ); + } + else + connectorData.startNodeId = element.attribute( "start-node-id", QString::null ); + + + connectorData.endNodeIsChild = element.attribute( "end-node-is-child", "0" ).toInt(); + if ( connectorData.endNodeIsChild ) + { + connectorData.endNodeCId = element.attribute( "end-node-cid", QString::null ); + connectorData.endNodeParent = element.attribute( "end-node-parent", QString::null ); + } + else + connectorData.endNodeId = element.attribute( "end-node-id", QString::null ); + + m_connectorDataMap[id] = connectorData; +} +//END functions for generating / reading QDomElements + + + +QString ItemDocumentData::documentTypeString() const +{ + switch (m_documentType) + { + case Document::dt_circuit: + return "circuit"; + break; + case Document::dt_flowcode: + return "flowcode"; + break; + case Document::dt_mechanics: + return "mechanics"; + break; + case Document::dt_text: + case Document::dt_none: + default: + return "none"; + break; + } +} + + +QString ItemDocumentData::revisionString() const +{ + return "1"; +} + + +void ItemDocumentData::saveDocumentState( ItemDocument *itemDocument ) +{ + if (!itemDocument) + return; + + reset(); + + addItems( itemDocument->itemList() ); + + if ( ICNDocument *icnd = dynamic_cast<ICNDocument*>(itemDocument) ) + { + addConnectors( icnd->connectorList() ); + addNodes( icnd->nodeList() ); + + if ( FlowCodeDocument *fcd = dynamic_cast<FlowCodeDocument*>(itemDocument) ) + { + if ( fcd->microSettings() ) + setMicroData( fcd->microSettings()->microData() ); + } + } + + m_documentType = itemDocument->type(); +} + + +void ItemDocumentData::generateUniqueIDs( ItemDocument *itemDocument ) +{ + if (!itemDocument) + return; + + QStringMap replaced; + replaced[""] = QString::null; + replaced[QString::null] = QString::null; + + ItemDataMap newItemDataMap; + ConnectorDataMap newConnectorDataMap; + NodeDataMap newNodeDataMap; + + //BEGIN Go through and replace the old ids + { + const ItemDataMap::iterator end = m_itemDataMap.end(); + for ( ItemDataMap::iterator it = m_itemDataMap.begin(); it != end; ++it ) + { + if ( !replaced.contains( it.key() ) ) + replaced[it.key()] = itemDocument->generateUID(it.key()); + + newItemDataMap[replaced[it.key()]] = it.data(); + } + } + { + const NodeDataMap::iterator end = m_nodeDataMap.end(); + for ( NodeDataMap::iterator it = m_nodeDataMap.begin(); it != end; ++it ) + { + if ( !replaced.contains( it.key() ) ) + replaced[it.key()] = itemDocument->generateUID(it.key()); + + newNodeDataMap[replaced[it.key()]] = it.data(); + } + } + { + const ConnectorDataMap::iterator end = m_connectorDataMap.end(); + for ( ConnectorDataMap::iterator it = m_connectorDataMap.begin(); it != end; ++it ) + { + if ( !replaced.contains( it.key() ) ) + replaced[it.key()] = itemDocument->generateUID(it.key()); + + newConnectorDataMap[replaced[it.key()]] = it.data(); + } + } + //END Go through and replace the old ids + + //BEGIN Go through and replace the internal references to the ids + { + const ItemDataMap::iterator end = newItemDataMap.end(); + for ( ItemDataMap::iterator it = newItemDataMap.begin(); it != end; ++it ) + { + it.data().parentId = replaced[it.data().parentId]; + } + } + { + const ConnectorDataMap::iterator end = newConnectorDataMap.end(); + for ( ConnectorDataMap::iterator it = newConnectorDataMap.begin(); it != end; ++it ) + { + it.data().startNodeParent = replaced[it.data().startNodeParent]; + it.data().endNodeParent = replaced[it.data().endNodeParent]; + + it.data().startNodeId = replaced[it.data().startNodeId]; + it.data().endNodeId = replaced[it.data().endNodeId]; + } + } + //END Go through and replace the internal references to the ids + + + m_itemDataMap = newItemDataMap; + m_connectorDataMap = newConnectorDataMap; + m_nodeDataMap = newNodeDataMap; +} + + +void ItemDocumentData::translateContents( int dx, int dy ) +{ + //BEGIN Go through and replace the old ids + { + const ItemDataMap::iterator end = m_itemDataMap.end(); + for ( ItemDataMap::iterator it = m_itemDataMap.begin(); it != end; ++it ) + { + it.data().x += dx; + it.data().y += dx; + } + } + { + const NodeDataMap::iterator end = m_nodeDataMap.end(); + for ( NodeDataMap::iterator it = m_nodeDataMap.begin(); it != end; ++it ) + { + it.data().x += dx; + it.data().y += dy; + } + } + { + const ConnectorDataMap::iterator end = m_connectorDataMap.end(); + for ( ConnectorDataMap::iterator it = m_connectorDataMap.begin(); it != end; ++it ) + { + const QPointList::iterator routeEnd = it.data().route.end(); + for ( QPointList::iterator routeIt = it.data().route.begin(); routeIt != routeEnd; ++routeIt ) + { + *routeIt += QPoint( dx/8, dy/8 ); + } + } + } +} + + +void ItemDocumentData::restoreDocument( ItemDocument *itemDocument ) +{ + if ( !itemDocument ) + return; + + ICNDocument *icnd = dynamic_cast<ICNDocument*>(itemDocument); + FlowCodeDocument *fcd = dynamic_cast<FlowCodeDocument*>(icnd); + if ( fcd && !m_microData.id.isEmpty() ) + { + fcd->setPicType(m_microData.id); + fcd->microSettings()->restoreFromMicroData(m_microData); + } + + mergeWithDocument(itemDocument,false); + + { + ItemList removeItems = itemDocument->itemList(); + removeItems.remove((Item*)0l); + + const ItemDataMap::iterator end = m_itemDataMap.end(); + for ( ItemDataMap::iterator it = m_itemDataMap.begin(); it != end; ++it ) + removeItems.remove( itemDocument->itemWithID(it.key()) ); + + const ItemList::iterator removeEnd = removeItems.end(); + for ( ItemList::iterator it = removeItems.begin(); it != removeEnd; ++it ) + { + if ( (*it)->canvas() && (*it)->type() != PicItem::typeString() ) + (*it)->removeItem(); + } + } + + if (icnd) + { + { + NodeList removeNodes = icnd->nodeList(); + removeNodes.remove((Node*)0l); + + const NodeDataMap::iterator end = m_nodeDataMap.end(); + for ( NodeDataMap::iterator it = m_nodeDataMap.begin(); it != end; ++it ) + removeNodes.remove( icnd->nodeWithID( it.key() ) ); + + const NodeList::iterator removeEnd = removeNodes.end(); + for ( NodeList::iterator it = removeNodes.begin(); it != removeEnd; ++it ) + { + if ( (*it)->canvas() && !(*it)->isChildNode() ) + (*it)->removeNode(); + } + } + { + ConnectorList removeConnectors = icnd->connectorList(); + removeConnectors.remove((Connector*)0l); + + const ConnectorDataMap::iterator end = m_connectorDataMap.end(); + for ( ConnectorDataMap::iterator it = m_connectorDataMap.begin(); it != end; ++it ) + removeConnectors.remove( icnd->connectorWithID(it.key()) ); + + const ConnectorList::iterator removeEnd = removeConnectors.end(); + for ( ConnectorList::iterator it = removeConnectors.begin(); it != removeEnd; ++it ) + { + if ( (*it)->canvas() ) + (*it)->removeConnector(); + } + } + } + + itemDocument->flushDeleteList(); +} + + +void ItemDocumentData::mergeWithDocument( ItemDocument *itemDocument, bool selectNew ) +{ + if ( !itemDocument ) + return; + + ICNDocument *icnd = dynamic_cast<ICNDocument*>(itemDocument); + + //BEGIN Restore Nodes + if (icnd) + { + const NodeDataMap::iterator nodeEnd = m_nodeDataMap.end(); + for ( NodeDataMap::iterator it = m_nodeDataMap.begin(); it != nodeEnd; ++it ) + { + if ( !icnd->nodeWithID( it.key() ) ) + { + QString id = it.key(); + if ( itemDocument->type() == Document::dt_circuit ) + new ECNode( icnd, Node::ec_junction, Node::dir_up, QPoint( int(it.data().x), int(it.data().y) ), &id ); + + else if ( itemDocument->type() == Document::dt_flowcode ) + new FPNode( icnd, Node::fp_junction, Node::dir_up, QPoint( int(it.data().x), int(it.data().y) ), &id ); + } + } + for ( NodeDataMap::iterator it = m_nodeDataMap.begin(); it != nodeEnd; ++it ) + { + Node *node = icnd->nodeWithID( it.key() ); + if (node) + node->move( it.data().x, it.data().y ); + } + } + //END Restore Nodes + + + //BEGIN Restore items + const ItemDataMap::iterator itemEnd = m_itemDataMap.end(); + for ( ItemDataMap::iterator it = m_itemDataMap.begin(); it != itemEnd; ++it ) + { + if ( !it.data().type.isEmpty() && !itemDocument->itemWithID( it.key() ) ) + { + Item *item = itemLibrary()->createItem( it.data().type, itemDocument, false, it.key(), false ); + if ( item && !itemDocument->isValidItem(item) ) + { + kdWarning() << "Attempted to create invalid item with id: " << it.key() << endl; + item->removeItem(); + itemDocument->flushDeleteList(); + item = 0l; + } + if (item) + { + //HACK We move the item now before restoreFromItemData is called later, in case it is to be parented + //(as we don't want to move children)... + item->move( it.data().x, it.data().y ); + } + } + } + for ( ItemDataMap::iterator it = m_itemDataMap.begin(); it != itemEnd; ++it ) + { + Item *item = itemDocument->itemWithID(it.key()); + if (!item) + continue; + + item->restoreFromItemData( it.data() ); + item->finishedCreation(); + if (selectNew) + itemDocument->select(item); + item->show(); + } + //END Restore Items + + //BEGIN Restore Connectors + if (icnd) + { + const ConnectorDataMap::iterator connectorEnd = m_connectorDataMap.end(); + for ( ConnectorDataMap::iterator it = m_connectorDataMap.begin(); it != connectorEnd; ++it ) + { + if ( icnd->connectorWithID( it.key() ) ) + continue; + + QString id = it.key(); + Node *startNode = 0l; + Node *endNode = 0l; + + if ( it.data().startNodeIsChild ) + { + CNItem *item = icnd->cnItemWithID( it.data().startNodeParent ); + if (!item) + kdError() << k_funcinfo << "Unable to find node parent with id: "<<it.data().startNodeParent<<endl; + else + startNode = item->childNode( it.data().startNodeCId ); + } + else + startNode = icnd->nodeWithID( it.data().startNodeId ); + + if ( it.data().endNodeIsChild ) + { + CNItem *item = icnd->cnItemWithID( it.data().endNodeParent ); + if (!item) + kdError() << k_funcinfo << "Unable to find node parent with id: "<<it.data().endNodeParent<<endl; + else + endNode = item->childNode( it.data().endNodeCId ); + } + else + endNode = icnd->nodeWithID( it.data().endNodeId ); + + if ( !startNode || !endNode ) + { + kdError() << k_funcinfo << "End and start nodes for the connector do not both exist" << endl; + } + else + { + Connector *connector = new Connector( startNode, endNode, icnd, &id ); + + startNode->addOutputConnector(connector); + endNode->addInputConnector(connector); + } + } + for ( ConnectorDataMap::iterator it = m_connectorDataMap.begin(); it != connectorEnd; ++it ) + { + Connector *connector = icnd->connectorWithID( it.key() ); + if (connector) + { + connector->restoreFromConnectorData( it.data() ); + if (selectNew) + icnd->select(connector); + } + } + } + //END Restore Connectors + + // This is kind of hackish, but never mind + if ( FlowCodeDocument *fcd = dynamic_cast<FlowCodeDocument*>(itemDocument) ) + { + const ItemList fcdItems = fcd->itemList(); + const ItemList::const_iterator fcdItemsEnd = fcdItems.constEnd(); + for ( ItemList::const_iterator it = fcdItems.constBegin(); it != fcdItemsEnd; ++it ) + { + if ( FlowContainer * fc = dynamic_cast<FlowContainer*>((Item*)*it) ) + fc->updateContainedVisibility(); + } + } +} + + +void ItemDocumentData::setMicroData( const MicroData &data ) +{ + m_microData = data; +} + + +void ItemDocumentData::addItems( const ItemList &itemList ) +{ + const ItemList::const_iterator end = itemList.constEnd(); + for ( ItemList::const_iterator it = itemList.constBegin(); it != end; ++it ) + { + if ( *it && (*it)->canvas() && (*it)->type() != PicItem::typeString() ) + addItemData( (*it)->itemData(), (*it)->id() ); + } +} + + +void ItemDocumentData::addConnectors( const ConnectorList &connectorList ) +{ + const ConnectorList::const_iterator end = connectorList.constEnd(); + for ( ConnectorList::const_iterator it = connectorList.constBegin(); it != end; ++it ) + { + if ( *it && (*it)->canvas() ) + { + if ( (*it)->startNode() && (*it)->endNode() ) + addConnectorData( (*it)->connectorData(), (*it)->id() ); + + else + kdDebug() << k_funcinfo << " *it="<<*it<<" (*it)->startNode()="<<(*it)->startNode()<<" (*it)->endNode()="<<(*it)->endNode()<<endl; + } + } +} + + +void ItemDocumentData::addNodes( const NodeList &nodeList ) +{ + const NodeList::const_iterator end = nodeList.constEnd(); + for ( NodeList::const_iterator it = nodeList.constBegin(); it != end; ++it ) + { + if ( *it && (*it)->canvas() && !(*it)->isChildNode() ) + addNodeData( (*it)->nodeData(), (*it)->id() ); + } +} + + +void ItemDocumentData::addItemData( ItemData itemData, QString id ) +{ + m_itemDataMap[id] = itemData; +} + + +void ItemDocumentData::addConnectorData( ConnectorData connectorData, QString id ) +{ + m_connectorDataMap[id] = connectorData; +} + + +void ItemDocumentData::addNodeData( NodeData nodeData, QString id ) +{ + m_nodeDataMap[id] = nodeData; +} +//END class ItemDocumentData + + +//BEGIN class ItemData +ItemData::ItemData() +{ + x = 0; + y = 0; + z = -1; + angleDegrees = 0; + flipped = false; + orientation = -1; + setSize = false; +} +//END class ItemData + + +//BEGIN class ConnectorData +ConnectorData::ConnectorData() +{ + manualRoute = false; + startNodeIsChild = false; + endNodeIsChild = false; +} +//END class ConnectorData + + +//BEGIN class NodeData +NodeData::NodeData() +{ + x = 0; + y = 0; +} +//END class NodeDaata + + +//BEGIN class PinData +PinData::PinData() +{ + type = PinSettings::pt_input; + state = PinSettings::ps_off; +} +//END class PinData + + +//BEGIN class MicroData +MicroData::MicroData() +{ +} + + +void MicroData::reset() +{ + id = QString::null; + pinMap.clear(); +} +//END class MicroData + + +//BEGIN class SubcircuitData +SubcircuitData::SubcircuitData() + : ItemDocumentData( Document::dt_circuit ) +{ +} + + +void SubcircuitData::initECSubcircuit( ECSubcircuit * ecSubcircuit ) +{ + if (!ecSubcircuit) + return; + + generateUniqueIDs( ecSubcircuit->itemDocument() ); + + // Generate a list of the External Connections, sorting by x coordinate + std::multimap< double, QString > extCon; + ItemDataMap::iterator itemEnd = m_itemDataMap.end(); + for ( ItemDataMap::iterator it = m_itemDataMap.begin(); it != itemEnd; ++it ) + { + if ( it.data().type == "ec/external_connection" ) + extCon.insert( std::make_pair( it.data().x, it.key() ) ); + } + + // How many external connections do we have? + ecSubcircuit->setNumExtCon(extCon.size()); + + // Sort the connections into the pins of the subcircuit by y coordinate + std::multimap< double, QString > leftPins; + std::multimap< double, QString > rightPins; + int at = 0; + int size = (extCon.size()/2) + (extCon.size()%2); + const std::multimap< double, QString >::iterator extConEnd = extCon.end(); + for ( std::multimap< double, QString >::iterator it = extCon.begin(); it != extConEnd; ++it ) + { + if ( at < size ) + leftPins.insert( std::make_pair( m_itemDataMap[it->second].y, it->second ) ); + else + rightPins.insert( std::make_pair( m_itemDataMap[it->second].y, it->second ) ); + at++; + } + + // Remove the external connections (recording their names and associated numerical position) + int nodeId = 0; + typedef QMap<QString,int> IntMap; + IntMap nodeMap; + const std::multimap< double, QString >::iterator leftPinsEnd = leftPins.end(); + for ( std::multimap< double, QString >::iterator it = leftPins.begin(); it != leftPinsEnd; ++it ) + { + nodeMap[ it->second ] = nodeId; + ecSubcircuit->setExtConName( nodeId, m_itemDataMap[ it->second ].dataString["name"].data() ); + nodeId++; + m_itemDataMap.remove( it->second ); + } + nodeId = extCon.size()-1; + const std::multimap< double, QString >::iterator rightPinsEnd = rightPins.end(); + for ( std::multimap< double, QString >::iterator it = rightPins.begin(); it != rightPinsEnd; ++it ) + { + nodeMap[ it->second ] = nodeId; + ecSubcircuit->setExtConName( nodeId, m_itemDataMap[ it->second ].dataString["name"].data() ); + nodeId--; + m_itemDataMap.remove( it->second ); + } + + // Replace connector references to the old External Connectors to the nodes + const ConnectorDataMap::iterator connectorEnd = m_connectorDataMap.end(); + for ( ConnectorDataMap::iterator it = m_connectorDataMap.begin(); it != connectorEnd; ++it ) + { + if ( it.data().startNodeIsChild && nodeMap.contains(it.data().startNodeParent ) ) + { + it.data().startNodeCId = QString::number( nodeMap[it.data().startNodeParent] ); + it.data().startNodeParent = ecSubcircuit->id(); + + } + if ( it.data().endNodeIsChild && nodeMap.contains(it.data().endNodeParent ) ) + { + it.data().endNodeCId = QString::number( nodeMap[it.data().endNodeParent] ); + it.data().endNodeParent = ecSubcircuit->id(); + } + } + + // Create all the new stuff + mergeWithDocument( ecSubcircuit->itemDocument(), false ); + + // Parent and hide the new stuff + itemEnd = m_itemDataMap.end(); + for ( ItemDataMap::iterator it = m_itemDataMap.begin(); it != itemEnd; ++it) + { + Component * component = static_cast<Component*>(ecSubcircuit->itemDocument()->itemWithID( it.key() )); + if (component) + { + component->setParentItem(ecSubcircuit); + component->updateConnectorPoints(false); + component->setVisible(false); + component->setCanvas(0l); + ecSubcircuit->connect( ecSubcircuit, SIGNAL(subcircuitDeleted()), component, SLOT(removeItem()) ); + } + } + for ( ConnectorDataMap::iterator it = m_connectorDataMap.begin(); it != connectorEnd; ++it ) + { + Connector * connector = (static_cast<ICNDocument*>(ecSubcircuit->itemDocument()))->connectorWithID( it.key() ); + if (connector) + { + connector->updateConnectorPoints(false); + connector->setVisible(false); + connector->setCanvas(0l); + ecSubcircuit->connect( ecSubcircuit, SIGNAL(subcircuitDeleted()), connector, SLOT(removeConnector()) ); + } + } + const NodeDataMap::iterator nodeEnd = m_nodeDataMap.end(); + for ( NodeDataMap::iterator it = m_nodeDataMap.begin(); it != nodeEnd; ++it ) + { + Node * node = (static_cast<ICNDocument*>(ecSubcircuit->itemDocument()))->nodeWithID( it.key() ); + if (node) + { + node->setVisible(false); + node->setCanvas(0l); + ecSubcircuit->connect( ecSubcircuit, SIGNAL(subcircuitDeleted()), node, SLOT(removeNode()) ); + } + } + + ecSubcircuit->doneSCInit(); +} +//END class SubcircuitData + diff --git a/src/itemdocumentdata.h b/src/itemdocumentdata.h new file mode 100644 index 0000000..40c92a6 --- /dev/null +++ b/src/itemdocumentdata.h @@ -0,0 +1,242 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef ITEMDOCUMENTDATA_H +#define ITEMDOCUMENTDATA_H + +#include "item.h" +#include "microsettings.h" + +#include <qdom.h> + +class Connector; +class ECSubcircuit; +class KURL; +class Node; +class PinMapping; + +typedef QValueList<QGuardedPtr<Connector> > ConnectorList; +typedef QValueList<QGuardedPtr<Item> > ItemList; +typedef QValueList<QGuardedPtr<Node> > NodeList; +typedef QMap< QString, PinMapping > PinMappingMap; + +typedef QValueList<QPoint> QPointList; +typedef QMap<QString, bool> BoolMap; +typedef QMap<QString, double> DoubleMap; +typedef QMap<QString, int> IntMap; +typedef QMap<QString, QColor> QColorMap; +typedef QMap<QString, QString> QStringMap; +typedef QMap<QString, QBitArray> QBitArrayMap; + + +class ItemData +{ + public: + ItemData(); + + QString type; + double x; + double y; + int z; + QRect size; + bool setSize; + int orientation; // used for flowparts, should be set to -1 if not used. + double angleDegrees; + bool flipped; + BoolMap buttonMap; + IntMap sliderMap; + QString parentId; + BoolMap dataBool; + DoubleMap dataNumber; + QColorMap dataColor; + QStringMap dataString; + QBitArrayMap dataRaw; +}; +typedef QMap< QString, ItemData > ItemDataMap; + + +class ConnectorData +{ + public: + ConnectorData(); + + QPointList route; + bool manualRoute; + + bool startNodeIsChild; + bool endNodeIsChild; + + QString startNodeCId; + QString endNodeCId; + + QString startNodeParent; + QString endNodeParent; + + QString startNodeId; + QString endNodeId; +}; +typedef QMap< QString, ConnectorData > ConnectorDataMap; + + +class NodeData +{ + public: + NodeData(); + + double x; + double y; +}; +typedef QMap< QString, NodeData > NodeDataMap; + + +class PinData +{ + public: + PinData(); + + PinSettings::pin_type type; + PinSettings::pin_state state; +}; +typedef QMap< QString, PinData > PinDataMap; + + +class MicroData +{ + public: + MicroData(); + void reset(); + + QString id; + PinDataMap pinMap; + QStringMap variableMap; + PinMappingMap pinMappings; +}; + + +/** +This class encapsulates all or part of an ItemDocument. It is used for writing +the document to file / reading from file, as well as for the clipboard and +undo/redo system. +@author David Saxton +*/ +class ItemDocumentData +{ + public: + ItemDocumentData( uint documentType ); + ~ItemDocumentData(); + /** + * Erases / resets all data to defaults + */ + void reset(); + /** + * Read in data from a saved file. Any existing data in this class will + * be deleted first. + * @returns true iff successful + */ + bool loadData( const KURL &url ); + /** + * Write the data to the given file. + * @returns true iff successful + */ + bool saveData( const KURL &url ); + /** + * Returns the xml used for describing the data + */ + QString toXML(); + /** + * Restore the document from the given xml + * @return true if successful + */ + bool fromXML( const QString &xml ); + /** + * Saves the document to the data + */ + void saveDocumentState( ItemDocument *itemDocument ); + /** + * Restores a document to the state stored in this class + */ + void restoreDocument( ItemDocument *itemDocument ); + /** + * Merges the stuff stored here with the given document. If this is + * being used for e.g. pasting, you should call generateUniqueIDs() + * @param selectNew if true then the newly created items & connectors will be selected + */ + void mergeWithDocument( ItemDocument *itemDocument, bool selectNew ); + /** + * Replaces the IDs of everything with unique ones for the document. + * Used in pasting. + */ + void generateUniqueIDs( ItemDocument *itemDocument ); + /** + * Move all the items, connectors, nodes, etc by the given amount + */ + void translateContents( int dx, int dy ); + /** + * Returns the document type. + * @see Document::DocumentType + */ + uint documentType() const { return m_documentType; } + + //BEGIN functions for adding data + void setMicroData( const MicroData &data ); + void addItems( const ItemList &itemList ); + void addConnectors( const ConnectorList &connectorList ); + void addNodes( const NodeList &nodeList ); + + /** + * Add the given ItemData to the stored data + */ + void addItemData( ItemData itemData, QString id ); + /** + * Add the given ConnectorData to the stored data + */ + void addConnectorData( ConnectorData connectorData, QString id ); + /** + * Add the given NodeData to the stored data + */ + void addNodeData( NodeData nodeData, QString id ); + //END functions for adding data + + //BEGIN functions for returning strings for saving to xml + QString documentTypeString() const; + QString revisionString() const; + //END functions for returning strings for saving to xml + + protected: + //BEGIN functions for generating QDomElements + QDomElement microDataToElement( QDomDocument &doc ); + QDomElement itemDataToElement( QDomDocument &doc, const ItemData &itemData ); + QDomElement nodeDataToElement( QDomDocument &doc, const NodeData &nodeData ); + QDomElement connectorDataToElement( QDomDocument &doc, const ConnectorData &connectorData ); + //END functions for generating QDomElements + + //BEGIN functions for reading QDomElements to stored data + void elementToMicroData( QDomElement element ); + void elementToItemData( QDomElement element ); + void elementToNodeData( QDomElement element ); + void elementToConnectorData( QDomElement element ); + //END functions for reading QDomElements to stored data + + ItemDataMap m_itemDataMap; + ConnectorDataMap m_connectorDataMap; + NodeDataMap m_nodeDataMap; + MicroData m_microData; + uint m_documentType; // See Document::DocumentType +}; + + +class SubcircuitData : public ItemDocumentData +{ + public: + SubcircuitData(); + void initECSubcircuit( ECSubcircuit * ecSubcircuit ); +}; + +#endif diff --git a/src/itemgroup.cpp b/src/itemgroup.cpp new file mode 100644 index 0000000..1b640d3 --- /dev/null +++ b/src/itemgroup.cpp @@ -0,0 +1,323 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "icndocument.h" +#include "item.h" +#include "itemgroup.h" +#include "mechanicsdocument.h" + +#include <qtimer.h> + + +ItemGroup::ItemGroup( ItemDocument *view, const char *name ) + : QObject( view, name ) +{ + m_activeItem = 0l; + b_itemsAreSameType = true; + p_view = view; + p_icnDocument = dynamic_cast<ICNDocument*>(p_view); + p_mechanicsDocument = dynamic_cast<MechanicsDocument*>(p_view); + QTimer::singleShot( 0, this, SLOT(getViewPtrs()) ); +} + + +ItemGroup::~ItemGroup() +{ +} + + +void ItemGroup::getViewPtrs() +{ + p_icnDocument = dynamic_cast<ICNDocument*>(p_view); + p_mechanicsDocument = dynamic_cast<MechanicsDocument*>(p_view); +} + + +ItemList ItemGroup::items( bool excludeParentedItems ) const +{ + if (excludeParentedItems) + return m_itemList; + + ItemList items = m_itemList; + ItemList parents = m_itemList; + + uint oldSize = items.size(); + do + { + oldSize = items.size(); + ItemList children; + + ItemList::iterator end = parents.end(); + for ( ItemList::iterator it = parents.begin(); it != end; ++it ) + children += (*it)->children(); + + end = children.end(); + for ( ItemList::iterator it = children.begin(); it != end; ++it ) + { + if ( children.contains(*it) > 1 ) + *it = 0l; + } + children.remove((Item*)0l); + + items += children; + parents = children; + } + while ( oldSize != items.size() ); + + return items; +} + + +bool ItemGroup::itemsHaveSameDataValue( const QString &id ) const +{ + if ( m_itemList.size() < 1 ) { + return true; + } + + if (!itemsAreSameType()) { + return false; + } + + ItemList::const_iterator it = m_itemList.begin(); + const ItemList::const_iterator end = m_itemList.end(); + QVariant firstData = (*it)->property(id)->value(); + for ( ++it; it != end; ++it ) + { + if ( (*it) && (*it)->property(id) && (*it)->property(id)->value() != firstData ) { + return false; + } + } + return true; +} + + +bool ItemGroup::itemsHaveSameData() const +{ + if ( m_itemList.size() < 1 ) { + return true; + } + + if (!itemsAreSameType()) { + return false; + } + + VariantDataMap *variantMap = m_itemList.first()->variantMap(); + const VariantDataMap::const_iterator vitEnd = variantMap->end(); + for ( VariantDataMap::const_iterator vit = variantMap->begin(); vit != vitEnd; ++vit ) + { + if ( !itemsHaveSameDataValue(vit.key()) ) { + return false; + } + } + return true; +} + + +bool ItemGroup::itemsHaveDefaultData() const +{ + if (!itemsHaveSameData()) { + return false; + } + + if ( m_itemList.size() < 1 ) { + return true; + } + + VariantDataMap *variantMap = (*m_itemList.begin())->variantMap(); + const VariantDataMap::const_iterator vitEnd = variantMap->end(); + for ( VariantDataMap::const_iterator vit = variantMap->begin(); vit != vitEnd; ++vit ) + { + if ( !vit.data()->isHidden() && vit.data()->value() != vit.data()->defaultValue() ) + return false; + } + return true; +} + + +void ItemGroup::registerItem( Item *item ) +{ + if ( !item || m_itemList.contains(item) ) { + return; + } + + m_itemList += item; + updateAreSameStatus(); +} + + +void ItemGroup::unregisterItem( Item *item ) +{ + if ( m_itemList.remove(item) > 0 ) { + updateAreSameStatus(); + } +} + + +void ItemGroup::updateAreSameStatus() +{ + b_itemsAreSameType = true; + + if ( m_itemList.size() < 2 ) { + return; + } + + QString activeId = (*m_itemList.begin())->id(); + int discardIndex = activeId.findRev("__"); + if ( discardIndex != -1 ) activeId.truncate(discardIndex); + + const ItemList::iterator end = m_itemList.end(); + for ( ItemList::iterator it = ++m_itemList.begin(); it != end && b_itemsAreSameType; ++it ) + { + if (*it) + { + QString id = (*it)->id(); + discardIndex = id.findRev("__"); + if ( discardIndex != -1 ) id.truncate(discardIndex); + if ( id != activeId ) + { + b_itemsAreSameType = false; + } + } + } +} + + +void ItemGroup::slotAlignHorizontally() +{ + if ( m_itemList.size() < 2 ) + return; + + double avg_y = 0.; + + const ItemList::iterator end = m_itemList.end(); + for ( ItemList::iterator it = m_itemList.begin(); it != end; ++it ) + avg_y += (*it)->y(); + + int new_y = int(avg_y/(8*m_itemList.size()))*8+4; + + for ( ItemList::iterator it = m_itemList.begin(); it != end; ++it ) + (*it)->move( (*it)->x(), new_y ); + + p_icnDocument->requestStateSave(); +} + + +void ItemGroup::slotAlignVertically() +{ + if ( m_itemList.size() < 2 ) + return; + + double avg_x = 0.; + + const ItemList::iterator end = m_itemList.end(); + for ( ItemList::iterator it = m_itemList.begin(); it != end; ++it ) + avg_x += (*it)->x(); + + int new_x = int(avg_x/(8*m_itemList.size()))*8+4; + + for ( ItemList::iterator it = m_itemList.begin(); it != end; ++it ) + (*it)->move( new_x, (*it)->y() ); + + p_icnDocument->requestStateSave(); +} + + +void ItemGroup::slotDistributeHorizontally() +{ + if ( m_itemList.size() < 2 ) + return; + + // We sort the items by their horizontal position so that we can calculate + // an average spacing + typedef std::multimap< double, Item * > DIMap; + + DIMap ranked; + const ItemList::iterator ilend = m_itemList.end(); + for ( ItemList::iterator it = m_itemList.begin(); it != ilend; ++it ) + ranked.insert( std::make_pair( (*it)->x(), *it ) ); + + double avg_spacing = 0; + + Item * previous = 0l; + const DIMap::iterator rankedEnd = ranked.end(); + for ( DIMap::iterator it = ranked.begin(); it != rankedEnd; ++it ) + { + Item * item = it->second; + if (previous) + { + double spacing = item->x() + item->offsetX() - (previous->x() + previous->width() + previous->offsetX()); + avg_spacing += spacing; + } + previous = item; + } + + avg_spacing /= (m_itemList.size()-1); + + DIMap::iterator it = ranked.begin(); + // Position that we are up to + double at = it->second->x() + it->second->width() + it->second->offsetX(); + for ( ++it; it != rankedEnd; ++it ) + { + Item * item = it->second; + double new_x = at - item->offsetX() + avg_spacing; + item->move( int(new_x/8)*8+4, item->y() ); + at = new_x + item->width() + item->offsetX(); + } + + p_icnDocument->requestStateSave(); +} + + +void ItemGroup::slotDistributeVertically() +{ + if ( m_itemList.size() < 2 ) + return; + + // We sort the items by their horizontal position so that we can calculate + // an average spacing + typedef std::multimap< double, Item * > DIMap; + + DIMap ranked; + const ItemList::iterator ilend = m_itemList.end(); + for ( ItemList::iterator it = m_itemList.begin(); it != ilend; ++it ) + ranked.insert( std::make_pair( (*it)->y(), *it ) ); + + double avg_spacing = 0; + + Item * previous = 0l; + const DIMap::iterator rankedEnd = ranked.end(); + for ( DIMap::iterator it = ranked.begin(); it != rankedEnd; ++it ) + { + Item * item = it->second; + if (previous) + { + double spacing = item->y() + item->offsetY() - (previous->y() + previous->height() + previous->offsetY()); + avg_spacing += spacing; + } + previous = item; + } + + avg_spacing /= (m_itemList.size()-1); + + DIMap::iterator it = ranked.begin(); + // Position that we are up to + double at = it->second->y() + it->second->height() + it->second->offsetY(); + for ( ++it; it != rankedEnd; ++it ) + { + Item * item = it->second; + double new_y = at - item->offsetY() + avg_spacing; + item->move( item->x(), int(new_y/8)*8+4 ); + at = new_y + item->height() + item->offsetY(); + } + + p_icnDocument->requestStateSave(); +} + +#include "itemgroup.moc" diff --git a/src/itemgroup.h b/src/itemgroup.h new file mode 100644 index 0000000..6913165 --- /dev/null +++ b/src/itemgroup.h @@ -0,0 +1,140 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef ITEMGROUP_H +#define ITEMGROUP_H + +#include <qguardedptr.h> + +class Item; +class ICNDocument; +class ItemDocument; +class DoubleSpinBox; +class ItemGroup; +class MechanicsDocument; +class Variant; + +typedef QValueList<QGuardedPtr<Item> > ItemList; + +class QCanvasItem; +class QCanvasItemList; + +/** +Generic base class for controlling a selection of Item. Provides +some functionality such as for dealing with item data +@author David Saxton +*/ +class ItemGroup : public QObject +{ +Q_OBJECT +public: + ItemGroup( ItemDocument *view, const char *name = 0 ); + virtual ~ItemGroup(); + + /** + * Returns a pointer to the "active" CNItem - i.e. the last CNItem + * to be added to the CNItemGroup. This will always return a pointer to + * a single item, unless there are no CNItems in the group + */ + Item *activeItem() const { return m_activeItem; } + uint itemCount() const { return m_itemList.count(); } + virtual bool addQCanvasItem( QCanvasItem *qcanvasItem ) = 0; + virtual void setItems( QCanvasItemList list ) = 0; + virtual void removeQCanvasItem( QCanvasItem *qcanvasItem ) = 0; + virtual bool contains( QCanvasItem *qcanvasItem ) const = 0; + virtual uint count() const = 0; + bool isEmpty() const { return (count() == 0); } + virtual void mergeGroup( ItemGroup *group ) = 0; + virtual void removeAllItems() = 0; + virtual void deleteAllItems() = 0; + /** + * Returns a list of all the Items in the group. + * @param excludeParented whether to return items whose (grand-) parents are + * already in the list. + */ + ItemList items( bool excludeParented = true ) const; + /** + * Sets the selected state of all items in the group + */ + virtual void setSelected( bool sel ) = 0; + + /** + * Returns true iff either there are no items, or itemsAreSameType and the + * value of each data (excluding hidden data) for each item is the same + */ + bool itemsHaveSameData() const; + /** + * Returns truee iff either there are no items, or itemsAreSameType and the + * value of the data with the given id is the same for each item + */ + bool itemsHaveSameDataValue( const QString &id ) const; + /** + * Returns true iff all the iff itemsHaveSameData() returns true and the + * value of the data are the defaults + */ + bool itemsHaveDefaultData() const; + /** + * Returns true if all the items in the group are the same (e.g. + * resistors). This is checked for by looking at the ids of the items, + * and seeing if the string before "__#" is the same Note: if there are zero + * items in the group, then this will return true + */ + bool itemsAreSameType() const { return b_itemsAreSameType; } + +public slots: + /** + * Align the selected items horizontally so that their positions have the + * same y coordinate. + */ + void slotAlignHorizontally(); + /** + * Align the selected items horizontally so that their positions have the + * same x coordinate. + */ + void slotAlignVertically(); + /** + * Distribute the selected items horizontally so that they have the same + * spacing in the horizontal direction. + */ + void slotDistributeHorizontally(); + /** + * Distribute the selected items vertically so that they have the same + * spacing in the vertical direction. + */ + void slotDistributeVertically(); + +signals: + void itemAdded( Item *item ); + void itemRemoved( Item *item ); + +protected: + /** + * Subclasses must call this to register the item with the data interface + */ + void registerItem( Item *item ); + /** + * Subclasses must call this to unregister the item with the data interface + */ + void unregisterItem( Item *item ); + void updateAreSameStatus(); + + ItemList m_itemList; + bool b_itemsAreSameType; + ItemDocument * p_view; + + ICNDocument *p_icnDocument; + MechanicsDocument *p_mechanicsDocument;; + Item *m_activeItem; + +private slots: + void getViewPtrs(); +}; + +#endif diff --git a/src/iteminterface.cpp b/src/iteminterface.cpp new file mode 100644 index 0000000..6b9c1af --- /dev/null +++ b/src/iteminterface.cpp @@ -0,0 +1,601 @@ +/*************************************************************************** + * Copyright (C) 2004-2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "circuitview.h" +#include "colorcombo.h" +#include "contexthelp.h" +#include "cnitem.h" +#include "cnitemgroup.h" +#include "doublespinbox.h" +#include "itemdocument.h" +#include "itemeditor.h" +#include "iteminterface.h" +#include "itemview.h" +#include "ktechlab.h" + +#include <kcombobox.h> +#include <kdebug.h> +#include <klineedit.h> +#include <knuminput.h> +#include <kurlrequester.h> +#include <ktoolbar.h> +#include <qapplication.h> +#include <qlabel.h> +#include <qcheckbox.h> + +#include <assert.h> + +ItemInterface * ItemInterface::m_pSelf = 0l; + +ItemInterface * ItemInterface::self( KTechlab * ktechlab ) +{ + if ( !m_pSelf ) + { + assert(ktechlab); + m_pSelf = new ItemInterface(ktechlab); + } + return m_pSelf; +} + + +ItemInterface::ItemInterface( KTechlab * ktechlab ) + : QObject(ktechlab), + p_ktechlab(ktechlab) +{ + m_pActiveItemEditorToolBar = 0; + p_cvb = 0l; + p_itemGroup = 0l; + p_lastItem = 0l; + m_currentActionTicket = -1; + m_toolBarWidgetID = -1; +} + + +ItemInterface::~ItemInterface() +{ +} + + +void ItemInterface::slotGetActionTicket() +{ + m_currentActionTicket = p_cvb ? p_cvb->getActionTicket() : -1; +} + + +void ItemInterface::slotItemDocumentChanged( ItemDocument * doc ) +{ + slotClearAll(); + if ( ItemDocument * itemDocument = dynamic_cast<ItemDocument*>((Document*)p_cvb) ) + { + disconnect( itemDocument, SIGNAL(itemSelected(Item*)), this, SLOT(slotUpdateItemInterface()) ); + disconnect( itemDocument, SIGNAL(itemUnselected(Item*)), this, SLOT(slotUpdateItemInterface()) ); + } + + p_itemGroup = 0l; + p_cvb = doc; + + slotGetActionTicket(); + + if (!p_cvb) + return; + + connect( p_cvb, SIGNAL(itemSelected(Item*)), this, SLOT(slotUpdateItemInterface()) ); + connect( p_cvb, SIGNAL(itemUnselected(Item*)), this, SLOT(slotUpdateItemInterface()) ); + + p_itemGroup = p_cvb->selectList(); + + slotUpdateItemInterface(); +} + + +void ItemInterface::clearItemEditorToolBar() +{ + if ( m_pActiveItemEditorToolBar && m_toolBarWidgetID != -1 ) + m_pActiveItemEditorToolBar->removeItem(m_toolBarWidgetID); + m_toolBarWidgetID = -1; + itemEditTBCleared(); +} + + +void ItemInterface::slotClearAll() +{ + ContextHelp::self()->slotClear(); + ItemEditor::self()->slotClear(); + clearItemEditorToolBar(); + p_lastItem = 0l; +} + + +void ItemInterface::slotMultipleSelected() +{ + ContextHelp::self()->slotMultipleSelected(); + ItemEditor::self()->slotMultipleSelected(); + clearItemEditorToolBar(); + p_lastItem = 0l; +} + + +void ItemInterface::slotUpdateItemInterface() +{ + if (!p_itemGroup) + return; + + slotGetActionTicket(); + updateItemActions(); + + if (!p_itemGroup->itemsAreSameType() ) + { + slotMultipleSelected(); + return; + } + if ( p_lastItem && p_itemGroup->activeItem() ) + { + ItemEditor::self()->updateMergeDefaults(p_itemGroup); + return; + } + + p_lastItem = p_itemGroup->activeItem(); + if (!p_lastItem) + { + slotClearAll(); + return; + } + + ContextHelp::self()->slotUpdate(p_lastItem); + ItemEditor::self()->slotUpdate(p_itemGroup); + if ( CNItem * cnItem = dynamic_cast<CNItem*>((Item*)p_lastItem) ) + ItemEditor::self()->slotUpdate(cnItem); + + // Update item editor toolbar + if ( ItemView * itemView = dynamic_cast<ItemView*>(p_cvb->activeView()) ) + { + if ( m_pActiveItemEditorToolBar = dynamic_cast<KToolBar*>(p_ktechlab->factory()->container("itemEditorTB",itemView)) ) + { + m_pActiveItemEditorToolBar->setFullSize( true ); + QWidget * widget = configWidget(); + m_toolBarWidgetID = 1; + m_pActiveItemEditorToolBar->insertWidget( m_toolBarWidgetID, 0, widget ); + } + } +} + + +void ItemInterface::updateItemActions() +{ + ItemView * itemView = ((ItemDocument*)p_cvb) ? dynamic_cast<ItemView*>(p_cvb->activeView()) : 0l; + if ( !itemView ) + return; + + bool itemsSelected = p_itemGroup && p_itemGroup->itemCount(); + + itemView->action("edit_raise")->setEnabled(itemsSelected); + itemView->action("edit_lower")->setEnabled(itemsSelected); + p_ktechlab->action("edit_cut")->setEnabled(itemsSelected); + p_ktechlab->action("edit_copy")->setEnabled(itemsSelected); + + CNItemGroup * cnItemGroup = dynamic_cast<CNItemGroup*>((ItemGroup*)p_itemGroup); + CircuitView * circuitView = dynamic_cast<CircuitView*>(itemView); + + if ( cnItemGroup && circuitView ) + { + circuitView->action("edit_flip")->setEnabled(cnItemGroup->canFlip()); + bool canRotate = cnItemGroup->canRotate(); + circuitView->action("edit_rotate_ccw")->setEnabled(canRotate); + circuitView->action("edit_rotate_cw")->setEnabled(canRotate); + } +} + + +void ItemInterface::setFlowPartOrientation( unsigned orientation ) +{ + CNItemGroup *cnItemGroup = dynamic_cast<CNItemGroup*>((ItemGroup*)p_itemGroup); + if (!cnItemGroup) + return; + + cnItemGroup->setFlowPartOrientation( orientation ); +} + + +void ItemInterface::setComponentOrientation( int angleDegrees, bool flipped ) +{ + CNItemGroup *cnItemGroup = dynamic_cast<CNItemGroup*>((ItemGroup*)p_itemGroup); + if (!cnItemGroup) + return; + + cnItemGroup->setComponentOrientation( angleDegrees, flipped ); +} + + +void ItemInterface::itemEditTBCleared() +{ + m_stringLineEditMap.clear(); + m_stringComboBoxMap.clear(); + m_stringURLReqMap.clear(); + m_intSpinBoxMap.clear(); + m_doubleSpinBoxMap.clear(); + m_colorComboMap.clear(); + m_boolCheckMap.clear(); +} + + +// The bool specifies whether advanced data should be shown +QWidget * ItemInterface::configWidget() +{ + if ( !p_itemGroup || !p_itemGroup->activeItem() || !m_pActiveItemEditorToolBar ) + return 0l; + + VariantDataMap *variantMap = p_itemGroup->activeItem()->variantMap(); + + QWidget * parent = m_pActiveItemEditorToolBar; + + // Create new widget with the toolbar or dialog as the parent + QWidget * configWidget = new QWidget( parent, "tbConfigWidget" ); + configWidget->setSizePolicy( QSizePolicy( QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding, 1, 1 ) ); + + QHBoxLayout * configLayout = new QHBoxLayout( configWidget ); +// configLayout->setAutoAdd( true ); + configLayout->setSpacing( 6 ); + +// configLayout->addItem( new QSpacerItem( 0, 0, QSizePolicy::Expanding, QSizePolicy::Fixed ) ); + + const VariantDataMap::iterator vaEnd = variantMap->end(); + for ( VariantDataMap::iterator vait = variantMap->begin(); vait != vaEnd; ++vait ) + { + if ( vait.data()->isHidden() || vait.data()->isAdvanced() ) + continue; + + const Variant::Type::Value type = vait.data()->type(); + + // common to all types apart from bool + QString toolbarCaption = vait.data()->toolbarCaption(); + if ( type != Variant::Type::Bool && !toolbarCaption.isEmpty() ) + configLayout->addWidget( new QLabel( toolbarCaption, configWidget ) ); + + QWidget * editWidget = 0l; // Should be set to the created widget + + switch( type ) + { + case Variant::Type::Port: + case Variant::Type::Pin: + case Variant::Type::VarName: + case Variant::Type::Combo: + case Variant::Type::Select: + case Variant::Type::KeyPad: + case Variant::Type::SevenSegment: + { + QString value = vait.data()->value().toString(); + if ( !value.isEmpty() && !vait.data()->allowed().contains(value) ) + vait.data()->appendAllowed(value); + + const QStringList allowed = vait.data()->allowed(); + + KComboBox * box = new KComboBox(configWidget); + + box->insertStringList(allowed); + box->setCurrentItem(value); + + if ( type == Variant::Type::VarName || type == Variant::Type::Combo ) + box->setEditable( true ); + + m_stringComboBoxMap[vait.key()] = box; + connectMapWidget( box, SIGNAL(textChanged(const QString &))); + connectMapWidget( box, SIGNAL(activated(const QString &))); + + editWidget = box; + break; + } + case Variant::Type::FileName: + { + QString value = vait.data()->value().toString(); + if ( !vait.data()->allowed().contains(value) ) + vait.data()->appendAllowed(value); + + const QStringList allowed = vait.data()->allowed(); + + KURLComboRequester * urlreq = new KURLComboRequester( configWidget ); + urlreq->setFilter( vait.data()->filter() ); + connectMapWidget( urlreq, SIGNAL(urlSelected(const QString &)) ); + m_stringURLReqMap[vait.key()] = urlreq; + + KComboBox * box = urlreq->comboBox(); + box->insertStringList(allowed); + box->setEditable( true ); + + // Note this has to be called after inserting the allowed list + urlreq->setURL( vait.data()->value().toString() ); + + // Generally we only want a file name once the user has finished typing out the full file name. + connectMapWidget( box, SIGNAL(returnPressed(const QString &))); + connectMapWidget( box, SIGNAL(activated(const QString &))); + + editWidget = urlreq; + break; + } + case Variant::Type::String: + { + KLineEdit * edit = new KLineEdit( configWidget ); + + edit->setText( vait.data()->value().toString() ); + connectMapWidget(edit,SIGNAL(textChanged(const QString &))); + m_stringLineEditMap[vait.key()] = edit; + editWidget = edit; + break; + } + case Variant::Type::Int: + { + KIntSpinBox *spin = new KIntSpinBox( (int)vait.data()->minValue(), (int)vait.data()->maxValue(), 1, vait.data()->value().toInt(), 10, configWidget ); + + connectMapWidget( spin, SIGNAL(valueChanged(int)) ); + m_intSpinBoxMap[vait.key()] = spin; + editWidget = spin; + break; + } + case Variant::Type::Double: + { + DoubleSpinBox *spin = new DoubleSpinBox( vait.data()->minValue(), vait.data()->maxValue(), vait.data()->minAbsValue(), vait.data()->value().toDouble(), vait.data()->unit(), configWidget ); + + connectMapWidget( spin, SIGNAL(valueChanged(double))); + m_doubleSpinBoxMap[vait.key()] = spin; + editWidget = spin; + break; + } + case Variant::Type::Color: + { + QColor value = vait.data()->value().toColor(); + + ColorCombo * colorBox = new ColorCombo( (ColorCombo::ColorScheme)vait.data()->colorScheme(), configWidget ); + + colorBox->setColor( value ); + connectMapWidget( colorBox, SIGNAL(activated(const QColor &))); + m_colorComboMap[vait.key()] = colorBox; + + editWidget = colorBox; + break; + } + case Variant::Type::Bool: + { + const bool value = vait.data()->value().toBool(); + QCheckBox * box = new QCheckBox( vait.data()->toolbarCaption(), configWidget ); + + box->setChecked(value); + connectMapWidget( box, SIGNAL(toggled(bool))); + m_boolCheckMap[vait.key()] = box; + editWidget = box; + break; + } + case Variant::Type::Raw: + case Variant::Type::PenStyle: + case Variant::Type::PenCapStyle: + case Variant::Type::Multiline: + case Variant::Type::None: + { + // Do nothing, as these data types are not handled in the toolbar + break; + } + } + + if ( !editWidget ) + continue; + + // In the case of the toolbar, we don't want it too high + if ( editWidget->height() > parent->height()-2 ) + editWidget->setMaximumHeight( parent->height()-2 ); + + switch ( type ) + { + case Variant::Type::VarName: + case Variant::Type::Combo: + case Variant::Type::String: + { + QSizePolicy p( QSizePolicy::MinimumExpanding, QSizePolicy::Fixed, 1, 1 ); + + editWidget->setSizePolicy( p ); + editWidget->setMaximumWidth( 250 ); + break; + } + + case Variant::Type::FileName: + case Variant::Type::Port: + case Variant::Type::Pin: + case Variant::Type::Select: + case Variant::Type::KeyPad: + case Variant::Type::SevenSegment: + case Variant::Type::Int: + case Variant::Type::Double: + case Variant::Type::Color: + case Variant::Type::Bool: + case Variant::Type::Raw: + case Variant::Type::PenStyle: + case Variant::Type::PenCapStyle: + case Variant::Type::Multiline: + case Variant::Type::None: + break; + } + + configLayout->addWidget( editWidget ); + } + + configLayout->addItem( new QSpacerItem( 0, 0, QSizePolicy::Expanding, QSizePolicy::Fixed ) ); + + return configWidget; +} + + +void ItemInterface::connectMapWidget( QWidget *widget, const char *_signal ) +{ + connect( widget, _signal, this, SLOT(tbDataChanged()) ); +} + + +void ItemInterface::tbDataChanged() +{ + // Manual string values + const KLineEditMap::iterator m_stringLineEditMapEnd = m_stringLineEditMap.end(); + for ( KLineEditMap::iterator leit = m_stringLineEditMap.begin(); leit != m_stringLineEditMapEnd; ++leit ) + { + slotSetData( leit.key(), leit.data()->text() ); + } + + // String values from comboboxes + const KComboBoxMap::iterator m_stringComboBoxMapEnd = m_stringComboBoxMap.end(); + for ( KComboBoxMap::iterator cmit = m_stringComboBoxMap.begin(); cmit != m_stringComboBoxMapEnd; ++cmit ) + { + slotSetData( cmit.key(), cmit.data()->currentText() ); + } + + // Colors values from colorcombos + const ColorComboMap::iterator m_colorComboMapEnd = m_colorComboMap.end(); + for ( ColorComboMap::iterator ccit = m_colorComboMap.begin(); ccit != m_colorComboMapEnd; ++ccit ) + { + slotSetData( ccit.key(), ccit.data()->color() ); + } + + // Bool values from checkboxes + const QCheckBoxMap::iterator m_boolCheckMapEnd = m_boolCheckMap.end(); + for ( QCheckBoxMap::iterator chit = m_boolCheckMap.begin(); chit != m_boolCheckMapEnd; ++chit ) + { + slotSetData( chit.key(), chit.data()->isChecked() ); + } + + const IntSpinBoxMap::iterator m_intSpinBoxMapEnd = m_intSpinBoxMap.end(); + for ( IntSpinBoxMap::iterator it = m_intSpinBoxMap.begin(); it != m_intSpinBoxMapEnd; ++it ) + { + slotSetData( it.key(), it.data()->value() ); + } + + // (?) Combined values from spin boxes and combo boxes + // (?) Get values from all spin boxes + + const DoubleSpinBoxMap::iterator m_doubleSpinBoxMapEnd = m_doubleSpinBoxMap.end(); + for ( DoubleSpinBoxMap::iterator sbit = m_doubleSpinBoxMap.begin(); sbit != m_doubleSpinBoxMapEnd; ++sbit ) + { +// VariantDataMap::iterator vait = variantData.find(sbit.key()); + slotSetData( sbit.key(), sbit.data()->value() ); + } + + // Filenames from KURLRequesters + const KURLReqMap::iterator m_stringURLReqMapEnd = m_stringURLReqMap.end(); + for ( KURLReqMap::iterator urlit = m_stringURLReqMap.begin(); urlit != m_stringURLReqMapEnd; ++urlit ) + { + slotSetData( urlit.key(), urlit.data()->url() ); + } + + if (p_cvb) + p_cvb->setModified(true); +} + + + + +void ItemInterface::slotSetData( const QString &id, QVariant value ) +{ + if ( !p_itemGroup || (p_itemGroup->itemCount() == 0) ) + return; + + if ( !p_itemGroup->itemsAreSameType() ) + { + kdDebug() << k_funcinfo << "Items are not the same type!"<<endl; + return; + } + + const ItemList itemList = p_itemGroup->items(true); + const ItemList::const_iterator end = itemList.end(); + for ( ItemList::const_iterator it = itemList.begin(); it != end; ++it ) + { + if (*it) + (*it)->property(id)->setValue(value); + } + if (p_cvb) + p_cvb->setModified(true); + + + VariantDataMap * variantMap = (*itemList.begin())->variantMap(); + VariantDataMap::iterator it = variantMap->find(id); + if ( it == variantMap->end() ) + return; + + + // setData might have been called from the PropertiesListView, so want + // to see if the toolbar widgets want setting + + switch( it.data()->type() ) + { + case Variant::Type::String: + { + KLineEditMap::iterator mit = m_stringLineEditMap.find(id); + if ( mit != m_stringLineEditMap.end() ) mit.data()->setText( it.data()->value().toString() ); + break; + } + case Variant::Type::FileName: + { + KURLReqMap::iterator mit = m_stringURLReqMap.find(id); + if ( mit != m_stringURLReqMap.end() ) mit.data()->setURL( it.data()->value().toString() ); + break; + } + case Variant::Type::PenCapStyle: + case Variant::Type::PenStyle: + case Variant::Type::Port: + case Variant::Type::Pin: + case Variant::Type::VarName: + case Variant::Type::Combo: + case Variant::Type::Select: + case Variant::Type::SevenSegment: + case Variant::Type::KeyPad: + { + KComboBoxMap::iterator mit = m_stringComboBoxMap.find(id); + if ( mit != m_stringComboBoxMap.end() ) mit.data()->setCurrentItem( it.data()->value().toString() ); + break; + } + case Variant::Type::Int: + { + IntSpinBoxMap::iterator mit = m_intSpinBoxMap.find(id); + if ( mit != m_intSpinBoxMap.end() ) { + KIntSpinBox *sb = mit.data(); + sb->setValue( it.data()->value().toInt() ); + } + break; + } + case Variant::Type::Double: + { + DoubleSpinBoxMap::iterator mit = m_doubleSpinBoxMap.find(id); + if ( mit != m_doubleSpinBoxMap.end() ) { + DoubleSpinBox *sb = mit.data(); + sb->setValue( it.data()->value().toDouble() ); + } + break; + } + case Variant::Type::Color: + { + ColorComboMap::iterator mit = m_colorComboMap.find(id); + if ( mit != m_colorComboMap.end() ) mit.data()->setColor( it.data()->value().toColor() ); + break; + } + case Variant::Type::Bool: + { + QCheckBoxMap::iterator mit = m_boolCheckMap.find(id); + if ( mit != m_boolCheckMap.end() ) mit.data()->setChecked( it.data()->value().toBool() ); + break; + } + case Variant::Type::Raw: + case Variant::Type::Multiline: + case Variant::Type::None: + { + // This data will never be handled in the toolbar/PLV, so no need to worry about it + break; + } + } + + ItemEditor::self()->updateMergeDefaults(p_itemGroup); + + if (p_cvb) + p_cvb->requestStateSave(m_currentActionTicket); +} + +#include "iteminterface.moc" diff --git a/src/iteminterface.h b/src/iteminterface.h new file mode 100644 index 0000000..22a4a04 --- /dev/null +++ b/src/iteminterface.h @@ -0,0 +1,131 @@ +/*************************************************************************** + * Copyright (C) 2004-2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef ITEMINTERFACE_H +#define ITEMINTERFACE_H + +#include <qguardedptr.h> + +class CNItemGroup; +class DoubleSpinBox; +class Item; +class ItemDocument; +class ItemGroup; +class ItemInterface; +class KTechlab; +class MechanicsGroup; +class Variant; + +class ColorCombo; +class KComboBox; +class KToolBar; +class KURLRequester; +class QCheckBox; +class KLineEdit; +class KIntSpinBox; + + +typedef QMap<QString, Variant*> VariantDataMap; +typedef QMap<QString, KComboBox*> KComboBoxMap; +typedef QMap<QString, KLineEdit*> KLineEditMap; +typedef QMap<QString, DoubleSpinBox*> DoubleSpinBoxMap; +typedef QMap<QString, KIntSpinBox*> IntSpinBoxMap; +typedef QMap<QString, ColorCombo*> ColorComboMap; +typedef QMap<QString, KURLRequester*> KURLReqMap; +typedef QMap<QString, QCheckBox*> QCheckBoxMap; + +/** +This acts as an interface between the ItemDocument's and the associated +Items's, and the various objects that like to know about them +(e.g. ContextHelp, ItemEditor, ItemEditTB) +@author David Saxton +*/ +class ItemInterface : public QObject +{ + Q_OBJECT + public: + ~ItemInterface(); + static ItemInterface * self( KTechlab * ktechlab = 0l ); + + /** + * Sets the orientation of all flowparts in the group. + */ + void setFlowPartOrientation( unsigned orientation ); + /** + * Sets the orientation of all components in the group. + */ + void setComponentOrientation( int angleDegrees, bool flipped ); + /** + * Updates actions based on the items currently selected (e.g. rotate, + * flip, etc) + */ + void updateItemActions(); + /** + * Returns a configuration widget for the component for insertion into te + * ItemEditTB. + * @param showAdvanced Whether advanced data should be shown + */ + virtual QWidget * configWidget(); + + public slots: + /** + * If cnItemsAreSameType() returns true, then set the + * data with the given id for all the CNItems in the group. + * Else, it only sets the data for the activeCNItem() + */ + void slotSetData( const QString &id, QVariant value ); + /** + * Called when the ItemEditTB is cleared. This clears all widget maps. + */ + void itemEditTBCleared(); + void tbDataChanged(); + void slotItemDocumentChanged( ItemDocument *view ); + void slotUpdateItemInterface(); + void slotClearAll(); + void slotMultipleSelected(); + void clearItemEditorToolBar(); + + protected: + /** + * Connects the specified widget to either tbDataChanged or advDataChanged + * as specified by mi. + */ + void connectMapWidget( QWidget *widget, const char *_signal); + + KTechlab * const p_ktechlab; + + // Widget maps. + KLineEditMap m_stringLineEditMap; + KComboBoxMap m_stringComboBoxMap; + KURLReqMap m_stringURLReqMap; + DoubleSpinBoxMap m_doubleSpinBoxMap; + IntSpinBoxMap m_intSpinBoxMap; + ColorComboMap m_colorComboMap; + QCheckBoxMap m_boolCheckMap; + + // Use by item editor toolbar + QGuardedPtr<KToolBar> m_pActiveItemEditorToolBar; + int m_toolBarWidgetID; + + + protected slots: + void slotGetActionTicket(); + + private: + ItemInterface( KTechlab * ktechlab ); + static ItemInterface * m_pSelf; + + QGuardedPtr<ItemDocument> p_cvb; + QGuardedPtr<ItemGroup> p_itemGroup; + QGuardedPtr<Item> p_lastItem; + int m_currentActionTicket; +}; + +#endif diff --git a/src/itemlibrary.cpp b/src/itemlibrary.cpp new file mode 100644 index 0000000..d749f7f --- /dev/null +++ b/src/itemlibrary.cpp @@ -0,0 +1,476 @@ +/*************************************************************************** + * Copyright (C) 2003-2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "config.h" + +#include "cnitem.h" +#include "canvasitemparts.h" +#include "circuitdocument.h" +#include "component.h" +#include "ecsubcircuit.h" +#include "ecnode.h" +#include "itemlibrary.h" +#include "node.h" +#include "subcircuits.h" + +#ifdef MECHANICS +#include "chassiscircular2.h" +#endif + +#include "dptext.h" +#include "dpline.h" +#include "solidshape.h" + +#include "callsub.h" +#include "count.h" +#include "delay.h" +#include "embed.h" +#include "end.h" +#include "inputbutton.h" +#include "interrupt.h" +#include "forloop.h" +#include "keypad.h" +#include "pulse.h" +#include "readport.h" +#include "repeat.h" +#include "setpin.h" +#include "sevenseg.h" +#include "start.h" +#include "sub.h" +#include "testpin.h" +#include "unary.h" +#include "varassignment.h" +#include "varcomparison.h" +#include "while.h" +#include "writeport.h" + +#include "addac.h" +#include "bidirled.h" +#include "binarycounter.h" +#include "bussplitter.h" +#include "demultiplexer.h" +#include "dependentsource.h" +#include "discretelogic.h" +#include "externalconnection.h" +#include "flipflop.h" +#include "fulladder.h" +#include "inductor.h" +#include "magnitudecomparator.h" +#include "matrixdisplay.h" +#include "matrixdisplaydriver.h" +#include "meter.h" +#include "multiinputgate.h" +#include "multiplexer.h" +#include "parallelportcomponent.h" +#include "piccomponent.h" +#include "pushswitch.h" +#include "probe.h" +#include "ram.h" +#include "resistordip.h" +#include "rotoswitch.h" +#include "serialportcomponent.h" +#include "toggleswitch.h" + +#include "ec555.h" +#include "ecbcdto7segment.h" +#include "eccapacitor.h" +#include "ecclockinput.h" +#include "eccurrentsignal.h" +#include "eccurrentsource.h" +#include "ecdiode.h" +#include "ecfixedvoltage.h" +#include "ecground.h" +#include "eckeypad.h" +#include "ecled.h" +#include "ecbjt.h" +#include "ecopamp.h" +#include "ecpotentiometer.h" +#include "ecresistor.h" +#include "ecsevensegment.h" +#include "ecsignallamp.h" +#include "ecvoltagesignal.h" +#include "ecvoltagesource.h" + +#include "pinmapping.h" + +#include "libraryitem.h" + +#include "kdebug.h" +#include <kiconloader.h> +#include <qbitmap.h> +#include <qimage.h> +#include <qpainter.h> +#include <qpixmap.h> +#include <qpushbutton.h> +#include <qregexp.h> + +#include <cassert> + +ItemLibrary::ItemLibrary() +{ + addFlowParts(); + addComponents(); + addMechanics(); + addDrawParts(); +} + + +ItemLibrary::~ItemLibrary() +{ + const LibraryItemList::iterator end = m_items.end(); + for ( LibraryItemList::iterator it = m_items.begin(); it != end; ++it ) + { + delete *it; + } + m_items.clear(); +} + + +void ItemLibrary::addFlowParts() +{ + // Container loops + addLibraryItem( Repeat::libraryItem() ); + addLibraryItem( While::libraryItem() ); + addLibraryItem( ForLoop::libraryItem() ); + + // Variable operations + addLibraryItem( Unary::libraryItem() ); + addLibraryItem( VarAssignment::libraryItem() ); + addLibraryItem( VarComparison::libraryItem() ); + + // I/O + addLibraryItem( SetPin::libraryItem() ); + addLibraryItem( TestPin::libraryItem() ); + addLibraryItem( WritePort::libraryItem() ); + addLibraryItem( ReadPort::libraryItem() ); + + // Functions + addLibraryItem( SevenSeg::libraryItem() ); +// addLibraryItem( Pulse::libraryItem() ); + addLibraryItem( Keypad::libraryItem() ); +// addLibraryItem( Count::libraryItem() ); +// addLibraryItem( InputButton::libraryItem() ); + addLibraryItem( Delay::libraryItem() ); + + // Common + addLibraryItem( Embed::libraryItem() ); + addLibraryItem( CallSub::libraryItem() ); +// addLibraryItem( Interrupt::libraryItem() ); + addLibraryItem( Sub::libraryItem() ); + addLibraryItem( End::libraryItem() ); + addLibraryItem( Start::libraryItem() ); +} + + +void ItemLibrary::addComponents() +{ + // Integrated Circuits + addLibraryItem( ECBCDTo7Segment::libraryItem() ); + addLibraryItem( MatrixDisplayDriver::libraryItem() ); + addLibraryItem( BinaryCounter::libraryItem() ); + addLibraryItem( DAC::libraryItem() ); + addLibraryItem( ADC::libraryItem() ); + addLibraryItem( ECOpAmp::libraryItem() ); + addLibraryItem( MagnitudeComparator::libraryItem() ); + addLibraryItem( Demultiplexer::libraryItem() ); + addLibraryItem( Multiplexer::libraryItem() ); + addLibraryItem( FullAdder::libraryItem() ); + addLibraryItem( RAM::libraryItem() ); + addLibraryItem( EC555::libraryItem() ); + addLibraryItem( ECDFlipFlop::libraryItem() ); + addLibraryItem( ECSRFlipFlop::libraryItem() ); + addLibraryItem( ECJKFlipFlop::libraryItem() ); +#ifndef NO_GPSIM + addLibraryItem( PICComponent::libraryItem() ); +#endif + + // Connections + addLibraryItem( ParallelPortComponent::libraryItem() ); + addLibraryItem( SerialPortComponent::libraryItem() ); + addLibraryItem( ExternalConnection::libraryItem() ); + addLibraryItem( BusSplitter::libraryItem() ); + + // Logic + addLibraryItem( ECXnor::libraryItem() ); + addLibraryItem( ECXor::libraryItem() ); + addLibraryItem( ECNor::libraryItem() ); + addLibraryItem( ECOr::libraryItem() ); + addLibraryItem( ECNand::libraryItem() ); + addLibraryItem( ECAnd::libraryItem() ); + addLibraryItem( Inverter::libraryItem() ); + addLibraryItem( Buffer::libraryItem() ); + addLibraryItem( ECClockInput::libraryItem() ); + addLibraryItem( ECLogicOutput::libraryItem() ); + addLibraryItem( ECLogicInput::libraryItem() ); + + + // Outputs +// addLibraryItem( FrequencyMeter::libraryItem() ); + addLibraryItem( CurrentProbe::libraryItem() ); + addLibraryItem( VoltageProbe::libraryItem() ); + addLibraryItem( LogicProbe::libraryItem() ); + addLibraryItem( ECAmmeter::libraryItem() ); + addLibraryItem( ECVoltMeter::libraryItem() ); + addLibraryItem( MatrixDisplay::libraryItem() ); + addLibraryItem( ECSevenSegment::libraryItem() ); + addLibraryItem( BiDirLED::libraryItem() ); + addLibraryItem( ECSignalLamp::libraryItem() ); + addLibraryItem( ECLed::libraryItem() ); + + // Switches + addLibraryItem( ECRotoSwitch::libraryItem() ); + addLibraryItem( ECDPDT::libraryItem() ); + addLibraryItem( ECSPDT::libraryItem() ); + addLibraryItem( ECDPST::libraryItem() ); + addLibraryItem( ECSPST::libraryItem() ); + addLibraryItem( ECKeyPad::libraryItem() ); + addLibraryItem( ECPTBSwitch::libraryItem() ); + addLibraryItem( ECPTMSwitch::libraryItem() ); + + + // Discrete + addLibraryItem( ECPotentiometer::libraryItem() ); + addLibraryItem( ResistorDIP::libraryItem() ); + addLibraryItem( ECBJT::libraryItemPNP() ); + addLibraryItem( ECBJT::libraryItemNPN() ); + addLibraryItem( Inductor::libraryItem() ); + addLibraryItem( ECDiode::libraryItem() ); + addLibraryItem( ECCapacitor::libraryItem() ); + addLibraryItem( ECResistor::libraryItem() ); + + // Dependent Sources + addLibraryItem( ECVCVS::libraryItem() ); + addLibraryItem( ECVCCS::libraryItem() ); + addLibraryItem( ECCCVS::libraryItem() ); + addLibraryItem( ECCCCS::libraryItem() ); + + // Independent Sources + addLibraryItem( ECCurrentSignal::libraryItem() ); + addLibraryItem( ECVoltageSignal::libraryItem() ); + addLibraryItem( ECCurrentSource::libraryItem() ); + addLibraryItem( ECGround::libraryItem() ); + addLibraryItem( ECFixedVoltage::libraryItem() ); + addLibraryItem( ECCell::libraryItem() ); + + // Other + addLibraryItem( ECSubcircuit::libraryItem() ); + addLibraryItem( PIC_IC::libraryItem() ); +} + + +void ItemLibrary::addDrawParts() +{ + addLibraryItem( DPText::libraryItem() ); + addLibraryItem( DPLine::libraryItem() ); + addLibraryItem( DPArrow::libraryItem() ); + addLibraryItem( DPRectangle::libraryItem() ); + addLibraryItem( DPEllipse::libraryItem() ); +} + + +void ItemLibrary::addMechanics() +{ +#ifdef MECHANICS + addLibraryItem( ChassisCircular2::libraryItem() ); +#endif +} + + +void ItemLibrary::addLibraryItem( LibraryItem *item ) +{ + m_items.append(item); +} + + +Item *ItemLibrary::createItem( const QString &id, ItemDocument *itemDocument, bool newItem, const char *newId, bool finishCreation ) +{ + Item * item = 0l; + if ( id.startsWith("sc/") ) + { + // Is a subcircuit... + + CircuitDocument * circuitDocument = dynamic_cast<CircuitDocument*>(itemDocument); + if (!circuitDocument) + { + kdWarning() << "Cannot create subcircuit without a circuit document" << endl; + return 0l; + } + + QString temp = id; + int numId = temp.remove("sc/").toInt(); + + item = subcircuits()->createSubcircuit( numId, /*id,*/ circuitDocument, newItem, newId ); + } + + else + { + const LibraryItemList::iterator end = m_items.end(); + LibraryItemList::iterator it = m_items.begin(); + for ( ; it != end; ++it ) + { + if ( (*it)->allIDs().contains(id) ) + { + item = (*it)->createItemFnPtr()( itemDocument, newItem, newId ); + item->m_type = (*it)->activeID(); + break; + } + } + + if ( it == end ) + kdWarning() << "Could not find the item constructor for id " << id << endl; + } + + if ( finishCreation && item ) + item->finishedCreation(); + + return item; +} + + +QImage ItemLibrary::itemImage( Item *item, const uint maxSize ) +{ + Component *component = dynamic_cast<Component*>(item); + + QRect bound = item->boundingRect().normalize(); + bound.setLeft( bound.left()-8 ); + bound.setRight( bound.right()+8 ); + bound.setTop( bound.top()-8 ); + bound.setBottom( bound.bottom()+8 ); + + // We want a nice square bounding rect + const int dy = bound.width() - bound.height(); + if ( dy > 0 ) + { + bound.setTop( bound.top()-(dy/2) ); + bound.setBottom( bound.bottom()+(dy/2) ); + } + else if ( dy < 0 ) + { + bound.setLeft( bound.left()+(dy/2) ); + bound.setRight( bound.right()-(dy/2) ); + } + + const bool cache = ((bound.width()*bound.height()) > (int)maxSize); + QString type; + if ( cache && m_imageMap.contains(item->type()) ) + return m_imageMap[item->type()]; + + // Create pixmap big enough to contain CNItem and surrounding nodes + // and copy the button grab to it + + QPixmap pm( bound.size() ); + + QBitmap mask( bound.size() ); + mask.fill( Qt::color0 ); + + QPainter maskPainter(&mask); + maskPainter.translate( -bound.x(), -bound.y() ); + maskPainter.setPen( Qt::color1 ); + maskPainter.setBrush( Qt::color1 ); + + + QPainter p(&pm); + p.translate( -bound.x(), -bound.y() ); + p.setPen( item->pen() ); + p.setBrush( item->brush() ); + + // Now draw the shape :-) + const bool sel = item->isSelected(); + if (sel) + { + // We block the signals as we end up in an infinite loop with cnitem emitting a selected signal + item->blockSignals(true); + item->setSelected(false); + item->blockSignals(false); + } + item->drawShape(p); + item->drawShape(maskPainter); + if (sel) + { + item->blockSignals(true); + item->setSelected(sel); + item->blockSignals(false); + } + + maskPainter.setPen( Qt::color1 ); + maskPainter.setBrush( Qt::color1 ); + + QWMatrix transMatrix; // Matrix to apply to the image + + CNItem *cnItem = dynamic_cast<CNItem*>(item); + if (cnItem) + { + NodeMap nodes = cnItem->nodeMap(); + const NodeMap::iterator nodesEnd = nodes.end(); + for ( NodeMap::iterator it = nodes.begin(); it != nodesEnd; ++it ) + { + Node *node = it.data().node; + const bool sel = node->isSelected(); + if (sel) + node->setSelected(false); + if ( ECNode *ecnode = dynamic_cast<ECNode*>(node) ) + { + const bool showVB = ecnode->showVoltageBars(); + ecnode->setShowVoltageBars(false); + ecnode->drawShape(p); + ecnode->drawShape(maskPainter); + ecnode->setShowVoltageBars(showVB); + } + else + { + node->drawShape(p); + node->drawShape(maskPainter); + } + if (sel) + node->setSelected(sel); + } + + p.setPen(Qt::black); + TextMap text = cnItem->textMap(); + const TextMap::iterator textEnd = text.end(); + for ( TextMap::iterator it = text.begin(); it != textEnd; ++it ) + { + it.data()->drawShape(p); + it.data()->drawShape(maskPainter); + } + +// maskPainter.setPen( Qt::color1 ); +// maskPainter.setBrush( Qt::color1 ); + cnItem->drawWidgets(p); +// cnItem->drawWidgets(maskPainter); + + transMatrix = Component::transMatrix( component->angleDegrees(), component->flipped(), bound.width()/2, bound.height()/2, true ); + } + + pm.setMask(mask); + + // Now, rotate the image so that it's the right way up, and scale it to size + QImage im = pm.convertToImage(); + im = im.xForm(transMatrix); + im = im.smoothScale( 50, 50, QImage::ScaleMin ); + + if (cache) + m_imageMap[item->type()] = im; + + return im; +} + +QPixmap ItemLibrary::itemIconFull( const QString &id ) +{ + LibraryItemList::iterator end = m_items.end(); + for ( LibraryItemList::iterator it = m_items.begin(); it != end; ++it ) + { + if ( *it && (*it)->allIDs().contains(id) ) + { + return (*it)->iconFull(); + } + } + return QPixmap(); +} diff --git a/src/itemlibrary.h b/src/itemlibrary.h new file mode 100644 index 0000000..181eb5f --- /dev/null +++ b/src/itemlibrary.h @@ -0,0 +1,82 @@ +/*************************************************************************** + * Copyright (C) 2003 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef ITEMLIBRARY_H +#define ITEMLIBRARY_H + +#include <qcolor.h> +#include <qmap.h> + +class Document; +class Item; +class ItemDocument; +class ItemLibrary; +class LibraryItem; +inline ItemLibrary* itemLibrary(); + +typedef QMap< QString, QImage > ImageMap; +typedef QValueList<LibraryItem*> LibraryItemList; + +/** +While the program is running, only one instance of this class is created. +You can get it by calling itemLibrary() +@short Holds the list of CNItems +@author David Saxton +*/ +class ItemLibrary +{ +public: + ~ItemLibrary(); + /** + * Returns a QPixmap of the item icon + */ + QPixmap itemIconFull( const QString &id ); + /** + * Append the given item into the library + */ + void addLibraryItem( LibraryItem *item ); + /** + * Returns a list of items in the library + */ + LibraryItemList* items() { return &m_items; } + /** + * Creates a new item with the given id, and returns a pointer to it + */ + Item *createItem( const QString &id, ItemDocument *itemDocument, bool newItem, const char *newId = 0L, bool finishCreation = true ); + /** + * Returns an image of the given cnitem. As QPixmap::convertToImage is + * a *very* slow function, this will cache the result and return that for + * large images. + * @param cnItem A pointer to the CNItem + * @param maxSize The maximum size (in pixels) before the image is cached + */ + QImage itemImage( Item *item, const uint maxSize = 36000 ); + +protected: + void addComponents(); + void addFlowParts(); + void addMechanics(); + void addDrawParts(); + + ItemLibrary(); + + LibraryItemList m_items; + ImageMap m_imageMap; + + friend ItemLibrary* itemLibrary(); +}; + +inline ItemLibrary* itemLibrary() +{ + static ItemLibrary *_itemLibrary = new ItemLibrary(); + return _itemLibrary; +} + +#endif diff --git a/src/itemview.cpp b/src/itemview.cpp new file mode 100644 index 0000000..452d33c --- /dev/null +++ b/src/itemview.cpp @@ -0,0 +1,581 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "canvasmanipulator.h" +#include "cnitem.h" +#include "connector.h" +#include "docmanager.h" +#include "drawpart.h" +#include "ecnode.h" +#include "itemdocument.h" +#include "itemview.h" +#include "ktechlab.h" +#include "core/ktlconfig.h" + +#include <kaccel.h> +#include <kiconloader.h> +#include <klocale.h> +#include <kpopupmenu.h> +#include <kurldrag.h> + +#include <cmath> +#include <qcursor.h> +#include <qtimer.h> +#include <qwmatrix.h> + + +//BEGIN class ItemView +ItemView::ItemView( ItemDocument * itemDocument, ViewContainer *viewContainer, uint viewAreaId, const char *name ) + : View( itemDocument, viewContainer, viewAreaId, name ) +{ + KActionCollection * ac = actionCollection(); + + KStdAction::selectAll( itemDocument, SLOT(selectAll()), ac ); + KStdAction::zoomIn( this, SLOT(zoomIn()), ac ); + KStdAction::zoomOut( this, SLOT(zoomOut()), ac ); + KStdAction::actualSize( this, SLOT(actualSize()), ac )->setEnabled(false); + + + KAccel *pAccel = new KAccel(this); + pAccel->insert( "Cancel", i18n("Cancel"), i18n("Cancel the current operation"), Qt::Key_Escape, itemDocument, SLOT(cancelCurrentOperation()) ); + pAccel->readSettings(); + + new KAction( i18n("Delete"), "editdelete", Qt::Key_Delete, itemDocument, SLOT(deleteSelection()), ac, "edit_delete" ); + new KAction( i18n("Export as Image..."), 0, 0, itemDocument, SLOT(exportToImage()), ac, "file_export_image"); + + //BEGIN Item Alignment actions + new KAction( i18n("Align Horizontally"), 0, 0, itemDocument, SLOT(alignHorizontally()), ac, "align_horizontally" ); + new KAction( i18n("Align Vertically"), 0, 0, itemDocument, SLOT(alignVertically()), ac, "align_vertically" ); + new KAction( i18n("Distribute Horizontally"), 0, 0, itemDocument, SLOT(distributeHorizontally()), ac, "distribute_horizontally" ); + new KAction( i18n("Distribute Vertically"), 0, 0, itemDocument, SLOT(distributeVertically()), ac, "distribute_vertically" ); + //END Item Alignment actions + + + //BEGIN Draw actions + KToolBarPopupAction * pa = new KToolBarPopupAction( i18n("Draw"), "paintbrush", 0, 0, 0, ac, "edit_draw" ); + pa->setDelayed(false); + + KPopupMenu * m = pa->popupMenu(); + m->insertTitle( i18n("Draw") ); + + m->insertItem( KGlobal::iconLoader()->loadIcon( "tool_text", KIcon::Small ), i18n("Text"), DrawPart::da_text ); + m->insertItem( KGlobal::iconLoader()->loadIcon( "tool_line", KIcon::Small ), i18n("Line"), DrawPart::da_line ); + m->insertItem( KGlobal::iconLoader()->loadIcon( "tool_arrow", KIcon::Small ), i18n("Arrow"), DrawPart::da_arrow ); + m->insertItem( KGlobal::iconLoader()->loadIcon( "tool_ellipse", KIcon::Small ), i18n("Ellipse"), DrawPart::da_ellipse ); + m->insertItem( KGlobal::iconLoader()->loadIcon( "tool_rectangle", KIcon::Small ), i18n("Rectangle"), DrawPart::da_rectangle ); + connect( m, SIGNAL(activated(int)), itemDocument, SLOT(slotSetDrawAction(int)) ); + //END Draw actions + + + //BEGIN Item Control actions + new KAction( i18n("Raise Selection"), "1uparrow", Qt::Key_PageUp, itemDocument, SLOT(raiseZ()), ac, "edit_raise" ); + new KAction( i18n("Lower Selection"), "1downarrow", Qt::Key_PageDown, itemDocument, SLOT(lowerZ()), ac, "edit_lower" ); + //END Item Control actions + + + KAction * na = new KAction( "", 0, 0, 0, 0, ac, "null_action" ); + na->setEnabled(false); + + setXMLFile( "ktechlabitemviewui.rc" ); + + m_pUpdateStatusTmr = new QTimer(this); + connect( m_pUpdateStatusTmr, SIGNAL(timeout()), this, SLOT(updateStatus()) ); + connect( this, SIGNAL(viewUnfocused()), this, SLOT(stopUpdatingStatus()) ); + + p_itemDocument = itemDocument; + m_zoomLevel = 1.; + m_CVBEditor = new CVBEditor( p_itemDocument->canvas(), this, "cvbEditor" ); + m_CVBEditor->setLineWidth(1); + + m_layout->insertWidget( 0, m_CVBEditor ); + + setAcceptDrops(true); +} + + +ItemView::~ItemView() +{ +} + + +bool ItemView::canZoomIn() const +{ + return true; +} +bool ItemView::canZoomOut() const +{ + return int(std::floor((100*m_zoomLevel)+0.5)) > 25; +} + + +void ItemView::zoomIn() +{ + int currentZoomPercent = int(std::floor((100*m_zoomLevel)+0.5)); + int newZoom = currentZoomPercent; + + if ( currentZoomPercent < 100 ) + newZoom += 25; + else if ( currentZoomPercent < 200 ) + newZoom += 50; + else + newZoom += 100; + + m_zoomLevel = newZoom/100.; + + QWMatrix m( m_zoomLevel, 0.0, 0.0, m_zoomLevel, 1.0, 1.0 ); + m_CVBEditor->setWorldMatrix(m); + + p_itemDocument->requestEvent( ItemDocument::ItemDocumentEvent::ResizeCanvasToItems ); + updateZoomActions(); +} + + +void ItemView::zoomOut() +{ + int currentZoomPercent = int(std::floor((100*m_zoomLevel)+0.5)); + int newZoom = currentZoomPercent; + + if ( currentZoomPercent <= 25 ) + return; + if ( currentZoomPercent <= 100 ) + newZoom -= 25; + else if ( currentZoomPercent <= 200 ) + newZoom -= 50; + else + newZoom -= 100; + + m_zoomLevel = newZoom/100.; + + QWMatrix m( m_zoomLevel, 0.0, 0.0, m_zoomLevel, 1.0, 1.0 ); + m_CVBEditor->setWorldMatrix(m); + + p_itemDocument->requestEvent( ItemDocument::ItemDocumentEvent::ResizeCanvasToItems ); + updateZoomActions(); +} + + +void ItemView::actualSize() +{ + m_zoomLevel = 1.0; + QWMatrix m( m_zoomLevel, 0.0, 0.0, m_zoomLevel, 1.0, 1.0 ); + m_CVBEditor->setWorldMatrix(m); + + p_itemDocument->requestEvent( ItemDocument::ItemDocumentEvent::ResizeCanvasToItems ); + updateZoomActions(); +} + + +void ItemView::updateZoomActions() +{ + action("view_zoom_in")->setEnabled( canZoomIn() ); + action("view_zoom_out")->setEnabled( canZoomOut() ); + action("view_actual_size")->setEnabled( m_zoomLevel != 1.0 ); +} + + +void ItemView::dropEvent( QDropEvent *event ) +{ + KURL::List urls; + if ( KURLDrag::decode( event, urls ) ) + { + // Then it is URLs that we can decode :) + const KURL::List::iterator end = urls.end(); + for ( KURL::List::iterator it = urls.begin(); it != end; ++it ) + { + DocManager::self()->openURL(*it); + } + return; + } + + if ( !QString(event->format()).startsWith("ktechlab/") ) + return; + + QString text; + QDataStream stream( event->encodedData(event->format()), IO_ReadOnly ); + stream >> text; + + QPoint position = event->pos(); + position.setX( int((position.x() + m_CVBEditor->contentsX())/m_zoomLevel) ); + position.setY( int((position.y() + m_CVBEditor->contentsY())/m_zoomLevel) ); + + // Get a new component item + p_itemDocument->addItem( text, position, true ); + + setFocus(); +} + + +void ItemView::scrollToMouse( const QPoint &pos ) +{ + QPoint position = pos * m_zoomLevel; + + int left = m_CVBEditor->contentsX(); + int top = m_CVBEditor->contentsY(); + int right = left + m_CVBEditor->visibleWidth(); + int bottom = top + m_CVBEditor->visibleHeight(); + + if( position.x() < left ) m_CVBEditor->scrollBy( position.x() - left, 0 ); + else if( position.x() > right ) m_CVBEditor->scrollBy( position.x() - right, 0 ); + + if( position.y() < top ) m_CVBEditor->scrollBy( 0, position.y() - top ); + else if( position.y() > bottom ) m_CVBEditor->scrollBy( 0, position.y() - bottom); +} + + +void ItemView::contentsMousePressEvent( QMouseEvent *e ) +{ + if (!e) + return; + + e->accept(); + + // For some reason, when we are initially unfocused, we only receive the + // release event if the user drags the mouse - not very often. So see if we + // were initially unfocused, and if so, do unclick as well. + bool wasFocused = isFocused(); + setFocused(); + + if ( !p_itemDocument ) + return; + + p_itemDocument->canvas()->setMessage( QString::null ); + p_itemDocument->m_cmManager->mousePressEvent( EventInfo( this, e ) ); + + if ( !wasFocused ) + p_itemDocument->m_cmManager->mouseReleaseEvent( EventInfo( this, e ) ); +} + + +void ItemView::contentsMouseDoubleClickEvent( QMouseEvent *e ) +{ + if (!e) + return; + + e->accept(); + + //HACK: Pass this of as a single press event if widget underneath + QCanvasItem * atTop = p_itemDocument->itemAtTop( e->pos()/zoomLevel() ); + if ( atTop && atTop->rtti() == ItemDocument::RTTI::Widget ) + contentsMousePressEvent(e); + else + p_itemDocument->m_cmManager->mouseDoubleClickEvent( EventInfo( this, e ) ); +} + + +void ItemView::contentsMouseMoveEvent( QMouseEvent *e ) +{ + if ( !e || !p_itemDocument ) + return; + + e->accept(); + + p_itemDocument->m_cmManager->mouseMoveEvent( EventInfo( this, e ) ); + if ( !m_pUpdateStatusTmr->isActive() ) + startUpdatingStatus(); +} + + +void ItemView::contentsMouseReleaseEvent( QMouseEvent *e ) +{ + if (!e) + return; + + e->accept(); + + p_itemDocument->m_cmManager->mouseReleaseEvent( EventInfo( this, e ) ); +} + + +void ItemView::contentsWheelEvent( QWheelEvent *e ) +{ + if (!e) + return; + + e->accept(); + + p_itemDocument->m_cmManager->wheelEvent( EventInfo( this, e ) ); +} + + +void ItemView::dragEnterEvent( QDragEnterEvent *event ) +{ + startUpdatingStatus(); + + KURL::List urls; + if ( KURLDrag::decode( event, urls ) ) + { + event->accept(true); + // Then it is URLs that we can decode later :) + return; + } +} + + +void ItemView::enterEvent( QEvent * e ) +{ + Q_UNUSED(e); + startUpdatingStatus(); +} + + +void ItemView::leaveEvent( QEvent * e ) +{ + Q_UNUSED(e); + stopUpdatingStatus(); + + // Cleanup + setCursor(Qt::ArrowCursor); + + if (p_ktechlab) + p_ktechlab->slotChangeStatusbar(QString::null); + + if ( p_itemDocument ) + p_itemDocument->m_canvasTip->setVisible(false); +} + + +void ItemView::slotUpdateConfiguration() +{ +// m_CVBEditor->setEraseColor( KTLConfig::bgColor() ); + m_CVBEditor->setEraseColor( Qt::white ); + + if ( m_pUpdateStatusTmr->isActive() ) + startUpdatingStatus(); +} + + +void ItemView::startUpdatingStatus() +{ + m_pUpdateStatusTmr->stop(); + m_pUpdateStatusTmr->start( int(1000./KTLConfig::refreshRate()) ); +} + + +void ItemView::stopUpdatingStatus() +{ + m_pUpdateStatusTmr->stop(); +} + + +void ItemView::updateStatus() +{ + QPoint pos = (m_CVBEditor->mapFromGlobal( QCursor::pos() ) + QPoint( m_CVBEditor->contentsX(), m_CVBEditor->contentsY() )) / zoomLevel(); + + ItemDocument * itemDocument = static_cast<ItemDocument*>(document()); + if ( !itemDocument ) + return; + + CMManager * cmManager = itemDocument->m_cmManager; + CanvasTip * canvasTip = itemDocument->m_canvasTip; + + bool displayTip = false; + QCursor cursor = Qt::ArrowCursor; + QString statusbar; + + if ( cmManager->cmState() & CMManager::cms_repeated_add ) + { + cursor = Qt::CrossCursor; + statusbar = i18n("Left click to add. Right click to resume normal editing"); + } + else if ( cmManager->cmState() & CMManager::cms_draw ) + { + cursor = Qt::CrossCursor; + statusbar = i18n("Click and hold to start drawing."); + } + else if ( cmManager->currentManipulator()) + { + switch ( cmManager->currentManipulator()->type() ) + { + case CanvasManipulator::RepeatedItemAdd: + cursor = Qt::CrossCursor; + statusbar = i18n("Left click to add. Right click to resume normal editing"); + break; + case CanvasManipulator::ManualConnector: + statusbar = i18n("Right click to cancel the connector"); + // no break + case CanvasManipulator::AutoConnector: + cursor = Qt::CrossCursor; + break; + case CanvasManipulator::ItemMove: + case CanvasManipulator::MechItemMove: + cursor = Qt::SizeAllCursor; + break; + case CanvasManipulator::Draw: + cursor = Qt::CrossCursor; + break; + default: + break; + } + + } + else if ( QCanvasItem *qcanvasItem = itemDocument->itemAtTop(pos) ) + { + switch( qcanvasItem->rtti() ) + { + case ItemDocument::RTTI::Connector: + { + cursor = Qt::CrossCursor; + if ( itemDocument->type() != Document::dt_circuit ) + break; + + canvasTip->displayVI( static_cast<Connector*>(qcanvasItem), pos ); + displayTip = true; + break; + } + case ItemDocument::RTTI::Node: + { + cursor = Qt::CrossCursor; + ECNode * ecnode = dynamic_cast<ECNode*>(qcanvasItem); + if ( !ecnode ) + break; + + canvasTip->displayVI( ecnode, pos ); + displayTip = true; + break; + } + case ItemDocument::RTTI::CNItem: + { + statusbar = (static_cast<CNItem*>(qcanvasItem))->name(); + break; + } + default: + { + break; + } + } + } + setCursor(cursor); + + if (p_ktechlab) + p_ktechlab->slotChangeStatusbar(statusbar); + + canvasTip->setVisible(displayTip); +} + +//END class ItemView + + + +//BEGIN class CVBEditor +CVBEditor::CVBEditor( QCanvas *canvas, ItemView *itemView, const char *name ) + : QCanvasView( canvas, itemView, name, WNoAutoErase | WStaticContents ) +{ + b_ignoreEvents = false; + b_passEventsToView = true; + p_itemView = itemView; + viewport()->setMouseTracking(true); + setAcceptDrops(true); + setFrameShape(NoFrame); +// setEraseColor( KTLConfig::bgColor() ); + setEraseColor( Qt::white ); + setPaletteBackgroundColor( Qt::white ); + viewport()->setEraseColor( Qt::white ); + viewport()->setPaletteBackgroundColor( Qt::white ); +} + + +void CVBEditor::contentsMousePressEvent( QMouseEvent* e ) +{ + if (b_passEventsToView) + p_itemView->contentsMousePressEvent(e); + else + QCanvasView::contentsMousePressEvent(e); +} + + +void CVBEditor::contentsMouseReleaseEvent( QMouseEvent* e ) +{ + if (b_passEventsToView) + p_itemView->contentsMouseReleaseEvent(e); + else + QCanvasView::contentsMouseReleaseEvent(e); +} + + +void CVBEditor::contentsMouseDoubleClickEvent( QMouseEvent* e ) +{ + if (b_passEventsToView) + p_itemView->contentsMouseDoubleClickEvent(e); + else + QCanvasView::contentsMouseDoubleClickEvent(e); +} + + +void CVBEditor::contentsMouseMoveEvent( QMouseEvent* e ) +{ + if (b_passEventsToView) + p_itemView->contentsMouseMoveEvent(e); + else + QCanvasView::contentsMouseMoveEvent(e); +} + + +void CVBEditor::dragEnterEvent( QDragEnterEvent* e ) +{ + if (b_passEventsToView) + p_itemView->dragEnterEvent(e); + else + QCanvasView::dragEnterEvent(e); +} + + +void CVBEditor::dropEvent( QDropEvent* e ) +{ + if (b_passEventsToView) + p_itemView->dropEvent(e); + else + QCanvasView::dropEvent(e); +} + + +void CVBEditor::enterEvent( QEvent * e ) +{ + if (b_passEventsToView) + p_itemView->enterEvent(e); + else + QCanvasView::enterEvent(e); +} + + +void CVBEditor::leaveEvent( QEvent* e ) +{ + if (b_passEventsToView) + p_itemView->leaveEvent(e); + else + QCanvasView::leaveEvent(e); +} + + +void CVBEditor::contentsWheelEvent( QWheelEvent *e ) +{ + if (b_ignoreEvents) + { + e->ignore(); + return; + } + + if (b_passEventsToView) + p_itemView->contentsWheelEvent(e); + else + { + b_ignoreEvents = true; + QCanvasView::wheelEvent(e); + b_ignoreEvents = false; + } +} + +void CVBEditor::viewportResizeEvent( QResizeEvent * e ) +{ + QCanvasView::viewportResizeEvent(e); + p_itemView->p_itemDocument->requestEvent( ItemDocument::ItemDocumentEvent::ResizeCanvasToItems ); +} +//END class CVBEditor + +#include "itemview.moc" diff --git a/src/itemview.h b/src/itemview.h new file mode 100644 index 0000000..2bca8fd --- /dev/null +++ b/src/itemview.h @@ -0,0 +1,113 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef ITEMVIEW_H +#define ITEMVIEW_H + +#include <view.h> + +#include <qcanvas.h> +#include <qguardedptr.h> + +class CVBEditor; +class ItemDocument; +class QTimer; + +/** +@author David Saxton +*/ +class ItemView : public View +{ + Q_OBJECT + public: + ItemView( ItemDocument *itemDocument, ViewContainer *viewContainer, uint viewAreaId, const char *name = 0 ); + ~ItemView(); + + virtual bool canZoomIn() const; + virtual bool canZoomOut() const; + CVBEditor *cvbEditor() const { return m_CVBEditor; } + /** + * @returns The zoom level + */ + double zoomLevel() const { return m_zoomLevel; } + + public slots: + void actualSize(); + void zoomIn(); + void zoomOut(); + void scrollToMouse( const QPoint &pos ); + virtual void updateStatus(); + + protected slots: + /** + * Called when the user changes the configuration. + */ + virtual void slotUpdateConfiguration(); + void startUpdatingStatus(); + void stopUpdatingStatus(); + + protected: + void updateZoomActions(); + /** + * Attempts to create a new CNItem if one was dragged onto the canvas + */ + void dropEvent( QDropEvent* ); + /** + * Reinherit to allow different types of items to be dragged in. + */ + virtual void dragEnterEvent( QDragEnterEvent* ); + void contentsMousePressEvent( QMouseEvent *e ); + void contentsMouseReleaseEvent( QMouseEvent *e ); + void contentsMouseDoubleClickEvent( QMouseEvent *e ); + void contentsMouseMoveEvent( QMouseEvent *e ); + void contentsWheelEvent( QWheelEvent *e ); + void enterEvent( QEvent * e ); + void leaveEvent( QEvent * e ); + + QGuardedPtr<ItemDocument> p_itemDocument; + CVBEditor *m_CVBEditor; + double m_zoomLevel; + QTimer * m_pUpdateStatusTmr; + + friend class CVBEditor; +}; + + +/** +@author David Saxton +*/ +class CVBEditor : public QCanvasView +{ +Q_OBJECT +public: + CVBEditor( QCanvas *canvas, ItemView *itemView, const char *name ); + + void setPassEventsToView( bool pass ) { b_passEventsToView = pass; } + + virtual void contentsMousePressEvent( QMouseEvent* e ); + virtual void contentsMouseReleaseEvent( QMouseEvent* e ); + virtual void contentsMouseDoubleClickEvent( QMouseEvent* e ); + virtual void contentsMouseMoveEvent( QMouseEvent* e ); + virtual void dragEnterEvent( QDragEnterEvent* e ); + virtual void dropEvent( QDropEvent* e ); + virtual void contentsWheelEvent( QWheelEvent *e ); + virtual void enterEvent( QEvent * e ); + virtual void leaveEvent( QEvent * e ); + +protected: + virtual void viewportResizeEvent( QResizeEvent * ); + ItemView *p_itemView; + bool b_passEventsToView; + bool b_ignoreEvents; +}; + + + +#endif diff --git a/src/katemdi.cpp b/src/katemdi.cpp new file mode 100644 index 0000000..0f34de1 --- /dev/null +++ b/src/katemdi.cpp @@ -0,0 +1,863 @@ +/* This file is part of the KDE libraries + Copyright (C) 2005 Christoph Cullmann <cullmann@kde.org> + Copyright (C) 2002, 2003 Joseph Wenninger <jowenn@kde.org> + + GUIClient partly based on ktoolbarhandler.cpp: Copyright (C) 2002 Simon Hausmann <hausmann@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "katemdi.h" + +#include <kdebug.h> +#include <kglobalsettings.h> +#include <kapplication.h> +#include <klocale.h> +#include <kconfig.h> +#include <kiconloader.h> +#include <kpopupmenu.h> + + +namespace KateMDI { + +//BEGIN SPLITTER + +Splitter::Splitter(Orientation o, QWidget* parent, const char* name) + : QSplitter(o, parent, name) +{ +} + +Splitter::~Splitter() +{ +} + +bool Splitter::isLastChild(QWidget* w) const +{ + return ( idAfter( w ) == 0 ); +} + +int Splitter::idAfter ( QWidget * w ) const +{ + return QSplitter::idAfter (w); +} + +//END SPLITTER + + +//BEGIN TOGGLETOOLVIEWACTION + +ToggleToolViewAction::ToggleToolViewAction ( const QString& text, const KShortcut& cut, ToolView *tv, + QObject* parent, const char* name ) + : KToggleAction(text,cut,parent,name) + , m_tv(tv) +{ + connect(this,SIGNAL(toggled(bool)),this,SLOT(slotToggled(bool))); + connect(m_tv,SIGNAL(visibleChanged(bool)),this,SLOT(visibleChanged(bool))); + + setChecked(m_tv->visible()); +} + +ToggleToolViewAction::~ToggleToolViewAction() +{ + unplugAll(); +} + +void ToggleToolViewAction::visibleChanged(bool) +{ + if (isChecked() != m_tv->visible()) + setChecked (m_tv->visible()); +} + +void ToggleToolViewAction::slotToggled(bool t) +{ + if (t) + { + m_tv->mainWindow()->showToolView (m_tv); + m_tv->setFocus (); + } + else + { + m_tv->mainWindow()->hideToolView (m_tv); + m_tv->mainWindow()->centralWidget()->setFocus (); + } +} + +//END TOGGLETOOLVIEWACTION + + +//BEGIN GUICLIENT + +GUIClient::GUIClient ( MainWindow *mw ) + : QObject ( mw ) + , KXMLGUIClient ( mw ) + , m_mw (mw) +{ + connect( m_mw->guiFactory(), SIGNAL( clientAdded( KXMLGUIClient * ) ), + this, SLOT( clientAdded( KXMLGUIClient * ) ) ); + + if (actionCollection()->kaccel()==0) + actionCollection()->setWidget(m_mw); + + + // read shortcuts + actionCollection()->readShortcutSettings( "Shortcuts", kapp->config() ); +} + +GUIClient::~GUIClient() +{ +} + +void GUIClient::registerToolView (ToolView *tv) +{ + QString aname = QString("kate_mdi_toolview_") + tv->id; + + // try to read the action shortcut + KShortcut sc; + KConfig *cfg = kapp->config(); + QString _grp = cfg->group(); + cfg->setGroup("Shortcuts"); + sc = KShortcut( cfg->readEntry( aname, "" ) ); + cfg->setGroup( _grp ); +} + +void GUIClient::clientAdded( KXMLGUIClient *client ) +{ + if ( client == this ) + updateActions(); +} + +void GUIClient::updateActions() +{ + if ( !factory() ) + return; +} + +//END GUICLIENT + + +//BEGIN TOOLVIEW + +ToolView::ToolView (MainWindow *mainwin, Sidebar *sidebar, QWidget *parent) + : QVBox (parent) + , m_mainWin (mainwin) + , m_sidebar (sidebar) + , m_visible (false) + , persistent (false) +{ +} + +ToolView::~ToolView () +{ + m_mainWin->toolViewDeleted (this); +} + +void ToolView::setVisible (bool vis) +{ + if (m_visible == vis) + return; + + m_visible = vis; + emit visibleChanged (m_visible); +} + +bool ToolView::visible () const +{ + return m_visible; +} + +void ToolView::childEvent ( QChildEvent *ev ) +{ + // set the widget to be focus proxy if possible + if (ev->inserted() && ev->child() && ev->child()->qt_cast("QWidget")) + setFocusProxy ((QWidget *)(ev->child()->qt_cast("QWidget"))); + + QVBox::childEvent (ev); +} + +//END TOOLVIEW + + +//BEGIN SIDEBAR + +Sidebar::Sidebar (KMultiTabBar::KMultiTabBarPosition pos, MainWindow *mainwin, QWidget *parent) + : KMultiTabBar ((pos == KMultiTabBar::Top || pos == KMultiTabBar::Bottom) ? KMultiTabBar::Horizontal : KMultiTabBar::Vertical, parent) + , m_mainWin (mainwin) + , m_splitter (0) + , m_ownSplit (0) + , m_lastSize (0) +{ + setSidebarPosition( pos ); + hide (); +} + +Sidebar::~Sidebar () +{ +} + +void Sidebar::setSidebarPosition( KMultiTabBarPosition pos ) +{ + m_pos = pos; + setPosition(pos); +} + +void Sidebar::setSidebarStyle( KMultiTabBarStyle style ) +{ + m_sidebarTabStyle = style; + setStyle(style); +} + +void Sidebar::setSplitter (Splitter *sp) +{ + m_splitter = sp; + m_ownSplit = new Splitter ((sidebarPosition() == KMultiTabBar::Top || sidebarPosition() == KMultiTabBar::Bottom) ? Qt::Horizontal : Qt::Vertical, m_splitter); + m_ownSplit->setOpaqueResize( KGlobalSettings::opaqueResize() ); + m_ownSplit->setChildrenCollapsible( false ); + m_splitter->setResizeMode( m_ownSplit, QSplitter::KeepSize ); + m_ownSplit->hide (); +} + +ToolView *Sidebar::addWidget (const QPixmap &icon, const QString &text, ToolView *widget) +{ + static int id = 0; + + if (widget) + { + if (widget->sidebar() == this) + return widget; + + widget->sidebar()->removeWidget (widget); + } + + int newId = ++id; + + appendTab (icon, newId, text); + + if (!widget) + { + widget = new ToolView (m_mainWin, this, m_ownSplit); + widget->hide (); + widget->icon = icon; + widget->text = text; + } + else + { + widget->hide (); + widget->reparent (m_ownSplit, 0, QPoint()); + widget->m_sidebar = this; + } + + // save it's pos ;) + widget->persistent = false; + + m_idToWidget.insert (newId, widget); + m_widgetToId.insert (widget, newId); + m_toolviews.push_back (widget); + + show (); + + connect(tab(newId),SIGNAL(clicked(int)),this,SLOT(tabClicked(int))); + tab(newId)->installEventFilter(this); + + return widget; +} + +bool Sidebar::removeWidget (ToolView *widget) +{ + if (!m_widgetToId.contains(widget)) + return false; + + removeTab(m_widgetToId[widget]); + + m_idToWidget.remove (m_widgetToId[widget]); + m_widgetToId.remove (widget); + m_toolviews.remove (widget); + + bool anyVis = false; + QIntDictIterator<ToolView> it( m_idToWidget ); + for ( ; it.current(); ++it ) + { + if (!anyVis) + anyVis = it.current()->isVisible(); + } + + if (m_idToWidget.isEmpty()) + { + m_ownSplit->hide (); + hide (); + } + else if (!anyVis) + m_ownSplit->hide (); + + return true; +} + +bool Sidebar::showWidget (ToolView *widget) +{ + if (!m_widgetToId.contains(widget)) + return false; + + // hide other non-persistent views + QIntDictIterator<ToolView> it( m_idToWidget ); + for ( ; it.current(); ++it ) + if ((it.current() != widget) && !it.current()->persistent) + { + it.current()->hide(); + setTab (it.currentKey(), false); + it.current()->setVisible(false); + } + + setTab (m_widgetToId[widget], true); + + m_ownSplit->show (); + widget->show (); + + widget->setVisible (true); + + return true; +} + +bool Sidebar::hideWidget (ToolView *widget) +{ + if (!m_widgetToId.contains(widget)) + return false; + + bool anyVis = false; + + updateLastSize (); + + for ( QIntDictIterator<ToolView> it( m_idToWidget ); it.current(); ++it ) + { + if (it.current() == widget) + { + it.current()->hide(); + continue; + } + + if (!anyVis) + anyVis = it.current()->isVisible(); + } + + // lower tab + setTab (m_widgetToId[widget], false); + + if (!anyVis) + m_ownSplit->hide (); + + widget->setVisible (false); + + return true; +} + +void Sidebar::tabClicked(int i) +{ + ToolView *w = m_idToWidget[i]; + + if (!w) + return; + + if (isTabRaised(i)) + { + showWidget (w); + w->setFocus (); + } + else + { + hideWidget (w); + m_mainWin->centralWidget()->setFocus (); + } +} + +bool Sidebar::eventFilter(QObject *obj, QEvent *ev) +{ + if (ev->type()==QEvent::ContextMenu) + { + QContextMenuEvent *e = (QContextMenuEvent *) ev; + KMultiTabBarTab *bt = dynamic_cast<KMultiTabBarTab*>(obj); + if (bt) + { + kdDebug()<<"Request for popup"<<endl; + + m_popupButton = bt->id(); + + ToolView *w = m_idToWidget[m_popupButton]; + + if (w) + { + KPopupMenu *p = new KPopupMenu (this); + + p->insertTitle(SmallIcon("view_remove"), i18n("Behavior"), 50); + + p->insertItem(w->persistent ? SmallIconSet("window_nofullscreen") : SmallIconSet("window_fullscreen"), w->persistent ? i18n("Make Non-Persistent") : i18n("Make Persistent"), 10); + + p->insertTitle(SmallIcon("move"), i18n("Move To"), 51); + + if (sidebarPosition() != 0) + p->insertItem(SmallIconSet("back"), i18n("Left Sidebar"),0); + + if (sidebarPosition() != 1) + p->insertItem(SmallIconSet("forward"), i18n("Right Sidebar"),1); + + if (sidebarPosition() != 2) + p->insertItem(SmallIconSet("up"), i18n("Top Sidebar"),2); + + if (sidebarPosition() != 3) + p->insertItem(SmallIconSet("down"), i18n("Bottom Sidebar"),3); + + connect(p, SIGNAL(activated(int)), + this, SLOT(buttonPopupActivate(int))); + + p->exec(e->globalPos()); + delete p; + + return true; + } + } + } + + return false; +} + +void Sidebar::buttonPopupActivate (int id) +{ + ToolView *w = m_idToWidget[m_popupButton]; + + if (!w) + return; + + // move ids + if (id < 4) + { + // move + show ;) + m_mainWin->moveToolView (w, (KMultiTabBar::KMultiTabBarPosition) id); + m_mainWin->showToolView (w); + } + + // toggle persistent + if (id == 10) + w->persistent = !w->persistent; +} + +void Sidebar::updateLastSize () +{ + QValueList<int> s = m_splitter->sizes (); + + int i = 0; + if ((sidebarPosition() == KMultiTabBar::Right || sidebarPosition() == KMultiTabBar::Bottom)) + i = 2; + + // little threshold + if (s[i] > 2) + m_lastSize = s[i]; +} + +class TmpToolViewSorter +{ + public: + ToolView *tv; + unsigned int pos; +}; + +void Sidebar::restoreSession (KConfig *config) +{ + // get the last correct placed toolview + unsigned int firstWrong = 0; + for ( ; firstWrong < m_toolviews.size(); ++firstWrong ) + { + ToolView *tv = m_toolviews[firstWrong]; + + unsigned int pos = config->readUnsignedNumEntry (QString ("Kate-MDI-ToolView-%1-Sidebar-Position").arg(tv->id), firstWrong); + + if (pos != firstWrong) + break; + } + + // we need to reshuffle, ahhh :( + if (firstWrong < m_toolviews.size()) + { + // first: collect the items to reshuffle + QValueList<TmpToolViewSorter> toSort; + for (unsigned int i=firstWrong; i < m_toolviews.size(); ++i) + { + TmpToolViewSorter s; + s.tv = m_toolviews[i]; + s.pos = config->readUnsignedNumEntry (QString ("Kate-MDI-ToolView-%1-Sidebar-Position").arg(m_toolviews[i]->id), i); + toSort.push_back (s); + } + + // now: sort the stuff we need to reshuffle + for (unsigned int m=0; m < toSort.size(); ++m) + for (unsigned int n=m+1; n < toSort.size(); ++n) + if (toSort[n].pos < toSort[m].pos) + { + TmpToolViewSorter tmp = toSort[n]; + toSort[n] = toSort[m]; + toSort[m] = tmp; + } + + // then: remove this items from the button bar + // do this backwards, to minimize the relayout efforts + for (int i=m_toolviews.size()-1; i >= (int)firstWrong; --i) + { + removeTab (m_widgetToId[m_toolviews[i]]); + } + + // insert the reshuffled things in order :) + for (unsigned int i=0; i < toSort.size(); ++i) + { + ToolView *tv = toSort[i].tv; + + m_toolviews[firstWrong+i] = tv; + + // readd the button + int newId = m_widgetToId[tv]; + appendTab (tv->icon, newId, tv->text); + connect(tab(newId),SIGNAL(clicked(int)),this,SLOT(tabClicked(int))); + tab(newId)->installEventFilter(this); + + // reshuffle in splitter + m_ownSplit->moveToLast (tv); + } + } + + // update last size if needed + updateLastSize (); + + // restore the own splitter sizes + QValueList<int> s = config->readIntListEntry (QString ("Kate-MDI-Sidebar-%1-Splitter").arg(sidebarPosition())); + m_ownSplit->setSizes (s); + + // show only correct toolviews, remember persistent values ;) + bool anyVis = false; + for ( unsigned int i=0; i < m_toolviews.size(); ++i ) + { + ToolView *tv = m_toolviews[i]; + + tv->persistent = config->readBoolEntry (QString ("Kate-MDI-ToolView-%1-Persistent").arg(tv->id), false); + tv->setVisible (config->readBoolEntry (QString ("Kate-MDI-ToolView-%1-Visible").arg(tv->id), false)); + + if (!anyVis) + anyVis = tv->visible(); + + setTab (m_widgetToId[tv],tv->visible()); + + if (tv->visible()) + tv->show(); + else + tv->hide (); + } + + if (anyVis) + m_ownSplit->show(); + else + m_ownSplit->hide(); +} + +void Sidebar::saveSession (KConfig *config) +{ + // store the own splitter sizes + QValueList<int> s = m_ownSplit->sizes(); + config->writeEntry (QString ("Kate-MDI-Sidebar-%1-Splitter").arg(sidebarPosition()), s); + + // store the data about all toolviews in this sidebar ;) + for ( unsigned int i=0; i < m_toolviews.size(); ++i ) + { + ToolView *tv = m_toolviews[i]; + + config->writeEntry (QString ("Kate-MDI-ToolView-%1-Position").arg(tv->id), tv->sidebar()->sidebarPosition()); + config->writeEntry (QString ("Kate-MDI-ToolView-%1-Sidebar-Position").arg(tv->id), i); + config->writeEntry (QString ("Kate-MDI-ToolView-%1-Visible").arg(tv->id), tv->visible()); + config->writeEntry (QString ("Kate-MDI-ToolView-%1-Persistent").arg(tv->id), tv->persistent); + } +} + +//END SIDEBAR + + +//BEGIN MAIN WINDOW + +MainWindow::MainWindow (QWidget* parentWidget, const char* name) + : KParts::MainWindow( parentWidget, name) + , m_restoreConfig (0) + , m_guiClient (new GUIClient (this)) +{ + // init the internal widgets + QHBox *hb = new QHBox (this); + setCentralWidget(hb); + + m_sidebars[KMultiTabBar::Left] = new Sidebar (KMultiTabBar::Left, this, hb); + + m_hSplitter = new Splitter (Qt::Horizontal, hb); + m_hSplitter->setOpaqueResize( KGlobalSettings::opaqueResize() ); + + m_sidebars[KMultiTabBar::Left]->setSplitter (m_hSplitter); + + QVBox *vb = new QVBox (m_hSplitter); + m_hSplitter->setCollapsible(vb, false); + + m_sidebars[KMultiTabBar::Top] = new Sidebar (KMultiTabBar::Top, this, vb); + + m_vSplitter = new Splitter (Qt::Vertical, vb); + m_vSplitter->setOpaqueResize( KGlobalSettings::opaqueResize() ); + + m_sidebars[KMultiTabBar::Top]->setSplitter (m_vSplitter); + + m_centralWidget = new QVBox (m_vSplitter); + m_vSplitter->setCollapsible(m_centralWidget, false); + + m_sidebars[KMultiTabBar::Bottom] = new Sidebar (KMultiTabBar::Bottom, this, vb); + m_sidebars[KMultiTabBar::Bottom]->setSplitter (m_vSplitter); + + m_sidebars[KMultiTabBar::Right] = new Sidebar (KMultiTabBar::Right, this, hb); + m_sidebars[KMultiTabBar::Right]->setSplitter (m_hSplitter); +} + +MainWindow::~MainWindow () +{ + // cu toolviews + while (!m_toolviews.isEmpty()) + delete m_toolviews[0]; + + // seems like we really should delete this by hand ;) + delete m_centralWidget; + + for (unsigned int i=0; i < 4; ++i) + delete m_sidebars[i]; +} + +QWidget *MainWindow::centralWidget () const +{ + return m_centralWidget; +} + +ToolView *MainWindow::createToolView (const QString &identifier, KMultiTabBar::KMultiTabBarPosition pos, const QPixmap &icon, const QString &text) +{ + if (m_idToWidget[identifier]) + return 0; + + // try the restore config to figure out real pos + if (m_restoreConfig && m_restoreConfig->hasGroup (m_restoreGroup)) + { + m_restoreConfig->setGroup (m_restoreGroup); + pos = (KMultiTabBar::KMultiTabBarPosition) m_restoreConfig->readNumEntry (QString ("Kate-MDI-ToolView-%1-Position").arg(identifier), pos); + } + + ToolView *v = m_sidebars[pos]->addWidget (icon, text, 0); + v->id = identifier; + + m_idToWidget.insert (identifier, v); + m_toolviews.push_back (v); + + // register for menu stuff + m_guiClient->registerToolView (v); + + return v; +} + +ToolView *MainWindow::toolView (const QString &identifier) const +{ + return m_idToWidget[identifier]; +} + +void MainWindow::toolViewDeleted (ToolView *widget) +{ + if (!widget) + return; + + if (widget->mainWindow() != this) + return; + + // unregister from menu stuff + + widget->sidebar()->removeWidget (widget); + + m_idToWidget.remove (widget->id); + m_toolviews.remove (widget); +} + +void MainWindow::setToolViewStyle (KMultiTabBar::KMultiTabBarStyle style) +{ + m_sidebars[0]->setSidebarStyle(style); + m_sidebars[1]->setSidebarStyle(style); + m_sidebars[2]->setSidebarStyle(style); + m_sidebars[3]->setSidebarStyle(style); +} + +KMultiTabBar::KMultiTabBarStyle MainWindow::toolViewStyle () const +{ + // all sidebars have the same style, so just take Top + return m_sidebars[KMultiTabBar::Top]->sidebarTabStyle(); +} + +bool MainWindow::moveToolView (ToolView *widget, KMultiTabBar::KMultiTabBarPosition pos) +{ + if (!widget || widget->mainWindow() != this) + return false; + + // try the restore config to figure out real pos + if (m_restoreConfig && m_restoreConfig->hasGroup (m_restoreGroup)) + { + m_restoreConfig->setGroup (m_restoreGroup); + pos = (KMultiTabBar::KMultiTabBarPosition) m_restoreConfig->readNumEntry (QString ("Kate-MDI-ToolView-%1-Position").arg(widget->id), pos); + } + + m_sidebars[pos]->addWidget (widget->icon, widget->text, widget); + + return true; +} + +bool MainWindow::showToolView (ToolView *widget) +{ + if (!widget || widget->mainWindow() != this) + return false; + + // skip this if happens during restoring, or we will just see flicker + if (m_restoreConfig && m_restoreConfig->hasGroup (m_restoreGroup)) + return true; + + return widget->sidebar()->showWidget (widget); +} + +bool MainWindow::hideToolView (ToolView *widget) +{ + if (!widget || widget->mainWindow() != this) + return false; + + // skip this if happens during restoring, or we will just see flicker + if (m_restoreConfig && m_restoreConfig->hasGroup (m_restoreGroup)) + return true; + + return widget->sidebar()->hideWidget (widget); +} + +void MainWindow::startRestore (KConfig *config, const QString &group) +{ + // first save this stuff + m_restoreConfig = config; + m_restoreGroup = group; + + if (!m_restoreConfig || !m_restoreConfig->hasGroup (m_restoreGroup)) + { + //BEGIN Added stuff specifically for ktechlab + QValueList<int> hs; + hs << 220 << 100 << 230; + QValueList<int> vs; + vs << 0 << 100 << 150; + + m_sidebars[0]->setLastSize (hs[0]); + m_sidebars[1]->setLastSize (hs[2]); + m_sidebars[2]->setLastSize (vs[0]); + m_sidebars[3]->setLastSize (vs[2]); + + m_hSplitter->setSizes(hs); + m_vSplitter->setSizes(vs); + //END Added stuff specifically for ktechlab + + return; + } + + // apply size once, to get sizes ready ;) + m_restoreConfig->setGroup (m_restoreGroup); + restoreWindowSize (m_restoreConfig); + + m_restoreConfig->setGroup (m_restoreGroup); + + // get main splitter sizes ;) + QValueList<int> hs = m_restoreConfig->readIntListEntry ("Kate-MDI-H-Splitter"); + QValueList<int> vs = m_restoreConfig->readIntListEntry ("Kate-MDI-V-Splitter"); + + m_sidebars[0]->setLastSize (hs[0]); + m_sidebars[1]->setLastSize (hs[2]); + m_sidebars[2]->setLastSize (vs[0]); + m_sidebars[3]->setLastSize (vs[2]); + + m_hSplitter->setSizes(hs); + m_vSplitter->setSizes(vs); + + setToolViewStyle( (KMultiTabBar::KMultiTabBarStyle)m_restoreConfig->readNumEntry ("Kate-MDI-Sidebar-Style", (int)toolViewStyle()) ); +} + +void MainWindow::finishRestore () +{ + if (!m_restoreConfig) + return; + + if (m_restoreConfig->hasGroup (m_restoreGroup)) + { + // apply all settings, like toolbar pos and more ;) + applyMainWindowSettings(m_restoreConfig, m_restoreGroup); + + // reshuffle toolviews only if needed + m_restoreConfig->setGroup (m_restoreGroup); + for ( unsigned int i=0; i < m_toolviews.size(); ++i ) + { + KMultiTabBar::KMultiTabBarPosition newPos = (KMultiTabBar::KMultiTabBarPosition) m_restoreConfig->readNumEntry (QString ("Kate-MDI-ToolView-%1-Position").arg(m_toolviews[i]->id), m_toolviews[i]->sidebar()->sidebarPosition()); + + if (m_toolviews[i]->sidebar()->sidebarPosition() != newPos) + { + moveToolView (m_toolviews[i], newPos); + } + } + + // restore the sidebars + m_restoreConfig->setGroup (m_restoreGroup); + for (unsigned int i=0; i < 4; ++i) + m_sidebars[i]->restoreSession (m_restoreConfig); + } + + // clear this stuff, we are done ;) + m_restoreConfig = 0; + m_restoreGroup = ""; +} + +void MainWindow::saveSession (KConfig *config, const QString &group) +{ + if (!config) + return; + + saveMainWindowSettings (config, group); + + config->setGroup (group); + + // save main splitter sizes ;) + QValueList<int> hs = m_hSplitter->sizes(); + QValueList<int> vs = m_vSplitter->sizes(); + + if (hs[0] <= 2 && !m_sidebars[0]->splitterVisible ()) + hs[0] = m_sidebars[0]->lastSize(); + if (hs[2] <= 2 && !m_sidebars[1]->splitterVisible ()) + hs[2] = m_sidebars[1]->lastSize(); + if (vs[0] <= 2 && !m_sidebars[2]->splitterVisible ()) + vs[0] = m_sidebars[2]->lastSize(); + if (vs[2] <= 2 && !m_sidebars[3]->splitterVisible ()) + vs[2] = m_sidebars[3]->lastSize(); + + config->writeEntry ("Kate-MDI-H-Splitter", hs); + config->writeEntry ("Kate-MDI-V-Splitter", vs); + + // save sidebar style + config->writeEntry ("Kate-MDI-Sidebar-Style", (int)toolViewStyle()); + + // save the sidebars + for (unsigned int i=0; i < 4; ++i) + m_sidebars[i]->saveSession (config); +} + +//END MAIN WINDOW + +} // namespace KateMDI + +#include "katemdi.moc" + diff --git a/src/katemdi.h b/src/katemdi.h new file mode 100644 index 0000000..aca8419 --- /dev/null +++ b/src/katemdi.h @@ -0,0 +1,412 @@ +/* This file is part of the KDE libraries + Copyright (C) 2005 Christoph Cullmann <cullmann@kde.org> + Copyright (C) 2002, 2003 Joseph Wenninger <jowenn@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef __KATE_MDI_H__ +#define __KATE_MDI_H__ + +#include <kparts/mainwindow.h> + +#include <kmultitabbar.h> + +#include <qdict.h> +#include <qintdict.h> +#include <qsplitter.h> + +namespace KateMDI { + + +/** This class is needed because QSplitter cant return an index for a widget. */ +class Splitter : public QSplitter +{ + Q_OBJECT + + public: + Splitter(Orientation o, QWidget* parent=0, const char* name=0); + ~Splitter(); + + /** Since there is supposed to be only 2 childs of a katesplitter, + * any child other than the last is the first. + * This method uses QSplitter::idAfter(widget) which + * returns 0 if there is no widget after this one. + * This results in an error if widget is not a child + * in this splitter */ + bool isLastChild(QWidget* w) const; + + int idAfter ( QWidget * w ) const; +}; + +class ToggleToolViewAction : public KToggleAction +{ + Q_OBJECT + + public: + ToggleToolViewAction ( const QString& text, const KShortcut& cut, + class ToolView *tv, QObject* parent = 0, const char* name = 0 ); + + virtual ~ToggleToolViewAction(); + + protected slots: + void slotToggled(bool); + void visibleChanged(bool); + + private: + ToolView *m_tv; +}; + +class GUIClient : public QObject, public KXMLGUIClient +{ + Q_OBJECT + + public: + GUIClient ( class MainWindow *mw ); + virtual ~GUIClient(); + + void registerToolView (ToolView *tv); + + private slots: + void clientAdded( KXMLGUIClient *client ); + void updateActions(); + + private: + MainWindow *m_mw; +}; + +class ToolView : public QVBox +{ + Q_OBJECT + + friend class Sidebar; + friend class MainWindow; + friend class GUIClient; + friend class ToggleToolViewAction; + + protected: + /** + * ToolView + * Objects of this clas represent a toolview in the mainwindow + * you should only add one widget as child to this toolview, it will + * be automatically set to be the focus proxy of the toolview + * @param mainwin main window for this toolview + * @param sidebar sidebar of this toolview + * @param parent parent widget, e.g. the splitter of one of the sidebars + */ + ToolView (class MainWindow *mainwin, class Sidebar *sidebar, QWidget *parent); + + public: + /** + * destuct me, this is allowed for all, will care itself that the toolview is removed + * from the mainwindow and sidebar + */ + virtual ~ToolView (); + + signals: + /** + * toolview hidden or shown + * @param bool is this toolview made visible? + */ + void visibleChanged (bool visible); + + /** + * some internal methodes needed by the main window and the sidebars + */ + protected: + MainWindow *mainWindow () { return m_mainWin; } + + Sidebar *sidebar () { return m_sidebar; } + + void setVisible (bool vis); + + public: + bool visible () const; + + protected: + void childEvent ( QChildEvent *ev ); + + private: + MainWindow *m_mainWin; + Sidebar *m_sidebar; + + /** + * unique id + */ + QString id; + + /** + * is visible in sidebar + */ + bool m_visible; + + /** + * is this view persistent? + */ + bool persistent; + + QPixmap icon; + QString text; +}; + +class Sidebar : public KMultiTabBar +{ + Q_OBJECT + + public: + Sidebar (KMultiTabBar::KMultiTabBarPosition pos, class MainWindow *mainwin, QWidget *parent); + virtual ~Sidebar (); + + void setSplitter (Splitter *sp); + + //HACK use these functions intead of their respective functions in + //KMultiTabBar so that we know what they were set to. + void setSidebarPosition( KMultiTabBarPosition pos ); + KMultiTabBar::KMultiTabBarPosition sidebarPosition() const { return m_pos; } + void setSidebarStyle( KMultiTabBarStyle style ); + KMultiTabBar::KMultiTabBarStyle sidebarTabStyle() const { return m_sidebarTabStyle; } + + public: + ToolView *addWidget (const QPixmap &icon, const QString &text, ToolView *widget); + bool removeWidget (ToolView *widget); + + bool showWidget (ToolView *widget); + bool hideWidget (ToolView *widget); + + void setLastSize (int s) { m_lastSize = s; } + int lastSize () const { return m_lastSize; } + void updateLastSize (); + + bool splitterVisible () const { return m_ownSplit->isVisible(); } + + void restoreSession (); + + /** + * restore the current session config from given object, use current group + * @param config config object to use + */ + void restoreSession (KConfig *config); + + /** + * save the current session config to given object, use current group + * @param config config object to use + */ + void saveSession (KConfig *config); + + private slots: + void tabClicked(int); + + protected: + bool eventFilter(QObject *obj, QEvent *ev); + + private slots: + void buttonPopupActivate (int id); + + private: + MainWindow *m_mainWin; + + KMultiTabBar::KMultiTabBarStyle m_sidebarTabStyle; + KMultiTabBar::KMultiTabBarPosition m_pos; + Splitter *m_splitter; + KMultiTabBar *m_tabBar; + Splitter *m_ownSplit; + + QIntDict<ToolView> m_idToWidget; + QMap<ToolView*, int> m_widgetToId; + + /** + * list of all toolviews around in this sidebar + */ + QValueList<ToolView*> m_toolviews; + + int m_lastSize; + + int m_popupButton; +}; + +class MainWindow : public KParts::MainWindow +{ + Q_OBJECT + + friend class ToolView; + + // + // Constructor area + // + public: + /** + * Constructor + */ + MainWindow (QWidget* parentWidget = 0, const char* name = 0); + + /** + * Destructor + */ + virtual ~MainWindow (); + + // + // public interfaces + // + public: + /** + * central widget ;) + * use this as parent for your content + * this widget will get focus if a toolview is hidden + * @return central widget + */ + QWidget *centralWidget () const; + + /** + * add a given widget to the given sidebar if possible, name is very important + * @param identifier unique identifier for this toolview + * @param pos position for the toolview, if we are in session restore, this is only a preference + * @param icon icon to use for the toolview + * @param text text to use in addition to icon + * @return created toolview on success or 0 + */ + ToolView *createToolView (const QString &identifier, KMultiTabBar::KMultiTabBarPosition pos, const QPixmap &icon, const QString &text); + + /** + * give you handle to toolview for the given name, 0 if no toolview around + * @param identifier toolview name + * @return toolview if existing, else 0 + */ + ToolView *toolView (const QString &identifier) const; + + /** + * set the toolview's tabbar style. + * @param style the tabbar style. + */ + void setToolViewStyle (KMultiTabBar::KMultiTabBarStyle style); + + /** + * get the toolview's tabbar style. Call this before @p startRestore(), + * otherwise you overwrite the usersettings. + * @return toolview's tabbar style + */ + KMultiTabBar::KMultiTabBarStyle toolViewStyle () const; + + protected: + /** + * called by toolview destructor + * @param widget toolview which is destroyed + */ + void toolViewDeleted (ToolView *widget); + + /** + * modifiers for existing toolviews + */ + public: + /** + * move a toolview around + * @param widget toolview to move + * @param pos position to move too, during session restore, only preference + * @return success + */ + bool moveToolView (ToolView *widget, KMultiTabBar::KMultiTabBarPosition pos); + + /** + * show given toolview, discarded while session restore + * @param widget toolview to show + * @return success + */ + bool showToolView (ToolView *widget); + + /** + * hide given toolview, discarded while session restore + * @param widget toolview to hide + * @return success + */ + bool hideToolView (ToolView *widget); + + /** + * session saving and restore stuff + */ + public: + /** + * start the restore + * @param config config object to use + * @param group config group to use + */ + void startRestore (KConfig *config, const QString &group); + + /** + * finish the restore + */ + void finishRestore (); + + /** + * save the current session config to given object and group + * @param config config object to use + * @param group config group to use + */ + void saveSession (KConfig *config, const QString &group); + + /** + * internal data ;) + */ + private: + /** + * map identifiers to widgets + */ + QDict<ToolView> m_idToWidget; + + /** + * list of all toolviews around + */ + QValueList<ToolView*> m_toolviews; + + /** + * widget, which is the central part of the + * main window ;) + */ + QWidget *m_centralWidget; + + /** + * horizontal splitter + */ + Splitter *m_hSplitter; + + /** + * vertical splitter + */ + Splitter *m_vSplitter; + + /** + * sidebars for the four sides + */ + Sidebar *m_sidebars[4]; + + /** + * config object for session restore, only valid between + * start and finish restore calls + */ + KConfig *m_restoreConfig; + + /** + * restore group + */ + QString m_restoreGroup; + + /** + * out guiclient + */ + GUIClient *m_guiClient; +}; + +} + +#endif diff --git a/src/ktechlab.cpp b/src/ktechlab.cpp new file mode 100644 index 0000000..cc89ffb --- /dev/null +++ b/src/ktechlab.cpp @@ -0,0 +1,1232 @@ +/*************************************************************************** + * Copyright (C) 2003-2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "circuitdocument.h" +#include "config.h" +#include "contexthelp.h" +#include "docmanager.h" +#include "filemetainfo.h" +#include "flowcodedocument.h" +#include "itemeditor.h" +#include "itemgroup.h" +#include "iteminterface.h" +#include "itemlibrary.h" +#include "ktechlab.h" +#include "core/ktlconfig.h" +#include "languagemanager.h" +#include "mechanicsdocument.h" +#include "microlibrary.h" +#include "newfiledlg.h" +#include "oscilloscope.h" +#include "projectmanager.h" +#include "recentfilesaction.h" +#include "settingsdlg.h" +#include "subcircuits.h" +#include "symbolviewer.h" +#include "textdocument.h" +#include "textview.h" +#include "viewcontainer.h" + +#include <qdockarea.h> +#include <qtimer.h> +#include <qtoolbutton.h> +#include <qwhatsthis.h> + +#include <kaccel.h> +#include <kapplication.h> +#include <kdebug.h> +#include <kedittoolbar.h> +#include <kfiledialog.h> +#include <kiconloader.h> +#include <kio/netaccess.h> +#include <kkeydialog.h> +#include <klocale.h> +#include <kmessagebox.h> +#include <kpopupmenu.h> +#include <kstandarddirs.h> +#include <ktabwidget.h> +#include <kurldrag.h> +#include <kwin.h> + +KTechlab::KTechlab() + : KateMDI::MainWindow( 0, "KTechlab" ) +{ + QTime ct; + ct.start(); + + m_bIsShown = false; + m_pContainerDropSource = 0l; + m_pContainerDropReceived = 0l; + m_pContextMenuContainer = 0l; + m_pFocusedContainer = 0l; + m_pToolBarOverlayLabel = 0l; + + m_pUpdateCaptionsTimer = new QTimer( this ); + connect( m_pUpdateCaptionsTimer, SIGNAL(timeout()), this, SLOT(slotUpdateCaptions()) ); + + setMinimumSize( 400, 400 ); + + DocManager::self(this); + ItemInterface::self(this); + + setupTabWidget(); + setupToolDocks(); + setupActions(); + setupView(); + readProperties( KGlobal::config() ); + +// kdDebug() << "Constructor time: " << ct.elapsed() << endl; +} + + +KTechlab::~KTechlab() +{ + fileMetaInfo()->saveAllMetaInfo(); + + delete fileMetaInfo(); + delete itemLibrary(); // This better be the last time the item library is used! + delete subcircuits(); +} + + +void KTechlab::show() +{ + KateMDI::MainWindow::show(); + m_bIsShown = true; +} + + +void KTechlab::load( const KURL & url ) +{ + if ( url.url().endsWith( ".ktechlab", false ) ) + { + // This is a ktechlab project; it has to be handled separetly from a + // normal file. + + ProjectManager::self()->slotOpenProject( url ); + return; + } + + + QString target; + // the below code is what you should normally do. in this + // example case, we want the url to our own. you probably + // want to use this code instead for your app + + // download the contents + if ( !KIO::NetAccess::download( url, target, this ) ) + { + // If the file could not be downloaded, for example does not + // exist on disk, NetAccess will tell us what error to use + KMessageBox::error(this, KIO::NetAccess::lastErrorString()); + + return; + } + + addRecentFile(url); + + // set our caption + setCaption( url.prettyURL() ); + + // load in the file (target is always local) + DocManager::self()->openURL( target ); + + // and remove the temp file + KIO::NetAccess::removeTempFile( target ); +} + + +QStringList KTechlab::recentFiles() +{ + return m_recentFiles->items(); +} + + +void KTechlab::setupToolDocks() +{ +#if defined(KDE_MAKE_VERSION) +# if KDE_VERSION >= KDE_MAKE_VERSION(3,3,0) + setToolViewStyle( KMultiTabBar::KDEV3ICON ); +# endif +#endif + + QPixmap pm; + KIconLoader * loader = KGlobal::iconLoader(); + KateMDI::ToolView * tv = 0l; + + tv = createToolView( ProjectManager::toolViewIdentifier(), + KMultiTabBar::Left, + loader->loadIcon( "attach", KIcon::Small ), + i18n("Project") ); + ProjectManager::self( this, tv ); + + pm.load( locate( "appdata", "icons/circuit.png" ) ); + tv = createToolView( ComponentSelector::toolViewIdentifier(), + KMultiTabBar::Left, + pm, + i18n("Components") ); + ComponentSelector::self(tv); + + // Create an instance of the subcircuits interface, now that we have created the component selector + subcircuits(); + Subcircuits::loadSubcircuits(); + + pm.load( locate( "appdata", "icons/flowcode.png" ) ); + tv = createToolView( FlowPartSelector::toolViewIdentifier(), + KMultiTabBar::Left, + pm, + i18n("Flow Parts") ); + FlowPartSelector::self(tv); + +#ifdef MECHANICS + pm.load( locate( "appdata", "icons/mechanics.png" ) ); + tv = createToolView( MechanicsSelector::toolViewIdentifier(), + KMultiTabBar::Left, + pm, + i18n("Mechanics") ); + MechanicsSelector::self(tv); +#endif + + pm.load( locate( "appdata", "icons/item.png" ) ); + tv = createToolView( ItemEditor::toolViewIdentifier(), + KMultiTabBar::Right, + pm, + i18n("Item Editor") ); + ItemEditor::self(tv); + + tv = createToolView( ContextHelp::toolViewIdentifier(), + KMultiTabBar::Right, + loader->loadIcon( "contents", KIcon::Small ), + i18n("Context Help") ); + ContextHelp::self(tv); + + tv = createToolView( LanguageManager::toolViewIdentifier(), + KMultiTabBar::Bottom, + loader->loadIcon( "log", KIcon::Small ), + i18n("Messages") ); + LanguageManager::self( tv, this ); + +#ifndef NO_GPSIM + tv = createToolView( SymbolViewer::toolViewIdentifier(), + KMultiTabBar::Right, + loader->loadIcon( "blockdevice", KIcon::Small ), + i18n("Symbol Viewer") ); + SymbolViewer::self(tv); +#endif + + addOscilloscopeAsToolView(this); +} + + +void KTechlab::addWindow( ViewContainer * vc ) +{ + if ( vc && !m_viewContainerList.contains(vc) ) + { + m_viewContainerList << vc; + connect( vc, SIGNAL(destroyed(QObject* )), this, SLOT(slotViewContainerDestroyed(QObject* )) ); + } + + m_viewContainerList.remove((ViewContainer*)0); + slotUpdateTabWidget(); + slotDocModifiedChanged(); +} + + +void KTechlab::setupView() +{ + setAcceptDrops(true); + setStandardToolBarMenuEnabled(true); + setXMLFile("ktechlabui.rc"); + createShellGUI(true); + action("newfile_popup")->plug( toolBar("mainToolBar"), 0 ); + action("file_new")->unplug( toolBar("mainToolBar") ); + statusBar()->show(); +} + + +void KTechlab::overlayToolBarScreenshot() +{ + if ( !m_pToolBarOverlayLabel ) + { + m_pToolBarOverlayLabel = new QLabel( 0, 0, WStyle_StaysOnTop | WStyle_Customize | WStyle_NoBorder | WNoAutoErase | WType_Popup ); + m_pToolBarOverlayLabel->hide(); + m_pToolBarOverlayLabel->setBackgroundMode( NoBackground ); + } + + if ( !m_bIsShown ) + { + // The window isn't visible yet, so there's nothing to overlay (and if we tried, + // it would appear as a strange floating toolbar). + return; + } + + if ( m_pToolBarOverlayLabel->isShown() ) + { + // This is to avoid successive calls to removeGUIClient when we have + // already popped it up for the first call, and don't want to take + // another screenshot (as that would be without the toolbar). + return; + } + + QPtrListIterator<KToolBar> toolBarIterator(); + +// QWidget * toolsWidget = toolBar( "toolsToolBar" ); +// QWidget * debugWidget = toolBar( "debugTB" ); + + KToolBar * toolsWidget = static_cast<KToolBar*>(child( "toolsToolBar", "KToolBar" )); + KToolBar * debugWidget = static_cast<KToolBar*>(child( "debugTB", "KToolBar" )); + + if ( !toolsWidget && !debugWidget ) + return; + + QWidget * parent = static_cast<QWidget*>(toolsWidget ? toolsWidget->parent() : debugWidget->parent()); + + QRect grabRect; + + // 128 is a sanity check (widget can do strange things when being destroyed) + + if ( toolsWidget && toolsWidget->height() <= 128 ) + grabRect |= toolsWidget->geometry(); + if ( debugWidget && debugWidget->height() <= 128 ) + grabRect |= debugWidget->geometry(); + + if ( !grabRect.isValid() ) + return; + + QPixmap shot = QPixmap::grabWidget( parent, grabRect.x(), grabRect.y(), grabRect.width(), grabRect.height() ); + + m_pToolBarOverlayLabel->move( parent->mapToGlobal( grabRect.topLeft() ) ); + m_pToolBarOverlayLabel->setFixedSize( grabRect.size() ); + m_pToolBarOverlayLabel->setPixmap( shot ); + m_pToolBarOverlayLabel->show(); + + QTimer::singleShot( 100, this, SLOT( hideToolBarOverlay() ) ); +} + + +void KTechlab::hideToolBarOverlay() +{ + if ( !m_pToolBarOverlayLabel ) + return; + +// QWidget * hiddenWidget = toolBar( "toolsToolBar" ); +// if ( !hiddenWidget ) +// return; + +// hiddenWidget->setBackgroundMode( NoBackground ); +// hiddenWidget->setWFlags( WNoAutoErase ); +// hiddenWidget->setUpdatesEnabled( false ); + + m_pToolBarOverlayLabel->hide(); +} + + +void KTechlab::addNoRemoveGUIClient( KXMLGUIClient * client ) +{ + if ( client && !m_noRemoveGUIClients.contains( client ) ) + m_noRemoveGUIClients << client; +} + + +void KTechlab::removeGUIClients() +{ + QValueList<KXMLGUIClient*> clientsToRemove; + + QPtrList<KXMLGUIClient> clients = factory()->clients(); + for ( KXMLGUIClient * client = clients.first(); client; client = clients.next() ) + { + if ( client && client != this && !m_noRemoveGUIClients.contains( client ) ) + clientsToRemove << client; + } + + if ( clients.isEmpty() ) + return; + + overlayToolBarScreenshot(); + + QValueList<KXMLGUIClient*>::iterator end = clientsToRemove.end(); + for ( QValueList<KXMLGUIClient*>::iterator it = clientsToRemove.begin(); it != end; ++it ) + factory()->removeClient(*it); +} + + +void KTechlab::setupTabWidget() +{ + m_pViewContainerTabWidget = new KTabWidget(centralWidget()); + connect( tabWidget(), SIGNAL(currentChanged(QWidget* )), this, SLOT(slotViewContainerActivated(QWidget* )) ); + connect( tabWidget(), SIGNAL(testCanDecode(const QDragMoveEvent*, bool& )), this, SLOT(slotTabDragEvent(const QDragMoveEvent*, bool& )) ); + connect( tabWidget(), SIGNAL(initiateDrag(QWidget* )), this, SLOT(slotTabDragInitiate(QWidget* )) ); + connect( tabWidget(), SIGNAL(receivedDropEvent(QDropEvent* )), this, SLOT(slotTabReceivedDropEvent(QDropEvent* )) ); + connect( tabWidget(), SIGNAL(receivedDropEvent(QWidget*, QDropEvent* )), this, SLOT(slotTabReceivedDropEvent(QWidget*, QDropEvent* )) ); + + KConfig *config = kapp->config(); + config->setGroup("UI"); + + bool CloseOnHover = config->readBoolEntry( "CloseOnHover", false ); + tabWidget()->setHoverCloseButton( CloseOnHover ); + + bool CloseOnHoverDelay = config->readBoolEntry( "CloseOnHoverDelay", false ); + tabWidget()->setHoverCloseButtonDelayed( CloseOnHoverDelay ); + +// bool openNewTabAfterCurrent = config->readBoolEntry( "OpenNewTabAfterCurrent", false ); +// bool showTabIcons = config->readBoolEntry( "ShowTabIcons", true ); + + if (config->readBoolEntry( "ShowCloseTabsButton", true )) + { + QToolButton *but = new QToolButton(tabWidget()); + but->setIconSet(SmallIcon("tab_remove")); + but->adjustSize(); + but->hide(); + connect( but, SIGNAL(clicked()), this, SLOT(slotViewContainerClose()) ); + tabWidget()->setCornerWidget(but, TopRight); + } +// tabWidget()->setTabReorderingEnabled(true); +// connect(tabWidget(), SIGNAL(movedTab(int, int)), this, SLOT(tabMoved(int, int))); + connect(tabWidget(), SIGNAL(contextMenu(QWidget*,const QPoint &)), this, SLOT(slotTabContext(QWidget*,const QPoint &))); + //END Tab bar stuff +} + + +void KTechlab::slotUpdateTabWidget() +{ + m_viewContainerList.remove( (ViewContainer*)0 ); + + bool noWindows = m_viewContainerList.isEmpty(); + + if ( QWidget * button = tabWidget()->cornerWidget(TopRight) ) + button->setHidden( noWindows ); + + if ( noWindows ) + setCaption( 0 ); +} + + +void KTechlab::setupActions() +{ + KActionCollection *ac = actionCollection(); + + KStdAction::openNew( this, SLOT(slotFileNew()), ac ); + KStdAction::open( this, SLOT(slotFileOpen()), ac ); + KStdAction::save( this, SLOT(slotFileSave()), ac ); + KStdAction::saveAs( this, SLOT(slotFileSaveAs()), ac ); + KStdAction::close( this, SLOT(slotViewClose()), ac ); + KStdAction::print( this, SLOT(slotFilePrint()), ac ); + KStdAction::quit( this, SLOT(slotFileQuit()), ac ); + KStdAction::undo( this, SLOT(slotEditUndo()), ac ); + KStdAction::redo( this, SLOT(slotEditRedo()), ac ); + KStdAction::cut( this, SLOT(slotEditCut()), ac ); + KStdAction::copy( this, SLOT(slotEditCopy()), ac ); + KStdAction::paste( this, SLOT(slotEditPaste()), ac ); + KStdAction::keyBindings( this, SLOT(slotOptionsConfigureKeys()), ac ); + KStdAction::configureToolbars( this, SLOT(slotOptionsConfigureToolbars()), ac ); + KStdAction::preferences( this, SLOT(slotOptionsPreferences()), ac ); + + //BEGIN New file popup + KToolBarPopupAction *p = new KToolBarPopupAction( i18n("&New"), "filenew", KStdAccel::shortcut(KStdAccel::New), this, SLOT(slotFileNew()), ac, "newfile_popup" ); + p->popupMenu()->insertTitle( i18n("New File") ); + (new KAction( i18n("Assembly"), "source", 0, this, SLOT(slotFileNewAssembly()), ac, "newfile_asm" ))->plug( p->popupMenu() ); + (new KAction( i18n("C source"), "source_c", 0, this, SLOT(slotFileNewC()), ac, "newfile_c" ))->plug( p->popupMenu() ); + (new KAction( i18n("Circuit"), "ktechlab_circuit", 0, this, SLOT(slotFileNewCircuit()), ac, "newfile_circuit" ))->plug( p->popupMenu() ); + (new KAction( i18n("FlowCode"), "ktechlab_flowcode", 0, this, SLOT(slotFileNewFlowCode()), ac, "newfile_flowcode" ))->plug( p->popupMenu() ); +#ifdef MECHANICS + (new KAction( i18n("Mechanics"), "ktechlab_mechanics", 0, this, SLOT(slotFileNewMechanics()), ac, "newfile_mechanics" ))->plug( p->popupMenu() ); +#endif + (new KAction( "Microbe", "ktechlab_microbe", 0, this, SLOT(slotFileNewMicrobe()), ac, "newfile_microbe" ))->plug( p->popupMenu() ); + //END New File popup + + +// m_recentFiles = KStdAction::openRecent( this, SLOT(load(const KURL&)), ac ); + m_recentFiles = new RecentFilesAction( "Recent Files", i18n("Open Recent"), this, SLOT(load(const KURL &)), ac, "file_open_recent" ); + m_statusbarAction = KStdAction::showStatusbar( this, SLOT(slotOptionsShowStatusbar()), ac ); + + //BEGIN Project Actions + ProjectManager *pm = ProjectManager::self(this); + new KAction( i18n("New Project.."), "window_new", 0, pm, SLOT(slotNewProject()), ac, "project_new" ); + new KAction( i18n("Open Project..."), "project_open", 0, pm, SLOT(slotOpenProject()), ac, "project_open" ); +// m_recentProjects = new KRecentFilesAction( i18n("Open &Recent Project..."), 0, ProjectManager::self(), SLOT(slotOpenProject(const KURL&)), ac, "project_open_recent" ); + m_recentProjects = new RecentFilesAction( "Recent Projects", i18n("Open &Recent Project..."), ProjectManager::self(), SLOT(slotOpenProject(const KURL&)), ac, "project_open_recent" ); + new KAction( i18n("Export to Makefile..."), "fileexport", 0, pm, SLOT(slotExportToMakefile()), ac, "project_export_makefile" ); + new KAction( i18n("Create Subproject..."), 0, 0, pm, SLOT(slotCreateSubproject()), ac, "project_create_subproject" ); + new KAction( i18n("Add Existing File..."), "fileopen", 0, pm, SLOT(slotAddFile()), ac, "project_add_existing_file" ); + new KAction( i18n("Add Current File..."), "fileimport", 0, pm, SLOT(slotAddCurrentFile()), ac, "project_add_current_file" ); +// new KAction( i18n("Project Options"), "configure", 0, pm, SLOT(slotProjectOptions()), ac, "project_options" ); + new KAction( i18n("Close Project"), "fileclose", 0, pm, SLOT(slotCloseProject()), ac, "project_close" ); + new KAction( i18n("Remove from Project"), "editdelete", 0, pm, SLOT(slotRemoveSelected()), ac, "project_remove_selected" ); + new KAction( i18n("Insert Existing File..."), "fileopen", 0, pm, SLOT(slotSubprojectAddExistingFile()), ac, "subproject_add_existing_file" ); + new KAction( i18n("Insert Current File..."), "fileimport", 0, pm, SLOT(slotSubprojectAddCurrentFile()),ac, "subproject_add_current_file" ); + new KAction( i18n("Linker Options..."), "configure", 0, pm, SLOT(slotSubprojectLinkerOptions()), ac, "project_item_linker_options" ); + new KAction( i18n("Build..."), "launch", 0, pm, SLOT(slotItemBuild()), ac, "project_item_build" ); + new KAction( i18n("Upload..."), "convert_to_pic", 0, pm, SLOT(slotItemUpload()), ac, "project_item_upload" ); + new KAction( i18n("Processing Options..."), "configure", 0, pm, SLOT(slotItemProcessingOptions()), ac, "project_item_processing_options" ); + //END Project Actions + + new KAction( i18n("Split View Left/Right"), "view_right", Qt::CTRL|Qt::SHIFT|Qt::Key_L, this, SLOT(slotViewSplitLeftRight()), ac, "view_split_leftright" ); + new KAction( i18n("Split View Top/Bottom"), "view_bottom", Qt::CTRL|Qt::SHIFT|Qt::Key_T, this, SLOT(slotViewSplitTopBottom()), ac, "view_split_topbottom" ); + + KToggleAction * ta = new KToggleAction( i18n("Run Simulation"), "player_play", Qt::Key_F10, 0, 0, ac, "simulation_run" ); + ta->setChecked(true); + connect( ta, SIGNAL(toggled(bool )), Simulator::self(), SLOT(slotSetSimulating(bool )) ); +#if defined(KDE_MAKE_VERSION) +# if KDE_VERSION >= KDE_MAKE_VERSION(3,3,0) + ta->setCheckedState( KGuiItem( i18n("Pause Simulation"), "player_pause", 0 ) ); +# endif +#endif + + // We can call slotCloseProject now that the actions have been created + ProjectManager::self(this)->updateActions(); + + DocManager::self(this)->disableContextActions(); +} + + +void KTechlab::slotViewContainerActivated( QWidget * viewContainer ) +{ + if (m_pFocusedContainer) + m_pFocusedContainer->setUnfocused(); + + m_pFocusedContainer = dynamic_cast<ViewContainer*>(viewContainer); + if ( !m_pFocusedContainer ) + return; + + m_pFocusedContainer->setFocused(); +} + + +void KTechlab::slotViewContainerDestroyed( QObject * object ) +{ + m_viewContainerList.remove( static_cast<ViewContainer*>(object) ); + m_viewContainerList.remove( (ViewContainer*)0 ); + slotUpdateTabWidget(); +} + + +void KTechlab::slotTabDragEvent( const QDragMoveEvent *e, bool &accept ) +{ + // Hmm...this function doesn't actually seem to get called. Instead, + // KTabBar just seems to go straight to slotTabDragInitiate. + Q_UNUSED(e); + accept = true; +} + + +void KTechlab::slotTabDragInitiate( QWidget *widget ) +{ + ViewContainer *viewContainer = dynamic_cast<ViewContainer*>(widget); + if (!viewContainer) + return; + QDragObject *dragObject = new ViewContainerDrag(viewContainer); + dragObject->drag(); +} + + +void KTechlab::slotTabReceivedDropEvent( QDropEvent *e ) +{ + if (!e) + return; + ViewContainer *viewContainerSource = dynamic_cast<ViewContainer*>(e->source()); + if (!viewContainerSource) + { + e->ignore(); + return; + } + e->accept(true); + viewContainerSource->duplicateViewContainer(); +} + + +void KTechlab::slotTabReceivedDropEvent( QWidget *widget, QDropEvent *e ) +{ + if (!e) + return; + m_pContainerDropSource = dynamic_cast<ViewContainer*>(e->source()); + m_pContainerDropReceived = dynamic_cast<ViewContainer*>(widget); + if ( !m_pContainerDropSource || !m_pContainerDropReceived || (m_pContainerDropSource == m_pContainerDropReceived) ) + { + e->ignore(); + return; + } + e->accept(true); + + KPopupMenu dropMenu; + dropMenu.insertItem( KGlobal::iconLoader()->loadIcon( "goto", KIcon::Small ), i18n("&Insert Into"), 0 ); + dropMenu.insertItem( KGlobal::iconLoader()->loadIcon( "editcopy", KIcon::Small ), i18n("&Copy Into"), 1 ); + dropMenu.insertSeparator(); + dropMenu.insertItem( KGlobal::iconLoader()->loadIcon( "stop", KIcon::Small ), i18n("C&ancel"), 2 ); + + connect( &dropMenu, SIGNAL(activated(int)), this, SLOT(slotDragContextActivated(int)) ); +// dropMenu.exec(e->pos() + widget->pos() ); + dropMenu.exec( QCursor::pos() ); +} + + +void KTechlab::slotDragContextActivated( int id ) +{ + if ( !m_pContainerDropSource || !m_pContainerDropReceived ) + return; + + switch (id) + { + case 0: + m_pContainerDropSource->copyViewContainerIntoExisting(m_pContainerDropReceived); + m_pContainerDropSource->closeViewContainer(); + break; + case 1: + m_pContainerDropSource->copyViewContainerIntoExisting(m_pContainerDropReceived); + break; + case 2: + default: + break; + } +} + + +KAction * KTechlab::action( const QString & name ) const +{ + KAction * action = actionCollection()->action(name); + if ( !action ) + kdError() << k_funcinfo << "No such action: " << name << endl; + return action; +} + + +void KTechlab::saveProperties( KConfig *conf ) +{ + // Dumbass KMainWindow - can't handle my width/height correctly. Whoever thought of the "+1" hack anyway?! + conf->setGroup("UI"); + conf->writeEntry( "Width", width() ); + conf->writeEntry( "Height", height() ); + conf->writeEntry( "WinState", KWin::windowInfo( winId(), NET::WMState ).state() ); + +#ifndef NO_GPSIM + SymbolViewer::self()->saveProperties( conf ); +#endif + + if ( ProjectManager::self()->currentProject() ) + { + conf->setGroup("Project"); + conf->writePathEntry( "Open", ProjectManager::self()->currentProject()->url().prettyURL() ); + } + else + conf->deleteGroup("Project"); + + //BEGIN Open Views State + // Remvoe old entries describing the save state - we don't want a horrible mish-mash of saved states + const QStringList groupList = conf->groupList(); + const QStringList::const_iterator groupListEnd = groupList.end(); + for ( QStringList::const_iterator it = groupList.begin(); it != groupListEnd; ++it ) + { + if ( (*it).startsWith("ViewContainer") ) + conf->deleteGroup(*it); + } + + uint viewContainerId = 1; + const ViewContainerList::iterator vcEnd = m_viewContainerList.end(); + for ( ViewContainerList::iterator it = m_viewContainerList.begin(); it != vcEnd; ++it ) + { + if ( !(*it) || !(*it)->canSaveUsefulStateInfo() ) + continue; + + // To make sure the ViewContainers are restored in the right order, we must create them in alphabetical order, + // as KConfig stores them as such... + const QString id = QString::number(viewContainerId++).rightJustify( 4, '0' ); + + conf->setGroup( "ViewContainer " + id ); + (*it)->saveState(conf); + } + //END Open Views State + + saveSession( conf, "KateMDI" ); + // Piss off KMainWindow + conf->setGroup("KateMDI"); + int scnum = QApplication::desktop()->screenNumber(parentWidget()); + QRect desk = QApplication::desktop()->screenGeometry(scnum); + conf->deleteEntry( QString::fromLatin1("Width %1").arg(desk.width()) ); + conf->deleteEntry( QString::fromLatin1("Height %1").arg(desk.height()) ); + + conf->sync(); +} + + +void KTechlab::readProperties( KConfig *conf ) +{ + startRestore( conf, "KateMDI" ); + + m_recentFiles->loadEntries(); + m_recentProjects->loadEntries(); + + //BEGIN Restore Open Views + if ( KTLConfig::restoreDocumentsOnStartup() ) + { + // If we have a lot of views open from last time, then opening them will take a long time. + // So we want to enter the qt event loop to finish drawing the window et al before adding the views. + qApp->processEvents(); + + const QStringList groupList = conf->groupList(); + const QStringList::const_iterator groupListEnd = groupList.end(); + for ( QStringList::const_iterator it = groupList.begin(); it != groupListEnd; ++it ) + { + if ( (*it).startsWith("ViewContainer") ) + { + ViewContainer *viewContainer = new ViewContainer( *it, this ); + + conf->setGroup(*it); + viewContainer->restoreState( conf, *it ); + + addWindow( viewContainer ); + } + } + } + //END Restore Open Views + + conf->setGroup("Project"); + if ( conf->readPathEntry("Open") != QString::null ) + ProjectManager::self()->slotOpenProject( KURL( conf->readPathEntry("Open") ) ); + +#ifndef NO_GPSIM + SymbolViewer::self()->readProperties( conf ); +#endif + + finishRestore(); + + // Dumbass KMainWindow - can't handle my width/height correctly. Whoever thought of the "+1" hack anyway?! + conf->setGroup("UI"); + resize( conf->readNumEntry( "Width", 800 ), conf->readNumEntry( "Height", 500 ) ); + KWin::setState( winId(), conf->readLongNumEntry( "WinState", NET::Max ) ); +} + + +void KTechlab::dragEnterEvent(QDragEnterEvent *event) +{ + // accept uri drops only + event->accept(KURLDrag::canDecode(event)); +} + + +void KTechlab::dropEvent(QDropEvent *event) +{ + // this is a very simplistic implementation of a drop event. we + // will only accept a dropped URL. the Qt dnd code can do *much* + // much more, so please read the docs there + KURL::List urls; + + // see if we can decode a URI.. if not, just ignore it + if (KURLDrag::decode(event, urls) && !urls.isEmpty()) + { + // okay, we have a URI.. process it + const KURL &url = urls.first(); + + // load in the file + load(url); + } +} + + +void KTechlab::slotOptionsShowStatusbar() +{ + // this is all very cut and paste code for showing/hiding the + // statusbar + if (m_statusbarAction->isChecked()) + statusBar()->show(); + else + statusBar()->hide(); +} + + +void KTechlab::slotOptionsConfigureKeys() +{ +// KKeyDialog::configureKeys(actionCollection(), "ktechlabui.rc"); + KKeyDialog::configure( actionCollection(), this, true ); +} + + +void KTechlab::slotOptionsConfigureToolbars() +{ + KEditToolbar *dlg = new KEditToolbar(guiFactory()); + + if (dlg->exec()) + { + createShellGUI( false ); + createShellGUI( true ); + } + + delete dlg; +} + + +void KTechlab::slotOptionsPreferences() +{ + // An instance of your dialog could be already created and could be cached, + // in which case you want to display the cached dialog instead of creating + // another one + if ( KConfigDialog::showDialog( "settings" ) ) + return; + + // KConfigDialog didn't find an instance of this dialog, so lets create it: + SettingsDlg* dialog = new SettingsDlg( this, "settings", KTLConfig::self() ); + + // User edited the configuration - update your local copies of the + // configuration data + connect( dialog, SIGNAL(settingsChanged()), this, SLOT(slotUpdateConfiguration()) ); + dialog->show(); +} + + +void KTechlab::slotUpdateConfiguration() +{ + emit configurationChanged(); +} + + +void KTechlab::slotChangeStatusbar( const QString & text ) +{ + // Avoid flicker by repeatedly displaying the same message, as QStatusBar does not check for this + if ( m_lastStatusBarMessage == text ) + return; + + statusBar()->message(text); + m_lastStatusBarMessage = text; +} + + +void KTechlab::slotTabContext( QWidget* widget,const QPoint & pos ) +{ + // Shamelessly stolen from KDevelop... + + KPopupMenu * tabMenu = new KPopupMenu; + tabMenu->insertTitle( (dynamic_cast<ViewContainer*>(widget))->caption() ); + + //Find the document on whose tab the user clicked + m_pContextMenuContainer = 0l; + + m_viewContainerList.remove((ViewContainer*)0l); + + const ViewContainerList::iterator vcEnd = m_viewContainerList.end(); + for ( ViewContainerList::iterator it = m_viewContainerList.begin(); it != vcEnd; ++it ) + { + ViewContainer * viewContainer = *it; + if ( viewContainer == widget ) + { + m_pContextMenuContainer = viewContainer; + + tabMenu->insertItem( i18n("Close"), 0 ); + + View *view = (viewContainer->viewCount() == 1) ? viewContainer->activeView() : 0l; + + if ( view && view->document()->isModified() ) + tabMenu->insertItem( i18n("Save"), 1 ); + + if ( view && !view->document()->url().isEmpty() ) + tabMenu->insertItem( i18n("Reload"), 2 ); + + if ( m_viewContainerList.count() > 1 ) + tabMenu->insertItem( i18n("Close All Others"), 4 ); + + } + } + + connect( tabMenu, SIGNAL( activated(int) ), this, SLOT(slotTabContextActivated(int)) ); + + tabMenu->exec(pos); + delete tabMenu; +} + + +void KTechlab::slotTabContextActivated( int id ) +{ + // Shamelessly stolen from KDevelop... + + if( !m_pContextMenuContainer ) + return; + + View *view = m_pContextMenuContainer->activeView(); + if (!view) + return; + QGuardedPtr<Document> document = view->document(); + + switch(id) + { + case 0: + { + m_pContextMenuContainer->closeViewContainer(); + break; + } + case 1: + document->fileSave(); + break; + case 2: + { + KURL url = document->url(); + if ( document->fileClose() ) + { + delete document; + DocManager::self()->openURL(url); + } + break; + } + case 4: + { + const ViewContainerList::iterator vcEnd = m_viewContainerList.end(); + for ( ViewContainerList::iterator it = m_viewContainerList.begin(); it != vcEnd; ++it ) + { + ViewContainer *viewContainer = *it; + if ( viewContainer && viewContainer != m_pContextMenuContainer ) + { + if ( !viewContainer->closeViewContainer() ) + return; + } + } + break; + } + default: + break; + } +} + + + +void KTechlab::slotFileNewAssembly() +{ + TextDocument *document = DocManager::self()->createTextDocument(); + if (document) + document->slotInitLanguage( TextDocument::ct_asm ); +} +void KTechlab::slotFileNewMicrobe() +{ + TextDocument *document = DocManager::self()->createTextDocument(); + if (document) + document->slotInitLanguage( TextDocument::ct_microbe ); +} +void KTechlab::slotFileNewC() +{ + TextDocument *document = DocManager::self()->createTextDocument(); + if (document) + document->slotInitLanguage( TextDocument::ct_c ); +} +void KTechlab::slotFileNewCircuit() +{ + DocManager::self()->createCircuitDocument(); +} +void KTechlab::slotFileNewFlowCode() +{ + slotFileNew(); +} +void KTechlab::slotFileNewMechanics() +{ + DocManager::self()->createMechanicsDocument(); +} + +void KTechlab::slotFileNew() +{ + NewFileDlg *newFileDlg = new NewFileDlg(this); + + newFileDlg->exec(); + + bool addToProject = newFileDlg->addToProject(); + bool accepted = newFileDlg->accepted(); + int finalType = newFileDlg->fileType(); + QString microID = newFileDlg->microID(); + int codeType = newFileDlg->codeType(); + + delete newFileDlg; + if (!accepted) + return; + + Document *created = 0l; + + if ( finalType == Document::dt_circuit ) + created = DocManager::self()->createCircuitDocument(); + + else if ( finalType == Document::dt_flowcode ) + { + FlowCodeDocument * fcd = DocManager::self()->createFlowCodeDocument(); + fcd->setPicType(microID); + created = fcd; + } + + else if ( finalType == Document::dt_mechanics ) + created = DocManager::self()->createMechanicsDocument(); + + else + { + // Presumably a text document + TextDocument * textDocument = DocManager::self()->createTextDocument(); + + if (textDocument) + textDocument->slotInitLanguage( (TextDocument::CodeType)codeType ); + + created = textDocument; + } + + if ( created && addToProject ) + created->setAddToProjectOnSave(true); +} + +void KTechlab::slotFileOpen() +{ + // this slot is called whenever the File->Open menu is selected, + // the Open shortcut is pressed (usually CTRL+O) or the Open toolbar + // button is clicked + + // standard filedialog + KURL::List urls = getFileURLs(); + const KURL::List::iterator end = urls.end(); + for ( KURL::List::iterator it = urls.begin(); it != end; ++ it) + load(*it); +} + +void KTechlab::addRecentFile( const KURL &url ) +{ + m_recentFiles->addURL( url ); + emit recentFileAdded(url); +} + + +KURL::List KTechlab::getFileURLs() +{ + return KFileDialog::getOpenURLs( + QString::null, + "*|All Files\n" + "*.asm *.src *.inc|Assembly Code (*.asm, *.src, *.inc)\n" + "*.hex|Intel Hex (*.hex)\n" + "*.circuit|Circuit (*.circuit)\n" + "*.flowcode|FlowCode (*.flowcode)\n" + "*.basic *.microbe|Microbe (*.microbe, *.basic)\n" + "*.mechanics|Mechanics (*.mechanics)\n", + 0L, + i18n("Open Location") ); +} + + +void KTechlab::slotDocModifiedChanged() +{ + //BEGIN Set tab icons + KIconLoader *loader = KGlobal::iconLoader(); + const ViewContainerList::iterator vcEnd = m_viewContainerList.end(); + for ( ViewContainerList::iterator it = m_viewContainerList.begin(); it != vcEnd; ++it ) + { + ViewContainer * vc = *it; + if ( !vc || !vc->activeView() || !vc->activeView()->document() ) + continue; + + QString iconName; + + if ( vc->activeView()->document()->isModified() ) + iconName = "filesave"; + + else switch ( vc->activeView()->document()->type() ) + { + case Document::dt_circuit: + iconName = "ktechlab_circuit"; + break; + + case Document::dt_flowcode: + iconName = "ktechlab_flowcode"; + break; + + case Document::dt_mechanics: + iconName = "ktechlab_mechanics"; + break; + + case Document::dt_text: + iconName = "txt"; + break; + + case Document::dt_pinMapEditor: + break; + + case Document::dt_none: + iconName = "unknown"; + break; + } + + tabWidget()->setTabIconSet( vc, loader->loadIcon( iconName, KIcon::Small ) ); + } + //END Set tab icons +} + + +void KTechlab::requestUpdateCaptions() +{ + m_pUpdateCaptionsTimer->start( 0, true ); +} + + +void KTechlab::slotUpdateCaptions() +{ + //BEGIN Set KTechlab caption + Document *document = DocManager::self()->getFocusedDocument(); + QString newCaption; + if ( document ) + { + KURL url = document->url(); + if ( url.isEmpty() ) + newCaption = document->caption(); + else + { + if ( url.isLocalFile() && url.ref().isNull() && url.query().isNull() ) + newCaption = url.path(); + else + newCaption = url.prettyURL(); + } + } + else + newCaption = ""; + + if (newCaption != caption().remove(" - KTechlab")) + setCaption(newCaption); + //END Set KTechlab caption + + + //BEGIN Set tab captions + emit needUpdateCaptions(); + + if ( document && document->activeView() && document->activeView()->viewContainer() ) + { + document->activeView()->viewContainer()->updateCaption(); + } + //END Set tab captions +} + + +void KTechlab::slotDocUndoRedoChanged() +{ + Document *document = DocManager::self()->getFocusedDocument(); + if (!document) + return; + + action("edit_undo")->setEnabled( document->isUndoAvailable() ); + action("edit_redo")->setEnabled( document->isRedoAvailable() ); +} + +void KTechlab::slotFileSave() +{ + Document *document = DocManager::self()->getFocusedDocument(); + if (document) + document->fileSave(); +} + +void KTechlab::slotFileSaveAs() +{ + Document *document = DocManager::self()->getFocusedDocument(); + if (document) + document->fileSaveAs(); +} + +void KTechlab::slotFilePrint() +{ + Document *document = DocManager::self()->getFocusedDocument(); + if (document) + document->print(); +} + + +bool KTechlab::queryClose() +{ + saveProperties( KGlobal::config() ); + + if ( DocManager::self()->closeAll() && ProjectManager::self()->slotCloseProject() ) + { + ViewContainerList::iterator end = m_viewContainerList.end(); + for ( ViewContainerList::iterator it = m_viewContainerList.begin(); it != end; ++it ) + { + if ( *it ) + (*it)->setKTechlabDeleted(); + } + + return true; + } + + return false; +} + +void KTechlab::slotFileQuit() +{ + // close the first window, the list makes the next one the first again. + // This ensures that queryClose() is called on each window to ask for closing + KMainWindow* w; + if(memberList) + { + for( w=memberList->first(); w!=0; w=memberList->next() ) + { + // only close the window if the closeEvent is accepted. If the user presses Cancel on the saveModified() dialog, + // the window and the application stay open. + if( !w->close() ) break; + } + } + + slotChangeStatusbar( i18n("Exiting...") ); +} + +void KTechlab::slotEditUndo() +{ + Document *document = DocManager::self()->getFocusedDocument(); + if (document) + document->undo(); +} + +void KTechlab::slotEditRedo() +{ + Document *document = DocManager::self()->getFocusedDocument(); + if (document) + document->redo(); +} + +void KTechlab::slotEditCut() +{ + Document *document = DocManager::self()->getFocusedDocument(); + if (document) + document->cut(); +} + +void KTechlab::slotEditCopy() +{ + Document *document = DocManager::self()->getFocusedDocument(); + if (document) + document->copy(); +} + +void KTechlab::slotEditPaste() +{ + Document *document = DocManager::self()->getFocusedDocument(); + if (document) + document->paste(); +} + +void KTechlab::slotViewContainerClose() +{ + if (m_pFocusedContainer) + m_pFocusedContainer->closeViewContainer(); +} +void KTechlab::slotViewClose() +{ + View *view = DocManager::self()->getFocusedView(); + if (view) + view->closeView(); +} +void KTechlab::slotViewSplitLeftRight() +{ + View *view = DocManager::self()->getFocusedView(); + if (!view) + return; + ViewContainer *vc = view->viewContainer(); + uint vaId = vc->createViewArea( view->viewAreaId(), ViewArea::Right ); + view->document()->createView( vc, vaId ); +} +void KTechlab::slotViewSplitTopBottom() +{ + View *view = DocManager::self()->getFocusedView(); + if (!view) + return; + ViewContainer *vc = view->viewContainer(); + uint vaId = vc->createViewArea( view->viewAreaId(), ViewArea::Bottom ); + view->document()->createView( vc, vaId ); +} + +#include "ktechlab.moc" diff --git a/src/ktechlab.desktop b/src/ktechlab.desktop new file mode 100644 index 0000000..14ccf66 --- /dev/null +++ b/src/ktechlab.desktop @@ -0,0 +1,11 @@ +[Desktop Entry] +Encoding=UTF-8 +Name=KTechlab +Exec=ktechlab %i %m -caption "%c" +Icon=ktechlab +Type=Application +DocPath=ktechlab/index.html +Comment=An IDE for microcontrollers and electronics +Terminal=0 +MimeType=application/x-ktechlab;application/x-flowcode;application/x-circuit;application/x-microbe; +Categories=Qt;KDE;Education;Science;Electronics diff --git a/src/ktechlab.h b/src/ktechlab.h new file mode 100644 index 0000000..b69d0c2 --- /dev/null +++ b/src/ktechlab.h @@ -0,0 +1,243 @@ +/*************************************************************************** + * Copyright (C) 2003-2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef KTECHLAB_H +#define KTECHLAB_H + +#include <katemdi.h> + +class CircuitDocument; +class TextDocument; +class ComponentSelector; +class Document; +class FlowCodeDocument; +class ItemEditor; +class LanguageManager; +class LogView; +class MechanicsDocument; +class ProjectManager; +class View; +class ViewContainer; + +typedef QValueList< QGuardedPtr<ViewContainer> > ViewContainerList; + +class KAction; +class RecentFilesAction; +class KTabWidget; +class KToolBar; +class KToggleAction; +class KURL; +class QLabel; + +/** + * This class serves as the main window for KTechlab. It handles the + * menus, toolbars, status bars, loading/saving files, config, etc. + * + * @short Main window class + * @author David Saxton + */ +class KTechlab : public KateMDI::MainWindow +{ + Q_OBJECT + public: + KTechlab(); + ~KTechlab(); + + /** + * Returns a pointer to an action with the given name. + */ + KAction * action( const QString & name ) const; + /** + * Returns a URL from a Open File dialog (with all ktechlab related file + * types allowed). + */ + static KURL::List getFileURLs(); + /** + * Returns a list of the recently opened/saved files + */ + QStringList recentFiles(); + /** + * The tab and window captions will get updated when we have re-entered + * the Qt event loop. + */ + void requestUpdateCaptions(); + /** + * Returns the tabwidget that shows the list of view containers. + */ + KTabWidget * tabWidget() const { return m_pViewContainerTabWidget; } + /** + * Registers the viewcontainer with the internal list. + */ + void addWindow( ViewContainer * vc ); + /** + * Removes all gui clients added to the factory other than ourself. + */ + void removeGUIClients(); + /** + * Work around a crash. Adds the given KXMLGUIClient to a list of those + * that ktechlab will not attempt to remove on calling removeGUIClients. + */ + void addNoRemoveGUIClient( KXMLGUIClient * client ); + /** + * For preventing flickering when we are updating the toolbars - grab + * a pixmap of the current toolbars, and overlay it in position. + */ + void overlayToolBarScreenshot(); + + virtual void show(); + + signals: + /** + * Emitted when the user changes the configuration settings in the config dialog + */ + void configurationChanged(); + /** + * Emitted when a recent file is added + */ + void recentFileAdded( const KURL &url ); + /** + * Emitted when ViewContainers should update their captions. + */ + void needUpdateCaptions(); + + public slots: + /** + * The user right clicked on a tab item. + */ + void slotTabContext( QWidget* widget,const QPoint & pos ); + void slotDragContextActivated( int id ); + /** + * The user clicked on an item in the tab-menu (from right clicking). + */ + void slotTabContextActivated( int id ); + void slotChangeStatusbar(const QString& text); + void load(const KURL& url); + void slotUpdateConfiguration(); + /** + * Adds a url to the list of recently opened files + */ + void addRecentFile( const KURL &url ); + /** + * A document had its modified state changed; will update actions, + * tab titles, etc as appropriate. + */ + void slotDocModifiedChanged(); + /** + * A document had its undo/redo state changed; will update actions, + * tab titles, etc as appropriate. + */ + void slotDocUndoRedoChanged(); + + protected: + /** + * Overridden virtuals for Qt drag 'n drop (XDND) + */ + virtual void dragEnterEvent(QDragEnterEvent *event); + virtual void dropEvent(QDropEvent *event); + /** + * This function is called when it is time for the app to save its + * properties for session management purposes. + */ + void saveProperties(KConfig *); + /** + * This function is called when this app is restored. The KConfig + * object points to the session management config file that was saved + * with @ref saveProperties + */ + void readProperties(KConfig *); + /** + * Called before the window is closed, either by the user or indirectly by the session manager. + * This function doesn't actually close the main window; it only queries the user and closes the active view. + * To quit the appliaction completly, you should use KTechlab::slotFileQuit() + */ + virtual bool queryClose(); + + protected slots: + void slotViewContainerActivated( QWidget * viewContainer ); + void slotTabDragEvent( const QDragMoveEvent *e, bool &accept ); + void slotTabDragInitiate( QWidget *widget ); + void slotTabReceivedDropEvent( QDropEvent *e ); + void slotTabReceivedDropEvent( QWidget *widget, QDropEvent *e ); + void slotUpdateTabWidget(); + /** + * Updates the tab and window captions from what is currently open and + * focused. + */ + void slotUpdateCaptions(); + + private slots: + /** + * Called from a QTimer timeout (which should be after the toolbars have + * finished constructing themselves). + */ + void hideToolBarOverlay(); + void slotViewContainerDestroyed( QObject * obj ); + + void slotFileNewAssembly(); + void slotFileNewMicrobe(); + void slotFileNewC(); + void slotFileNewCircuit(); + void slotFileNewFlowCode(); + void slotFileNewMechanics(); + void slotFileNew(); + void slotFileOpen(); + void slotFileSave(); + void slotFileSaveAs(); + void slotFilePrint(); + void slotFileQuit(); + + // Editing operations + void slotEditUndo(); + void slotEditRedo(); + void slotEditCut(); + void slotEditCopy(); + void slotEditPaste(); + + /** + * Split the current view into two + */ + void slotViewSplitLeftRight(); + /** + * Split the current view into two + */ + void slotViewSplitTopBottom(); + void slotViewContainerClose(); + void slotViewClose(); + + void slotOptionsShowStatusbar(); + void slotOptionsConfigureKeys(); + void slotOptionsConfigureToolbars(); + void slotOptionsPreferences(); + + private: + void setupActions(); + void setupToolDocks(); + void setupView(); + void setupTabWidget(); + + RecentFilesAction * m_recentFiles; + RecentFilesAction * m_recentProjects; + KToggleAction * m_statusbarAction; + KTabWidget * m_pViewContainerTabWidget; + QString m_lastStatusBarMessage; + QValueList<KXMLGUIClient*> m_noRemoveGUIClients; + QLabel * m_pToolBarOverlayLabel; + bool m_bIsShown; // Set true when show() is called + ViewContainerList m_viewContainerList; + QTimer * m_pUpdateCaptionsTimer; + + QGuardedPtr<ViewContainer> m_pContextMenuContainer; + QGuardedPtr<ViewContainer> m_pFocusedContainer; + QGuardedPtr<ViewContainer> m_pContainerDropSource; + QGuardedPtr<ViewContainer> m_pContainerDropReceived; +}; + +#endif // KTECHLAB_H + diff --git a/src/ktechlabcircuitui.rc b/src/ktechlabcircuitui.rc new file mode 100644 index 0000000..a21cb18 --- /dev/null +++ b/src/ktechlabcircuitui.rc @@ -0,0 +1,31 @@ +<!DOCTYPE kpartgui SYSTEM "kpartgui.dtd"> +<kpartgui name="KTechlabCircuit" version="4"> + <MenuBar> + <Menu name="tools"> + <text>&Tools</text> + <Separator/> + <Menu name="routing_mode_menu"> + <text>Routing Mode</text> + <Action name="routing_mode_auto"/> + <Action name="routing_mode_manual"/> + </Menu> + <Action name="edit_mode_manual_route"/> + <Action name="edit_rotate_cw"/> + <Action name="edit_rotate_ccw"/> + </Menu> + </MenuBar> + + <ToolBar name="toolsToolBar"> + <Separator/> + <Action name="routing_mode"/> + <Action name="edit_draw"/> + <Action name="edit_rotate_ccw"/> + <Action name="edit_rotate_cw"/> + </ToolBar> + + <ToolBar name="itemEditorTB" noMerge="1"> + <text>Item Editor</text> +<!-- <Action name="null_action"/> --> + </ToolBar> +</kpartgui> + diff --git a/src/ktechlabflowcodeui.rc b/src/ktechlabflowcodeui.rc new file mode 100644 index 0000000..ea08666 --- /dev/null +++ b/src/ktechlabflowcodeui.rc @@ -0,0 +1,31 @@ +<!DOCTYPE kpartgui SYSTEM "kpartgui.dtd"> +<kpartgui name="KTechlabFlowCode" version="6"> + <MenuBar> + <Menu name="tools"><text>&Tools</text> + <Separator/> + <Menu name="routing_mode_menu"> + <text>Routing Mode</text> + <Action name="routing_mode_auto"/> + <Action name="routing_mode_manual"/> + </Menu> + <Separator/> + <Action name="tools_to_microbe"/> + <Action name="tools_to_assembly"/> + <Action name="tools_to_hex"/> + <Action name="tools_to_pic"/> + </Menu> + </MenuBar> + + <ToolBar name="toolsToolBar"> + <Separator/> + <Action name="routing_mode"/> + <Action name="edit_draw"/> + <Action name="program_convert"/> + </ToolBar> + + <ToolBar name="itemEditorTB" noMerge="1"> + <text>Item Editor</text> +<!-- <Action name="null_action"/> --> + </ToolBar> +</kpartgui> + diff --git a/src/ktechlabitemviewui.rc b/src/ktechlabitemviewui.rc new file mode 100644 index 0000000..bcfd6a7 --- /dev/null +++ b/src/ktechlabitemviewui.rc @@ -0,0 +1,38 @@ +<!DOCTYPE kpartgui SYSTEM "kpartgui.dtd"> +<kpartgui name="KTechlabItemView" version="8"> + <MenuBar> + <Menu name="file"> + <text>&File</text> + <Action name="file_export_image" group="save_merge"/> + </Menu> + + <Menu name="edit"> + <text>&Edit</text> + <Action name="edit_select_all"/> + <Separator/> + <Action name="edit_delete"/> + </Menu> + + <Menu name="view"> + <text>&View</text> + <Action name="view_zoom_in"/> + <Action name="view_zoom_out"/> + <Action name="view_actual_size"/> + </Menu> + + <Menu name="tools"> + <Separator/> + <text>&Tools</text> + <Action name="edit_raise"/> + <Action name="edit_lower"/> + </Menu> + </MenuBar> + +<!-- <ToolBar name="mainToolBar"> --> + <ToolBar name="toolsToolBar"> + <text>Tools</text> + <Action name="view_zoom_in" /> + <Action name="view_zoom_out" /> + </ToolBar> +</kpartgui> + diff --git a/src/ktechlabkateui.rc b/src/ktechlabkateui.rc new file mode 100644 index 0000000..9d901f1 --- /dev/null +++ b/src/ktechlabkateui.rc @@ -0,0 +1,120 @@ +<!DOCTYPE kpartgui SYSTEM "kpartgui.dtd"> +<kpartgui name="KTechlabText" version="9"> + <MenuBar> + <Menu name="file"> + <text>&File</text> + <!-- Why do we have both actions defined? Because Kate changed the name + for KDE 3.5. Only one of them will show up in the user interface, + depending on the version of KDE that the user has installed. --> + <Action name="file_export" group="save_merge"/> + <Action name="file_export_html" group="save_merge"/> + </Menu> + + <Menu name="edit"> + <text>&Edit</text> + <Action name="edit_deselect" group="edit_select_merge" /> + <Action name="set_verticalSelect" group="edit_select_merge" /> + <Separator group="edit_select_merge" /> + <Action name="set_insert" group="edit_select_merge" /> + <Separator group="edit_select_merge" /> + <Action name="edit_find" group="edit_find_merge" /> + <Action name="edit_find_next" group="edit_find_merge" /> + <Action name="edit_find_prev" group="edit_find_merge" /> + <Action name="edit_replace" group="edit_find_merge" /> + <Separator group="edit_find_merge" /> + <Action name="go_goto_line" group="edit_find_merge"/> + </Menu> + + <Menu name="view"> + <text>&View</text> + <Action name="switch_to_cmd_line" group="view_operations" /> + <Separator group="view_operations" /> + <Action name="view_schemas" group="view_operations" /> + <Separator group="view_operations" /> + <Action name="view_dynamic_word_wrap" group="view_operations" /> + <Action name="dynamic_word_wrap_indicators" group="view_operations" /> + <Action name="view_word_wrap_marker" group="view_operations" /> + <Separator group="view_operations" /> + <Action name="view_border" group="view_operations" /> + <Action name="view_line_numbers" group="view_operations" /> + <Action name="view_scrollbar_marks" group="view_operations" /> + <Separator group="view_operations" /> + <Action name="view_folding_markers" group="view_operations" /> + <Menu name="codefolding" group="view_operations"><text>&Code Folding</text> + <Action name="folding_toplevel" group="view_operations" /> + <Action name="folding_expandtoplevel" group="view_operations" /> + <Action name="folding_collapselocal" group="view_operations" /> + <Action name="folding_expandlocal" group="view_operations" /> + </Menu> +<!-- <Separator group="view_operations" /> --> + </Menu> + + <Menu name="tools"> + <text>&Tools</text> + <Separator/> + <Action name="tools_toggle_write_lock" group="tools_operations" /> + <Separator group="tools_operations" /> + <Action name="set_filetype" group="tools_operations" /> + <Action name="set_highlight" group="tools_operations" /> + <Action name="tools_indentation" group="tools_operations" /> + <Action name="set_encoding" group="tools_operations" /> + <Action name="set_eol" group="tools_operations" /> +<!-- <Separator group="tools_operations" /> --> +<!-- <Action name="tools_spelling" group="tools_operations" /> --> +<!-- <Action name="tools_spelling_from_cursor" group="tools_operations" /> --> +<!-- <Action name="tools_spelling_selection" group="tools_operations" /> --> + <Separator group="tools_operations" /> + <Action name="tools_indent" group="tools_operations" /> + <Action name="tools_unindent" group="tools_operations" /> + <Action name="tools_cleanIndent" group="tools_operations" /> + <Action name="tools_align" group="tools_operations" /> + <Separator group="tools_operations" /> + <Action name="tools_comment" group="tools_operations" /> + <Action name="tools_uncomment" group="tools_operations" /> +<!-- <Separator group="tools_operations" /> --> +<!-- <Action name="tools_uppercase" group="tools_operations" /> --> +<!-- <Action name="tools_lowercase" group="tools_operations" /> --> +<!-- <Action name="tools_capitalize" group="tools_operations" /> --> + <Separator group="tools_operations" /> + <Action name="tools_join_lines" group="tools_operations" /> + <Action name="tools_apply_wordwrap" group="tools_operations" /> + <Separator/> + <Action name="tools_to_microbe"/> + <Action name="tools_to_assembly"/> + <Action name="tools_to_hex"/> + <Action name="tools_to_pic"/> + <Separator/> + <Action name="format_asm"/> + </Menu> + + <Menu name="settings"> + <text>&Settings</text> + <Action name="set_confdlg" group="configure_merge" /> + </Menu> + + <Action name="bookmarks" group="bookmarks_merge"/> + </MenuBar> + + <!--<Menu name="ktexteditor_popup"> + <Action name="edit_undo" group="popup_operations" /> + <Action name="edit_redo" group="popup_operations" /> + <Separator group="popup_operations" /> + <Action name="edit_cut" group="popup_operations" /> + <Action name="edit_copy" group="popup_operations" /> + <Action name="edit_paste" group="popup_operations" /> + <Separator group="popup_operations" /> + <Action name="edit_select_all" group="popup_operations" /> + <Action name="edit_deselect" group="popup_operations" /> + <Separator group="popup_operations" /> + <Action name="bookmarks" group="popup_operations" /> + <Separator group="popup_operations" /> + </Menu>--> + + <ToolBar name="toolsToolBar"> + <text>Main Toolbar</text> + <Action name="incFontSizes" /> + <Action name="decFontSizes" /> + <Action name="program_convert"/> + </ToolBar> +</kpartgui> + diff --git a/src/ktechlabmechanicsui.rc b/src/ktechlabmechanicsui.rc new file mode 100644 index 0000000..a36d2f0 --- /dev/null +++ b/src/ktechlabmechanicsui.rc @@ -0,0 +1,11 @@ +<!DOCTYPE kpartgui SYSTEM "kpartgui.dtd"> +<kpartgui name="KTechlabMechanics" version="2"> + <MenuBar> + </MenuBar> + + <ToolBar name="itemEditorTB" noMerge="1"> + <text>Item Editor</text> +<!-- <Action name="null_action"/> --> + </ToolBar> +</kpartgui> + diff --git a/src/ktechlabtextui.rc b/src/ktechlabtextui.rc new file mode 100644 index 0000000..0c35801 --- /dev/null +++ b/src/ktechlabtextui.rc @@ -0,0 +1,96 @@ +<!DOCTYPE kpartgui SYSTEM "kpartgui.dtd"> +<kpartgui name="KTechlabText" version="10"> + <MenuBar> + <Menu name="file"> + <text>&File</text> + <!-- Why do we have both actions defined? Because Kate changed the name + for KDE 3.5. Only one of them will show up in the user interface, + depending on the version of KDE that the user has installed. --> + <Action name="file_export" group="save_merge"/> + <Action name="file_export_html" group="save_merge"/> + </Menu> + + <Menu name="tools"> + <text>&Tools</text> + <Separator/> + <Action name="tools_toggle_write_lock" group="tools_operations" /> + <Separator group="tools_operations" /> + <Action name="set_filetype" group="tools_operations" /> + <Action name="set_highlight" group="tools_operations" /> + <Action name="tools_indentation" group="tools_operations" /> + <Action name="set_encoding" group="tools_operations" /> + <Action name="set_eol" group="tools_operations" /> +<!-- <Separator group="tools_operations" /> --> +<!-- <Action name="tools_spelling" group="tools_operations" /> --> +<!-- <Action name="tools_spelling_from_cursor" group="tools_operations" /> --> +<!-- <Action name="tools_spelling_selection" group="tools_operations" /> --> + <Separator group="tools_operations" /> + <Action name="tools_indent" group="tools_operations" /> + <Action name="tools_unindent" group="tools_operations" /> + <Action name="tools_cleanIndent" group="tools_operations" /> + <Action name="tools_align" group="tools_operations" /> + <Separator group="tools_operations" /> + <Action name="tools_comment" group="tools_operations" /> + <Action name="tools_uncomment" group="tools_operations" /> +<!-- <Separator group="tools_operations" /> --> +<!-- <Action name="tools_uppercase" group="tools_operations" /> --> +<!-- <Action name="tools_lowercase" group="tools_operations" /> --> +<!-- <Action name="tools_capitalize" group="tools_operations" /> --> + <Separator group="tools_operations" /> + <Action name="tools_join_lines" group="tools_operations" /> + <Action name="tools_apply_wordwrap" group="tools_operations" /> + <Separator/> + <Action name="tools_to_microbe"/> + <Action name="tools_to_assembly"/> + <Action name="tools_to_hex"/> + <Action name="tools_to_pic"/> + <Separator/> + <Action name="format_asm"/> + </Menu> + + <Menu name="debug" group="debug_merge"> + <text>&Debug</text> + <Action name="debug_toggle_breakpoint"/> + <Separator/> + <Action name="debug_run"/> + <Action name="debug_interrupt"/> + <Action name="debug_stop"/> + <Separator/> + <Action name="debug_step"/> + <Action name="debug_step_over"/> + <Action name="debug_step_out"/> + </Menu> + </MenuBar> + + <!--<Menu name="ktexteditor_popup"> + <Action name="edit_undo" group="popup_operations" /> + <Action name="edit_redo" group="popup_operations" /> + <Separator group="popup_operations" /> + <Action name="edit_cut" group="popup_operations" /> + <Action name="edit_copy" group="popup_operations" /> + <Action name="edit_paste" group="popup_operations" /> + <Separator group="popup_operations" /> + <Action name="edit_select_all" group="popup_operations" /> + <Action name="edit_deselect" group="popup_operations" /> + <Separator group="popup_operations" /> + <Action name="bookmarks" group="popup_operations" /> + <Separator group="popup_operations" /> + </Menu>--> + + <ToolBar name="toolsToolBar"> + <text>Main Toolbar</text> + <Action name="incFontSizes" /> + <Action name="decFontSizes" /> + <Separator nomerge="1"/> + <Action name="program_convert"/> + </ToolBar> + + <ToolBar name="debugTB"> + <text>Debugger</text> + <Action name="debug_run"/> + <Action name="debug_step"/> + <Action name="debug_step_over"/> + <Action name="debug_step_out"/> + </ToolBar> +</kpartgui> + diff --git a/src/ktechlabui.rc b/src/ktechlabui.rc new file mode 100644 index 0000000..ade29e5 --- /dev/null +++ b/src/ktechlabui.rc @@ -0,0 +1,135 @@ +<!DOCTYPE kpartgui SYSTEM "kpartgui.dtd"> +<kpartgui name="KTechlab" version="7"> + <MenuBar> + <Menu name="file"> + <text>&File</text> + <DefineGroup name="save_merge"/> + </Menu> + + <Menu name="view"> + <text>&View</text> + <Action name="view_split_leftright"/> + <Action name="view_split_topbottom"/> + <Action name="view_close_activeview"/> + <Separator/> + </Menu> + + <Menu name="project"> + <text>&Project</text> + <Action name="project_new"/> + <Action name="project_open"/> + <Action name="project_open_recent"/> + <Separator/> + <Action name="project_options"/> + <Action name="project_create_subproject"/> +<!-- <Action name="project_export_makefile"/> --> + <Separator/> + <Action name="project_add_current_file"/> + <Action name="project_add_existing_file"/> + <Separator/> + <Action name="project_close"/> + </Menu> + + <Menu name="tools"> + <text>&Tools</text> + <Action name="simulation_run"/> + </Menu> + + <DefineGroup name="bookmarks_merge"/> + <DefineGroup name="debug_merge"/> + </MenuBar> + + <!-- When there is no project open --> + <Menu name="project_none_popup"> + <Action name="project_new"/> + <Action name="project_open"/> + <Action name="project_open_recent"/> + </Menu> + + + <Menu name="project_file_popup"> + <Action name="project_item_linker_options"/> + <Action name="project_item_processing_options"/> + <Separator/> + <Action name="project_item_build"/> + <Action name="project_item_upload"/> + <Separator/> + <Action name="project_remove_selected"/> + <Separator/> + <Action name="project_close"/> + </Menu> + + <Menu name="project_file_other_popup"> + <Action name="project_remove_selected"/> + <Separator/> + <Action name="project_close"/> + </Menu> + + <Menu name="project_program_popup"> + <Action name="project_item_linker_options"/> + <Action name="project_item_processing_options"/> + <Separator/> + <Action name="project_item_build"/> + <Action name="project_item_upload"/> + <Separator/> + <Action name="subproject_add_current_file"/> + <Action name="subproject_add_existing_file"/> + <Separator/> + <Action name="project_remove_selected"/> + <Separator/> + <Action name="project_close"/> + </Menu> + + <Menu name="project_library_popup"> + <Action name="project_item_linker_options"/> + <Action name="project_item_processing_options"/> + <Separator/> + <Action name="project_item_build"/> + <Separator/> + <Action name="subproject_add_current_file"/> + <Action name="subproject_add_existing_file"/> + <Separator/> + <Action name="project_remove_selected"/> + <Separator/> + <Action name="project_close"/> + </Menu> + + <Menu name="project_blank_popup"> + <Action name="project_create_subproject"/> + <Separator/> + <Action name="project_add_current_file"/> + <Action name="project_add_existing_file"/> + <Separator/> + <Action name="project_close"/> + </Menu> + + <Menu name="item_popup"> + <Action name="edit_undo"/> + <Action name="edit_redo"/> + <Separator/> + <Action name="edit_delete"/> + <Action name="edit_cut"/> + <Action name="edit_copy"/> + <Action name="edit_paste"/> + <Separator/> + <ActionList name="alignment_actionlist"/> + <ActionList name="orientation_actionlist"/> + <ActionList name="component_actionlist"/> + </Menu> + + <Menu name="ktexteditor_popup"> + <Action name="edit_undo" group="popup_operations" /> + <Action name="edit_redo" group="popup_operations" /> + <Separator group="popup_operations" /> + <Action name="edit_cut" group="popup_operations" /> + <Action name="edit_copy" group="popup_operations" /> + <Action name="edit_paste" group="popup_operations" /> + <Separator group="popup_operations" /> + <Action name="edit_select_all" group="popup_operations" /> + <Action name="edit_deselect" group="popup_operations" /> + <Separator group="popup_operations" /> + <Action name="bookmarks" group="popup_operations" /> + <Separator group="popup_operations" /> + </Menu> +</kpartgui> + diff --git a/src/languages/Makefile.am b/src/languages/Makefile.am new file mode 100644 index 0000000..fef84e7 --- /dev/null +++ b/src/languages/Makefile.am @@ -0,0 +1,14 @@ +INCLUDES = -I$(top_srcdir)/src \ + -I$(top_srcdir)/src/electronics -I$(top_srcdir)/src/electronics/components \ + -I$(top_srcdir)/src/electronics/simulation -I$(top_srcdir)/src/flowparts -I$(top_srcdir)/src/gui \ + -I$(top_srcdir)/src/languages -I$(top_srcdir)/src/mechanics -I$(top_srcdir)/src/micro -Igui \ + $(all_includes) +METASOURCES = AUTO +noinst_LTLIBRARIES = liblanguages.la +liblanguages_la_SOURCES = language.cpp languagemanager.cpp microbe.cpp \ + externallanguage.cpp gpasm.cpp gpdasm.cpp processchain.cpp flowcode.cpp asmparser.cpp \ + sdcc.cpp gplink.cpp gplib.cpp sourceline.cpp picprogrammer.cpp +noinst_HEADERS = externallanguage.h gpasm.h gpdasm.h language.h \ + languagemanager.h microbe.h processchain.h flowcode.h asmparser.h sdcc.h gplink.h gplib.h \ + sourceline.h picprogrammer.h +liblanguages_la_LIBADD = $(top_builddir)/src/gui/libgui.la diff --git a/src/languages/asmparser.cpp b/src/languages/asmparser.cpp new file mode 100644 index 0000000..eb4b7cd --- /dev/null +++ b/src/languages/asmparser.cpp @@ -0,0 +1,150 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "asmparser.h" +#include "config.h" +#include "gpsimprocessor.h" + +#include <kdebug.h> +#include <qfile.h> +#include <qregexp.h> + +AsmParser::AsmParser( const QString &url ) + : m_url(url) +{ + m_bContainsRadix = false; + m_type = Absolute; +} + + +AsmParser::~AsmParser() +{ +} + + +bool AsmParser::parse( GpsimDebugger * debugger ) +{ + QFile file(m_url); + if ( !file.open(IO_ReadOnly) ) + return false; + + QTextStream stream( &file ); + + m_type = Absolute; + m_bContainsRadix = false; + m_picID = QString::null; + + QStringList nonAbsoluteOps = QStringList::split( ",", + "code,.def,.dim,.direct,endw,extern,.file,global,idata,.ident,.line,.type,udata,udata_acs,udata_ovr,udata_shr" ); + + unsigned inputAtLine = 0; + while ( !stream.atEnd() ) + { + const QString line = stream.readLine().stripWhiteSpace(); + if ( m_type != Relocatable ) + { + QString col0 = line.section( QRegExp("[; ]"), 0, 0 ); + col0 = col0.stripWhiteSpace(); + if ( nonAbsoluteOps.contains(col0) ) + m_type = Relocatable; + } + if ( !m_bContainsRadix ) + { + if ( line.contains( QRegExp("^RADIX[\\s]*") ) || line.contains( QRegExp("^radix[\\s]*") ) ) + m_bContainsRadix = true; + } + if ( m_picID.isEmpty() ) + { + // We look for "list p = ", and "list p = picid ", and subtract the positions / lengths away from each other to get the picid text position + QRegExp fullRegExp("[lL][iI][sS][tT][\\s]+[pP][\\s]*=[\\s]*[\\d\\w]+"); + QRegExp halfRegExp("[lL][iI][sS][tT][\\s]+[pP][\\s]*=[\\s]*"); + + int startPos = fullRegExp.search(line); + if ( (startPos != -1) && (startPos == halfRegExp.search(line)) ) + { + m_picID = line.mid( startPos + halfRegExp.matchedLength(), fullRegExp.matchedLength() - halfRegExp.matchedLength() ); + m_picID = m_picID.upper(); + if ( !m_picID.startsWith("P") ) + m_picID.prepend("P"); + } + } +#ifndef NO_GPSIM + if ( debugger && line.startsWith(";#CSRC\t") ) + { + // Assembly file produced (by sdcc) from C, line is in format: + // ;#CSRC\t[file-name] [file-line] + // The filename can contain spaces. + int fileLineAt = line.findRev(" "); + + if ( fileLineAt == -1 ) + kdWarning() << k_funcinfo << "Syntax error in line \"" << line << "\" while looking for file-line" << endl; + else + { + // 7 = length_of(";#CSRC\t") + QString fileName = line.mid( 7, fileLineAt-7 ); + QString fileLineString = line.mid( fileLineAt+1, line.length() - fileLineAt - 1 ); + + if ( fileName.startsWith("\"") ) + { + // Newer versions of SDCC insert " around the filename + fileName.remove( 0, 1 ); // First " + fileName.remove( fileName.length()-1, 1 ); // Last " + } + + bool ok; + int fileLine = fileLineString.toInt(&ok) - 1; + if ( ok && fileLine >= 0 ) + debugger->associateLine( fileName, fileLine, m_url, inputAtLine ); + else + kdDebug() << k_funcinfo << "Not a valid line number: \"" << fileLineString << "\"" << endl; + } + } + if ( debugger && (line.startsWith(".line\t") || line.startsWith(";#MSRC") ) ) + { + // Assembly file produced by either sdcc or microbe, line is in format: + // \t[".line"/"#MSRC"]\t[file-line]; [file-name]\t[c/microbe source code for that line] + // We're screwed if the file name contains tabs, but hopefully not many do... + QStringList lineParts = QStringList::split( '\t', line ); + if ( lineParts.size() < 2 ) + kdWarning() << k_funcinfo << "Line is in wrong format for extracing source line and file: \""<<line<<"\""<<endl; + else + { + const QString lineAndFile = lineParts[1]; + int lineFileSplit = lineAndFile.find("; "); + if ( lineFileSplit == -1 ) + kdDebug() << k_funcinfo << "Could not find file / line split in \""<<lineAndFile<<"\""<<endl; + else + { + QString fileName = lineAndFile.mid( lineFileSplit + 2 ); + QString fileLineString = lineAndFile.left( lineFileSplit ); + + if ( fileName.startsWith("\"") ) + { + // Newer versions of SDCC insert " around the filename + fileName.remove( 0, 1 ); // First " + fileName.remove( fileName.length()-1, 1 ); // Last " + } + + bool ok; + int fileLine = fileLineString.toInt(&ok) - 1; + if ( ok && fileLine >= 0 ) + debugger->associateLine( fileName, fileLine, m_url, inputAtLine ); + else + kdDebug() << k_funcinfo << "Not a valid line number: \"" << fileLineString << "\"" << endl; + } + } + } +#endif // !NO_GPSIM + inputAtLine++; + } + + return true; +} + diff --git a/src/languages/asmparser.h b/src/languages/asmparser.h new file mode 100644 index 0000000..ab21837 --- /dev/null +++ b/src/languages/asmparser.h @@ -0,0 +1,61 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef ASMPARSER_H +#define ASMPARSER_H + +#include <qstring.h> + +class GpsimDebugger; + +/** +Reads in an assembly file, and extracts useful information from it, such as the +PIC ID + +@author David Saxton +*/ +class AsmParser +{ + public: + AsmParser( const QString &url ); + ~AsmParser(); + + enum Type { Relocatable, Absolute }; + + /** + * Read in data from file, return success status. + * @param debugger if this is non-null, then source-line markers read + * from the assembly file (such as those beginning with ";#CSRC" will be + * passed to hllDebugger). + */ + bool parse( GpsimDebugger * debugger = 0l ); + /** + * Returns the PIC ID + */ + QString picID() const { return m_picID; } + /** + * Returns whether or not the assembly file contained the "set radix" + * directive + */ + bool containsRadix() const { return m_bContainsRadix; } + /** + * If the assembly file contains any of several key words that identify + * it as a relocatable object, then this will return Relocatable. + */ + Type type() const { return m_type; } + + protected: + const QString m_url; + QString m_picID; + bool m_bContainsRadix; + Type m_type; +}; + +#endif diff --git a/src/languages/externallanguage.cpp b/src/languages/externallanguage.cpp new file mode 100644 index 0000000..7297e63 --- /dev/null +++ b/src/languages/externallanguage.cpp @@ -0,0 +1,167 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "externallanguage.h" +#include "languagemanager.h" +#include "logview.h" + +#include <kdebug.h> +#include <kprocess.h> +#include <qregexp.h> +#include <qtimer.h> + +ExternalLanguage::ExternalLanguage( ProcessChain *processChain, KTechlab *parent, const QString &name ) + : Language( processChain, parent, name ) +{ + m_languageProcess = 0l; +} + + +ExternalLanguage::~ExternalLanguage() +{ + deleteLanguageProcess(); +} + + +void ExternalLanguage::deleteLanguageProcess() +{ + if (!m_languageProcess) + return; + + // I'm not too sure if this combination of killing the process is the best way.... +// m_languageProcess->tryTerminate(); +// QTimer::singleShot( 5000, m_languageProcess, SLOT( kill() ) ); +// delete m_languageProcess; + m_languageProcess->kill(); + m_languageProcess->deleteLater(); + + m_languageProcess = 0L; +} + + +void ExternalLanguage::receivedStdout( KProcess *, char * buffer, int buflen ) +{ + QStringList lines = QStringList::split( '\n', QString::fromLocal8Bit( buffer, buflen ), false ); + QStringList::iterator end = lines.end(); + + for ( QStringList::iterator it = lines.begin(); it != end; ++it ) + { + if ( isError( *it ) ) + { + outputError( *it ); + outputtedError( *it ); + } + else if ( isWarning( *it ) ) + { + outputWarning( *it ); + outputtedWarning( *it ); + } + else + { + outputMessage( *it ); + outputtedMessage( *it ); + } + } +} + + +void ExternalLanguage::receivedStderr( KProcess *, char * buffer, int buflen ) +{ + QStringList lines = QStringList::split( '\n', QString::fromLocal8Bit( buffer, buflen ), false ); + QStringList::iterator end = lines.end(); + + for ( QStringList::iterator it = lines.begin(); it != end; ++it ) + { + if ( isStderrOutputFatal( *it ) ) + { + outputError( *it ); + outputtedError( *it ); + } + else + { + outputWarning( *it ); + outputtedWarning( *it ); + } + } +} + + +void ExternalLanguage::processExited( KProcess * ) +{ + if ( !m_languageProcess ) + return; + bool allOk = processExited( m_languageProcess->normalExit() && m_errorCount == 0 ); + finish(allOk); + deleteLanguageProcess(); +} + + +void ExternalLanguage::processInitFailed() +{ + finish(false); + deleteLanguageProcess(); +} + + +bool ExternalLanguage::start() +{ + displayProcessCommand(); + + return m_languageProcess->start( KProcess::NotifyOnExit, KProcess::All ); +} + + +void ExternalLanguage::resetLanguageProcess() +{ + reset(); + deleteLanguageProcess(); + m_errorCount = 0; + + m_languageProcess = new KProcess(this); + + connect( m_languageProcess, SIGNAL(receivedStdout( KProcess*, char*, int )), + this, SLOT(receivedStdout( KProcess*, char*, int )) ); + + connect( m_languageProcess, SIGNAL(receivedStderr( KProcess*, char*, int )), + this, SLOT(receivedStderr( KProcess*, char*, int )) ); + + connect( m_languageProcess, SIGNAL(processExited( KProcess* )), + this, SLOT(processExited( KProcess* )) ); +} + + +void ExternalLanguage::displayProcessCommand() +{ + QStringList quotedArguments; + QValueList<QCString> arguments = m_languageProcess->args(); + + if ( arguments.size() == 1 ) + quotedArguments << arguments[0]; + + else + { + QValueList<QCString>::const_iterator end = arguments.end(); + + for ( QValueList<QCString>::const_iterator it = arguments.begin(); it != end; ++it ) + { + if ( (*it).isEmpty() || (*it).contains( QRegExp("[\\s]") ) ) + quotedArguments << KProcess::quote( *it ); + else + quotedArguments << *it; + } + } + +// outputMessage( "<i>" + quotedArguments.join(" ") + "</i>" ); + outputMessage( quotedArguments.join(" ") ); +// LanguageManager::self()->logView()->addOutput( quotedArguments.join(" "), LogView::ot_info ); +} + + +#include "externallanguage.moc" diff --git a/src/languages/externallanguage.h b/src/languages/externallanguage.h new file mode 100644 index 0000000..401c2b8 --- /dev/null +++ b/src/languages/externallanguage.h @@ -0,0 +1,97 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef EXTERNALLANGUAGE_H +#define EXTERNALLANGUAGE_H + +#include "language.h" + +class KProcess; + +/** +Base class for Language support that relies on an external program; so this +class provides functionality for dealing with external processes. + +@author Daniel Clarke +@author David Saxton +*/ +class ExternalLanguage : public Language +{ +Q_OBJECT +public: + ExternalLanguage( ProcessChain *processChain, KTechlab *parent, const QString &name ); + ~ExternalLanguage(); + +protected slots: + void receivedStdout( KProcess *, char * buffer, int buflen ); + void receivedStderr( KProcess *, char * buffer, int buflen ); + void processExited( KProcess * ); + +protected: + /** + * Call this to start the language process. ExternalLanguage will ensure + * that communication et all is properly set up. + * @return true on success, false on error + */ + bool start(); + /** + * @returns whether the string outputted to stdout is an error or not + */ + virtual bool isError( const QString &message ) const = 0; + /** + * @returns whether the string outputted to stderr is fatal (stopped compilation) + */ + virtual bool isStderrOutputFatal( const QString & message ) const { Q_UNUSED(message); return true; } + /** + * @returns whether the string outputted to stdout is a warning or not + */ + virtual bool isWarning( const QString &message ) const = 0; + /** + * Called when the process outputs a (non warning/error) message + */ + virtual void outputtedMessage( const QString &/*message*/ ) {}; + /** + * Called when the process outputs a warning + */ + virtual void outputtedWarning( const QString &/*message*/ ) {}; + /** + * Called when the process outputs an error + */ + virtual void outputtedError( const QString &/*message*/ ) {}; + /** + * Called when the process exits (called before any signals are emitted, + * etc). If you reinherit this function, you should return whether + * everything is OK. + */ + virtual bool processExited( bool successfully ) { return successfully; } + /** + * Call this function if your process could not be started - the process + * will be deleted, and a failure signal emitted. + */ + void processInitFailed(); + /** + * Disconnects and deletes the language's process. + */ + void deleteLanguageProcess(); + /** + * Creates process and makes connections, ready for the derived class to + * add arguments and start the process. + */ + void resetLanguageProcess(); + /** + * Prints out the command used for running the process, with any arguments + * that contain spaces put into quotation marks. + */ + void displayProcessCommand(); + + KProcess * m_languageProcess; +}; + +#endif diff --git a/src/languages/flowcode.cpp b/src/languages/flowcode.cpp new file mode 100644 index 0000000..d19d17e --- /dev/null +++ b/src/languages/flowcode.cpp @@ -0,0 +1,496 @@ +/*************************************************************************** + * Copyright (C) 2003-2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "flowcodedocument.h" +#include "flowcode.h" +#include "flowcontainer.h" +#include "flowpart.h" +#include "microsettings.h" +#include "microinfo.h" +#include "micropackage.h" +#include "node.h" +#include "pinmapping.h" + +#include <klocale.h> +// #include <kmessagebox.h> +#include <qfile.h> + +FlowCode::FlowCode( ProcessChain *processChain, KTechlab *parent ) + : Language( processChain, parent, i18n("FlowCode") ) +{ + m_successfulMessage = i18n("*** Microbe generation successful ***"); + m_failedMessage = i18n("*** Microbe generation failed ***"); + p_startPart = 0l; +} + +FlowCode::~FlowCode() +{ +} + + +void FlowCode::processInput( ProcessOptions options ) +{ + m_processOptions = options; + + if ( !options.p_flowCodeDocument ) + { + options.p_flowCodeDocument = new FlowCodeDocument( QString::null, 0l ); + options.p_flowCodeDocument->openURL( options.inputFiles().first() ); + + connect( this, SIGNAL(processSucceeded( Language *)), options.p_flowCodeDocument, SLOT(deleteLater()) ); + connect( this, SIGNAL(processFailed( Language *)), options.p_flowCodeDocument, SLOT(deleteLater()) ); + } + + if ( !options.p_flowCodeDocument->microSettings() ) + { + finish(false); + return; + } + + QFile file(options.intermediaryOutput()); + if ( file.open(IO_WriteOnly | IO_ReadOnly) == false ) + { + finish(false); + return; + } + file.close(); + + if ( file.open(IO_WriteOnly) == false ) + { + finish(false); + return; + } + + const QString code = generateMicrobe( options.p_flowCodeDocument->itemList(), options.p_flowCodeDocument->microSettings() ); + if (code.isEmpty()) + { + finish(false); + return; + } + + QTextStream stream(&file); + stream << code; + file.close(); + finish(true); +} + + +void FlowCode::setStartPart( FlowPart *startPart ) +{ + p_startPart = startPart; +} + + +void FlowCode::addCode( const QString& code ) +{ + m_code += code; + if ( !m_code.endsWith("\n") ) m_code += '\n'; +} + +bool FlowCode::isValidBranch( FlowPart *flowPart ) +{ + return flowPart && (flowPart->level() >= m_curLevel) && !m_stopParts.contains(flowPart); +} + +void FlowCode::addCodeBranch( FlowPart * flowPart ) +{ + if (!flowPart) + return; + + if ( !isValidBranch(flowPart) ) + return; + + if ( m_addedParts.contains(flowPart) ) + { + const QString labelName = genLabel(flowPart->id()); + addCode( "goto "+labelName ); + m_gotos.append(labelName); + return; + } + else + { + m_addedParts.append(flowPart); + int prevLevel = m_curLevel; + m_curLevel = flowPart->level(); + + const QString labelName = genLabel(flowPart->id()); + addCode(labelName+':'); + m_labels.append(labelName); + + flowPart->generateMicrobe(this); + m_curLevel = prevLevel; + } +} + +QString FlowCode::genLabel( const QString &id ) +{ + return "__label_"+id; +} + +void FlowCode::addStopPart( FlowPart *part ) +{ + if (part) m_stopParts.append(part); +} + +void FlowCode::removeStopPart( FlowPart *part ) +{ + if (!part) return; + + // We only want to remove one instance of the FlowPart, in case it has been + // used as a StopPart for more than one FlowPart + FlowPartList::iterator it = m_stopParts.find(part); + if ( it != m_stopParts.end() ) m_stopParts.remove(it); +} + +QString FlowCode::generateMicrobe( const ItemList &itemList, MicroSettings *settings ) +{ + bool foundStart = false; + const ItemList::const_iterator end = itemList.end(); + for ( ItemList::const_iterator it = itemList.begin(); it != end; ++it ) + { + if (!*it) + continue; + + FlowPart * startPart = dynamic_cast<FlowPart*>((Item*)*it); + + if (!startPart) + continue; + + // Check to see if we have any floating connections + const NodeMap nodeMap = startPart->nodeMap(); + NodeMap::const_iterator nodeMapEnd = nodeMap.end(); + for ( NodeMap::const_iterator nodeMapIt = nodeMap.begin(); nodeMapIt != nodeMapEnd; ++nodeMapIt ) + { + Node * node = nodeMapIt.data().node; + if ( !node || (node->type() != Node::fp_out) ) + continue; + + if ( !startPart->outputPart( nodeMapIt.key() ) ) + outputWarning( i18n("Warning: Floating connection for %1").arg( startPart->id() ) ); + } + + FlowContainer * fc = dynamic_cast<FlowContainer*>((Item*)*it); + + if ( (*it)->id().startsWith("START") && startPart ) + { + foundStart = true; + setStartPart(startPart); + } + else if ( ((*it)->id().startsWith("interrupt") || (*it)->id().startsWith("sub")) && fc ) + { + addSubroutine(fc); + } + } + + if (!foundStart) + { + outputError( i18n("KTechlab was unable to find the \"Start\" part.\nThis must be included as the starting point for your program.") ); + return 0; + } + + m_addedParts.clear(); + m_stopParts.clear(); + m_gotos.clear(); + m_labels.clear(); + m_code = QString::null; + + // PIC type + { + const QString codeString = settings->microInfo()->id() + "\n"; + addCode(codeString); + } + + // Initial variables + { + QStringList vars = settings->variableNames(); + + // If "inited" is true at the end, we comment at the insertion point + bool inited = false; + const QString codeString = "// Initial variable values:\n"; + addCode(codeString); + + const QStringList::iterator end = vars.end(); + for ( QStringList::iterator it = vars.begin(); it != end; ++it ) + { + VariableInfo *info = settings->variableInfo(*it); + if ( info /*&& info->initAtStart*/ ) + { + inited = true; + addCode(*it+" = "+info->valueAsString()); + } + } + if (!inited) { + m_code.remove(codeString); + } else { + addCode("\n"); + } + } + + // Initial pin maps + { + const PinMappingMap pinMappings = settings->pinMappings(); + PinMappingMap::const_iterator end = pinMappings.end(); + for ( PinMappingMap::const_iterator it = pinMappings.begin(); it != end; ++it ) + { + QString type; + + switch ( it.data().type() ) + { + case PinMapping::Keypad_4x3: + case PinMapping::Keypad_4x4: + type = "keypad"; + break; + + case PinMapping::SevenSegment: + type = "sevenseg"; + break; + + case PinMapping::Invalid: + break; + } + + if ( type.isEmpty() ) + continue; + + addCode( QString("%1 %2 %3").arg( type ).arg( it.key() ).arg( it.data().pins().join(" ") ) ); + } + } + + // Initial port settings + { + QStringList portNames = settings->microInfo()->package()->portNames(); + const QStringList::iterator end = portNames.end(); + + // TRIS registers (remember that this is set to ..11111 on all resets) + for ( QStringList::iterator it = portNames.begin(); it != end; ++it ) + { + const int portType = settings->portType(*it); + const int pinCount = settings->microInfo()->package()->pinCount( 0, *it ); + + // We don't need to reset it if portType == 2^(pinCount-1) + if ( portType != (1<<pinCount)-1 ) + { + QString name = *it; + name.replace("PORT","TRIS"); + addCode( name+" = "+QString::number(portType) ); + } + } + + // PORT registers + for ( QStringList::iterator it = portNames.begin(); it != end; ++it ) + { + const int portState = settings->portState(*it); + addCode( (*it)+" = "+QString::number(portState) ); + } + } + + + m_curLevel = p_startPart->level(); + addCodeBranch(p_startPart); + addCode("end"); + + { + const FlowPartList::iterator end = m_subroutines.end(); + for ( FlowPartList::iterator it = m_subroutines.begin(); it != end; ++it ) + { + m_curLevel = 0; + if (*it) + { + addCode("\n"); + addCodeBranch(*it); + } + } + } + + tidyCode(); + return m_code; +} + +void FlowCode::tidyCode() +{ + // First, get rid of the unused labels + const QStringList::iterator end = m_labels.end(); + for ( QStringList::iterator it = m_labels.begin(); it != end; ++it ) + { + if ( !m_gotos.contains(*it) ) m_code.remove(*it+':'); + } + + + // And now on to handling indentation :-) + + if ( !m_code.endsWith("\n") ) m_code.append("\n"); + QString newCode; + bool multiLineComment = false; // For "/*"..."*/" + bool comment = false; // For "//" + bool asmEmbed = false; + bool asmEmbedAllowed = true; + bool asmKeyword = false; + int asmEmbedLevel = -1; + int level = 0; + + int pos=-1; + const int length = m_code.length(); + while ( ++pos<length ) + { + switch ( m_code[pos].latin1() ) + { + case '\n': + { + if (comment && !multiLineComment) comment = false; + newCode += '\n'; + if ( !comment && !asmEmbed ) + { + while ( pos+1<length && m_code[pos+1].isSpace() ) pos++; + bool closeBrace = false; + if ( pos+1<length && m_code[pos+1] == '}' ) + { + level--; + pos++; + closeBrace = true; + } + for ( int i=0; i<level; i++ ) newCode += '\t'; + if (closeBrace) newCode += '}'; + asmEmbedAllowed = true; + } + break; + } + case '/': + { + newCode += '/'; + if ( pos+1<length ) + { + if ( m_code[pos+1] == '/' ) comment = true; + else if ( m_code[pos+1] == '*' ) multiLineComment = comment = true; + newCode += m_code[++pos]; + } + asmEmbedAllowed = false; + asmKeyword = false; + break; + } + case '*': + { + newCode += '*'; + if ( pos+1<length ) + { + if ( m_code[pos++] == '/' && multiLineComment ) comment = multiLineComment = false; + newCode += m_code[pos]; + } + asmEmbedAllowed = false; + asmKeyword = false; + break; + } + case '{': + { + if (asmKeyword) { + asmEmbed = true; + asmEmbedLevel = level; + } + + if ( !comment ) level++; + newCode += '{'; + + asmEmbedAllowed = false; + asmKeyword = false; + break; + } + case '}': + { + if ( !comment ) level--; + + if (asmEmbed && asmEmbedLevel == level) + { + asmEmbed = false; + newCode += "\n"; + for ( int i=0; i<level; i++ ) newCode += '\t'; + } + newCode += '}'; + + asmEmbedAllowed = true; + asmKeyword = false; + break; + } + case 'a': + { + newCode += m_code[pos]; + if ( asmEmbedAllowed && !comment && pos+2<length ) + { + if ( m_code[pos+1] == 's' && m_code[pos+2] == 'm' ) + { + asmKeyword = true; + newCode += "sm"; + pos += 2; + } + } + break; + } + default: + { + asmEmbedAllowed = false; + asmKeyword = false; + newCode += m_code[pos]; + break; + } + } + } + m_code = newCode; +} + +void FlowCode::addSubroutine( FlowPart *part ) +{ + if ( !part || m_subroutines.contains(part) || part->parentItem() || !dynamic_cast<FlowContainer*>(part) ) return; + m_subroutines.append(part); +} + + +ProcessOptions::ProcessPath::Path FlowCode::outputPath( ProcessOptions::ProcessPath::Path inputPath ) const +{ + switch (inputPath) + { + case ProcessOptions::ProcessPath::FlowCode_AssemblyAbsolute: + return ProcessOptions::ProcessPath::Microbe_AssemblyAbsolute; + + case ProcessOptions::ProcessPath::FlowCode_Microbe: + return ProcessOptions::ProcessPath::None; + + case ProcessOptions::ProcessPath::FlowCode_PIC: + return ProcessOptions::ProcessPath::Microbe_PIC; + + case ProcessOptions::ProcessPath::FlowCode_Program: + return ProcessOptions::ProcessPath::Microbe_Program; + + case ProcessOptions::ProcessPath::AssemblyAbsolute_PIC: + case ProcessOptions::ProcessPath::AssemblyAbsolute_Program: + case ProcessOptions::ProcessPath::AssemblyRelocatable_Library: + case ProcessOptions::ProcessPath::AssemblyRelocatable_Object: + case ProcessOptions::ProcessPath::AssemblyRelocatable_PIC: + case ProcessOptions::ProcessPath::AssemblyRelocatable_Program: + case ProcessOptions::ProcessPath::C_AssemblyRelocatable: + case ProcessOptions::ProcessPath::C_Library: + case ProcessOptions::ProcessPath::C_Object: + case ProcessOptions::ProcessPath::C_PIC: + case ProcessOptions::ProcessPath::C_Program: + case ProcessOptions::ProcessPath::Microbe_AssemblyAbsolute: + case ProcessOptions::ProcessPath::Microbe_PIC: + case ProcessOptions::ProcessPath::Microbe_Program: + case ProcessOptions::ProcessPath::Object_Disassembly: + case ProcessOptions::ProcessPath::Object_Library: + case ProcessOptions::ProcessPath::Object_PIC: + case ProcessOptions::ProcessPath::Object_Program: + case ProcessOptions::ProcessPath::PIC_AssemblyAbsolute: + case ProcessOptions::ProcessPath::Program_Disassembly: + case ProcessOptions::ProcessPath::Program_PIC: + case ProcessOptions::ProcessPath::Invalid: + case ProcessOptions::ProcessPath::None: + return ProcessOptions::ProcessPath::Invalid; + } + + return ProcessOptions::ProcessPath::Invalid; +} + diff --git a/src/languages/flowcode.h b/src/languages/flowcode.h new file mode 100644 index 0000000..afa17db --- /dev/null +++ b/src/languages/flowcode.h @@ -0,0 +1,107 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef FLOWCODE_H +#define FLOWCODE_H + +#include "language.h" + +#include <qguardedptr.h> +#include <qobject.h> +#include <qstring.h> +#include <qstringlist.h> +#include <qvaluelist.h> + +class CNItem; +class FlowPart; +class Item; +class MicroSettings; + +typedef QValueList<FlowPart*> FlowPartList; +typedef QValueList<QGuardedPtr<Item> > ItemList; + +/** +"FlowCode" can possibly be considered a misnomer, as the output is actually Microbe. +However, the function of this class is to take a set of FlowParts, and generate the +basic from the code that they create. The 3 simple steps for usage of this function: +(1) Create an instance of this class, giving the Start point and setings +(2) Add all the subroutines present using addSubroutine() +(3) Call generateMicrobe() to get the Microbe code. +@author David Saxton +*/ +class FlowCode : public Language +{ +public: + FlowCode( ProcessChain *processChain, KTechlab *parent ); + + virtual void processInput( ProcessOptions options ); + virtual ProcessOptions::ProcessPath::Path outputPath( ProcessOptions::ProcessPath::Path inputPath ) const; + + /** + * You must set the start part + */ + void setStartPart( FlowPart *startPart ); + ~FlowCode(); + /** + * You must add all top level subroutines using this function + */ + void addSubroutine( FlowPart *part ); + /** + * Adds code at the current insertion point + */ + void addCode( const QString& code ); + /** + * Adds a code branch to the current insertion point. This will stop when the level gets + * below the original starting level (so for insertion of the contents of a for loop, + * insertion will stop at the end of that for loop). + * @param flowPart The next FlowPart to get code from + */ + void addCodeBranch( FlowPart *flowPart ); + /** + * Designates a FlowPart as a stopping part (i.e. will refuse requests to addCodeBranch + * for that FlowPart until removeStopPart is called + */ + void addStopPart( FlowPart *part ); + /** + * Undesignates a FlowPart as a stopping part + */ + void removeStopPart( FlowPart *part ); + /** + * Generates and returns the microbe code + * @param nonVerbal if true then will not inform the user when something goes wrong + */ + QString generateMicrobe( const ItemList &itemList, MicroSettings *settings ); + /** + * Returns true if the FlowPart is a valid one for adding a branch + */ + bool isValidBranch( FlowPart *flowPart ); + /** + * Generates a nice label name from the string, e.g. genLabel("callsub") + * returns "__label_callsub". + */ + static QString genLabel( const QString &id ); + +protected: + /** + * Performs indenting, removal of unnecessary labels, etc. + */ + void tidyCode(); + + QStringList m_gotos; // Gotos used + QStringList m_labels; // Labels used + FlowPartList m_subroutines; + FlowPartList m_addedParts; + FlowPartList m_stopParts; + FlowPart *p_startPart; + QString m_code; + int m_curLevel; +}; + +#endif diff --git a/src/languages/gpasm.cpp b/src/languages/gpasm.cpp new file mode 100644 index 0000000..447354e --- /dev/null +++ b/src/languages/gpasm.cpp @@ -0,0 +1,187 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "asmparser.h" +#include "docmanager.h" +#include "gpasm.h" +#include "logview.h" +#include "languagemanager.h" +#include "src/core/ktlconfig.h" + +#include <klocale.h> +#include <kmessagebox.h> +#include <kprocess.h> +#include <qregexp.h> + +Gpasm::Gpasm( ProcessChain *processChain, KTechlab * parent ) + : ExternalLanguage( processChain, parent, "Gpasm" ) +{ + m_successfulMessage = i18n("*** Assembly successful ***"); + m_failedMessage = i18n("*** Assembly failed ***"); +} + + +Gpasm::~Gpasm() +{ +} + + +void Gpasm::processInput( ProcessOptions options ) +{ + resetLanguageProcess(); + m_processOptions = options; + + AsmParser p( options.inputFiles().first() ); + p.parse(); + + *m_languageProcess << ("gpasm"); + + if ( ProcessOptions::ProcessPath::from( options.processPath() ) == ProcessOptions::ProcessPath::AssemblyRelocatable ) + *m_languageProcess << ("--object"); + +// *m_languageProcess << ("--debug-info"); // Debug info + + // Output filename + *m_languageProcess << ("--output"); + *m_languageProcess << ( options.intermediaryOutput() ); + + if ( !options.m_hexFormat.isEmpty() ) + { + *m_languageProcess << ("--hex-format"); + *m_languageProcess << (options.m_hexFormat); + } + + // Radix + if ( !p.containsRadix() ) + { + *m_languageProcess << ("--radix"); + switch( KTLConfig::radix() ) + { + case KTLConfig::EnumRadix::Binary: + *m_languageProcess << ("BIN"); + break; + case KTLConfig::EnumRadix::Octal: + *m_languageProcess << ("OCT"); + break; + case KTLConfig::EnumRadix::Hexadecimal: + *m_languageProcess << ("HEX"); + break; + case KTLConfig::EnumRadix::Decimal: + default: + *m_languageProcess << ("DEC"); + break; + } + } + + // Warning Level + *m_languageProcess << ("--warning"); + switch( KTLConfig::gpasmWarningLevel() ) + { + case KTLConfig::EnumGpasmWarningLevel::Warnings: + *m_languageProcess << ("1"); + break; + case KTLConfig::EnumGpasmWarningLevel::Errors: + *m_languageProcess << ("2"); + break; + default: + case KTLConfig::EnumGpasmWarningLevel::All: + *m_languageProcess << ("0"); + break; + } + + // Ignore case + if ( KTLConfig::ignoreCase() ) + *m_languageProcess << ("--ignore-case"); + + // Dos formatting + if ( KTLConfig::dosFormat() ) + *m_languageProcess << ("--dos"); + + // Force list + if ( options.b_forceList ) + *m_languageProcess << ("--force-list"); + + // Other options + if ( !KTLConfig::miscGpasmOptions().isEmpty() ) + *m_languageProcess << ( KTLConfig::miscGpasmOptions() ); + + // Input Asm file + *m_languageProcess << ( options.inputFiles().first() ); + + if ( !start() ) + { + KMessageBox::sorry( LanguageManager::self()->logView(), i18n("Assembly failed. Please check you have gputils installed.") ); + processInitFailed(); + return; + } +} + + +bool Gpasm::isError( const QString &message ) const +{ + return message.contains( "Error", false ); +} + + +bool Gpasm::isWarning( const QString &message ) const +{ + return message.contains( "Warning", false ); +} + + +ProcessOptions::ProcessPath::Path Gpasm::outputPath( ProcessOptions::ProcessPath::Path inputPath ) const +{ + switch (inputPath) + { + case ProcessOptions::ProcessPath::AssemblyAbsolute_PIC: + return ProcessOptions::ProcessPath::Program_PIC; + + case ProcessOptions::ProcessPath::AssemblyAbsolute_Program: + return ProcessOptions::ProcessPath::None; + + case ProcessOptions::ProcessPath::AssemblyRelocatable_Library: + return ProcessOptions::ProcessPath::Object_Library; + + case ProcessOptions::ProcessPath::AssemblyRelocatable_Object: + return ProcessOptions::ProcessPath::None; + + case ProcessOptions::ProcessPath::AssemblyRelocatable_PIC: + return ProcessOptions::ProcessPath::Object_PIC; + + case ProcessOptions::ProcessPath::AssemblyRelocatable_Program: + return ProcessOptions::ProcessPath::Object_Program; + + case ProcessOptions::ProcessPath::C_AssemblyRelocatable: + case ProcessOptions::ProcessPath::C_Library: + case ProcessOptions::ProcessPath::C_Object: + case ProcessOptions::ProcessPath::C_PIC: + case ProcessOptions::ProcessPath::C_Program: + case ProcessOptions::ProcessPath::FlowCode_AssemblyAbsolute: + case ProcessOptions::ProcessPath::FlowCode_Microbe: + case ProcessOptions::ProcessPath::FlowCode_PIC: + case ProcessOptions::ProcessPath::FlowCode_Program: + case ProcessOptions::ProcessPath::Microbe_AssemblyAbsolute: + case ProcessOptions::ProcessPath::Microbe_PIC: + case ProcessOptions::ProcessPath::Microbe_Program: + case ProcessOptions::ProcessPath::Object_Disassembly: + case ProcessOptions::ProcessPath::Object_Library: + case ProcessOptions::ProcessPath::Object_PIC: + case ProcessOptions::ProcessPath::Object_Program: + case ProcessOptions::ProcessPath::PIC_AssemblyAbsolute: + case ProcessOptions::ProcessPath::Program_Disassembly: + case ProcessOptions::ProcessPath::Program_PIC: + case ProcessOptions::ProcessPath::Invalid: + case ProcessOptions::ProcessPath::None: + return ProcessOptions::ProcessPath::Invalid; + } + + return ProcessOptions::ProcessPath::Invalid; +} + diff --git a/src/languages/gpasm.h b/src/languages/gpasm.h new file mode 100644 index 0000000..c92a969 --- /dev/null +++ b/src/languages/gpasm.h @@ -0,0 +1,34 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef GPASM_H +#define GPASM_H + +#include "externallanguage.h" + +/** +@short Interface to the GNU PIC assembler +@author David Saxton +*/ +class Gpasm : public ExternalLanguage +{ + public: + Gpasm( ProcessChain *processChain, KTechlab *parent ); + ~Gpasm(); + + virtual void processInput( ProcessOptions options ); + virtual ProcessOptions::ProcessPath::Path outputPath( ProcessOptions::ProcessPath::Path inputPath ) const; + + protected: + virtual bool isError( const QString &message ) const; + virtual bool isWarning( const QString &message ) const; +}; + +#endif diff --git a/src/languages/gpdasm.cpp b/src/languages/gpdasm.cpp new file mode 100644 index 0000000..8c255d3 --- /dev/null +++ b/src/languages/gpdasm.cpp @@ -0,0 +1,158 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "docmanager.h" +#include "gpdasm.h" +#include "logview.h" +#include "languagemanager.h" + +#include <klocale.h> +#include <kmessagebox.h> +#include <kprocess.h> +#include <qfile.h> +#include <qregexp.h> + +Gpdasm::Gpdasm( ProcessChain *processChain, KTechlab *parent ) + : ExternalLanguage( processChain, parent, "Gpdasm" ) +{ + m_successfulMessage = i18n("*** Disassembly successful ***"); + m_failedMessage = i18n("*** Disassembly failed ***"); +} + + +Gpdasm::~Gpdasm() +{ +} + + +void Gpdasm::processInput( ProcessOptions options ) +{ + resetLanguageProcess(); + m_asmOutput = ""; + m_processOptions = options;; + + *m_languageProcess << ("gpdasm"); + + *m_languageProcess << ("--processor"); + *m_languageProcess << ( options.m_picID ); + *m_languageProcess << ( options.inputFiles().first() ); + + if ( !start() ) + { + KMessageBox::sorry( LanguageManager::self()->logView(), i18n("Disassembly failed. Please check you have gputils installed.") ); + processInitFailed(); + return; + } +} + + +void Gpdasm::outputtedMessage( const QString &message ) +{ + m_asmOutput += message + "\n"; +} + + +bool Gpdasm::processExited( bool successfully ) +{ + if (!successfully) + return false; + + QFile file(m_processOptions.intermediaryOutput()); + if ( file.open(IO_WriteOnly) == false ) + return false; + + QTextStream stream(&file); + stream << m_asmOutput; + file.close(); + return true; +} + + +bool Gpdasm::isError( const QString &message ) const +{ + return (message.find( "error", -1, false ) != -1); +} + + +bool Gpdasm::isWarning( const QString &message ) const +{ + return (message.find( "warning", -1, false ) != -1); +} + + +MessageInfo Gpdasm::extractMessageInfo( const QString &text ) +{ + if ( text.length()<5 || !text.startsWith("/") ) + return MessageInfo(); + + const int index = text.find( ".asm", 0, false )+4; + if ( index == -1+4 ) + return MessageInfo(); + const QString fileName = text.left(index); + + // Extra line number + const QString message = text.right(text.length()-index); + const int linePos = message.find( QRegExp(":[\\d]+") ); + int line = -1; + if ( linePos != -1 ) + { + const int linePosEnd = message.find( ':', linePos+1 ); + if ( linePosEnd != -1 ) + { + const QString number = message.mid( linePos+1, linePosEnd-linePos-1 ).stripWhiteSpace(); + bool ok; + line = number.toInt(&ok)-1; + if (!ok) line = -1; + } + } + + return MessageInfo( fileName, line ); +} + + + +ProcessOptions::ProcessPath::Path Gpdasm::outputPath( ProcessOptions::ProcessPath::Path inputPath ) const +{ + switch (inputPath) + { + case ProcessOptions::ProcessPath::Object_Disassembly: + case ProcessOptions::ProcessPath::Program_Disassembly: + return ProcessOptions::ProcessPath::None; + + case ProcessOptions::ProcessPath::AssemblyAbsolute_PIC: + case ProcessOptions::ProcessPath::AssemblyAbsolute_Program: + case ProcessOptions::ProcessPath::AssemblyRelocatable_Library: + case ProcessOptions::ProcessPath::AssemblyRelocatable_Object: + case ProcessOptions::ProcessPath::AssemblyRelocatable_PIC: + case ProcessOptions::ProcessPath::AssemblyRelocatable_Program: + case ProcessOptions::ProcessPath::C_AssemblyRelocatable: + case ProcessOptions::ProcessPath::C_Library: + case ProcessOptions::ProcessPath::C_Object: + case ProcessOptions::ProcessPath::C_PIC: + case ProcessOptions::ProcessPath::C_Program: + case ProcessOptions::ProcessPath::FlowCode_AssemblyAbsolute: + case ProcessOptions::ProcessPath::FlowCode_Microbe: + case ProcessOptions::ProcessPath::FlowCode_PIC: + case ProcessOptions::ProcessPath::FlowCode_Program: + case ProcessOptions::ProcessPath::Microbe_AssemblyAbsolute: + case ProcessOptions::ProcessPath::Microbe_PIC: + case ProcessOptions::ProcessPath::Microbe_Program: + case ProcessOptions::ProcessPath::Object_Library: + case ProcessOptions::ProcessPath::Object_PIC: + case ProcessOptions::ProcessPath::Object_Program: + case ProcessOptions::ProcessPath::PIC_AssemblyAbsolute: + case ProcessOptions::ProcessPath::Program_PIC: + case ProcessOptions::ProcessPath::Invalid: + case ProcessOptions::ProcessPath::None: + return ProcessOptions::ProcessPath::Invalid; + } + + return ProcessOptions::ProcessPath::Invalid; +} diff --git a/src/languages/gpdasm.h b/src/languages/gpdasm.h new file mode 100644 index 0000000..149ed26 --- /dev/null +++ b/src/languages/gpdasm.h @@ -0,0 +1,39 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef GPDASM_H +#define GPDASM_H + +#include <externallanguage.h> + +/** +Interface to the GNU Pic Disassembler +@author David Saxton +*/ +class Gpdasm : public ExternalLanguage +{ +public: + Gpdasm( ProcessChain *processChain, KTechlab *parent ); + ~Gpdasm(); + + virtual void processInput( ProcessOptions options ); + virtual MessageInfo extractMessageInfo( const QString &text ); + virtual ProcessOptions::ProcessPath::Path outputPath( ProcessOptions::ProcessPath::Path inputPath ) const; + +protected: + virtual void outputtedMessage( const QString &message ); + virtual bool isError( const QString &message ) const; + virtual bool isWarning( const QString &message ) const; + virtual bool processExited( bool successfully ); + + QString m_asmOutput; // Outputed by gpdasm +}; + +#endif diff --git a/src/languages/gplib.cpp b/src/languages/gplib.cpp new file mode 100644 index 0000000..db4a32b --- /dev/null +++ b/src/languages/gplib.cpp @@ -0,0 +1,139 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "gplib.h" +#include "languagemanager.h" +#include "logview.h" + +#include <klocale.h> +#include <kmessagebox.h> +#include <kprocess.h> + +Gplib::Gplib( ProcessChain *processChain, KTechlab * parent ) + : ExternalLanguage( processChain, parent, "Gpasm" ) +{ + m_successfulMessage = i18n("*** Archiving successful ***"); + m_failedMessage = i18n("*** Archiving failed ***"); +} + + +Gplib::~Gplib() +{ +} + + +void Gplib::processInput( ProcessOptions options ) +{ + resetLanguageProcess(); + m_processOptions = options; + + *m_languageProcess << ("gplib"); + *m_languageProcess << ("--create"); + + *m_languageProcess << ( options.intermediaryOutput() ); + + const QStringList inputFiles = options.inputFiles(); + QStringList::const_iterator end = inputFiles.end(); + for ( QStringList::const_iterator it = inputFiles.begin(); it != end; ++it ) + *m_languageProcess << ( *it ); + + if ( !start() ) + { + KMessageBox::sorry( LanguageManager::self()->logView(), i18n("Linking failed. Please check you have gputils installed.") ); + processInitFailed(); + return; + } +} + + +bool Gplib::isError( const QString &message ) const +{ + return message.contains( "Error", false ); +} + + +bool Gplib::isWarning( const QString &message ) const +{ + return message.contains( "Warning", false ); +} + + +MessageInfo Gplib::extractMessageInfo( const QString &text ) +{ +#if 0 + if ( text.length()<5 || !text.startsWith("/") ) + return MessageInfo(); + + const int index = text.find( ".asm", 0, false )+4; + if ( index == -1+4 ) + return MessageInfo(); + const QString fileName = text.left(index); + + // Extra line number + const QString message = text.right(text.length()-index); + const int linePos = message.find( QRegExp(":[\\d]+") ); + int line = -1; + if ( linePos != -1 ) +{ + const int linePosEnd = message.find( ':', linePos+1 ); + if ( linePosEnd != -1 ) +{ + const QString number = message.mid( linePos+1, linePosEnd-linePos-1 ).stripWhiteSpace(); + bool ok; + line = number.toInt(&ok)-1; + if (!ok) line = -1; +} +} + return MessageInfo( fileName, line ); +#endif + return MessageInfo(); +} + + + + +ProcessOptions::ProcessPath::Path Gplib::outputPath( ProcessOptions::ProcessPath::Path inputPath ) const +{ + switch (inputPath) + { + case ProcessOptions::ProcessPath::Object_Library: + return ProcessOptions::ProcessPath::None; + + case ProcessOptions::ProcessPath::AssemblyAbsolute_PIC: + case ProcessOptions::ProcessPath::AssemblyAbsolute_Program: + case ProcessOptions::ProcessPath::AssemblyRelocatable_Library: + case ProcessOptions::ProcessPath::AssemblyRelocatable_Object: + case ProcessOptions::ProcessPath::AssemblyRelocatable_PIC: + case ProcessOptions::ProcessPath::AssemblyRelocatable_Program: + case ProcessOptions::ProcessPath::C_AssemblyRelocatable: + case ProcessOptions::ProcessPath::C_Library: + case ProcessOptions::ProcessPath::C_Object: + case ProcessOptions::ProcessPath::C_PIC: + case ProcessOptions::ProcessPath::C_Program: + case ProcessOptions::ProcessPath::FlowCode_AssemblyAbsolute: + case ProcessOptions::ProcessPath::FlowCode_Microbe: + case ProcessOptions::ProcessPath::FlowCode_PIC: + case ProcessOptions::ProcessPath::FlowCode_Program: + case ProcessOptions::ProcessPath::Microbe_AssemblyAbsolute: + case ProcessOptions::ProcessPath::Microbe_PIC: + case ProcessOptions::ProcessPath::Microbe_Program: + case ProcessOptions::ProcessPath::Object_Disassembly: + case ProcessOptions::ProcessPath::Object_PIC: + case ProcessOptions::ProcessPath::Object_Program: + case ProcessOptions::ProcessPath::PIC_AssemblyAbsolute: + case ProcessOptions::ProcessPath::Program_Disassembly: + case ProcessOptions::ProcessPath::Program_PIC: + case ProcessOptions::ProcessPath::Invalid: + case ProcessOptions::ProcessPath::None: + return ProcessOptions::ProcessPath::Invalid; + } + + return ProcessOptions::ProcessPath::Invalid; +} diff --git a/src/languages/gplib.h b/src/languages/gplib.h new file mode 100644 index 0000000..35cb1da --- /dev/null +++ b/src/languages/gplib.h @@ -0,0 +1,34 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef GPLIB_H +#define GPLIB_H + +#include <externallanguage.h> + +/** +@author David Saxton +*/ +class Gplib : public ExternalLanguage +{ + public: + Gplib( ProcessChain *processChain, KTechlab *parent ); + ~Gplib(); + + virtual void processInput( ProcessOptions options ); + virtual MessageInfo extractMessageInfo( const QString &text ); + virtual ProcessOptions::ProcessPath::Path outputPath( ProcessOptions::ProcessPath::Path inputPath ) const; + + protected: + virtual bool isError( const QString &message ) const; + virtual bool isWarning( const QString &message ) const; +}; + +#endif diff --git a/src/languages/gplink.cpp b/src/languages/gplink.cpp new file mode 100644 index 0000000..548449a --- /dev/null +++ b/src/languages/gplink.cpp @@ -0,0 +1,174 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "gplink.h" +#include "languagemanager.h" +#include "logview.h" + +#include <klocale.h> +#include <kmessagebox.h> +#include <kprocess.h> + +Gplink::Gplink( ProcessChain *processChain, KTechlab * parent ) + : ExternalLanguage( processChain, parent, "Gpasm" ) +{ + m_successfulMessage = i18n("*** Linking successful ***"); + m_failedMessage = i18n("*** Linking failed ***"); +} + + +Gplink::~Gplink() +{ +} + + +void Gplink::processInput( ProcessOptions options ) +{ + resetLanguageProcess(); + m_processOptions = options; + + *m_languageProcess << ("gplink"); + + if ( !options.m_hexFormat.isEmpty() ) + { + *m_languageProcess << ("--hex-format"); + *m_languageProcess << (options.m_hexFormat); + } + + if ( options.m_bOutputMapFile ) + *m_languageProcess << ("--map"); + + if ( !options.m_libraryDir.isEmpty() ) + { + *m_languageProcess << ("--include"); + *m_languageProcess << ( options.m_libraryDir ); + } + + if ( !options.m_linkerScript.isEmpty() ) + { + *m_languageProcess << ("--script"); + *m_languageProcess << ( options.m_linkerScript ); + } + + if ( !options.m_linkOther.isEmpty() ) + *m_languageProcess << (options.m_linkOther); + + // Output hex file + *m_languageProcess << ("--output"); + *m_languageProcess << ( options.intermediaryOutput() ); + + // Input object file + const QStringList inputFiles = options.inputFiles(); + QStringList::const_iterator end = inputFiles.end(); + for ( QStringList::const_iterator it = inputFiles.begin(); it != end; ++it ) + *m_languageProcess << ( *it ); + + // Other libraries + end = options.m_linkLibraries.end(); + for ( QStringList::const_iterator it = options.m_linkLibraries.begin(); it != end; ++it ) + *m_languageProcess << ( *it ); + + if ( !start() ) + { + KMessageBox::sorry( LanguageManager::self()->logView(), i18n("Linking failed. Please check you have gputils installed.") ); + processInitFailed(); + return; + } +} + + +bool Gplink::isError( const QString &message ) const +{ + return message.contains( "Error", false ); +} + + +bool Gplink::isWarning( const QString &message ) const +{ + return message.contains( "Warning", false ); +} + + +MessageInfo Gplink::extractMessageInfo( const QString &text ) +{ +#if 0 + if ( text.length()<5 || !text.startsWith("/") ) + return MessageInfo(); + + const int index = text.find( ".asm", 0, false )+4; + if ( index == -1+4 ) + return MessageInfo(); + const QString fileName = text.left(index); + + // Extra line number + const QString message = text.right(text.length()-index); + const int linePos = message.find( QRegExp(":[\\d]+") ); + int line = -1; + if ( linePos != -1 ) + { + const int linePosEnd = message.find( ':', linePos+1 ); + if ( linePosEnd != -1 ) + { + const QString number = message.mid( linePos+1, linePosEnd-linePos-1 ).stripWhiteSpace(); + bool ok; + line = number.toInt(&ok)-1; + if (!ok) line = -1; + } + } + return MessageInfo( fileName, line ); +#endif + return MessageInfo(); +} + + + +ProcessOptions::ProcessPath::Path Gplink::outputPath( ProcessOptions::ProcessPath::Path inputPath ) const +{ + switch (inputPath) + { + case ProcessOptions::ProcessPath::Object_PIC: + return ProcessOptions::ProcessPath::Program_PIC; + + case ProcessOptions::ProcessPath::Object_Program: + return ProcessOptions::ProcessPath::None; + + case ProcessOptions::ProcessPath::AssemblyAbsolute_PIC: + case ProcessOptions::ProcessPath::AssemblyAbsolute_Program: + case ProcessOptions::ProcessPath::AssemblyRelocatable_Library: + case ProcessOptions::ProcessPath::AssemblyRelocatable_Object: + case ProcessOptions::ProcessPath::AssemblyRelocatable_PIC: + case ProcessOptions::ProcessPath::AssemblyRelocatable_Program: + case ProcessOptions::ProcessPath::C_AssemblyRelocatable: + case ProcessOptions::ProcessPath::C_Library: + case ProcessOptions::ProcessPath::C_Object: + case ProcessOptions::ProcessPath::C_PIC: + case ProcessOptions::ProcessPath::C_Program: + case ProcessOptions::ProcessPath::FlowCode_AssemblyAbsolute: + case ProcessOptions::ProcessPath::FlowCode_Microbe: + case ProcessOptions::ProcessPath::FlowCode_PIC: + case ProcessOptions::ProcessPath::FlowCode_Program: + case ProcessOptions::ProcessPath::Microbe_AssemblyAbsolute: + case ProcessOptions::ProcessPath::Microbe_PIC: + case ProcessOptions::ProcessPath::Microbe_Program: + case ProcessOptions::ProcessPath::Object_Disassembly: + case ProcessOptions::ProcessPath::Object_Library: + case ProcessOptions::ProcessPath::PIC_AssemblyAbsolute: + case ProcessOptions::ProcessPath::Program_Disassembly: + case ProcessOptions::ProcessPath::Program_PIC: + case ProcessOptions::ProcessPath::Invalid: + case ProcessOptions::ProcessPath::None: + return ProcessOptions::ProcessPath::Invalid; + } + + return ProcessOptions::ProcessPath::Invalid; +} + + + diff --git a/src/languages/gplink.h b/src/languages/gplink.h new file mode 100644 index 0000000..c60f9f9 --- /dev/null +++ b/src/languages/gplink.h @@ -0,0 +1,35 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef GPLINK_H +#define GPLINK_H + +#include <externallanguage.h> + +/** +@short Interface to the GNU PIC linker +@author David Saxton +*/ +class Gplink : public ExternalLanguage +{ + public: + Gplink( ProcessChain *processChain, KTechlab *parent ); + ~Gplink(); + + virtual void processInput( ProcessOptions options ); + virtual MessageInfo extractMessageInfo( const QString &text ); + virtual ProcessOptions::ProcessPath::Path outputPath( ProcessOptions::ProcessPath::Path inputPath ) const; + + protected: + virtual bool isError( const QString &message ) const; + virtual bool isWarning( const QString &message ) const; +}; + +#endif diff --git a/src/languages/language.cpp b/src/languages/language.cpp new file mode 100644 index 0000000..e7ce759 --- /dev/null +++ b/src/languages/language.cpp @@ -0,0 +1,558 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "asmparser.h" +#include "ktechlab.h" +#include "language.h" +#include "logview.h" +#include "outputmethoddlg.h" +#include "processchain.h" +#include "projectmanager.h" +#include "languagemanager.h" +#include "src/core/ktlconfig.h" + +#include <kdebug.h> +#include <kio/netaccess.h> +#include <kmessagebox.h> +#include <kprocess.h> +#include <qregexp.h> +#include <qtimer.h> + +//BEGIN class Language +Language::Language( ProcessChain *processChain, KTechlab *parent, const QString &name ) + : QObject(parent,name) +{ + p_ktechlab = parent; + p_processChain = processChain; +} + + +Language::~Language() +{ +} + + +void Language::outputMessage( const QString &message ) +{ + LanguageManager::self()->slotMessage( message, extractMessageInfo(message) ); +} + + +void Language::outputWarning( const QString &message ) +{ + LanguageManager::self()->slotWarning( message, extractMessageInfo(message) ); +} + + +void Language::outputError( const QString &message ) +{ + LanguageManager::self()->slotError( message, extractMessageInfo(message) ); + m_errorCount++; +} + + +void Language::finish( bool successful ) +{ + if (successful) + { + outputMessage(m_successfulMessage + "\n"); + p_ktechlab->slotChangeStatusbar(m_successfulMessage); + + ProcessOptions::ProcessPath::Path newPath = outputPath( m_processOptions.processPath() ); + + if ( newPath == ProcessOptions::ProcessPath::None ) + emit processSucceeded(this); + + else if (p_processChain) + { + m_processOptions.setInputFiles( m_processOptions.intermediaryOutput() ); + m_processOptions.setIntermediaryOutput( m_processOptions.targetFile() ); + m_processOptions.setProcessPath(newPath); +// p_processChain->compile(m_processOptions); + p_processChain->setProcessOptions(m_processOptions); + p_processChain->compile(); + } + } + else + { + outputError(m_failedMessage + "\n"); + p_ktechlab->slotChangeStatusbar(m_failedMessage); + emit processFailed(this); + return; + } +} + + +void Language::reset() +{ + m_errorCount = 0; +} + + +MessageInfo Language::extractMessageInfo( const QString &text ) +{ + if ( !text.startsWith("/") ) + return MessageInfo(); + + const int index = text.find( ":", 0, false ); + if ( index == -1 ) + return MessageInfo(); + const QString fileName = text.left(index); + + // Extra line number + const QString message = text.right(text.length()-index); + const int linePos = message.find( QRegExp(":[\\d]+") ); + int line = -1; + if ( linePos != -1 ) + { + const int linePosEnd = message.find( ':', linePos+1 ); + if ( linePosEnd != -1 ) + { + const QString number = message.mid( linePos+1, linePosEnd-linePos-1 ).stripWhiteSpace(); + bool ok; + line = number.toInt(&ok)-1; + if (!ok) line = -1; + } + } + return MessageInfo( fileName, line ); +} +//END class Language + + + +//BEGIN class ProcessOptionsSpecial +ProcessOptionsSpecial::ProcessOptionsSpecial() +{ + m_bOutputMapFile = true; + b_forceList = true; + b_addToProject = ProjectManager::self()->currentProject(); + + p_flowCodeDocument = 0l; + + switch ( KTLConfig::hexFormat() ) + { + case KTLConfig::EnumHexFormat::inhx8m: + m_hexFormat = "inhx8m"; + break; + + case KTLConfig::EnumHexFormat::inhx8s: + m_hexFormat = "inhx8s"; + break; + + case KTLConfig::EnumHexFormat::inhx16: + m_hexFormat = "inhx16"; + break; + + case KTLConfig::EnumHexFormat::inhx32: + default: + m_hexFormat = "inhx32"; + break; + } +} +//END class ProcessOptionsSpecial + + +//BEGIN class ProcessOptions +ProcessOptions::ProcessOptions() +{ + m_pHelper = new ProcessOptionsHelper; + + b_targetFileSet = false; + m_pTextOutputTarget = 0l; +} + + +ProcessOptions::ProcessOptions( OutputMethodInfo info ) +{ + m_pHelper = new ProcessOptionsHelper; + + b_addToProject = info.addToProject(); + m_picID = info.picID(); + b_targetFileSet = false; + + QString target; + if ( !KIO::NetAccess::download( info.outputFile(), target, 0l ) ) + { + // If the file could not be downloaded, for example does not + // exist on disk, NetAccess will tell us what error to use + KMessageBox::error( 0l, KIO::NetAccess::lastErrorString() ); + + return; + } + setTargetFile(target); + + switch ( info.method() ) + { + case OutputMethodInfo::Method::Direct: + m_method = Method::LoadAsNew; + break; + + case OutputMethodInfo::Method::SaveAndForget: + m_method = Method::Forget; + break; + + case OutputMethodInfo::Method::SaveAndLoad: + m_method = Method::Load; + break; + } +} + + +void ProcessOptions::setTextOutputTarget( TextDocument * target, QObject * receiver, const char * slot ) +{ + m_pTextOutputTarget = target; + QObject::connect( m_pHelper, SIGNAL(textOutputtedTo( TextDocument* )), receiver, slot ); +} + + +void ProcessOptions::setTextOutputtedTo( TextDocument * outputtedTo ) +{ + m_pTextOutputTarget = outputtedTo; + emit m_pHelper->textOutputtedTo( m_pTextOutputTarget ); +} + + +void ProcessOptions::setTargetFile( const QString &file ) +{ + if (b_targetFileSet) + { + kdWarning() << "Trying to reset target file!"<<endl; + return; + } + m_targetFile = file; + m_intermediaryFile = file; + b_targetFileSet = true; +} + + +ProcessOptions::ProcessPath::MediaType ProcessOptions::guessMediaType( const QString & url ) +{ + QString extension = url.right( url.length() - url.findRev('.') - 1 ); + extension = extension.lower(); + + if ( extension == "asm" ) + { + // We'll have to look at the file contents to determine its type... + AsmParser p( url ); + p.parse(); + switch ( p.type() ) + { + case AsmParser::Relocatable: + return ProcessPath::AssemblyRelocatable; + + case AsmParser::Absolute: + return ProcessPath::AssemblyAbsolute; + } + } + + if ( extension == "c" ) + return ProcessPath::C; + + if ( extension == "flowcode" ) + return ProcessPath::FlowCode; + + if ( extension == "a" || extension == "lib" ) + return ProcessPath::Library; + + if ( extension == "microbe" || extension == "basic" ) + return ProcessPath::Microbe; + + if ( extension == "o" ) + return ProcessPath::Object; + + if ( extension == "hex" ) + return ProcessPath::Program; + + return ProcessPath::Unknown; +} + + +ProcessOptions::ProcessPath::Path ProcessOptions::ProcessPath::path( MediaType from, MediaType to ) +{ + switch (from) + { + case AssemblyAbsolute: + switch (to) + { + case AssemblyAbsolute: + return None; + case Pic: + return AssemblyAbsolute_PIC; + case Program: + return AssemblyAbsolute_Program; + + case AssemblyRelocatable: + case C: + case Disassembly: + case FlowCode: + case Library: + case Microbe: + case Object: + case Unknown: + return Invalid; + } + + case AssemblyRelocatable: + switch (to) + { + case Library: + return AssemblyRelocatable_Library; + case Object: + return AssemblyRelocatable_Object; + case Pic: + return AssemblyRelocatable_PIC; + case Program: + return AssemblyRelocatable_Program; + + case AssemblyAbsolute: + case AssemblyRelocatable: + case C: + case Disassembly: + case FlowCode: + case Microbe: + case Unknown: + return Invalid; + } + + case C: + switch (to) + { + case AssemblyRelocatable: + return C_AssemblyRelocatable; + case Library: + return C_Library; + case Object: + return C_Object; + case Pic: + return C_PIC; + case Program: + return C_Program; + + case AssemblyAbsolute: + case C: + case Disassembly: + case FlowCode: + case Microbe: + case Unknown: + return Invalid; + } + + case Disassembly: + return Invalid; + + case FlowCode: + switch (to) + { + case AssemblyAbsolute: + return FlowCode_AssemblyAbsolute; + case Microbe: + return FlowCode_Microbe; + case Pic: + return FlowCode_PIC; + case Program: + return FlowCode_Program; + + case AssemblyRelocatable: + case C: + case Disassembly: + case FlowCode: + case Library: + case Object: + case Unknown: + return Invalid; + } + + case Library: + return Invalid; + + case Microbe: + switch (to) + { + case AssemblyAbsolute: + return Microbe_AssemblyAbsolute; + case Pic: + return Microbe_PIC; + case Program: + return Microbe_Program; + + case AssemblyRelocatable: + case C: + case Disassembly: + case FlowCode: + case Library: + case Microbe: + case Object: + case Unknown: + return Invalid; + } + + case Object: + switch (to) + { + case Disassembly: + return Object_Disassembly; + case Library: + return Object_Library; + case Pic: + return Object_PIC; + case Program: + return Object_Program; + + case AssemblyAbsolute: + case AssemblyRelocatable: + case C: + case FlowCode: + case Microbe: + case Object: + case Unknown: + return Invalid; + } + + case Pic: + return Invalid; + + case Program: + switch (to) + { + case Disassembly: + return Program_Disassembly; + case Pic: + return Program_PIC; + + case AssemblyAbsolute: + case AssemblyRelocatable: + case C: + case FlowCode: + case Library: + case Microbe: + case Object: + case Program: + case Unknown: + return Invalid; + } + + case Unknown: + return Invalid; + } + + return Invalid; +} + + +ProcessOptions::ProcessPath::MediaType ProcessOptions::ProcessPath::from( Path path ) +{ + switch (path) + { + case ProcessPath::AssemblyAbsolute_PIC: + case ProcessPath::AssemblyAbsolute_Program: + return AssemblyAbsolute; + + case ProcessPath::AssemblyRelocatable_Library: + case ProcessPath::AssemblyRelocatable_Object: + case ProcessPath::AssemblyRelocatable_PIC: + case ProcessPath::AssemblyRelocatable_Program: + return AssemblyRelocatable; + + case ProcessPath::C_AssemblyRelocatable: + case ProcessPath::C_Library: + case ProcessPath::C_Object: + case ProcessPath::C_PIC: + case ProcessPath::C_Program: + return C; + + case ProcessPath::FlowCode_AssemblyAbsolute: + case ProcessPath::FlowCode_Microbe: + case ProcessPath::FlowCode_PIC: + case ProcessPath::FlowCode_Program: + return FlowCode; + + case ProcessPath::Microbe_AssemblyAbsolute: + case ProcessPath::Microbe_PIC: + case ProcessPath::Microbe_Program: + return Microbe; + + case ProcessPath::Object_Disassembly: + case ProcessPath::Object_Library: + case ProcessPath::Object_PIC: + case ProcessPath::Object_Program: + return Object; + + case ProcessPath::PIC_AssemblyAbsolute: + return Pic; + + case ProcessPath::Program_Disassembly: + case ProcessPath::Program_PIC: + return Program; + + case ProcessPath::Invalid: + case ProcessPath::None: + return Unknown; + } + + return Unknown; +} + + +ProcessOptions::ProcessPath::MediaType ProcessOptions::ProcessPath::to( Path path ) +{ + switch (path) + { + case ProcessPath::FlowCode_AssemblyAbsolute: + case ProcessPath::Microbe_AssemblyAbsolute: + case ProcessPath::PIC_AssemblyAbsolute: + return AssemblyAbsolute; + + case ProcessPath::C_AssemblyRelocatable: + return AssemblyRelocatable; + + case ProcessPath::Object_Disassembly: + case ProcessPath::Program_Disassembly: + return Disassembly; + + case ProcessPath::AssemblyRelocatable_Library: + case ProcessPath::C_Library: + case ProcessPath::Object_Library: + return Library; + + case ProcessPath::FlowCode_Microbe: + return Microbe; + + case ProcessPath::AssemblyRelocatable_Object: + case ProcessPath::C_Object: + return Object; + + case ProcessPath::AssemblyAbsolute_PIC: + case ProcessPath::AssemblyRelocatable_PIC: + case ProcessPath::C_PIC: + case ProcessPath::FlowCode_PIC: + case ProcessPath::Microbe_PIC: + case ProcessPath::Object_PIC: + case ProcessPath::Program_PIC: + return Pic; + + case ProcessPath::AssemblyAbsolute_Program: + case ProcessPath::AssemblyRelocatable_Program: + case ProcessPath::C_Program: + case ProcessPath::FlowCode_Program: + case ProcessPath::Microbe_Program: + case ProcessPath::Object_Program: + return Program; + + case ProcessPath::Invalid: + case ProcessPath::None: + return Unknown; + } + + return Unknown; +} +//END class ProcessOptions + + +#include "language.moc" diff --git a/src/languages/language.h b/src/languages/language.h new file mode 100644 index 0000000..68ef187 --- /dev/null +++ b/src/languages/language.h @@ -0,0 +1,268 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef LANGUAGE_H +#define LANGUAGE_H + +#include <qobject.h> +#include <qstringlist.h> + +class FlowCodeDocument; +class KTechlab; +class LogView; +class MessageInfo; +class MicroSettings; +class OutputMethodInfo; +class ProcessChain; +class ProcessOptions; +class TextDocument; +class QProcess; + +typedef QValueList<ProcessOptions> ProcessOptionsList; + +class ProcessOptionsSpecial +{ + public: + ProcessOptionsSpecial(); + + bool b_addToProject; + bool b_forceList; + QString m_picID; + FlowCodeDocument * p_flowCodeDocument; + + // Linking + QString m_hexFormat; + bool m_bOutputMapFile; + QString m_libraryDir; + QString m_linkerScript; + QStringList m_linkLibraries; + QString m_linkOther; + + // Programming + QString m_port; + QString m_program; +}; + + +class ProcessOptionsHelper : public QObject +{ + Q_OBJECT +#define protected public + signals: +#undef protected + void textOutputtedTo( TextDocument * outputtedTo ); +}; + + +class ProcessOptions : public ProcessOptionsSpecial +{ + public: + ProcessOptions(); + ProcessOptions( OutputMethodInfo info ); + + class ProcessPath { public: + enum MediaType + { + AssemblyAbsolute, + AssemblyRelocatable, + C, + Disassembly, + FlowCode, + Library, + Microbe, + Object, + Pic, + Program, + + Unknown // Used for guessing the media type + }; + + enum Path // From_To // processor that will be invoked first + { + AssemblyAbsolute_PIC, // gpasm (indirect) + AssemblyAbsolute_Program, // gpasm (direct) + + AssemblyRelocatable_Library, // gpasm (indirect) + AssemblyRelocatable_Object, // gpasm (direct) + AssemblyRelocatable_PIC, // gpasm (indirect) + AssemblyRelocatable_Program, // gpasm (indirect) + + C_AssemblyRelocatable, // sdcc (direct) + C_Library, // sdcc (indirect) + C_Object, // sdcc (indirect) + C_PIC, // sdcc (indirect) + C_Program, // sdcc (indirect) + + FlowCode_AssemblyAbsolute, // flowcode (indirect) + FlowCode_Microbe, // flowcode (direct) + FlowCode_PIC, // flowcode (indirect) + FlowCode_Program, // flowcode (indirect) + + Microbe_AssemblyAbsolute, // microbe (direct) + Microbe_PIC, // microbe (indirect) + Microbe_Program, // microbe (indirect) + + Object_Disassembly, // gpdasm (direct) + Object_Library, // gplib (direct) + Object_PIC, // gplink (indirect) + Object_Program, // gplink (direct) + + PIC_AssemblyAbsolute, // download from pic (direct) + + Program_Disassembly, // gpdasm (direct) + Program_PIC, // upload to pic (direct) + + Invalid, // From and to types are incompatible + None // From and to types are the same + }; + + static Path path( MediaType from, MediaType to ); + static MediaType from( Path path ); + static MediaType to( Path path ); + }; + + class Method + { + public: enum type + { + Forget, // Don't do anything after processing successfully + LoadAsNew, // Load the output as a new file + Load // Load the output file + }; + }; + + /** + * Tries to guess the media type from the url (and possible the contents + * of the file as well). + */ + static ProcessPath::MediaType guessMediaType( const QString & url ); + /** + * The *final* target file (not any intermediatary ones) + */ + QString targetFile() const { return m_targetFile; } + /** + * This sets the final target file, as well as the initial intermediatary one + */ + void setTargetFile( const QString &file ); + + void setIntermediaryOutput( const QString &file ) { m_intermediaryFile = file; } + QString intermediaryOutput() const { return m_intermediaryFile; } + + void setInputFiles( const QStringList & files ) { m_inputFiles = files; } + QStringList inputFiles() const { return m_inputFiles; } + + void setMethod( Method::type method ) { m_method = method; } + Method::type method() const { return m_method; } + + void setProcessPath( ProcessPath::Path path ) { m_processPath = path; } + ProcessPath::Path processPath() const { return m_processPath; } + + /** + * If the output is text; If the user has selected (in config options) + * ReuseSameViewForOutput, then the given TextDocument will have its + * text set to the output if the TextDocument is not modified and has + * an empty url. Otherwise a new TextDocument will be created. Either + * way, once the the processing has finished, a signal will be emitted + * to the given receiver passing a TextDocument * as an argument. This + * is not to be confused with setTextOutputtedTo, which is called once + * the processing has finished, and will call-back to the slot given. + */ + void setTextOutputTarget( TextDocument * target, QObject * receiver, const char * slot ); + /** + * @see setTextOutputTarget + */ + TextDocument * textOutputTarget() const { return m_pTextOutputTarget; } + /** + * @see setTextOuputTarget + */ + void setTextOutputtedTo( TextDocument * outputtedTo ); + + protected: + TextDocument * m_pTextOutputTarget; + ProcessOptionsHelper * m_pHelper; + bool b_targetFileSet; + QStringList m_inputFiles; + QString m_targetFile; + QString m_intermediaryFile; + Method::type m_method; + ProcessPath::Path m_processPath; +}; + + +/** +@author Daniel Clarke +@author David Saxton +*/ +class Language : public QObject +{ + Q_OBJECT + public: + Language( ProcessChain *processChain, KTechlab *parent, const QString &name ); + ~Language(); + + /** + * Compile / assemble / dissassembly / whatever the given input. + * @returns true if processing was started succesfully (this is different to finishing successfuly). + */ + virtual void processInput( ProcessOptions options ) = 0; + /** + * Return the ProcessOptions object current state + */ + ProcessOptions processOptions() const { return m_processOptions; } + /** + * Return the output path from the given input path. Will return None + * if we've done processing. + */ + virtual ProcessOptions::ProcessPath::Path outputPath( ProcessOptions::ProcessPath::Path inputPath ) const = 0; + + signals: + /** + * Emitted when the processing was successful. + * @param language Pointer to this class + */ + void processSucceeded( Language *language ); + /** + * Emitted when the processing failed. + * @param language Pointer to this class + */ + void processFailed( Language *language ); + + protected: + /** + * Examines the string for the line number if applicable, and creates a new + * MessageInfo for it. + */ + virtual MessageInfo extractMessageInfo( const QString &text ); + + /** + * Reset the error count + */ + void reset(); + void outputMessage( const QString &message ); + void outputWarning( const QString &message ); + void outputError( const QString &error ); + void finish( bool successful ); + + int m_errorCount; + KTechlab *p_ktechlab; + ProcessOptions m_processOptions; + ProcessChain *p_processChain; + + /** + * A message appropriate to the language's success after compilation or similar. + */ + QString m_successfulMessage; + /** + * A message appropriate to the language's failure after compilation or similar. + */ + QString m_failedMessage; +}; + +#endif diff --git a/src/languages/languagemanager.cpp b/src/languages/languagemanager.cpp new file mode 100644 index 0000000..a6dddd8 --- /dev/null +++ b/src/languages/languagemanager.cpp @@ -0,0 +1,108 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "docmanager.h" +#include "languagemanager.h" +#include "logview.h" +#include "ktechlab.h" +#include "ktempfile.h" +#include "src/core/ktlconfig.h" +#include "outputmethoddlg.h" +#include "processchain.h" +#include "projectmanager.h" + +#include "microbe.h" +#include "gpasm.h" +#include "gpdasm.h" + +#include <kdockwidget.h> +#include <kiconloader.h> +#include <klocale.h> +#include <qwhatsthis.h> + +#include <assert.h> + + +LanguageManager * LanguageManager::m_pSelf = 0l; + + +LanguageManager * LanguageManager::self( KateMDI::ToolView * parent, KTechlab * ktl ) +{ + if (!m_pSelf) + { + assert(parent); + assert(ktl); + m_pSelf = new LanguageManager( parent, ktl ); + } + return m_pSelf; +} + + +LanguageManager::LanguageManager( KateMDI::ToolView * parent, KTechlab * ktl ) + : QObject((QObject*)ktl) +{ + p_ktechlab = ktl; + m_logView = new LogView( parent, "LanguageManager LogView"); + + QWhatsThis::add( m_logView, i18n("These messages show the output of language-related functionality such as compiling and assembling.<br><br>For error messages, clicking on the line will automatically open up the file at the position of the error.") ); + connect( m_logView, SIGNAL(paraClicked(const QString&, MessageInfo )), this, SLOT(slotParaClicked(const QString&, MessageInfo )) ); + reset(); +} + + +LanguageManager::~LanguageManager() +{ +} + + +void LanguageManager::reset() +{ + m_logView->clear(); +} + + +ProcessChain * LanguageManager::compile( ProcessOptions options ) +{ + if ( KTLConfig::raiseMessagesLog() ) + p_ktechlab->showToolView( p_ktechlab->toolView( toolViewIdentifier() ) ); + + return new ProcessChain( options, p_ktechlab ); +} + + +ProcessListChain * LanguageManager::compile( ProcessOptionsList pol ) +{ + if ( KTLConfig::raiseMessagesLog() ) + p_ktechlab->showToolView( p_ktechlab->toolView( toolViewIdentifier() ) ); + + return new ProcessListChain( pol, p_ktechlab ); +} + + +void LanguageManager::slotError( const QString &error, MessageInfo messageInfo ) +{ + m_logView->addOutput( error, LogView::ot_error, messageInfo ); +} +void LanguageManager::slotWarning( const QString &error, MessageInfo messageInfo ) +{ + m_logView->addOutput( error, LogView::ot_warning, messageInfo ); +} +void LanguageManager::slotMessage( const QString &error, MessageInfo messageInfo ) +{ + m_logView->addOutput( error, LogView::ot_message, messageInfo ); +} + +void LanguageManager::slotParaClicked( const QString& message, MessageInfo messageInfo ) +{ + Q_UNUSED(message); + DocManager::self()->gotoTextLine( messageInfo.fileURL(), messageInfo.fileLine() ); +} + +#include "languagemanager.moc" diff --git a/src/languages/languagemanager.h b/src/languages/languagemanager.h new file mode 100644 index 0000000..c5bda98 --- /dev/null +++ b/src/languages/languagemanager.h @@ -0,0 +1,91 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef LANGUAGEMANAGER_H +#define LANGUAGEMANAGER_H + +#include <qobject.h> +#include <qvaluelist.h> + +#include "language.h" + +class FlowCode; +class Gpasm; +class Gpdasm; +class KTechlab; +class Language; +class LanguageManager; +class LogView; +class MessageInfo; +class Microbe; +class ProcessChain; +class ProcessListChain; +class ProcessOptions; +namespace KateMDI { class ToolView; } + +/** +@author David Saxton +*/ +class LanguageManager : public QObject +{ + Q_OBJECT + public: + static LanguageManager * self( KateMDI::ToolView * parent = 0l, KTechlab * ktl = 0l ); + static QString toolViewIdentifier() { return "LanguageManager"; } + ~LanguageManager(); + + /** + * Call to compile a file of one type all the way to another type, this can + * also be used in reverse to disassemble code. Connect to the returned + * ProcessChain for notification of compile success / failure + * @return Pointer to the ProcessChain used to compile + */ + ProcessChain * compile( ProcessOptions options ); + ProcessListChain * compile( ProcessOptionsList pol ); + /** + * @return Pointer to the LogView that displays the output messages + */ + LogView * logView() const { return m_logView; } + /** + * Clear any errors and clear the log view + */ + void reset(); + + public slots: + /** + * Called when the user clicks on any text in the LogView + */ + void slotParaClicked( const QString& message, MessageInfo messageInfo ); + /** + * Called by languages to report an error message + * @param error Error message to report + */ + void slotError( const QString &error, MessageInfo messageInfo ); + /** + * Called by languages to report a warning message + * @param warning Warning message to report + */ + void slotWarning( const QString &warning, MessageInfo messageInfo ); + /** + * Called by languages to report a general message + * @param message General message to report + */ + void slotMessage( const QString &message, MessageInfo messageInfo ); + + protected: + LanguageManager( KateMDI::ToolView * parent, KTechlab * ktl ); + + private: + LogView * m_logView; + static LanguageManager * m_pSelf; + KTechlab * p_ktechlab; +}; + +#endif diff --git a/src/languages/microbe.cpp b/src/languages/microbe.cpp new file mode 100644 index 0000000..628b3c1 --- /dev/null +++ b/src/languages/microbe.cpp @@ -0,0 +1,136 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "contexthelp.h" +#include "docmanager.h" +#include "logview.h" +#include "microbe.h" +#include "languagemanager.h" + +#include <kdebug.h> +#include <klocale.h> +#include <kmessagebox.h> +#include <kstandarddirs.h> + +#include <qfile.h> +#include <kprocess.h> + +Microbe::Microbe( ProcessChain *processChain, KTechlab *parent ) + : ExternalLanguage( processChain, parent, "Microbe" ) +{ + m_failedMessage = i18n("*** Compilation failed ***"); + m_successfulMessage = i18n("*** Compilation successful ***"); + + // Setup error messages list + QFile file( locate("appdata",i18n("error_messages_en_gb")) ); + if ( file.open( IO_ReadOnly ) ) + { + QTextStream stream( &file ); + QString line; + while ( !stream.atEnd() ) + { + line = stream.readLine(); // line of text excluding '\n' + if ( !line.isEmpty() ) + { + bool ok; + const int pos = line.left( line.find("#") ).toInt(&ok); + if (ok) { + m_errorMessages[pos] = line.right(line.length()-line.find("#")); + } else { + kdError() << k_funcinfo << "Error parsing Microbe error-message file"<<endl; + } + } + } + file.close(); + } +} + +Microbe::~Microbe() +{ +} + + +void Microbe::processInput( ProcessOptions options ) +{ + resetLanguageProcess(); + m_processOptions = options; + + *m_languageProcess << ("microbe"); + + // Input Asm file + *m_languageProcess << ( options.inputFiles().first() ); + + // Output filename + *m_languageProcess << ( options.intermediaryOutput() ); + + *m_languageProcess << ("--show-source"); + + if ( !start() ) + { + KMessageBox::sorry( LanguageManager::self()->logView(), i18n("Assembly failed. Please check you have KTechlab installed properly (\"microbe\" could not be started).") ); + processInitFailed(); + return; + } +} + + +bool Microbe::isError( const QString &message ) const +{ + return message.contains( "Error", false ); +} + +bool Microbe::isWarning( const QString &message ) const +{ + return message.contains( "Warning", false ); +} + + +ProcessOptions::ProcessPath::Path Microbe::outputPath( ProcessOptions::ProcessPath::Path inputPath ) const +{ + switch (inputPath) + { + case ProcessOptions::ProcessPath::Microbe_AssemblyAbsolute: + return ProcessOptions::ProcessPath::None; + + case ProcessOptions::ProcessPath::Microbe_PIC: + return ProcessOptions::ProcessPath::AssemblyAbsolute_PIC; + + case ProcessOptions::ProcessPath::Microbe_Program: + return ProcessOptions::ProcessPath::AssemblyAbsolute_Program; + + case ProcessOptions::ProcessPath::AssemblyAbsolute_PIC: + case ProcessOptions::ProcessPath::AssemblyAbsolute_Program: + case ProcessOptions::ProcessPath::AssemblyRelocatable_Library: + case ProcessOptions::ProcessPath::AssemblyRelocatable_Object: + case ProcessOptions::ProcessPath::AssemblyRelocatable_PIC: + case ProcessOptions::ProcessPath::AssemblyRelocatable_Program: + case ProcessOptions::ProcessPath::C_AssemblyRelocatable: + case ProcessOptions::ProcessPath::C_Library: + case ProcessOptions::ProcessPath::C_Object: + case ProcessOptions::ProcessPath::C_PIC: + case ProcessOptions::ProcessPath::C_Program: + case ProcessOptions::ProcessPath::FlowCode_AssemblyAbsolute: + case ProcessOptions::ProcessPath::FlowCode_Microbe: + case ProcessOptions::ProcessPath::FlowCode_PIC: + case ProcessOptions::ProcessPath::FlowCode_Program: + case ProcessOptions::ProcessPath::Object_Disassembly: + case ProcessOptions::ProcessPath::Object_Library: + case ProcessOptions::ProcessPath::Object_PIC: + case ProcessOptions::ProcessPath::Object_Program: + case ProcessOptions::ProcessPath::PIC_AssemblyAbsolute: + case ProcessOptions::ProcessPath::Program_Disassembly: + case ProcessOptions::ProcessPath::Program_PIC: + case ProcessOptions::ProcessPath::Invalid: + case ProcessOptions::ProcessPath::None: + return ProcessOptions::ProcessPath::Invalid; + } + + return ProcessOptions::ProcessPath::Invalid; +} diff --git a/src/languages/microbe.h b/src/languages/microbe.h new file mode 100644 index 0000000..568db4b --- /dev/null +++ b/src/languages/microbe.h @@ -0,0 +1,40 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef MICROBE_H +#define MICROBE_H + +#include "externallanguage.h" + +#include <qmap.h> + +typedef QMap< int, QString > ErrorMap; + +/** +@author Daniel Clarke +@author David Saxton +*/ +class Microbe : public ExternalLanguage +{ +public: + Microbe( ProcessChain *processChain, KTechlab *parent ); + ~Microbe(); + + virtual void processInput( ProcessOptions options ); + virtual ProcessOptions::ProcessPath::Path outputPath( ProcessOptions::ProcessPath::Path inputPath ) const; + +protected: + virtual bool isError( const QString &message ) const; + virtual bool isWarning( const QString &message ) const; + + ErrorMap m_errorMessages; +}; + +#endif diff --git a/src/languages/picprogrammer.cpp b/src/languages/picprogrammer.cpp new file mode 100644 index 0000000..6f5e76f --- /dev/null +++ b/src/languages/picprogrammer.cpp @@ -0,0 +1,456 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "languagemanager.h" +#include "picprogrammer.h" +#include "src/core/ktlconfig.h" + +#include <kapplication.h> +#include <kconfig.h> +#include <kdebug.h> +#include <klocale.h> +#include <kmessagebox.h> + +#include <qapplication.h> +#include <qfile.h> +#include <kprocess.h> +#include <qregexp.h> +#include <qtextstream.h> +#include <qdatetime.h> + +#include <stdio.h> + +//BEGIN class ProgrammerConfig +ProgrammerConfig::ProgrammerConfig() +{ +} + + +void ProgrammerConfig::reset() +{ + initCommand = QString::null; + readCommand = QString::null; + writeCommand = QString::null; + verifyCommand = QString::null; + blankCheckCommand = QString::null; + eraseCommand = QString::null; +} +//END class ProgrammerConfig + + + +//BEGIN class PicProgrammerSettings +bool PicProgrammerSettings::m_bDoneStaticConfigsInit = false; +ProgrammerConfigMap PicProgrammerSettings::m_staticConfigs = ProgrammerConfigMap(); + + +PicProgrammerSettings::PicProgrammerSettings() +{ + if ( !m_bDoneStaticConfigsInit ) + initStaticConfigs(); +} + + +void PicProgrammerSettings::initStaticConfigs() +{ + m_bDoneStaticConfigsInit = true; + ProgrammerConfig config; + + config.description = i18n("Supported programmers: %1").arg("JuPic, PICStart Plus, Warp-13"); + config.description += i18n("<br>Interface: Serial Port"); + config.initCommand = ""; + config.readCommand = "picp %port %device -rp %file"; + config.writeCommand = "picp %port %device -wp %file"; + config.verifyCommand = ""; + config.blankCheckCommand = "picp %port %device -b"; + config.eraseCommand = "picp %port %device -e"; +// config.executable = "picp"; + m_staticConfigs[ "PICP" ] = config; + + + config.description = i18n("Supported programmers: %1").arg("Epic Plus"); + config.description += i18n("<br>Interface: Parallel Port"); + config.initCommand = "odyssey init"; + config.readCommand = "odyssey %device read %file"; + config.writeCommand = "odyssey %device write %file"; + config.verifyCommand = "odyssey %device verify %file"; + config.blankCheckCommand = "odyssey %device blankcheck"; + config.eraseCommand = "odyssey %device erase"; +// config.executable = "odyssey"; + m_staticConfigs[ "Odyssey" ] = config; + + + config.description = i18n("Supported programmers: %1").arg("JDM PIC-Programmer 2, PIC-PG2C"); + config.description += i18n("<br>Interface: Serial Port"); + config.initCommand = ""; + config.readCommand = "picprog --output %file --pic %port"; + config.writeCommand = "picprog --burn --input %file --pic %port --device %device"; + config.verifyCommand = ""; + config.blankCheckCommand = ""; + config.eraseCommand = "picprog --erase --pic %device"; + m_staticConfigs[ "PICProg" ] = config; + + + config.description = i18n("Supported programmers: %1").arg("Epic Plus"); + config.description += i18n("<br>Interface: Parallel Port"); + config.initCommand = ""; + config.readCommand = "dump84 --dump-all --output=%file"; + config.writeCommand = "prog84 --intel16=%file"; + config.verifyCommand = ""; + config.blankCheckCommand = ""; + config.eraseCommand = "prog84 --clear"; + m_staticConfigs[ "prog84" ] = config; + + + config.description = i18n("Supported programmers: %1").arg("Kit 149, Kit 150"); + config.description += i18n("<br>Interface: USB Port"); + config.initCommand = ""; + config.readCommand = "pp -d %device -r %file"; + config.writeCommand = "pp -d %device -w %file"; + config.verifyCommand = "pp -d %device -v %file"; + config.blankCheckCommand = ""; + config.eraseCommand = "pp -d %device -e"; + m_staticConfigs[ "PP" ] = config; + + + config.description = i18n("Supported programmers: %1").arg("Wisp628"); + config.description += i18n("<br>Interface: Serial Port"); + config.initCommand = ""; + config.readCommand = "xwisp ID %device PORT %device DUMP"; + config.writeCommand = "xwisp ID %device PORT %device WRITE %file"; + config.verifyCommand = ""; + config.blankCheckCommand = ""; + config.eraseCommand = "xwisp ID %device PORT %device ERASE"; + m_staticConfigs[ "XWisp" ] = config; + + +#if 0 + config.description = i18n("Supported programmers: %1").arg("Epic Plus, JDM PIC-Programmer 2, PICCOLO, PICCOLO Grande, Trivial HVP Programmer"); + config.description += i18n("<br>Interface: Serial Port and Parallel Port"); + config.initCommand = ""; + config.readCommand = ""; + config.writeCommand = ""; + config.verifyCommand = ""; + config.blankCheckCommand = ""; + config.eraseCommand = ""; + config.executable = "pkp"; + m_staticConfigs[ "PiKdev" ] = config; + config.executable = ""; + + + config.description = i18n("Supported programmers: %1").arg("Trivial LVP programmer, Trivial HVP Programmer"); + config.description += i18n("<br>Interface: Parallel Port"); + config.initCommand = ""; + config.readCommand = ""; + config.writeCommand = ""; + config.verifyCommand = ""; + config.blankCheckCommand = ""; + config.eraseCommand = ""; + m_staticConfigs[ "PicPrg2" ] = config; + + + config.description = i18n("Supported programmers: %1").arg("El Cheapo"); + config.description += i18n("<br>Interface: Parallel Port"); + config.initCommand = ""; + config.readCommand = ""; + config.writeCommand = ""; + config.verifyCommand = ""; + config.blankCheckCommand = ""; + config.eraseCommand = ""; + m_staticConfigs[ "PP06" ] = config; + + + config.description = i18n("Supported programmers: %1").arg("NOPPP"); + config.description += i18n("<br>Interface: Parallel Port"); + config.initCommand = ""; + config.readCommand = ""; + config.writeCommand = ""; + config.verifyCommand = ""; + config.blankCheckCommand = ""; + config.eraseCommand = ""; + m_staticConfigs[ "NOPPP" ] = config; + + + config.description = i18n("Supported programmers: %1").arg("SNOPPP"); + config.description += i18n("<br>Interface: Parallel Port"); + config.initCommand = ""; + config.readCommand = ""; + config.writeCommand = ""; + config.verifyCommand = ""; + config.blankCheckCommand = ""; + config.eraseCommand = ""; + m_staticConfigs[ "SNOPPP" ] = config; +#endif +} + + +void PicProgrammerSettings::load( KConfig * config ) +{ + QStringList oldCustomProgrammers = config->groupList().grep("CustomProgrammer_"); + QStringList::iterator ocpEnd = oldCustomProgrammers.end(); + for ( QStringList::iterator it = oldCustomProgrammers.begin(); it != ocpEnd; ++it ) + { + // The CustomProgrammer_ string we searched for might appear half way through... (don't want) + if ( (*it).startsWith("CustomProgrammer_") ) + { + config->setGroup(*it); + + ProgrammerConfig pc; + pc.initCommand = config->readEntry( "InitCommand" ); + pc.readCommand = config->readEntry( "ReadCommand" ); + pc.writeCommand = config->readEntry( "WriteCommand" ); + pc.verifyCommand = config->readEntry( "VerifyCommand" ); + pc.blankCheckCommand = config->readEntry( "BlankCheckCommand" ); + pc.eraseCommand = config->readEntry( "EraseCommand" ); + + QString name = config->readEntry( "Name" ); + m_customConfigs[name] = pc; + } + } +} + + +void PicProgrammerSettings::save( KConfig * config ) +{ + QStringList oldCustomProgrammers = config->groupList().grep("CustomProgrammer_"); + QStringList::iterator ocpEnd = oldCustomProgrammers.end(); + for ( QStringList::iterator it = oldCustomProgrammers.begin(); it != ocpEnd; ++it ) + { + // The CustomProgrammer_ string we searched for might appear half way through... (don't want) + if ( (*it).startsWith("CustomProgrammer_") ) + config->deleteGroup(*it); + } + + int at = 0; + ProgrammerConfigMap::iterator end = m_customConfigs.end(); + for ( ProgrammerConfigMap::iterator it = m_customConfigs.begin(); it != end; ++it ) + { + config->setGroup( QString("CustomProgrammer_%1").arg(at++) ); + + config->writeEntry( "Name", it.key() ); + config->writeEntry( "InitCommand", it.data().initCommand ); + config->writeEntry( "ReadCommand", it.data().readCommand ); + config->writeEntry( "WriteCommand", it.data().writeCommand ); + config->writeEntry( "VerifyCommand", it.data().verifyCommand ); + config->writeEntry( "BlankCheckCommand", it.data().blankCheckCommand ); + config->writeEntry( "EraseCommand", it.data().eraseCommand ); + } +} + + +ProgrammerConfig PicProgrammerSettings::config( const QString & name ) +{ + if ( name.isEmpty() ) + return ProgrammerConfig(); + + QString l = name.lower(); + + ProgrammerConfigMap::const_iterator end = m_customConfigs.end(); + for ( ProgrammerConfigMap::const_iterator it = m_customConfigs.begin(); it != end; ++it ) + { + if ( it.key().lower() == l ) + return *it; + } + + end = m_staticConfigs.end(); + for ( ProgrammerConfigMap::const_iterator it = m_staticConfigs.begin(); it != end; ++it ) + { + if ( it.key().lower() == l ) + return *it; + } + + return m_customConfigs[ name ]; +} + + +void PicProgrammerSettings::removeConfig( const QString & name ) +{ + if ( isPredefined( name ) ) + { + kdWarning() << k_funcinfo << "Cannot remove a predefined PIC programmer configuration." << endl; + return; + } + + QString l = name.lower(); + + ProgrammerConfigMap::iterator end = m_customConfigs.end(); + for ( ProgrammerConfigMap::iterator it = m_customConfigs.begin(); it != end; ++it ) + { + if ( it.key().lower() == l ) + { + m_customConfigs.remove( it ); + return; + } + } +} + + +void PicProgrammerSettings::saveConfig( const QString & name, const ProgrammerConfig & config ) +{ + if ( isPredefined( name ) ) + { + kdWarning() << k_funcinfo << "Cannot save to a predefined PIC programmer configuration." << endl; + return; + } + + QString l = name.lower(); + + ProgrammerConfigMap::iterator end = m_customConfigs.end(); + for ( ProgrammerConfigMap::iterator it = m_customConfigs.begin(); it != end; ++it ) + { + if ( it.key().lower() == l ) + { + *it = config; + return; + } + } + + m_customConfigs[ name ] = config; +} + + +QStringList PicProgrammerSettings::configNames( bool makeLowercase ) const +{ + if ( !makeLowercase ) + return m_customConfigs.keys() + m_staticConfigs.keys(); + + QStringList names; + + ProgrammerConfigMap::const_iterator end = m_customConfigs.end(); + for ( ProgrammerConfigMap::const_iterator it = m_customConfigs.begin(); it != end; ++it ) + names << it.key().lower(); + + end = m_staticConfigs.end(); + for ( ProgrammerConfigMap::const_iterator it = m_staticConfigs.begin(); it != end; ++it ) + names << it.key().lower(); + + return names; +} + + +bool PicProgrammerSettings::isPredefined( const QString & name ) const +{ + QString l = name.lower(); + + ProgrammerConfigMap::const_iterator end = m_staticConfigs.end(); + for ( ProgrammerConfigMap::const_iterator it = m_staticConfigs.begin(); it != end; ++it ) + { + if ( it.key().lower() == l ) + return true; + } + + return false; +} +//END class PicProgrammerSettings + + + +//BEGIN class PicProgrammer +PicProgrammer::PicProgrammer( ProcessChain *processChain, KTechlab * parent ) + : ExternalLanguage( processChain, parent, "PicProgrammer" ) +{ + m_successfulMessage = i18n("*** Programming successful ***"); + m_failedMessage = i18n("*** Programming failed ***"); +} + + +PicProgrammer::~PicProgrammer() +{ +} + + +void PicProgrammer::processInput( ProcessOptions options ) +{ + resetLanguageProcess(); + m_processOptions = options; + + PicProgrammerSettings settings; + settings.load( kapp->config() ); + + QString program = options.m_program; + if ( !settings.configNames( true ).contains( program.lower() ) ) + { + kdError() << k_funcinfo << "Invalid program" << endl; + finish( false ); + return; + } + + ProgrammerConfig config = settings.config( program ); + + QString command = config.writeCommand; + command.replace( "%port", options.m_port ); + command.replace( "%device", QString( options.m_picID ).remove("P") ); + command.replace( "%file", KProcess::quote( options.inputFiles().first() ) ); + + m_languageProcess->setUseShell( true ); + *m_languageProcess << command; + + if ( !start() ) + { +// KMessageBox::sorry( LanguageManager::self()->logView(), i18n("Could not program PIC.") ); + processInitFailed(); + return; + } +} + + +bool PicProgrammer::isError( const QString &message ) const +{ + return message.contains( "Error", false ); +} + + +bool PicProgrammer::isWarning( const QString &message ) const +{ + return message.contains( "Warning", false ); +} + + +ProcessOptions::ProcessPath::Path PicProgrammer::outputPath( ProcessOptions::ProcessPath::Path inputPath ) const +{ + switch (inputPath) + { + case ProcessOptions::ProcessPath::PIC_AssemblyAbsolute: + case ProcessOptions::ProcessPath::Program_PIC: + return ProcessOptions::ProcessPath::None; + + case ProcessOptions::ProcessPath::AssemblyAbsolute_PIC: + case ProcessOptions::ProcessPath::AssemblyAbsolute_Program: + case ProcessOptions::ProcessPath::AssemblyRelocatable_Library: + case ProcessOptions::ProcessPath::AssemblyRelocatable_Object: + case ProcessOptions::ProcessPath::AssemblyRelocatable_PIC: + case ProcessOptions::ProcessPath::AssemblyRelocatable_Program: + case ProcessOptions::ProcessPath::C_AssemblyRelocatable: + case ProcessOptions::ProcessPath::C_Library: + case ProcessOptions::ProcessPath::C_Object: + case ProcessOptions::ProcessPath::C_PIC: + case ProcessOptions::ProcessPath::C_Program: + case ProcessOptions::ProcessPath::FlowCode_AssemblyAbsolute: + case ProcessOptions::ProcessPath::FlowCode_Microbe: + case ProcessOptions::ProcessPath::FlowCode_PIC: + case ProcessOptions::ProcessPath::FlowCode_Program: + case ProcessOptions::ProcessPath::Microbe_AssemblyAbsolute: + case ProcessOptions::ProcessPath::Microbe_PIC: + case ProcessOptions::ProcessPath::Microbe_Program: + case ProcessOptions::ProcessPath::Object_Disassembly: + case ProcessOptions::ProcessPath::Object_Library: + case ProcessOptions::ProcessPath::Object_PIC: + case ProcessOptions::ProcessPath::Object_Program: + case ProcessOptions::ProcessPath::Program_Disassembly: + case ProcessOptions::ProcessPath::Invalid: + case ProcessOptions::ProcessPath::None: + return ProcessOptions::ProcessPath::Invalid; + } + + return ProcessOptions::ProcessPath::Invalid; +} +//END class PicProgrammer + + diff --git a/src/languages/picprogrammer.h b/src/languages/picprogrammer.h new file mode 100644 index 0000000..580cf6b --- /dev/null +++ b/src/languages/picprogrammer.h @@ -0,0 +1,127 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef PICPROGRAMMER_H +#define PICPROGRAMMER_H + +#include "externallanguage.h" + + +class KConfig; +class KProcess; + + +class ProgrammerConfig +{ + public: + ProgrammerConfig(); + + /** + * Clears the type and all commands. + */ + void reset(); + + QString initCommand; + QString readCommand; + QString writeCommand; + QString verifyCommand; + QString blankCheckCommand; + QString eraseCommand; + + QString description; + QString executable; // The name of the program executable +}; + +typedef QMap< QString, ProgrammerConfig > ProgrammerConfigMap; + + + +/** +This class provides access to the PIC Programmer configurations. Several are +predefinied; the rest can be read from and written to, and removed. Names are +case insensitive. + +Each programmer configuration is in the form of the ProgrammerConfig struct. + +@author David Saxton +*/ +class PicProgrammerSettings +{ + public: + PicProgrammerSettings(); + + /** + * Reads in custom ProgrammerConfigs from config. Any previously loaded + * configurations stored in this class will removed first. + */ + void load( KConfig * config ); + /** + * Saves the custom ProgrammConfigs to config. + */ + void save( KConfig * config ); + /** + * @return the ProgrammConfig for the programmer with the given name. If + * no such ProgrammerConfigs with the given name exist, then one will be + * created. The name is case insensitive (although the full case of the + * name will be stored if a new ProgrammerConfig is created). + */ + ProgrammerConfig config( const QString & name ); + /** + * Removes the config (if it is custom) with the give name. + */ + void removeConfig( const QString & name ); + /** + * Sets the ProgrammerConfig with the given name (or creates one if no + * such config exists). The name is case insensitive. + */ + void saveConfig( const QString & name, const ProgrammerConfig & config ); + /** + * @param makeLowercase whether the names should be converted to + * lowercase before returning. + * @return a list of names of the custom and predefined configs. + */ + QStringList configNames( bool makeLowercase ) const; + /** + * @return whether the given config is predefined. + */ + bool isPredefined( const QString & name ) const; + + protected: + /** + * Called when a PicProgrammerSettings object is first created. Does + * initialization of the predefined configs. + */ + void initStaticConfigs(); + + ProgrammerConfigMap m_customConfigs; + + static bool m_bDoneStaticConfigsInit; + static ProgrammerConfigMap m_staticConfigs; +}; + + +/** +@author David Saxton +*/ +class PicProgrammer : public ExternalLanguage +{ + public: + PicProgrammer( ProcessChain *processChain, KTechlab *parent ); + ~PicProgrammer(); + + virtual void processInput( ProcessOptions options ); + virtual ProcessOptions::ProcessPath::Path outputPath( ProcessOptions::ProcessPath::Path inputPath ) const; + + protected: + virtual bool isError( const QString &message ) const; + virtual bool isWarning( const QString &message ) const; +}; + +#endif diff --git a/src/languages/processchain.cpp b/src/languages/processchain.cpp new file mode 100644 index 0000000..e17c6ae --- /dev/null +++ b/src/languages/processchain.cpp @@ -0,0 +1,326 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "asmparser.h" +#include "docmanager.h" +#include "gplib.h" +#include "src/core/ktlconfig.h" +#include "language.h" +#include "languagemanager.h" +#include "logview.h" +#include "outputmethoddlg.h" +#include "processchain.h" +#include "projectmanager.h" +#include "textdocument.h" + +#include "flowcode.h" +#include "gpasm.h" +#include "gpdasm.h" +#include "gplink.h" +#include "microbe.h" +#include "picprogrammer.h" +#include "sdcc.h" + +#include <kdebug.h> +#include <klocale.h> +#include <ktempfile.h> +#include <qfile.h> +#include <qtimer.h> + + +//BEGIN class ProcessChain +ProcessChain::ProcessChain( ProcessOptions options, KTechlab * ktechlab, const char *name ) + : QObject( (QObject*)ktechlab, name) +{ + m_pKTechlab = ktechlab; + m_pFlowCode = 0l; + m_pGpasm = 0l; + m_pGpdasm = 0l; + m_pGplib = 0l; + m_pGplink = 0l; + m_pMicrobe = 0l; + m_pPicProgrammer = 0l; + m_pSDCC = 0l; + m_processOptions = options; + + QString target; + if ( ProcessOptions::ProcessPath::to( options.processPath() ) == ProcessOptions::ProcessPath::Pic ) + target = options.m_picID; + else + target = options.targetFile(); + + LanguageManager::self()->logView()->addOutput( i18n("Building: %1").arg( target ), LogView::ot_important ); + QTimer::singleShot( 0, this, SLOT(compile()) ); +} + + +ProcessChain::~ProcessChain() +{ + delete m_pFlowCode; + m_pFlowCode = 0l; + delete m_pGpasm; + m_pGpasm = 0l; + delete m_pGpdasm; + m_pGpdasm = 0l; + delete m_pGplib; + m_pGplib = 0l; + delete m_pGplink; + m_pGplink = 0l; + delete m_pMicrobe; + m_pMicrobe = 0l; + delete m_pPicProgrammer; + m_pPicProgrammer = 0l; + delete m_pSDCC; + m_pSDCC = 0l; +} + + +// void ProcessChain::compile( ProcessOptions * options ) +void ProcessChain::compile() +{ + // If the micro id in the options is empty, then attempt to get it from any + // open project (it might not be necessarily...but won't hurt if it isn't). + if ( m_processOptions.m_picID.isEmpty() ) + { + if ( ProjectInfo * projectInfo = ProjectManager::self()->currentProject() ) + { + ProjectItem * projectItem = projectInfo->findItem( m_processOptions.inputFiles().first() ); + if (projectItem) + m_processOptions.m_picID = projectItem->microID(); + } + } + + switch ( m_processOptions.processPath() ) + { +#define DIRECT_PROCESS( path, processor ) case ProcessOptions::ProcessPath::path: { processor()->processInput(m_processOptions); break; } +#define INDIRECT_PROCESS( path, processor, extension ) case ProcessOptions::ProcessPath::path: { KTempFile f( QString::null, extension ); f.close(); m_processOptions.setIntermediaryOutput( f.name() ); processor()->processInput(m_processOptions); break; } + + INDIRECT_PROCESS( AssemblyAbsolute_PIC, gpasm, ".hex" ) + DIRECT_PROCESS( AssemblyAbsolute_Program, gpasm ) + INDIRECT_PROCESS( AssemblyRelocatable_Library, gpasm, ".o" ) + DIRECT_PROCESS( AssemblyRelocatable_Object, gpasm ) + INDIRECT_PROCESS( AssemblyRelocatable_PIC, gpasm, ".o" ) + INDIRECT_PROCESS( AssemblyRelocatable_Program, gpasm, ".o" ) + DIRECT_PROCESS( C_AssemblyRelocatable, sdcc ) + INDIRECT_PROCESS( C_Library, sdcc, ".asm" ) + INDIRECT_PROCESS( C_Object, sdcc, ".asm" ) + INDIRECT_PROCESS( C_PIC, sdcc, ".asm" ) + INDIRECT_PROCESS( C_Program, sdcc, ".asm" ) + INDIRECT_PROCESS( FlowCode_AssemblyAbsolute, flowCode, ".microbe" ) + DIRECT_PROCESS( FlowCode_Microbe, flowCode ) + INDIRECT_PROCESS( FlowCode_PIC, flowCode, ".microbe" ) + INDIRECT_PROCESS( FlowCode_Program, flowCode, ".microbe" ) + DIRECT_PROCESS( Microbe_AssemblyAbsolute, microbe ) + INDIRECT_PROCESS( Microbe_PIC, microbe, ".asm" ) + INDIRECT_PROCESS( Microbe_Program, microbe, ".asm" ) + DIRECT_PROCESS( Object_Disassembly, gpdasm ) + DIRECT_PROCESS( Object_Library, gplib ) + INDIRECT_PROCESS( Object_PIC, gplink, ".lib" ) + DIRECT_PROCESS( Object_Program, gplink ) + DIRECT_PROCESS( PIC_AssemblyAbsolute, picProgrammer ) + DIRECT_PROCESS( Program_Disassembly, gpdasm ) + DIRECT_PROCESS( Program_PIC, picProgrammer ) +#undef DIRECT_PROCESS +#undef INDIRECT_PROCESS + + case ProcessOptions::ProcessPath::Invalid: + kdWarning() << k_funcinfo << "Process path is invalid" << endl; + + case ProcessOptions::ProcessPath::None: + kdWarning() << k_funcinfo << "Nothing to do" << endl; + break; + } +} + + +void ProcessChain::slotFinishedCompile(Language *language) +{ + ProcessOptions options = language->processOptions(); + + if ( options.b_addToProject && ProjectManager::self()->currentProject() ) + ProjectManager::self()->currentProject()->addFile( KURL(options.targetFile()) ); + + ProcessOptions::ProcessPath::MediaType typeTo = ProcessOptions::ProcessPath::to( m_processOptions.processPath() ); + + TextDocument * editor = 0l; + if ( KTLConfig::reuseSameViewForOutput() ) + { + editor = options.textOutputTarget(); + if ( editor && (!editor->url().isEmpty() || editor->isModified()) ) + editor = 0l; + } + + switch (typeTo) + { + case ProcessOptions::ProcessPath::AssemblyAbsolute: + case ProcessOptions::ProcessPath::AssemblyRelocatable: + case ProcessOptions::ProcessPath::C: + case ProcessOptions::ProcessPath::Disassembly: + case ProcessOptions::ProcessPath::Library: + case ProcessOptions::ProcessPath::Microbe: + case ProcessOptions::ProcessPath::Object: + case ProcessOptions::ProcessPath::Program: + { + switch ( options.method() ) + { + case ProcessOptions::Method::LoadAsNew: + { + if ( !editor ) + editor = DocManager::self()->createTextDocument(); + + if ( !editor ) + break; + + QString text; + QFile f( options.targetFile() ); + if ( !f.open( IO_ReadOnly ) ) + { + editor->deleteLater(); + editor = 0l; + break; + } + + QTextStream stream(&f); + + while ( !stream.atEnd() ) + text += stream.readLine()+'\n'; + + f.close(); + + editor->setText( text, true ); + break; + } + + case ProcessOptions::Method::Load: + { + editor = dynamic_cast<TextDocument*>( DocManager::self()->openURL(options.targetFile()) ); + break; + } + + case ProcessOptions::Method::Forget: + break; + } + } + + case ProcessOptions::ProcessPath::FlowCode: + case ProcessOptions::ProcessPath::Pic: + case ProcessOptions::ProcessPath::Unknown: + break; + } + + + if (editor) + { + switch (typeTo) + { + case ProcessOptions::ProcessPath::AssemblyAbsolute: + case ProcessOptions::ProcessPath::AssemblyRelocatable: + { + if ( KTLConfig::autoFormatMBOutput() ) + editor->formatAssembly(); + editor->slotInitLanguage( TextDocument::ct_asm ); + break; + } + + case ProcessOptions::ProcessPath::C: + editor->slotInitLanguage( TextDocument::ct_c ); + break; + + case ProcessOptions::ProcessPath::Disassembly: + break; + + case ProcessOptions::ProcessPath::Library: + case ProcessOptions::ProcessPath::Object: + case ProcessOptions::ProcessPath::Program: + editor->slotInitLanguage( TextDocument::ct_hex ); + break; + + case ProcessOptions::ProcessPath::Microbe: + editor->slotInitLanguage( TextDocument::ct_microbe ); + break; + + case ProcessOptions::ProcessPath::FlowCode: + case ProcessOptions::ProcessPath::Pic: + case ProcessOptions::ProcessPath::Unknown: + break; + } + + DocManager::self()->giveDocumentFocus( editor ); + } + + options.setTextOutputtedTo( editor ); + + emit successful(options); + emit successful(); +} + +#define LanguageFunction(a,b,c) \ +a * ProcessChain::b( ) \ +{ \ + if ( !c ) \ + { \ + c = new a( this, m_pKTechlab ); \ + connect( c, SIGNAL(processSucceeded(Language* )), this, SLOT(slotFinishedCompile(Language* )) ); \ + connect( c, SIGNAL(processFailed(Language* )), this, SIGNAL(failed()) ); \ + } \ + return c; \ +} + +LanguageFunction( FlowCode, flowCode, m_pFlowCode ) +LanguageFunction( Gpasm, gpasm, m_pGpasm ) +LanguageFunction( Gpdasm, gpdasm, m_pGpdasm ) +LanguageFunction( Gplib, gplib, m_pGplib ) +LanguageFunction( Gplink, gplink, m_pGplink ) +LanguageFunction( Microbe, microbe, m_pMicrobe ) +LanguageFunction( PicProgrammer, picProgrammer, m_pPicProgrammer ) +LanguageFunction( SDCC, sdcc, m_pSDCC ) +//END class ProcessChain + + + +//BEGIN class ProcessListChain +ProcessListChain::ProcessListChain( ProcessOptionsList pol, KTechlab * parent, const char * name ) + : QObject( (QObject*)parent, name ) +{ + m_processOptionsList = pol; + m_pKTechlab = parent; + + // Start us off... + slotProcessChainSuccessful(); +} + + +void ProcessListChain::slotProcessChainSuccessful() +{ + if ( m_processOptionsList.isEmpty() ) + { + emit successful(); + return; + } + + ProcessOptionsList::iterator it = m_processOptionsList.begin(); + ProcessOptions po = *it; + m_processOptionsList.remove(it); + + ProcessChain * pc = LanguageManager::self()->compile(po); + + connect( pc, SIGNAL(successful()), this, SLOT(slotProcessChainSuccessful()) ); + connect( pc, SIGNAL(failed()), this, SLOT(slotProcessChainFailed()) ); +} + + +void ProcessListChain::slotProcessChainFailed() +{ + emit failed(); +} +//END class ProcessListChain + + +#include "processchain.moc" diff --git a/src/languages/processchain.h b/src/languages/processchain.h new file mode 100644 index 0000000..90b871e --- /dev/null +++ b/src/languages/processchain.h @@ -0,0 +1,127 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef PROCESSCHAIN_H +#define PROCESSCHAIN_H + +#include "language.h" +#include <qobject.h> +#include <qvaluelist.h> + +class FlowCode; +class Gpasm; +class Gpdasm; +class Gplib; +class Gplink; +class KTechlab; +class Microbe; +class PicProgrammer; +class ProcesOptions; +class SDCC; + +typedef QValueList<ProcessOptions> ProcessOptionsList; + +/** +@author Daniel Clarke +@author David Saxton +*/ +class ProcessChain : public QObject +{ + Q_OBJECT + public: + ProcessChain( ProcessOptions options, KTechlab *parent, const char *name = 0l ); + ~ProcessChain(); + + void setProcessOptions( ProcessOptions options ) { m_processOptions = options; } + + public slots: + /** + * Adds the output file to project if requested in the options, and opens + * the file in a code editor. Called to signal that a language in the last + * step of a compile has finished its compiling successfully. + */ + void slotFinishedCompile( Language * language ); + /** + * Call to compile a file of one type all the way to another type. This + * uses the ProcessOptions given in the constructor of this function, or + * later in setProcessOptions. + */ + void compile(); + + signals: + /** + * Emitted when compiling has successfully gone all the way through to the + * specified 'typeTo' + * @param options The ProcessOptions holding the output filename + * @see compile + */ + void successful(ProcessOptions options); + /** + * Convenience signal + */ + void successful(); + /** + * Emitted if not successful + */ + void failed(); + + protected: + FlowCode * flowCode(); + Gpasm * gpasm(); + Gpdasm * gpdasm(); + Gplib * gplib(); + Gplink * gplink(); + Microbe * microbe(); + PicProgrammer * picProgrammer(); + SDCC * sdcc(); + + int m_errorCount; + ProcessOptions m_processOptions; + KTechlab * m_pKTechlab; + + private: + FlowCode * m_pFlowCode; + Microbe * m_pMicrobe; + Gpasm * m_pGpasm; + Gpdasm * m_pGpdasm; + Gplib * m_pGplib; + Gplink * m_pGplink; + PicProgrammer * m_pPicProgrammer; + SDCC * m_pSDCC; +}; + + +class ProcessListChain : public QObject +{ + Q_OBJECT + + public: + ProcessListChain( ProcessOptionsList pol, KTechlab *parent, const char *name = 0l ); + + signals: + /** + * Emitted if successful + */ + void successful(); + /** + * Emitted if not successful + */ + void failed(); + + protected slots: + void slotProcessChainSuccessful(); + void slotProcessChainFailed(); + + protected: + ProcessOptionsList m_processOptionsList; + KTechlab * m_pKTechlab; +}; + +#endif diff --git a/src/languages/sdcc.cpp b/src/languages/sdcc.cpp new file mode 100644 index 0000000..553e974 --- /dev/null +++ b/src/languages/sdcc.cpp @@ -0,0 +1,205 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "asminfo.h" +#include "languagemanager.h" +#include "logview.h" +#include "microinfo.h" +#include "microlibrary.h" +#include "sdcc.h" +#include "src/core/ktlconfig.h" + +#include <klocale.h> +#include <kmessagebox.h> +#include <kprocess.h> + +SDCC::SDCC( ProcessChain * processChain, KTechlab * parent ) + : ExternalLanguage( processChain, parent, "SDCC" ) +{ + m_successfulMessage = i18n("*** Compilation successful ***"); + m_failedMessage = i18n("*** Compilation failed ***"); +} + + +SDCC::~SDCC() +{ +} + + +void SDCC::processInput( ProcessOptions options ) +{ + resetLanguageProcess(); + + MicroInfo * info = MicroLibrary::self()->microInfoWithID( options.m_picID ); + if (!info) + { + outputError( i18n("Could not find PIC with ID \"%1\".").arg(options.m_picID) ); + return; + } + + m_processOptions = options; + + *m_languageProcess << ("sdcc"); + + + //BEGIN Pass custom sdcc options +#define ARG(text,option) if ( KTLConfig::text() ) *m_languageProcess << ( QString("--%1").arg(option) ); + // General + ARG( sDCC_nostdlib, "nostdlib" ) + ARG( sDCC_nostdinc, "nostdinc" ) + ARG( sDCC_less_pedantic, "less-pedantic" ) + ARG( sDCC_std_c89, "std-c89" ) + ARG( sDCC_std_c99, "std-c99" ) + + // Code generation + ARG( sDCC_stack_auto, "stack-auto" ) + ARG( sDCC_int_long_reent, "int-long-reent" ) + ARG( sDCC_float_reent, "float-reent" ) + ARG( sDCC_fommit_frame_pointer, "fommit-frame-pointer" ) + ARG( sDCC_no_xinit_opt, "no-xinit-opt" ) + ARG( sDCC_all_callee_saves, "all-callee-saves" ) + + // Optimization + ARG( sDCC_nooverlay, "nooverlay" ) + ARG( sDCC_nogcse, "nogcse" ) + ARG( sDCC_nolabelopt, "nolabelopt" ) + ARG( sDCC_noinvariant, "noinvariant" ) + ARG( sDCC_noinduction, "noinduction" ) + ARG( sDCC_no_peep, "no-peep" ) + ARG( sDCC_noloopreverse, "noloopreverse" ) + ARG( sDCC_opt_code_size, "opt-code-size" ) + ARG( sDCC_opt_code_speed, "opt-code-speed" ) + ARG( sDCC_peep_asm, "peep-asm" ) + ARG( sDCC_nojtbound, "nojtbound" ) + + // PIC16 Specific + if ( info->instructionSet()->set() == AsmInfo::PIC16 ) + { + ARG( sDCC_nodefaultlibs, "nodefaultlibs" ) + ARG( sDCC_pno_banksel, "pno-banksel" ) + ARG( sDCC_pstack_model_large, "pstack-model=large" ) + ARG( sDCC_debug_xtra, "debug-xtra" ) + ARG( sDCC_denable_peeps, "denable-peeps" ) + ARG( sDCC_calltree, "calltree" ) + ARG( sDCC_fstack, "fstack" ) + ARG( sDCC_optimize_goto, "optimize-goto" ) + ARG( sDCC_optimize_cmp, "optimize-cmp" ) + ARG( sDCC_optimize_df, "optimize-df" ) + } +#undef ARG + + if ( !KTLConfig::miscSDCCOptions().isEmpty() ) + *m_languageProcess << ( KTLConfig::miscSDCCOptions() ); + //END Pass custom sdcc options + + + *m_languageProcess << ("--debug"); // Enable debugging symbol output + *m_languageProcess << ("-S"); // Compile only; do not assemble or link + + QString asmSwitch; + switch ( info->instructionSet()->set() ) + { + case AsmInfo::PIC12: + // Last time I checked, SDCC doesn't support Pic12, and probably never will, but whatever... + asmSwitch = "-mpic12"; + break; + case AsmInfo::PIC14: + asmSwitch = "-mpic14"; + break; + case AsmInfo::PIC16: + asmSwitch = "-mpic16"; + break; + } + + *m_languageProcess << (asmSwitch); + + *m_languageProcess << ( "-"+options.m_picID.lower() ); + + *m_languageProcess << ( options.inputFiles().first() ); + + *m_languageProcess << ("-o"); + *m_languageProcess << ( options.intermediaryOutput() ); + + if ( !start() ) + { + KMessageBox::sorry( LanguageManager::self()->logView(), i18n("Compilation failed. Please check you have sdcc installed.") ); + processInitFailed(); + return; + } +} + + +bool SDCC::isError( const QString &message ) const +{ + return false; +} + + +bool SDCC::isStderrOutputFatal( const QString & message ) const +{ + if ( message.startsWith("Processor:") ) + return false; + + return true; +} + + +bool SDCC::isWarning( const QString &message ) const +{ + return false; +} + + +ProcessOptions::ProcessPath::Path SDCC::outputPath( ProcessOptions::ProcessPath::Path inputPath ) const +{ + switch (inputPath) + { + case ProcessOptions::ProcessPath::C_AssemblyRelocatable: + return ProcessOptions::ProcessPath::None; + + case ProcessOptions::ProcessPath::C_Library: + return ProcessOptions::ProcessPath::AssemblyRelocatable_Library; + + case ProcessOptions::ProcessPath::C_Object: + return ProcessOptions::ProcessPath::AssemblyRelocatable_Object; + + case ProcessOptions::ProcessPath::C_PIC: + return ProcessOptions::ProcessPath::AssemblyAbsolute_PIC; + + case ProcessOptions::ProcessPath::C_Program: + return ProcessOptions::ProcessPath::AssemblyRelocatable_Program; + + case ProcessOptions::ProcessPath::AssemblyAbsolute_PIC: + case ProcessOptions::ProcessPath::AssemblyAbsolute_Program: + case ProcessOptions::ProcessPath::AssemblyRelocatable_Library: + case ProcessOptions::ProcessPath::AssemblyRelocatable_Object: + case ProcessOptions::ProcessPath::AssemblyRelocatable_PIC: + case ProcessOptions::ProcessPath::AssemblyRelocatable_Program: + case ProcessOptions::ProcessPath::FlowCode_AssemblyAbsolute: + case ProcessOptions::ProcessPath::FlowCode_Microbe: + case ProcessOptions::ProcessPath::FlowCode_PIC: + case ProcessOptions::ProcessPath::FlowCode_Program: + case ProcessOptions::ProcessPath::Microbe_AssemblyAbsolute: + case ProcessOptions::ProcessPath::Microbe_PIC: + case ProcessOptions::ProcessPath::Microbe_Program: + case ProcessOptions::ProcessPath::Object_Disassembly: + case ProcessOptions::ProcessPath::Object_Library: + case ProcessOptions::ProcessPath::Object_PIC: + case ProcessOptions::ProcessPath::Object_Program: + case ProcessOptions::ProcessPath::PIC_AssemblyAbsolute: + case ProcessOptions::ProcessPath::Program_Disassembly: + case ProcessOptions::ProcessPath::Program_PIC: + case ProcessOptions::ProcessPath::Invalid: + case ProcessOptions::ProcessPath::None: + return ProcessOptions::ProcessPath::Invalid; + } + + return ProcessOptions::ProcessPath::Invalid; +} diff --git a/src/languages/sdcc.h b/src/languages/sdcc.h new file mode 100644 index 0000000..a6de933 --- /dev/null +++ b/src/languages/sdcc.h @@ -0,0 +1,34 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef SDCC_H +#define SDCC_H + +#include <externallanguage.h> + +/** +@author David Saxton +*/ +class SDCC : public ExternalLanguage +{ + public: + SDCC( ProcessChain * processChain, KTechlab * parent ); + ~SDCC(); + + virtual void processInput( ProcessOptions options ); + virtual ProcessOptions::ProcessPath::Path outputPath( ProcessOptions::ProcessPath::Path inputPath ) const; + + protected: + virtual bool isError( const QString & message ) const; + virtual bool isWarning( const QString & message ) const; + virtual bool isStderrOutputFatal( const QString & message ) const; +}; + +#endif diff --git a/src/languages/sourceline.cpp b/src/languages/sourceline.cpp new file mode 100644 index 0000000..9ca89bb --- /dev/null +++ b/src/languages/sourceline.cpp @@ -0,0 +1,42 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "sourceline.h" + + +//BEGIN class SourceLine +SourceLine::SourceLine() + : m_line(-1) +{ +} + + +SourceLine::SourceLine( const QString & fileName, int line ) + : m_fileName(fileName), + m_line(line) +{ +} + + +bool SourceLine::operator < ( const SourceLine & sourceLine ) const +{ + return (m_fileName < sourceLine.fileName()) || + (m_fileName == sourceLine.fileName() && m_line < sourceLine.line()); +} + + +bool SourceLine::operator == ( const SourceLine & sourceLine ) const +{ + return (sourceLine.fileName() == fileName()) && + (sourceLine.line() == line()); +} +//END class SourceLine + + diff --git a/src/languages/sourceline.h b/src/languages/sourceline.h new file mode 100644 index 0000000..d2e774d --- /dev/null +++ b/src/languages/sourceline.h @@ -0,0 +1,42 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef SOURCELINE_H +#define SOURCELINE_H + +#include <qstring.h> + +/** +@author David Saxton + */ +class SourceLine +{ + public: + /** + * Creates an invalid source line (line is negative). + */ + SourceLine(); + SourceLine( const QString & fileName, int line ); + + QString fileName() const { return m_fileName; } + int line() const { return m_line; } + + bool isValid() const { return m_line >= 0; } + + bool operator < ( const SourceLine & sourceLine ) const; + bool operator == ( const SourceLine & sourceLine ) const; + + protected: + QString m_fileName; + int m_line; +}; + + +#endif diff --git a/src/libraryitem.cpp b/src/libraryitem.cpp new file mode 100644 index 0000000..f7ba013 --- /dev/null +++ b/src/libraryitem.cpp @@ -0,0 +1,81 @@ +/*************************************************************************** + * Copyright (C) 2003 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "libraryitem.h" + +#include <kiconloader.h> +#include <kicontheme.h> +#include <kstandarddirs.h> +#include <qimage.h> + +LibraryItem::LibraryItem( QStringList idList, const QString &name, const QString &category, QPixmap icon, Type type, createItemPtr _createItem ) +{ + m_idList = idList; + m_name = name; + m_category = category; + m_icon_full = icon; + m_type = type; + createItem = _createItem; + createIcon16(); +} + + +LibraryItem::LibraryItem( QStringList idList, const QString &name, const QString &category, const QString &iconName, Type type, createItemPtr _createItem ) +{ + m_idList = idList; + m_name = name; + m_category = category; + m_icon_full.load( locate( "appdata", "icons/"+iconName ) ); + m_type = type; + createItem = _createItem; + createIcon16(); +} + + +LibraryItem::LibraryItem( QStringList idList, const QString &name, const QString &category, Type type, createItemPtr _createItem ) +{ + m_idList = idList; + m_name = name; + m_category = category; + m_type = type; + createItem = _createItem; + createIcon16(); +} + + +LibraryItem::~LibraryItem() +{ +} + + +void LibraryItem::createIcon16() +{ + if ( m_icon_full.isNull() ) + m_icon_full = KGlobal::iconLoader()->loadIcon( "null", KIcon::Small ); + +// const int size = KIcon::SizeSmallMedium; +// const int size = 22; + const int size = 16; + + if ( m_icon_full.width() == size && m_icon_full.height() == size ) + { + m_icon_16 = m_icon_full; + return; + } + + QImage im = m_icon_full.convertToImage(); + im = im.smoothScale( size, size, QImage::ScaleMin ); + m_icon_16.convertFromImage(im); +} + +QString LibraryItem::activeID( ) const +{ + return m_idList.isEmpty() ? "" : m_idList[0]; +} diff --git a/src/libraryitem.h b/src/libraryitem.h new file mode 100644 index 0000000..1e6b429 --- /dev/null +++ b/src/libraryitem.h @@ -0,0 +1,69 @@ +/*************************************************************************** + * Copyright (C) 2003-2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef LIBRARYITEM_H +#define LIBRARYITEM_H + +#include "item.h" + +class QStringList; + +/** +This holds details of an item - id, name, category it is displayed in in its +respective item selector, icon, function pointers to creating the item, etc. +Normally each item will only pass one id, but some items have had their IDs +changed during the history of ktl, so passing a stringlist will take the first +ID as the "active" id, and the rest as IDs that will also be recognized, but +never displayed to the user. +@short Details of an Item +@author David Saxton +*/ +class LibraryItem +{ + public: + ~LibraryItem(); + + enum Type + { + lit_flowpart, + lit_component, + lit_mechanical, + lit_drawpart, + lit_subcircuit, + lit_other + }; + LibraryItem( QStringList idList, const QString &name, const QString &category, QPixmap icon, Type type, createItemPtr createItem ); + LibraryItem( QStringList idList, const QString &name, const QString &category, const QString &iconName, Type type, createItemPtr createItem ); + LibraryItem( QStringList idList, const QString &name, const QString &category, Type type, createItemPtr createItem ); + + QString activeID() const; + QStringList allIDs() const { return m_idList; } + QString name() const { return m_name; } + QString category() const { return m_category; } + QPixmap iconFull() const { return m_icon_full; } + QPixmap icon16() const { return m_icon_16; } + createItemPtr createItemFnPtr() const { return createItem; } + int type() const { return m_type; } + + protected: + void createIcon16(); + + private: + QStringList m_idList; + QString m_name; + QString m_category; + QPixmap m_icon_full; + QPixmap m_icon_16; + createItemPtr createItem; + int m_type; +}; +typedef QValueList<LibraryItem*> LibraryItemList; + +#endif diff --git a/src/mechanics/Makefile.am b/src/mechanics/Makefile.am new file mode 100644 index 0000000..0444358 --- /dev/null +++ b/src/mechanics/Makefile.am @@ -0,0 +1,8 @@ +INCLUDES = -I$(top_srcdir)/src -I$(top_srcdir)/src/drawparts $(all_includes) +METASOURCES = AUTO +noinst_HEADERS = mechanicsitem.h chassiscircular2.h mechanicssimulation.h \ + mechanicsdocument.h mechanicsgroup.h mechanicsview.h + +noinst_LTLIBRARIES = libmechanics.la +libmechanics_la_SOURCES = mechanicsitem.cpp chassiscircular2.cpp \ + mechanicssimulation.cpp mechanicsdocument.cpp mechanicsgroup.cpp mechanicsview.cpp diff --git a/src/mechanics/chassiscircular2.cpp b/src/mechanics/chassiscircular2.cpp new file mode 100644 index 0000000..91b9f0a --- /dev/null +++ b/src/mechanics/chassiscircular2.cpp @@ -0,0 +1,144 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "chassiscircular2.h" + +#include "libraryitem.h" + +#include <klocale.h> +#include <qpainter.h> +#include <qwmatrix.h> + +#include <algorithm> +#include <cmath> + +double normalizeAngle( double angle ); + + +Item* ChassisCircular2::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new ChassisCircular2( (MechanicsDocument*)itemDocument, newItem, id ); +} + + +LibraryItem* ChassisCircular2::libraryItem() +{ + return new LibraryItem( + QString("mech/chassis_circular_2"), + i18n("Circular 2-Wheel Chassis"), + i18n("Chassis'"), + "chassis.png", + LibraryItem::lit_mechanical, + ChassisCircular2::construct ); +} + + +ChassisCircular2::ChassisCircular2( MechanicsDocument *mechanicsDocument, bool newItem, const char *id ) + : MechanicsItem( mechanicsDocument, newItem, (id) ? id : "chassis_circular_2" ) +{ + m_name = i18n("Circular 2-Wheel Chassis"); + m_desc = i18n("A circular base with two wheels and a support point."); + + m_theta1 = 0.0; + m_theta2 = 0.0; + + QPointArray pa; + pa.makeEllipse( -25, -25, 50, 50 ); + QWMatrix m(4,0,0,4,0,0); + m.setTransformationMode( QWMatrix::Areas ); + pa = m.map(pa); + setItemPoints(pa); + + itemResized(); +} + + +ChassisCircular2::~ChassisCircular2() +{ +} + + +void ChassisCircular2::itemResized() +{ + const double w = sizeRect().width(); + const double h = sizeRect().height(); + + m_wheel1Pos = QRect( int(w/5), int(h/6), int(w/4), int(h/8) ); + m_wheel2Pos = QRect( int(w/5), int(5*h/6-h/8), int(w/4), int(h/8) ); +} + + +void ChassisCircular2::advance( int phase ) +{ + if ( phase != 1 ) + return; + + double speed1 = 60.; // pixels per second + double speed2 = 160.; // pixels per second + + m_theta1 = normalizeAngle( m_theta1 + (speed1/1000.)/m_wheel1Pos.width() ); + m_theta2 = normalizeAngle( m_theta2 + (speed2/1000.)/m_wheel2Pos.width() ); + + const double d1 = speed1/1000.; + const double d2 = speed2/1000.; + const double sep = m_wheel2Pos.center().y()-m_wheel1Pos.center().y(); + + double dtheta = std::atan( (d2-d1)/sep ); // Change in orientation of chassis + double moveAngle = absolutePosition().angle()+dtheta/2; + rotateBy(dtheta); + moveBy( ((d1+d2)/2.)*std::cos(moveAngle), ((d1+d2)/2.)*std::sin(moveAngle) ); +} + + +void ChassisCircular2::drawShape( QPainter &p ) +{ + const double _x = int(sizeRect().x() + x()); + const double _y = int(sizeRect().y() + y()); + const double w = sizeRect().width(); + const double h = sizeRect().height(); + + initPainter(p); + p.setBrush( QColor( 255, 246, 210 ) ); + QRect circleRect = sizeRect(); + circleRect.moveLeft( int(circleRect.left() + x()) ); + circleRect.moveTop( int(circleRect.top() + y()) ); + p.drawEllipse(circleRect); + + // Draw wheels + // TODO get this info from m_wheel1Pos and m_wheel2Pos + const double X = _x+(w/5); // Wheel's left pos + const double H = h/8; // Wheel's height + const double y1 = _y+(h/6); // Wheel 1 y-pos + const double y2 = _y+(5*h/6)-H; // Wheel 2 y-pos + + p.setPen( Qt::NoPen ); + const double stripeWidth = 5; + const double offset2 = 1 + int(m_theta1*m_wheel1Pos.width())%int(2*stripeWidth); + const double offset1 = 1 + int(m_theta2*m_wheel2Pos.width())%int(2*stripeWidth); + p.setBrush( QColor( 255, 232, 182 ) ); + for ( double i=-1; i<std::ceil(m_wheel1Pos.width()/stripeWidth); ++i ) + { + + p.setClipRect( QRect( int(_x+m_wheel1Pos.x()+2), int(_y+m_wheel1Pos.y()+2), int(m_wheel1Pos.width()-4), int(m_wheel1Pos.height()-4) ), QPainter::CoordPainter ); + p.drawRect( int(offset1+X + i*stripeWidth*2), int(y1+1), int(stripeWidth), int(H-2) ); + + p.setClipRect( QRect( int(_x+m_wheel2Pos.x()+2), int(_y+m_wheel2Pos.y()+2), int(m_wheel2Pos.width()-4), int(m_wheel2Pos.height()-4) ), QPainter::CoordPainter ); + p.drawRect( int(offset2+X + i*stripeWidth*2), int(y2+1), int(stripeWidth), int(H-2) ); + } + p.setClipping(false); + + p.setPen( Qt::black ); + p.setBrush( Qt::NoBrush ); + p.drawRoundRect( int(X), int(y1), int(w/4), int(H), 25, 50 ); + p.drawRoundRect( int(X), int(y2), int(w/4), int(H), 25, 50 ); + + + deinitPainter(p); +} diff --git a/src/mechanics/chassiscircular2.h b/src/mechanics/chassiscircular2.h new file mode 100644 index 0000000..1c8897a --- /dev/null +++ b/src/mechanics/chassiscircular2.h @@ -0,0 +1,42 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef CHASSISCIRCULAR2_H +#define CHASSISCIRCULAR2_H + +#include "mechanicsitem.h" + +/** +@short Mechanics Framework, circular base, two wheels +@author David Saxton +*/ +class ChassisCircular2 : public MechanicsItem +{ +public: + ChassisCircular2( MechanicsDocument *mechanicsDocument, bool newItem, const char *id = 0l ); + ~ChassisCircular2(); + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + + virtual void advance( int phase ); + +protected: + virtual void itemResized(); + void drawShape( QPainter &p ); + + double m_theta1; // Angle of rotation of wheel 1 (used for drawing) + double m_theta2; // Angle of rotation of wheel 1 (used for drawing) + + QRect m_wheel1Pos; // Position of first wheel, with respect to top left of item + QRect m_wheel2Pos; // Position of second wheel, with respect to top left of item +}; + +#endif diff --git a/src/mechanics/mechanicsdocument.cpp b/src/mechanics/mechanicsdocument.cpp new file mode 100644 index 0000000..0b2fa3b --- /dev/null +++ b/src/mechanics/mechanicsdocument.cpp @@ -0,0 +1,197 @@ +/*************************************************************************** + * Copyright (C) 2004-2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "canvasmanipulator.h" +#include "documentiface.h" +#include "drawpart.h" +#include "itemlibrary.h" +#include "mechanicsdocument.h" +#include "mechanicsitem.h" +#include "mechanicsgroup.h" +#include "mechanicssimulation.h" +#include "mechanicsview.h" + +#include <klocale.h> + +MechanicsDocument::MechanicsDocument( const QString &caption, KTechlab *ktechlab, const char *name ) + : ItemDocument( caption, ktechlab, name ) +{ + m_type = Document::dt_mechanics; + m_pDocumentIface = new MechanicsDocumentIface(this); + m_fileExtensionInfo = i18n("*.mechanics|Mechanics (*.mechanics)\n*|All Files"); + m_canvas->retune(128); + + m_selectList = new MechanicsGroup(this); + + m_cmManager->addManipulatorInfo( CMSelect::manipulatorInfo() ); + m_cmManager->addManipulatorInfo( CMDraw::manipulatorInfo() ); + m_cmManager->addManipulatorInfo( CMRightClick::manipulatorInfo() ); + m_cmManager->addManipulatorInfo( CMRepeatedItemAdd::manipulatorInfo() ); + m_cmManager->addManipulatorInfo( CMItemResize::manipulatorInfo() ); + m_cmManager->addManipulatorInfo( CMMechItemMove::manipulatorInfo() ); + m_mechanicsSimulation = new MechanicsSimulation(this); + requestStateSave(); +} + + +MechanicsDocument::~MechanicsDocument() +{ + m_bDeleted = true; + + // Remove all items from the canvas + selectAll(); + deleteSelection(); + delete m_mechanicsSimulation; +} + +View *MechanicsDocument::createView( ViewContainer *viewContainer, uint viewAreaId, const char *name ) +{ + ItemView *itemView = new MechanicsView( this, viewContainer, viewAreaId, name ); + handleNewView(itemView); + return itemView; +} + + +ItemGroup *MechanicsDocument::selectList() const +{ + return m_selectList; +} + + + + +bool MechanicsDocument::isValidItem( const QString &itemId ) +{ + return itemId.startsWith("mech/") || itemId.startsWith("dp/"); +} + + +bool MechanicsDocument::isValidItem( Item *item ) +{ + return item && ((dynamic_cast<MechanicsItem*>(item)) || (dynamic_cast<DrawPart*>(item))); +} + + +Item* MechanicsDocument::addItem( const QString &id, const QPoint &p, bool newItem ) +{ + if ( !isValidItem(id) ) + return 0l; + + Item *item = itemLibrary()->createItem( id, this, newItem ); + if (!item) + return 0L; + + QRect rect = item->boundingRect(); + + int dx = (int)(p.x())-rect.width()/2; + int dy = (int)(p.y())-rect.height()/2; + + if ( dx < 16 || dx > canvas()->width() ) dx = 16; + if ( dy < 16 || dy > canvas()->height() ) dy = 16; + + item->move( dx, dy ); + item->show(); + + registerItem(item); +// setModified(true); + requestStateSave(); + return item; +} + + +void MechanicsDocument::deleteSelection() +{ + // End whatever editing mode we are in, as we don't want to start editing + // something that is about to no longer exist... + m_cmManager->cancelCurrentManipulation(); + + if ( m_selectList->isEmpty() ) + return; + + // We nee to tell the selete items to remove themselves, and then + // pass the items that have add themselves to the delete list to the + // CommandAddItems command + + m_selectList->deleteAllItems(); + flushDeleteList(); + setModified(true); + + // We need to emit this so that property widgets etc... + // can clear themselves. + emit itemUnselected(0L); + requestStateSave(); +} + + +bool MechanicsDocument::registerItem( QCanvasItem *qcanvasItem ) +{ + return ItemDocument::registerItem(qcanvasItem); +} + + +void MechanicsDocument::appendDeleteList( QCanvasItem *qcanvasItem ) +{ + MechanicsItem *mechItem = dynamic_cast<MechanicsItem*>(qcanvasItem); + if ( !mechItem || m_itemDeleteList.contains(mechItem) ) { + return; + } + + m_itemDeleteList.append(mechItem); + m_itemList.remove(mechItem); + + disconnect( mechItem, SIGNAL(selected(Item*,bool)), this, SIGNAL(itemSelected(Item*)) ); + disconnect( mechItem, SIGNAL(unselected(Item*,bool)), this, SIGNAL(itemUnselected(Item*)) ); + + mechItem->removeItem(); +} + + +void MechanicsDocument::flushDeleteList() +{ + // Remove duplicate items in the delete list + ItemList::iterator end = m_itemDeleteList.end(); + for ( ItemList::iterator it = m_itemDeleteList.begin(); it != end; ++it ) + { + if ( *it && m_itemDeleteList.contains(*it) > 1 ) + *it = 0l; + } + m_itemDeleteList.remove(QGuardedPtr<Item>(0l)); + + end = m_itemDeleteList.end(); + for ( ItemList::iterator it = m_itemDeleteList.begin(); it != end; ++it ) + { + m_itemList.remove(*it); + (*it)->setCanvas(0l); + delete *it; + } +} + + +MechanicsItem* MechanicsDocument::mechanicsItemWithID( const QString &id ) +{ + return dynamic_cast<MechanicsItem*>(itemWithID(id)); +} + + +void MechanicsDocument::selectAll() +{ + const ItemList::iterator end = m_itemList.end(); + for ( ItemList::iterator it = m_itemList.begin(); it != end; ++it ) + { + select(*it); + } +} + + +void MechanicsDocument::copy() +{ +} + +#include "mechanicsdocument.moc" diff --git a/src/mechanics/mechanicsdocument.h b/src/mechanics/mechanicsdocument.h new file mode 100644 index 0000000..dfe454f --- /dev/null +++ b/src/mechanics/mechanicsdocument.h @@ -0,0 +1,66 @@ +/*************************************************************************** + * Copyright (C) 2004 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef MECHANICSDOCUMENT_H +#define MECHANICSDOCUMENT_H + +#include "itemdocument.h" + +class KTechlab; +class MechanicsGroup; +class MechanicsItem; +class MechanicsSimulation; + +typedef QValueList<MechanicsItem*> MechItemList; +typedef QValueList<MechanicsItem*> MechanicsItemList; + +/** +@author David Saxton +*/ +class MechanicsDocument : public ItemDocument +{ +Q_OBJECT +public: + MechanicsDocument( const QString &caption, KTechlab *ktechlab, const char *name = 0 ); + ~MechanicsDocument(); + + virtual View *createView( ViewContainer *viewContainer, uint viewAreaId, const char *name = 0l ); + + virtual bool isValidItem( const QString &itemId ); + virtual bool isValidItem( Item *item ); + + virtual void deleteSelection(); + virtual void copy(); + virtual void selectAll(); + virtual ItemGroup *selectList() const; + MechanicsItem *mechanicsItemWithID( const QString &id ); + virtual Item* addItem( const QString &id, const QPoint &p, bool newItem ); + /** + * Adds a QCanvasItem to the delete list to be deleted, when + * flushDeleteList() is called + */ + virtual void appendDeleteList( QCanvasItem *qcanvasItem ); + /** + * Permantly deletes all items that have been added to the delete list with + * the appendDeleteList( QCanvasItem *qcanvasItem ) function. + */ + virtual void flushDeleteList(); + /** + * Register an item with the ICNDocument. + */ + virtual bool registerItem( QCanvasItem *qcanvasItem ); + +protected: + MechanicsGroup *m_selectList; + MechanicsSimulation *m_mechanicsSimulation; +}; + + +#endif diff --git a/src/mechanics/mechanicsgroup.cpp b/src/mechanics/mechanicsgroup.cpp new file mode 100644 index 0000000..94fe703 --- /dev/null +++ b/src/mechanics/mechanicsgroup.cpp @@ -0,0 +1,243 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "mechanicsgroup.h" +#include "mechanicsitem.h" +#include "mechanicsdocument.h" + +MechanicsGroup::MechanicsGroup( MechanicsDocument *mechanicsDocument, const char *name ) + : ItemGroup( mechanicsDocument, name ) +{ + b_isRaised = false; +} + + +MechanicsGroup::~MechanicsGroup() +{ +} + + +bool MechanicsGroup::addItem( Item *item ) +{ + if ( !item || !item->canvas() || m_itemList.contains(item) ) { + return false; + } + + // Check that the item's parent isn't already selected + Item *parent = item->parentItem(); + while (parent) + { + if ( m_itemList.contains(parent) ) + return false; + parent = parent->parentItem(); + } + removeChildren(item); + + registerItem(item); + updateInfo(); + item->setSelected(true); + if ( MechanicsItem *mechanicsItem = dynamic_cast<MechanicsItem*>(item) ) + mechanicsItem->setRaised(b_isRaised); + emit itemAdded(item); + return true; +} + + +bool MechanicsGroup::removeItem( Item *item ) +{ + if ( !item || !m_itemList.contains(item) ) { + return false; + } + unregisterItem(item); + updateInfo(); + item->setSelected(false); + MechanicsItem *mechanicsItem = dynamic_cast<MechanicsItem*>(item); + if (mechanicsItem) + mechanicsItem->setRaised(false); + emit itemRemoved(item); + return true; +} + + +void MechanicsGroup::removeChildren( Item *item ) +{ + if (!item) + return; + + const ItemList children = item->children(); + const ItemList::const_iterator end = children.end(); + for ( ItemList::const_iterator it = children.begin(); it != end; ++it ) + { + removeChildren(*it); + removeItem(*it); + } +} + + +void MechanicsGroup::setRaised( bool isRaised ) +{ + b_isRaised = isRaised; + const ItemList::iterator end = m_itemList.end(); + for ( ItemList::iterator it = m_itemList.begin(); it != end; ++it ) + { + MechanicsItem *mechanicsItem = dynamic_cast<MechanicsItem*>((Item*)*it); + if (mechanicsItem) + mechanicsItem->setRaised(b_isRaised); + } +} + + +void MechanicsGroup::setSelectionMode( uint sm ) +{ + const ItemList::iterator end = m_itemList.end(); + for ( ItemList::iterator it = m_itemList.begin(); it != end; ++it ) + { + MechanicsItem *mechanicsItem = dynamic_cast<MechanicsItem*>((Item*)*it); + if (mechanicsItem) + mechanicsItem->setSelectionMode( (MechanicsItem::SelectionMode)sm ); + } +} + + +MechanicsItemList MechanicsGroup::extractMechanicsItems() const +{ + MechanicsItemList mechanicsItemList; + + const ItemList::const_iterator end = m_itemList.end(); + for ( ItemList::const_iterator it = m_itemList.begin(); it != end; ++it ) + { + MechanicsItem *mechanicsItem = dynamic_cast<MechanicsItem*>((Item*)*it); + if (mechanicsItem) + mechanicsItemList.append(mechanicsItem); + } + + return mechanicsItemList; +} + + +MechanicsItemList MechanicsGroup::toplevelMechItemList() const +{ + MechanicsItemList toplevel; + + MechanicsItemList mechItemList = extractMechanicsItems(); + + const MechanicsItemList::const_iterator end = mechItemList.end(); + for ( MechanicsItemList::const_iterator it = mechItemList.begin(); it != end; ++it ) + { + MechanicsItem* parent = *it; + while (parent) + { + if ( !parent->parentItem() && !toplevel.contains(parent) ) + toplevel.append(parent); + + parent = dynamic_cast<MechanicsItem*>(parent->parentItem()); + } + } + + return toplevel; +} + + +void MechanicsGroup::setSelected( bool sel ) +{ + const ItemList::iterator end = m_itemList.end(); + for ( ItemList::iterator it = m_itemList.begin(); it != end; ++it ) + { + if (*it && (*it)->isSelected() != sel ) { + (*it)->setSelected(sel); + } + } +} + + +bool MechanicsGroup::addQCanvasItem( QCanvasItem* item ) +{ + return addItem( dynamic_cast<Item*>(item) ); +} + +bool MechanicsGroup::contains(QCanvasItem* item) const +{ + return m_itemList.contains(dynamic_cast<Item*>(item)); +} + + +void MechanicsGroup::deleteAllItems() +{ + const ItemList::iterator end = m_itemList.end(); + for ( ItemList::iterator it = m_itemList.begin(); it != end; ++it ) + { + if (*it) + (*it)->removeItem(); + } + + removeAllItems(); +} + +void MechanicsGroup::mergeGroup(ItemGroup* itemGroup) +{ + MechanicsGroup *group = dynamic_cast<MechanicsGroup*>(itemGroup); + if (!group) { + return; + } + + const ItemList items = group->items(); + const ItemList::const_iterator end = items.end(); + for ( ItemList::const_iterator it = items.begin(); it != end; ++it ) + { + addItem(*it); + } +} + +void MechanicsGroup::removeAllItems() +{ + while ( !m_itemList.isEmpty() ) + removeItem(m_itemList.first()); +} + +void MechanicsGroup::removeQCanvasItem(QCanvasItem* item) +{ + removeItem(dynamic_cast<Item*>(item)); +} + + +void MechanicsGroup::setItems(QCanvasItemList list) +{ + { + ItemList removeList; + const ItemList::iterator end = m_itemList.end(); + for ( ItemList::iterator it = m_itemList.begin(); it != end; ++it ) + { + if ( !list.contains(*it) ) { + removeList.append(*it); + } + } + const ItemList::iterator rend = removeList.end(); + for ( ItemList::iterator it = removeList.begin(); it != rend; ++it ) + { + removeItem(*it); + (*it)->setSelected(false); + } + } + + const QCanvasItemList::iterator end = list.end(); + for ( QCanvasItemList::iterator it = list.begin(); it != end; ++it ) + { + // We don't need to check that we've already got the item as it will + // be checked in the function call + addQCanvasItem(*it); + } +} + + +void MechanicsGroup::updateInfo() +{ +} + +#include "mechanicsgroup.moc" diff --git a/src/mechanics/mechanicsgroup.h b/src/mechanics/mechanicsgroup.h new file mode 100644 index 0000000..6b49966 --- /dev/null +++ b/src/mechanics/mechanicsgroup.h @@ -0,0 +1,72 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef MECHANICSGROUP_H +#define MECHANICSGROUP_H + +#include <itemgroup.h> + + +class MechanicsItem; +class MechanicsDocument; +typedef QValueList<MechanicsItem*> MechanicsItemList; + +/** +@author David Saxton +*/ +class MechanicsGroup : public ItemGroup +{ +Q_OBJECT +public: + MechanicsGroup( MechanicsDocument *mechanicsDocument, const char *name = 0); + ~MechanicsGroup(); + + /** + * Returns a list of top-level mechanics items only + */ + MechanicsItemList toplevelMechItemList() const; + /** + * Sets the selection mode of all MechanicsItems in the group + */ + void setSelectionMode( uint sm ); + /** + * "Raises" (increases the z value of) the selected group of items + */ + void setRaised( bool isRaised ); + /** + * Removes all the children of the given item from the group + */ + void removeChildren( Item *item ); + bool addItem( Item *item ); + bool removeItem( Item *item ); + virtual bool addQCanvasItem(QCanvasItem* item); + virtual bool contains(QCanvasItem* item) const; + virtual uint count() const { return itemCount(); } + virtual void deleteAllItems(); + virtual void mergeGroup(ItemGroup* group); + virtual void removeAllItems(); + virtual void removeQCanvasItem(QCanvasItem* item); + virtual void setItems(QCanvasItemList list); + /** + * Sets the selected state of all items in the group + */ + virtual void setSelected( bool sel ); + /** + * Extracts the mechanics items from the item list + */ + MechanicsItemList extractMechanicsItems() const; + +protected: + void updateInfo(); + + bool b_isRaised; +}; + +#endif diff --git a/src/mechanics/mechanicsitem.cpp b/src/mechanics/mechanicsitem.cpp new file mode 100644 index 0000000..acf2239 --- /dev/null +++ b/src/mechanics/mechanicsitem.cpp @@ -0,0 +1,433 @@ +/*************************************************************************** + * Copyright (C) 2004-2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "itemdocumentdata.h" +#include "mechanicsitem.h" +#include "mechanicsdocument.h" + +#include <kdebug.h> +#include <klocale.h> +#include <qbitarray.h> +#include <qpainter.h> +#include <qwmatrix.h> +#include <cmath> + +/** +@returns an angle between 0 and 2 pi +*/ +double normalizeAngle( double angle ) +{ + if ( angle < 0 ) + angle += 6.2832*(std::ceil(-angle)); + + return angle - 6.2832*std::floor(angle/6.2832); +} + +MechanicsItem::MechanicsItem( MechanicsDocument *mechanicsDocument, bool newItem, const QString &id ) + : Item( mechanicsDocument, newItem, id ) +{ + p_mechanicsDocument = mechanicsDocument; + m_selectionMode = MechanicsItem::sm_move; + + createProperty( "mass", Variant::Type::Double ); + property("mass")->setCaption( i18n("Mass") ); + property("mass")->setUnit("g"); + property("mass")->setValue(10.0); + property("mass")->setMinValue(1e-3); + property("mass")->setMaxValue(1e12); + property("mass")->setAdvanced(true); + + createProperty( "moi", Variant::Type::Double ); + property("moi")->setCaption( i18n("Moment of Inertia") ); + property("moi")->setUnit("gm"); + property("moi")->setValue(0.01); + property("moi")->setMinValue(1e-3); + property("moi")->setMaxValue(1e12); + property("moi")->setAdvanced(true); + + setZ(ItemDocument::Z::Item); + setAnimated(true); + p_mechanicsDocument->registerItem(this); +} + + +MechanicsItem::~MechanicsItem() +{ +} + + +int MechanicsItem::rtti() const +{ + return ItemDocument::RTTI::MechanicsItem; +} + + +void MechanicsItem::setSelectionMode( SelectionMode sm ) +{ + if ( sm == m_selectionMode ) + return; + + m_selectionMode = sm; +} + + +void MechanicsItem::setSelected( bool yes ) +{ + if ( yes == isSelected() ) + return; + + if (!yes) + // Reset the selection mode + m_selectionMode = MechanicsItem::sm_resize; + + Item::setSelected(yes); +} + + +void MechanicsItem::dataChanged() +{ + Item::dataChanged(); + m_mechanicsInfo.mass = dataDouble("mass"); + m_mechanicsInfo.momentOfInertia = dataDouble("moi"); + updateMechanicsInfoCombined(); +} + + +PositionInfo MechanicsItem::absolutePosition() const +{ + MechanicsItem *parentMechItem = dynamic_cast<MechanicsItem*>((Item*)(p_parentItem)); + if (parentMechItem) + return parentMechItem->absolutePosition() + m_relativePosition; + + return m_relativePosition; +} + + +void MechanicsItem::reparented( Item *oldItem, Item *newItem ) +{ + MechanicsItem *oldMechItem = dynamic_cast<MechanicsItem*>(oldItem); + MechanicsItem *newMechItem = dynamic_cast<MechanicsItem*>(newItem); + + if (oldMechItem) + { + m_relativePosition = oldMechItem->absolutePosition() + m_relativePosition; + disconnect( oldMechItem, SIGNAL(moved()), this, SLOT(parentMoved()) ); + } + + if (newMechItem) + { + m_relativePosition = m_relativePosition - newMechItem->absolutePosition(); + connect( newMechItem, SIGNAL(moved()), this, SLOT(parentMoved()) ); + } + + updateCanvasPoints(); +} + + +void MechanicsItem::childAdded( Item *child ) +{ + MechanicsItem *mechItem = dynamic_cast<MechanicsItem*>(child); + if (!mechItem) + return; + + connect( mechItem, SIGNAL(updateMechanicsInfoCombined()), this, SLOT(childMoved()) ); + updateMechanicsInfoCombined(); +} + + +void MechanicsItem::childRemoved( Item *child ) +{ + MechanicsItem *mechItem = dynamic_cast<MechanicsItem*>(child); + if (!mechItem) + return; + + disconnect( mechItem, SIGNAL(updateMechanicsInfoCombined()), this, SLOT(childMoved()) ); + updateMechanicsInfoCombined(); +} + + +void MechanicsItem::parentMoved() +{ + PositionInfo absPos = absolutePosition(); + Item::moveBy( absPos.x() - x(), absPos.y() - y() ); + updateCanvasPoints(); + emit moved(); +} + + +void MechanicsItem::updateCanvasPoints() +{ + const QRect ipbr = m_itemPoints.boundingRect(); + + double scalex = double(m_sizeRect.width()) / double(ipbr.width()); + double scaley = double(m_sizeRect.height()) / double(ipbr.height()); + + PositionInfo abs = absolutePosition(); + + QWMatrix m; + m.rotate(abs.angle()*57.29577951308232); + m.translate( m_sizeRect.left(), m_sizeRect.top() ); + m.scale( scalex, scaley ); + m.translate( -int(ipbr.left()), -int(ipbr.top()) ); + setPoints( m.map(m_itemPoints) ); + + QRect tempt = m.mapRect(ipbr); +} + + +void MechanicsItem::rotateBy( double dtheta ) +{ + m_relativePosition.rotate(dtheta); + updateCanvasPoints(); + updateMechanicsInfoCombined(); + emit moved(); +} + + +void MechanicsItem::moveBy( double dx, double dy ) +{ + m_relativePosition.translate( dx, dy ); + Item::moveBy( m_relativePosition.x() - x(), m_relativePosition.y() - y() ); + emit moved(); +} + + +void MechanicsItem::updateMechanicsInfoCombined() +{ + m_mechanicsInfoCombined = m_mechanicsInfo; + + double mass_x = 0.; + double mass_y = 0.; + + const ItemList::const_iterator end = m_children.end(); + for ( ItemList::const_iterator it = m_children.begin(); it != end; ++it ) + { + MechanicsItem *child = dynamic_cast<MechanicsItem*>((Item*)*it); + if (child) + { + CombinedMechanicsInfo *childInfo = child->mechanicsInfoCombined(); + const PositionInfo relativeChildPosition = child->relativePosition(); + + double mass = childInfo->mass; +// double angle = relativeChildPosition.angle(); + double dx = relativeChildPosition.x() /*+ cos(angle)*childInfo->m_x - sin(angle)*childInfo->m_y*/; + double dy = relativeChildPosition.y() /*+ sin(angle)*childInfo->m_x + cos(angle)*childInfo->m_y*/; + + m_mechanicsInfoCombined.mass += mass; + mass_x += mass * dx; + mass_y += mass * dy; + + double length_squared = dx*dx + dy*dy; + m_mechanicsInfoCombined.momentOfInertia += length_squared * childInfo->momentOfInertia; + } + } + + m_mechanicsInfoCombined.x = mass_x / m_mechanicsInfoCombined.mass; + m_mechanicsInfoCombined.y = mass_y / m_mechanicsInfoCombined.mass; +} + + +ItemData MechanicsItem::itemData() const +{ + ItemData itemData = Item::itemData(); + itemData.angleDegrees = m_relativePosition.angle()*57.29577951308232; + return itemData; +} + + +bool MechanicsItem::mousePressEvent( const EventInfo &eventInfo ) +{ + Q_UNUSED(eventInfo); + return false; +} + + +bool MechanicsItem::mouseReleaseEvent( const EventInfo &eventInfo ) +{ + Q_UNUSED(eventInfo); + return false; +} + + +bool MechanicsItem::mouseDoubleClickEvent( const EventInfo &eventInfo ) +{ + Q_UNUSED(eventInfo); + return false; +} + + +bool MechanicsItem::mouseMoveEvent( const EventInfo &eventInfo ) +{ + Q_UNUSED(eventInfo); + return false; +} + + +bool MechanicsItem::wheelEvent( const EventInfo &eventInfo ) +{ + Q_UNUSED(eventInfo); + return false; +} + + +void MechanicsItem::enterEvent() +{ +} + + +void MechanicsItem::leaveEvent() +{ +} + + +QRect MechanicsItem::maxInnerRectangle( const QRect &outerRect ) const +{ + QRect normalizedOuterRect = outerRect.normalize(); + const double LEFT = normalizedOuterRect.left(); + const double TOP = normalizedOuterRect.top(); + const double X = normalizedOuterRect.width(); + const double Y = normalizedOuterRect.height(); + const double a = normalizeAngle(absolutePosition().angle()); + + double left; + double top; + double width; + double height; + +// if ( can change width/height ratio ) + { + double x1 = X*std::cos(a) - Y*std::sin(a); + double y1 = X*std::sin(a) + Y*std::cos(a); + double x2 = X*std::cos(a); + double y2 = X*std::sin(a); + double x3 = -Y*std::sin(a); + double y3 = Y*std::cos(a); + + double xbig;/* = std::max( std::abs(x2-x3), std::abs(x1) );*/ + double ybig;/* = std::max( std::abs(y2-y3), std::abs(y1) );*/ + if ( (a - floor(a/6.2832)*6.2832) < 3.1416 ) + { + xbig = std::abs(x3-x2); + ybig = std::abs(y1); + } + else + { + xbig = std::abs(x1); + ybig = std::abs(y3-y2); + } + + width = X*(X/xbig); + height = Y*(Y/ybig); + + top = -std::sin(a) * (LEFT + width*std::sin(a)) + std::cos(a)*TOP; + left = std::cos(a) * (LEFT + width*std::sin(a)) + std::sin(a)*TOP; + } + + return QRect( int(left), int(top), int(width), int(height) ); +} + + +void MechanicsItem::initPainter( QPainter &p ) +{ + PositionInfo absPos = absolutePosition(); + p.translate( absPos.x(), absPos.y() ); + // 57.29577951308232 is the number of degrees per radian. + p.rotate( absPos.angle()*57.29577951308232 ); + p.translate( -absPos.x(), -absPos.y() ); +} + + +void MechanicsItem::deinitPainter( QPainter &p ) +{ + PositionInfo absPos = absolutePosition(); + p.translate( absPos.x(), absPos.y() ); + // 57.29577951308232 is the number of degrees per radian. + p.rotate( -absPos.angle()*57.29577951308232 ); + p.translate( -absPos.x(), -absPos.y() ); +} + + + + + +PositionInfo::PositionInfo() +{ + reset(); +} + + +const PositionInfo PositionInfo::operator+( const PositionInfo &info ) +{ + // Copy the child to a new position + PositionInfo newInfo = info; + + // Translate the newInfo by our translation amount + newInfo.translate( x(), y() ); + + // Rotate the child about us + newInfo.rotateAboutPoint( x(), y(), angle() ); + + return newInfo; +} + + +const PositionInfo PositionInfo::operator-( const PositionInfo &info ) +{ + + PositionInfo newInfo = *this; + + newInfo.translate( -info.x(), -info.y() ); + newInfo.rotate( -info.angle() ); + + return newInfo; +} + + +void PositionInfo::rotateAboutPoint( double x, double y, double angle ) +{ + m_angle += angle; + + double newx = x + (m_x-x)*std::cos(angle) - (m_y-y)*std::sin(angle); + double newy = y + (m_x-x)*std::sin(angle) + (m_y-y)*std::cos(angle); + + m_x = newx; + m_y = newy; +} + + +void PositionInfo::reset() +{ + m_x = 0.; + m_y = 0.; + m_angle = 0.; +} + + + +MechanicsInfo::MechanicsInfo() +{ + mass = 0.; + momentOfInertia = 0.; +} +CombinedMechanicsInfo::CombinedMechanicsInfo() + : MechanicsInfo() +{ + x = 0.; + y = 0.; +} +CombinedMechanicsInfo::CombinedMechanicsInfo( const MechanicsInfo &info ) + : MechanicsInfo(info) +{ + x = 0.; + y = 0.; +} + + +#include "mechanicsitem.moc" diff --git a/src/mechanics/mechanicsitem.h b/src/mechanics/mechanicsitem.h new file mode 100644 index 0000000..db500fc --- /dev/null +++ b/src/mechanics/mechanicsitem.h @@ -0,0 +1,237 @@ +/*************************************************************************** + * Copyright (C) 2004-2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef MECHANICSITEM_H +#define MECHANICSITEM_H + +#include <item.h> +#include <qvaluelist.h> + +class LibraryItem; +class MechanicsItem; +// class MechanicsItemOverlayItem; +class MechanicsDocument; +typedef QValueList<MechanicsItem*> MechanicsItemList; + +/** +@short Stores mass, moment of inertia +@author David Saxton +*/ +class MechanicsInfo +{ +public: + MechanicsInfo(); + + double mass; // Mass + double momentOfInertia; // Moment of inertia +}; + +class CombinedMechanicsInfo : public MechanicsInfo +{ +public: + CombinedMechanicsInfo(); + CombinedMechanicsInfo( const MechanicsInfo &info ); + + double x; // X coordinate of center of mass + double y; // Y coordinate of center of mass +}; + +/** +@short Stores a position and orientation +@author David Saxton +*/ +class PositionInfo +{ +public: + PositionInfo(); + /** + * Adds together two positions: for this=PARENT +(CHILD), the new position + * is formed by translating this position by that of the CHILDs + * translation, and then rotating everything about the center of this item + */ + const PositionInfo operator+( const PositionInfo &info ); + /** + * Not quite the inverse of operator+. Subtracts the given position info + * as if it was applied before this current info. + */ + const PositionInfo operator-( const PositionInfo &info ); + /** + * x position (0 is left) + */ + double x() const { return m_x; } + /** + * y position (0 is top) + */ + double y() const { return m_y; } + /** + * Angle in radians, positive direction is anticlockwise + */ + double angle() const { return m_angle; } + /** + * Sets the x-position + */ + void setX( double x ) { m_x = x; } + /** + * Sets the y-position + */ + void setY( double y ) { m_y = y; } + /** + * Sets the angle + */ + void setAngle( double angle ) { m_angle = angle; } + /** + * Adds (x,y) to the current position + */ + void translate( double dx, const double dy ) { m_x += dx; m_y += dy; } + /** + * Rotates anticlockwise by the given amount (in radians) + */ + void rotate( double angle ) { m_angle += angle; } + /** + * Resets the position to (0,0), and the orientation to 0 + */ + void reset(); + /** + * Rotates the current position about the given point through the given + * angle in radians anticlockwise. This will change the position and + * orientation. + */ + void rotateAboutPoint( double x, double y, double angle ); + +protected: + double m_x; + double m_y; + double m_angle; +}; + + +/** +@author David Saxton +*/ +class MechanicsItem : public Item +{ +Q_OBJECT +public: + MechanicsItem( MechanicsDocument *mechanicsDocument, bool newItem, const QString &id ); + virtual ~MechanicsItem(); + + enum SelectionMode + { + sm_move, + sm_resize, + sm_rotate + }; + /** + * Returns the run-time identifier for the MechanicsItem + */ + int rtti() const; + /** + * Sets the selection mode (sm_resize or sm_rotate). Note that setSelected + * also needs to be called to select the item. + */ + void setSelectionMode( SelectionMode sm ); + virtual void setSelected( bool yes ); + /** + * @returns the selection mode + */ + SelectionMode selectionMode() const { return m_selectionMode; } + /** + * Move the MechanicsItem by the given amount + */ + virtual void moveBy( double dx, double dy ); + /** + * Returns the absolute position on the canvas + */ + PositionInfo absolutePosition() const; + /** + * Returns the position relative to the parent item (or the absolute + * position if there is no parent item) + */ + PositionInfo relativePosition() const { return m_relativePosition; } + /** + * Returns the mechanics info for this item (so not taking into account that + * of attached children) + */ + MechanicsInfo *mechanicsInfo() { return &m_mechanicsInfo; } + /** + * Returns the combined mechanics info for this item (which takes into + * account that of attached children). + */ + CombinedMechanicsInfo *mechanicsInfoCombined() { return &m_mechanicsInfoCombined; } + /** + * Returns the rectangle that can legitimately fit inside the given bounding + * rectangle, given this items current rotation. Legitimately means that + * whether this item is allowed to be distorted, inverted, resized, etc. + */ + QRect maxInnerRectangle( const QRect &outerRect ) const; + + virtual ItemData itemData() const; + + virtual bool mousePressEvent( const EventInfo &eventInfo ); + virtual bool mouseReleaseEvent( const EventInfo &eventInfo ); + virtual bool mouseDoubleClickEvent ( const EventInfo &eventInfo ); + virtual bool mouseMoveEvent( const EventInfo &eventInfo ); + virtual bool wheelEvent( const EventInfo &eventInfo ); + virtual void enterEvent(); + virtual void leaveEvent(); + +public slots: + /** + * Rotate the item by the given amount (in radians) + */ + void rotateBy( double dtheta ); + void parentMoved(); + +signals: + /** + * Emitted when this item moves (translates or rotates) + */ + void moved(); + +protected slots: + /** + * Recalculate the combined mechanics info (e.g. when mass is changed, or child added) + */ + void updateMechanicsInfoCombined(); + +protected: + virtual void reparented( Item *oldItem, Item *newItem ); + virtual void childAdded( Item *child ); + virtual void childRemoved( Item *child ); + /** + * Called when this item is resized, so that sub classes can do whatever + */ + virtual void itemResized() {}; + /** + * Sets the correct orientation on the painter + */ + void initPainter( QPainter &p ); + /** + * *Must* be called after calling initPainter, if initPainter was called + */ + void deinitPainter( QPainter &p ); + virtual void dataChanged(); + virtual void itemPointsChanged() { updateCanvasPoints(); } + /** + * Calculates the setPoints required from the current m_itemPoints and the + * current position / angle + */ + void updateCanvasPoints(); + + MechanicsDocument *p_mechanicsDocument; + PositionInfo m_relativePosition; // Absolution position if not attached to a parent item, or otherwise relative to parent item + MechanicsInfo m_mechanicsInfo; + CombinedMechanicsInfo m_mechanicsInfoCombined; + +private: + SelectionMode m_selectionMode; +}; + +#endif diff --git a/src/mechanics/mechanicssimulation.cpp b/src/mechanics/mechanicssimulation.cpp new file mode 100644 index 0000000..d5ec98d --- /dev/null +++ b/src/mechanics/mechanicssimulation.cpp @@ -0,0 +1,139 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "mechanicsdocument.h" +#include "mechanicsitem.h" +#include "mechanicssimulation.h" + +#include <cmath> +#include <qtimer.h> + +MechanicsSimulation::MechanicsSimulation( MechanicsDocument *mechanicsDocument ) + : QObject(mechanicsDocument) +{ + p_mechanicsDocument = mechanicsDocument; + m_advanceTmr = new QTimer(this); + connect( m_advanceTmr, SIGNAL(timeout()), this, SLOT(slotAdvance()) ); + m_advanceTmr->start(1); +} + + +MechanicsSimulation::~MechanicsSimulation() +{ +} + + +void MechanicsSimulation::slotAdvance() +{ + if ( p_mechanicsDocument && p_mechanicsDocument->canvas() ) + p_mechanicsDocument->canvas()->advance(); +} + + +RigidBody::RigidBody( MechanicsDocument *mechanicsDocument ) +{ + p_mechanicsDocument = mechanicsDocument; + p_overallParent = 0l; +} + + +RigidBody::~RigidBody() +{ +} + + +bool RigidBody::addMechanicsItem( MechanicsItem *item ) +{ + if ( !item || m_mechanicsItemList.contains(item) ) + return false; + + m_mechanicsItemList.append(item); + findOverallParent(); + return true; +} + + +void RigidBody::moveBy( double dx, double dy ) +{ + if (overallParent()) + overallParent()->moveBy( dx, dy ); +} + + +void RigidBody::rotateBy( double dtheta ) +{ + if (overallParent()) + overallParent()->rotateBy(dtheta); +} + + +bool RigidBody::findOverallParent() +{ + p_overallParent = 0l; + if ( m_mechanicsItemList.isEmpty() ) + return false; + + m_mechanicsItemList.remove(0l); + + const MechanicsItemList::iterator end = m_mechanicsItemList.end(); + for ( MechanicsItemList::iterator it = m_mechanicsItemList.begin(); it != end; ++it ) + { + MechanicsItem *parentItem = *it; + MechanicsItem *parentCandidate = dynamic_cast<MechanicsItem*>((*it)->parentItem()); + + while (parentCandidate) + { + parentItem = parentCandidate; + parentCandidate = dynamic_cast<MechanicsItem*>(parentItem->parentItem()); + } + + if ( !p_overallParent ) + // Must be the first item to test + p_overallParent = parentItem; + + if ( p_overallParent != parentItem ) + { + p_overallParent = 0l; + return false; + } + } + return true; +} + + +void RigidBody::updateRigidBodyInfo() +{ + if (!p_overallParent) + return; + + m_mass = p_overallParent->mechanicsInfoCombined()->mass; + m_momentOfInertia = p_overallParent->mechanicsInfoCombined()->momentOfInertia; +} + + +Vector2D::Vector2D() +{ + x = 0.; + y = 0.; +} + +double Vector2D::length() const +{ + return std::sqrt( x*x + y*y ); +} + +RigidBodyState::RigidBodyState() +{ + angularMomentum = 0.; +} + + + +#include "mechanicssimulation.moc" diff --git a/src/mechanics/mechanicssimulation.h b/src/mechanics/mechanicssimulation.h new file mode 100644 index 0000000..19b474b --- /dev/null +++ b/src/mechanics/mechanicssimulation.h @@ -0,0 +1,133 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef MECHANICSSIMULATION_H +#define MECHANICSSIMULATION_H + +#include <qguardedptr.h> +#include <qobject.h> +#include <qvaluelist.h> + +class MechanicsItem; +class MechanicsDocument; +typedef QValueList<MechanicsItem*> MechanicsItemList; + + +/** +@short 2 dimensional vector with associated length functions et al +@author David Saxton +*/ +class Vector2D +{ +public: + Vector2D(); + + double length() const; + double lengthSquared() const { return x*x + y*y; } + + double x; + double y; +}; + + +/** +@short State of a rigid body, in an inertial MechanicsDocument frame +@author David Saxton +*/ +class RigidBodyState +{ +public: + RigidBodyState(); + + Vector2D linearMomentum; + double angularMomentum; + Vector2D position; // Position of center of mass +}; + + +/** +@author David Saxton +*/ +class MechanicsSimulation : public QObject +{ +Q_OBJECT +public: + MechanicsSimulation( MechanicsDocument *mechanicsDocument ); + ~MechanicsSimulation(); + + MechanicsDocument* mechanicsDocument() const { return p_mechanicsDocument; } + +protected slots: + void slotAdvance(); + +protected: + QGuardedPtr<MechanicsDocument> p_mechanicsDocument; + QTimer *m_advanceTmr; +}; + + +/** +Rigid body with mass, inertia, etc. Collection of mechanics items with +functionality for moving them about, rotating, etc. Only one mother-parent +(has no parent itself, all other items are descendents) allowed. +@short Rigid body, handles MechanicsItems +@author David Saxton +*/ +class RigidBody +{ +public: + RigidBody( MechanicsDocument *mechanicsDocument ); + ~RigidBody(); + + /** + * + */ + void advance( int phase, double delta ); + /** + * Add the MechanicsItem to the entity. + * @returns true iff successful in adding + */ + bool addMechanicsItem( MechanicsItem *item ); + /** + * Pointer to the mother MechanicsItem. + */ + MechanicsItem *overallParent() const { return p_overallParent; } + /** + * Updates the mass and the moment of inertia info + */ + void updateRigidBodyInfo(); + +protected: + /** + * Attempt to find the overall parent. + * @returns false iff unsucessful (including if there are no MechanicsItems present) + */ + bool findOverallParent(); + /** + * Move the set of MechanicsItems by the given amount + */ + void moveBy( double dx, double dy ); + /** + * Rotate the set of MechanicsItems by the given amount about the center of + * mass. + * @param angle Rotate amount in radians + */ + void rotateBy( double dtheta ); + + MechanicsItemList m_mechanicsItemList; + MechanicsItem *p_overallParent; + MechanicsDocument *p_mechanicsDocument; + + RigidBodyState m_rigidBodyState; + double m_mass; + double m_momentOfInertia; +}; + +#endif diff --git a/src/mechanics/mechanicsview.cpp b/src/mechanics/mechanicsview.cpp new file mode 100644 index 0000000..d49c66a --- /dev/null +++ b/src/mechanics/mechanicsview.cpp @@ -0,0 +1,42 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "mechanicsdocument.h" +#include "mechanicsview.h" +#include "viewiface.h" + + +MechanicsView::MechanicsView( MechanicsDocument *mechanicsDocument, ViewContainer *viewContainer, uint viewAreaId, const char *name ) + : ItemView( mechanicsDocument, viewContainer, viewAreaId, name ) +{ + setXMLFile( "ktechlabmechanicsui.rc", true ); + m_pViewIface = new MechanicsViewIface(this); +} + + +MechanicsView::~MechanicsView() +{ + delete m_pViewIface; + m_pViewIface = 0l; +} + + + + +void MechanicsView::dragEnterEvent( QDragEnterEvent * e ) +{ + ItemView::dragEnterEvent(e); + if ( e->isAccepted() ) + return; + + e->accept( e->provides("ktechlab/mechanical") ); +} + +#include "mechanicsview.moc" diff --git a/src/mechanics/mechanicsview.h b/src/mechanics/mechanicsview.h new file mode 100644 index 0000000..f787b1c --- /dev/null +++ b/src/mechanics/mechanicsview.h @@ -0,0 +1,33 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef MECHANICSVIEW_H +#define MECHANICSVIEW_H + +#include <itemview.h> + +class MechanicsDocument; + +/** +@author David Saxton +*/ +class MechanicsView : public ItemView +{ + Q_OBJECT + public: + MechanicsView( MechanicsDocument *mechanicsDocument, ViewContainer *viewContainer, uint viewAreaId, const char *name = 0l ); + ~MechanicsView(); + + protected: + virtual void dragEnterEvent( QDragEnterEvent * e ); + MechanicsDocument *m_pMechanicsDocument; +}; + +#endif diff --git a/src/micro/Makefile.am b/src/micro/Makefile.am new file mode 100644 index 0000000..37dc64f --- /dev/null +++ b/src/micro/Makefile.am @@ -0,0 +1,7 @@ +INCLUDES = $(all_includes) +METASOURCES = AUTO +noinst_LTLIBRARIES = libmicro.la +noinst_HEADERS = microinfo.h picinfo.h picinfo16bit.h picinfo14bit.h \ + picinfo12bit.h microlibrary.h asminfo.h +libmicro_la_SOURCES = microinfo.cpp picinfo.cpp picinfo16bit.cpp \ + picinfo14bit.cpp picinfo12bit.cpp microlibrary.cpp micropackage.cpp asminfo.cpp diff --git a/src/micro/asminfo.cpp b/src/micro/asminfo.cpp new file mode 100644 index 0000000..3a12b57 --- /dev/null +++ b/src/micro/asminfo.cpp @@ -0,0 +1,68 @@ +/*************************************************************************** + * Copyright (C) 2003,2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "asminfo.h" + +#include <kdebug.h> + +AsmInfo::AsmInfo() +{ +} + + +AsmInfo::~AsmInfo() +{ +} + + +void AsmInfo::addInstruction( const QString & operand, const QString & description, const QString & opcode ) +{ + Instruction instruction; + instruction.operand = operand; + instruction.description = description; + instruction.opcode = opcode; + m_instructionList.append( instruction ); + m_operandList.append( operand ); +} + + +QString AsmInfo::setToString( Set set ) +{ + switch (set) + { + case AsmInfo::PIC12: + return "PIC12"; + + case AsmInfo::PIC14: + return "PIC14"; + + case AsmInfo::PIC16: + return "PIC16"; + } + + kdWarning() << k_funcinfo << "Unrecognized set="<<set<<endl; + return QString::null; +} + + +AsmInfo::Set AsmInfo::stringToSet( const QString & set ) +{ + if ( set == "PIC12" ) + return PIC12; + + if ( set == "PIC14" ) + return PIC14; + + if ( set == "PIC16" ) + return PIC16; + +// kdWarning() << k_funcinfo << "Unrecognized set="<<set<<endl; + return PIC14; +} diff --git a/src/micro/asminfo.h b/src/micro/asminfo.h new file mode 100644 index 0000000..2026b7d --- /dev/null +++ b/src/micro/asminfo.h @@ -0,0 +1,71 @@ +/*************************************************************************** + * Copyright (C) 2003,2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef ASMINFO_H +#define ASMINFO_H + +#include <qstring.h> +#include <qstringlist.h> +#include <qvaluelist.h> + +/** +@author David Saxton +*/ +struct Instruction +{ + QString operand; + QString description; + QString opcode; +}; +typedef QValueList<Instruction> InstructionList; + +/** +@short Base class for all instruction sets +@author David Saxton +*/ +class AsmInfo +{ +public: + AsmInfo(); + virtual ~AsmInfo(); + + enum Set + { + PIC12 = 1 << 0, + PIC14 = 1 << 1, + PIC16 = 1 << 2 + }; + enum { AsmSetAll = PIC12 | PIC14 | PIC16 }; + + static QString setToString( Set set ); + static Set stringToSet( const QString & set ); + + /** + * @return the instruction set in use + */ + virtual Set set() const = 0; + /** + * Returns a list of instruction operands (all uppercase). + */ + QStringList operandList() const { return m_operandList; } + /** + * @param operand is the name of the instruction - eg 'addwf' or 'clrwdt'. + * @param description is instruction description - eg 'Add W and f'. + * @param opcode' is the 14-bit code for the instruction, eg + * '000111dfffffff'for addwf. + */ + void addInstruction( const QString &operand, const QString &description, const QString &opcode ); + +private: + InstructionList m_instructionList; + QStringList m_operandList; +}; + +#endif diff --git a/src/micro/microinfo.cpp b/src/micro/microinfo.cpp new file mode 100644 index 0000000..b6d59a2 --- /dev/null +++ b/src/micro/microinfo.cpp @@ -0,0 +1,62 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "microinfo.h" +#include "micropackage.h" + +MicroInfo::MicroInfo() +{ + m_package = 0L; +} + +MicroInfo::~MicroInfo() +{ + delete m_package; + m_package = 0L; +} + +#if 0 +QStringList MicroInfo::portNames() +{ + if (m_package) return m_package->portNames(); + else return ""; +} + +QStringList MicroInfo::pinIDs() +{ + if (m_package) return m_package->pinIDs(); + else return ""; +} + +QStringList MicroInfo::bidirPinIDs() +{ + if (m_package) return m_package->bidirPinIDs(); + else return ""; +} + +int MicroInfo::totalNumPins() +{ + if (m_package) return m_package->totalNumPins(); + else return 0; +} + +int MicroInfo::numIOPins() +{ + if (m_package) return m_package->numIOPins(); + else return 0; +} + +int MicroInfo::numIOPins( const QString &portName ) +{ + if (m_package) return m_package->numIOPins(portName); + else return 0; +} +#endif + diff --git a/src/micro/microinfo.h b/src/micro/microinfo.h new file mode 100644 index 0000000..7a22bf7 --- /dev/null +++ b/src/micro/microinfo.h @@ -0,0 +1,64 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef MICROINFO_H +#define MICROINFO_H + +#include <qstringlist.h> + +class AsmInfo; +class MicroPackage; + +/** +@author David Saxton +*/ +class MicroInfo +{ +public: + enum Support + { + FullSupport = 1 << 0, + PartialSupport = 1 << 1, + NoSupport = 1 << 2 + }; + enum { AllSupport = FullSupport | PartialSupport | NoSupport }; + + MicroInfo(); + virtual ~MicroInfo(); + + virtual AsmInfo * instructionSet() = 0; + /** + * Returns the gpsim emulator support status + */ + virtual Support gpsimSupport() const { return NoSupport; } + /** + * Returns the FlowCode support (i.e. constructing flowcode for the PIC) + */ + virtual Support flowcodeSupport() const { return NoSupport; } + /** + * Returns the Microbe support (i.e. compiling) + */ + virtual Support microbeSupport() const { return NoSupport; } + /** + * Returns a pointer to the Micro Package in use + */ + MicroPackage *package() const { return m_package; } + /** + * Returns an id unique to the Micro + */ + QString id() const { return m_id; } + +protected: + QString m_id; + MicroPackage *m_package; +}; + +#endif + diff --git a/src/micro/microlibrary.cpp b/src/micro/microlibrary.cpp new file mode 100644 index 0000000..f0faabc --- /dev/null +++ b/src/micro/microlibrary.cpp @@ -0,0 +1,115 @@ +/*************************************************************************** + * Copyright (C) 2003 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "microinfo.h" +#include "microlibrary.h" + +#include <kdebug.h> +#include <kstaticdeleter.h> + +#include "picinfo12bit.h" +#include "picinfo14bit.h" +#include "picinfo16bit.h" + +#include "micropackage.h" + +MicroLibrary * MicroLibrary::m_pSelf = 0l; +static KStaticDeleter<MicroLibrary> staticMicroLibraryDeleter; + +MicroLibrary * MicroLibrary::self() +{ + if ( !m_pSelf ) + staticMicroLibraryDeleter.setObject( m_pSelf, new MicroLibrary() ); + return m_pSelf; +} + +MicroLibrary::MicroLibrary() +{ + addMicroInfo( new PicInfo12C508() ); + addMicroInfo( new PicInfo12C509() ); + addMicroInfo( new PicInfo16C54 () ); + addMicroInfo( new PicInfo16C55() ); + addMicroInfo( new PicInfo16C61() ); + addMicroInfo( new PicInfo16C62() ); + addMicroInfo( new PicInfo16C63() ); + addMicroInfo( new PicInfo16C64() ); + addMicroInfo( new PicInfo16F627() ); + addMicroInfo( new PicInfo16F628() ); + addMicroInfo( new PicInfo16C65() ); + addMicroInfo( new PicInfo16C71() ); + addMicroInfo( new PicInfo16C72() ); + addMicroInfo( new PicInfo16C73() ); + addMicroInfo( new PicInfo16C712() ); + addMicroInfo( new PicInfo16C716() ); + addMicroInfo( new PicInfo16C74() ); + addMicroInfo( new PicInfo16C84() ); + addMicroInfo( new PicInfo16CR83() ); + addMicroInfo( new PicInfo16F83() ); + addMicroInfo( new PicInfo16CR84() ); + addMicroInfo( new PicInfo16F84() ); + addMicroInfo( new PicInfo16F873() ); + addMicroInfo( new PicInfo16F874() ); + addMicroInfo( new PicInfo16F877() ); + addMicroInfo( new PicInfo17C752() ); + addMicroInfo( new PicInfo17C756() ); + addMicroInfo( new PicInfo17C762() ); + addMicroInfo( new PicInfo17C766() ); + addMicroInfo( new PicInfo18C242() ); + addMicroInfo( new PicInfo18C252() ); + addMicroInfo( new PicInfo18C442() ); + addMicroInfo( new PicInfo18C452() ); + addMicroInfo( new PicInfo18F442() ); + addMicroInfo( new PicInfo18F452() ); +} + +MicroLibrary::~MicroLibrary() +{ + const MicroInfoList::iterator end = m_microInfoList.end(); + for ( MicroInfoList::iterator it = m_microInfoList.begin(); it != end; ++it ) + { + delete *it; + *it = 0l; + } +} + +MicroInfo * const MicroLibrary::microInfoWithID( QString id ) +{ + id = id.upper(); + const MicroInfoList::iterator end = m_microInfoList.end(); + for ( MicroInfoList::iterator it = m_microInfoList.begin(); it != end; ++it ) + { + if ( (*it)->id() == id ) return *it; + } + + return 0L; +} + +void MicroLibrary::addMicroInfo( MicroInfo *microInfo ) +{ + if (microInfo) + m_microInfoList += microInfo; +} + +QStringList MicroLibrary::microIDs( unsigned asmSet, unsigned gpsimSupport, unsigned flowCodeSupport, unsigned microbeSupport ) +{ + QStringList ids; + + const MicroInfoList::iterator end = m_microInfoList.end(); + for ( MicroInfoList::iterator it = m_microInfoList.begin(); it != end; ++it ) + { + MicroInfo * info = *it; + if ( (info->instructionSet()->set() & asmSet) && + (info->gpsimSupport() & gpsimSupport) && + (info->flowcodeSupport() & flowCodeSupport) && + (info->microbeSupport() & microbeSupport) ) + ids << info->id(); + } + return ids; +} diff --git a/src/micro/microlibrary.h b/src/micro/microlibrary.h new file mode 100644 index 0000000..537cf6c --- /dev/null +++ b/src/micro/microlibrary.h @@ -0,0 +1,55 @@ +/*************************************************************************** + * Copyright (C) 2003-2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef MICRoLIBRARY_H +#define PICLIBRARY_H + +#include "asminfo.h" +#include "microinfo.h" + +#include <qstring.h> +#include <qstringlist.h> +#include <qvaluelist.h> + +class MicroInfo; +class MicroLibrary; +typedef QValueList<MicroInfo*> MicroInfoList; + +inline MicroLibrary *microLibrary(); + +/** +@short Stores all the avaiable PICs (info) +@author David Saxton +*/ +class MicroLibrary +{ + public: + static MicroLibrary * self(); + + ~MicroLibrary(); + + MicroInfo * const microInfoWithID( QString id ); + void addMicroInfo( MicroInfo *microInfo ); + + /** + * Returns a list of micro ids with the given properties (OR'ed + * together). + */ + QStringList microIDs( unsigned asmSet = AsmInfo::AsmSetAll, unsigned gpsimSupport = MicroInfo::AllSupport, unsigned flowCodeSupport = MicroInfo::AllSupport, unsigned microbeSupport = MicroInfo::AllSupport ); + + private: + MicroLibrary(); + static MicroLibrary * m_pSelf; + + MicroInfoList m_microInfoList; + friend MicroLibrary *microLibrary(); +}; + +#endif diff --git a/src/micro/micropackage.cpp b/src/micro/micropackage.cpp new file mode 100644 index 0000000..9becbab --- /dev/null +++ b/src/micro/micropackage.cpp @@ -0,0 +1,109 @@ +/*************************************************************************** + * Copyright (C) 2003 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "micropackage.h" + +#include <kdebug.h> + +PicPin::PicPin() +{ + pinID = "INVALID"; + type = PicPin::type_bidir; + portName = "INVALID"; + portPosition = -1; +} + + +PicPin::PicPin( const QString &_pinID, PicPin::pin_type _type, const QString &_portName, int _portPosition ) +{ + pinID = _pinID; + type = _type; + portName = _portName; + portPosition = _portPosition; +} + +MicroPackage::MicroPackage( const int pinCount ) +{ + m_numPins = pinCount; +} + +MicroPackage::~MicroPackage() +{ +} + +void MicroPackage::assignPin( int pinPosition, PicPin::pin_type type, const QString& pinID, const QString& portName, int portPosition ) +{ + if ( m_picPinMap.find(pinPosition) != m_picPinMap.end() ) + { + kdError() << "PicDevice::assignBidirPin: Attempting to reset pin "<<pinPosition<<endl; + return; + } + if ( !m_portNames.contains(portName) && !portName.isEmpty() ) + { + m_portNames.append(portName); + m_portNames.sort(); + } + + m_picPinMap[pinPosition] = PicPin( pinID, type, portName, portPosition ); + +} + +PicPinMap MicroPackage::pins( uint pinType, const QString& portName ) +{ + if ( pinType == 0 ) pinType = (1<<30)-1; + + PicPinMap list; + + const PicPinMap::iterator picPinListEnd = m_picPinMap.end(); + for ( PicPinMap::iterator it = m_picPinMap.begin(); it != picPinListEnd; ++it ) + { + if ( (it.data().type & pinType) && + (portName.isEmpty() || it.data().portName == portName) ) + { + list[it.key()] = it.data(); + } + } + + return list; +} + +QStringList MicroPackage::pinIDs( uint pinType, const QString& portName ) +{ + if ( pinType == 0 ) pinType = (1<<30)-1; + QStringList list; + + const PicPinMap::iterator picPinListEnd = m_picPinMap.end(); + for ( PicPinMap::iterator it = m_picPinMap.begin(); it != picPinListEnd; ++it ) + { + if ( (it.data().type & pinType) && + (portName.isEmpty() || it.data().portName == portName) ) + { + list.append( it.data().pinID ); + } + } + + return list; +} + +int MicroPackage::pinCount( uint pinType, const QString& portName ) +{ + if ( pinType == 0 ) pinType = (1<<30)-1; + int count = 0; + + const PicPinMap::iterator picPinListEnd = m_picPinMap.end(); + for ( PicPinMap::iterator it = m_picPinMap.begin(); it != picPinListEnd; ++it ) + { + if ( (it.data().type & pinType) && + (portName.isEmpty() || it.data().portName == portName) ) count++; + } + + return count; +} + diff --git a/src/micro/micropackage.h b/src/micro/micropackage.h new file mode 100644 index 0000000..0dc98fe --- /dev/null +++ b/src/micro/micropackage.h @@ -0,0 +1,99 @@ +/*************************************************************************** + * Copyright (C) 2003-2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef PICPACKAGES_H +#define PICPACKAGES_H + +#include <qstring.h> +#include <qstringlist.h> + +#include <qmap.h> + +/** +@author David Saxton +*/ +class PicPin +{ +public: + enum pin_type + { + type_input = 0x1, // Input only pin + type_bidir = 0x2, // Bi-directional (input/output) + type_open = 0x4, // Open collector + type_vss = 0x8, // Voltage source + type_vdd = 0x10, // Voltage drain + type_mclr = 0x20, // Memory clear + type_osc = 0x40 // Oscillator + }; + + PicPin(); + PicPin( const QString &_pinID, PicPin::pin_type _type, const QString &_portName = "", int _portPosition = -1 ); + + PicPin::pin_type type; + + QString pinID; // Id of pin, eg 'MCLR' + + // For bidir (io) pins + QString portName; // Name of port, eg 'PORTA' + int portPosition; // Position in port +}; +typedef QMap<int, PicPin> PicPinMap; + +/** +@short Describes the PIC package (i.e. pins) +@author David Saxton +*/ +class MicroPackage +{ +public: + MicroPackage( const int pinCount ); + virtual ~MicroPackage(); + + /** + * Assigns a pin to a position in the package. + */ + void assignPin( int pinPosition, PicPin::pin_type type, const QString& pinID, const QString& portName = "", int portPosition = -1); + /** + * Returns the pins of the given type(s). If portName is not specified, all pins will be returned; + * not just those belonging to a given port. pin_type's can be OR'ed together + * e.g. pins( PicPin::type_input | PicPin::type_bidir, "PORTA" ) will return all bidirectional or + * input pins belonging to PORTA. If pinType is 0, then this will return all pins + */ + PicPinMap pins( uint pinType = 0, const QString& portName = "" ); + /** + * Returns just a QStringList of the pin ids. + * @see pins( uint pinType, const QString& portName ) + */ + QStringList pinIDs( uint pinType = 0, const QString& portName = "" ); + /** + * Returns the number of pins of the given type(s) (which can be OR'ed together), with the given + * port name if specified, while avoiding the construction of a new PicPinMap. if pinType is 0, + * then all pin types are considered + * @see pins + */ + int pinCount( uint pinType = 0, const QString& portName = "" ); + /** + * Returns a list of port names, eg 'PORTA', 'PORTB' for the package + */ + QStringList portNames() const { return m_portNames; } + /** + * Returns the number of ports + */ + uint portCount() const { return m_portNames.size(); } + +private: + PicPinMap m_picPinMap; + QStringList m_portNames; + int m_numPins; +}; + +#endif + + diff --git a/src/micro/picinfo.cpp b/src/micro/picinfo.cpp new file mode 100644 index 0000000..2e38c12 --- /dev/null +++ b/src/micro/picinfo.cpp @@ -0,0 +1,24 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "picinfo.h" + +PicInfo::PicInfo() + : MicroInfo() +{ + m_package = 0l; +} + + +PicInfo::~PicInfo() +{ +} + + diff --git a/src/micro/picinfo.h b/src/micro/picinfo.h new file mode 100644 index 0000000..cd9d155 --- /dev/null +++ b/src/micro/picinfo.h @@ -0,0 +1,31 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef PICINFO_H +#define PICINFO_H + +#include "microinfo.h" + +/** +This is the base class for all 12bit, 14bit and 16bit PICs. +Thanks go to gpsim & Scott Dattalo for some of the the hierachal PIC class +structure used here :-) +@short Base class for all PICMicros +@author David Saxton +*/ +class PicInfo : public MicroInfo +{ + public: + PicInfo(); + virtual ~PicInfo(); + virtual AsmInfo * instructionSet() = 0; +}; + +#endif diff --git a/src/micro/picinfo12bit.cpp b/src/micro/picinfo12bit.cpp new file mode 100644 index 0000000..12f8dd5 --- /dev/null +++ b/src/micro/picinfo12bit.cpp @@ -0,0 +1,181 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "micropackage.h" +#include "picinfo12bit.h" + +#include <klocale.h> +#include <kstaticdeleter.h> + +PicAsm12bit *PicAsm12bit::m_self = 0; +static KStaticDeleter<PicAsm12bit> staticDeleter; + +PicAsm12bit *PicAsm12bit::self() +{ + if ( !m_self ) staticDeleter.setObject( m_self, new PicAsm12bit() ); + return m_self; +} + + +PicInfo12bit::PicInfo12bit() + : PicInfo() +{ +} + + +PicInfo12bit::~PicInfo12bit() +{ +} + +PicAsm12bit::PicAsm12bit() + : AsmInfo() +{ + // Byte-orientated file register operations + addInstruction( "ADDWF", 0, "000111dfffff" ); + addInstruction( "ANDWF", 0, "000101dfffff" ); + addInstruction( "CLRF", 0, "0000011fffff" ); + addInstruction( "CLRW", 0, "000001000000" ); + addInstruction( "COMF", 0, "001001dfffff" ); + addInstruction( "DECF", 0, "000011dfffff" ); + addInstruction( "DECFSZ", 0, "001011dfffff" ); + addInstruction( "INCF", 0, "001010dfffff" ); + addInstruction( "INCFSZ", 0, "001111dfffff" ); + addInstruction( "IORWF", 0, "000100dfffff" ); + addInstruction( "MOVF", 0, "001000dfffff" ); + addInstruction( "MOVWF", 0, "0000001fffff" ); + addInstruction( "NOP", 0, "000000000000" ); + addInstruction( "RLF", 0, "001101dfffff" ); + addInstruction( "RRF", 0, "001100dfffff" ); + addInstruction( "SUBWF", 0, "000010dfffff" ); + addInstruction( "SWAPF", 0, "001110dfffff" ); + addInstruction( "XORWF", 0, "000110dfffff" ); + + // Bit-orientated file register operations + addInstruction( "BCF", 0, "0100bbbfffff" ); + addInstruction( "BSF", 0, "0101bbbfffff" ); + addInstruction( "BTFSC", 0, "0110bbbfffff" ); + addInstruction( "BTFSS", 0, "0111bbbfffff" ); + + // Literal and control operations + addInstruction( "ANDLW", 0, "1110kkkkkkkk" ); + addInstruction( "CALL", 0, "1001kkkkkkkk" ); + addInstruction( "CLRWDT", 0, "000000000100" ); + addInstruction( "GOTO", 0, "101kkkkkkkkk" ); + addInstruction( "IORLW", 0, "1101kkkkkkkk" ); + addInstruction( "MOVLW", 0, "1100kkkkkkkk" ); +// addInstruction( "RETFIE", 0, "00000000001001" ); + addInstruction( "OPTION", 0, "000000000010" ); + addInstruction( "RETLW", 0, "1000kkkkkkkk" ); +// addInstruction( "RETURN", 0, "00000000001000" ); + addInstruction( "SLEEP", 0, "000000000011" ); +// addInstruction( "SUBLW", 0, "11110xkkkkkkkk" ); + addInstruction( "TRIS", 0, "000000000fff" ); + addInstruction( "XORLW", 0, "1111kkkkkkkk" ); +} + + + +PicInfo16C54::PicInfo16C54() + : PicInfo12bit() +{ + m_id = "P16C54"; + + delete m_package; + m_package = new MicroPackage(18); + + m_package->assignPin( 17, PicPin::type_bidir, "RA0", "PORTA", 0 ); + m_package->assignPin( 18, PicPin::type_bidir, "RA1", "PORTA", 1 ); + m_package->assignPin( 1, PicPin::type_bidir, "RA2", "PORTA", 2 ); + m_package->assignPin( 2, PicPin::type_bidir, "RA3", "PORTA", 3 ); + m_package->assignPin( 3, PicPin::type_open, "RA4", "PORTA", 4 ); + + m_package->assignPin( 6, PicPin::type_bidir, "RB0", "PORTB", 0 ); + m_package->assignPin( 7, PicPin::type_bidir, "RB1", "PORTB", 1 ); + m_package->assignPin( 8, PicPin::type_bidir, "RB2", "PORTB", 2 ); + m_package->assignPin( 9, PicPin::type_bidir, "RB3", "PORTB", 3 ); + m_package->assignPin( 10, PicPin::type_bidir, "RB4", "PORTB", 4 ); + m_package->assignPin( 11, PicPin::type_bidir, "RB5", "PORTB", 5 ); + m_package->assignPin( 12, PicPin::type_bidir, "RB6", "PORTB", 6 ); + m_package->assignPin( 13, PicPin::type_bidir, "RB7", "PORTB", 7 ); + + m_package->assignPin( 4, PicPin::type_mclr, "MCLR" ); + m_package->assignPin( 5, PicPin::type_vss, "VSS" ); + m_package->assignPin( 14, PicPin::type_vdd, "VDD" ); + m_package->assignPin( 15, PicPin::type_osc, "OSC2" ); + m_package->assignPin( 16, PicPin::type_osc, "OSC1" ); +} + +PicInfo16C54::~PicInfo16C54() +{ + delete m_package; + m_package = 0l; +} + +PicInfo16C55::PicInfo16C55() + : PicInfo12bit() +{ + m_id = "P16C55"; +} +PicInfo16C55::~PicInfo16C55() +{ +} + + +PicInfo12C508::PicInfo12C508() + : PicInfo12bit() +{ + m_id = "P12C508"; + + delete m_package; + m_package = new MicroPackage(8); + + m_package->assignPin( 7, PicPin::type_bidir, "GP0", "GPIO", 0 ); + m_package->assignPin( 6, PicPin::type_bidir, "GP1", "GPIO", 1 ); + m_package->assignPin( 5, PicPin::type_bidir, "GP2", "GPIO", 2 ); + m_package->assignPin( 4, PicPin::type_input, "GP3", "GPIO", 3 ); + m_package->assignPin( 3, PicPin::type_bidir, "GP4", "GPIO", 4 ); + m_package->assignPin( 2, PicPin::type_bidir, "GP5", "GPIO", 5 ); + + m_package->assignPin( 8, PicPin::type_vss, "VSS" ); + m_package->assignPin( 1, PicPin::type_vdd, "VDD" ); +} +PicInfo12C508::~PicInfo12C508() +{ + delete m_package; + m_package = 0l; +} + +PicInfo12C509::PicInfo12C509() + : PicInfo12C508() +{ + m_id = "P12C509"; +} +PicInfo12C509::~PicInfo12C509() +{ +} + +PicInfo12C671::PicInfo12C671() + : PicInfo12C508() +{ + m_id = "P12C671"; +} +PicInfo12C671::~PicInfo12C671() +{ +} + +PicInfo12C672::PicInfo12C672() + : PicInfo12C508() +{ + m_id = "P12C672"; +} +PicInfo12C672::~PicInfo12C672() +{ +} + diff --git a/src/micro/picinfo12bit.h b/src/micro/picinfo12bit.h new file mode 100644 index 0000000..899bfc7 --- /dev/null +++ b/src/micro/picinfo12bit.h @@ -0,0 +1,112 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef PICINFO12BIT_H +#define PICINFO12BIT_H + +#include "picinfo.h" +#include "asminfo.h" + +/** +@short 12 Bit PIC Instructions +@author David Saxton + */ +class PicAsm12bit : public AsmInfo +{ + public: + static PicAsm12bit *self(); + virtual Set set() const { return AsmInfo::PIC12; } + + protected: + static PicAsm12bit *m_self; + + private: + PicAsm12bit(); +}; + +/** +@author David Saxton + */ +class PicInfo12bit : public PicInfo +{ + public: + PicInfo12bit(); + ~PicInfo12bit(); + + virtual AsmInfo * instructionSet() { return PicAsm12bit::self(); } +}; + +/** +@author David Saxton + */ +class PicInfo16C54 : public PicInfo12bit +{ + public: + PicInfo16C54(); + ~PicInfo16C54(); + virtual Support gpsimSupport() const { return FullSupport; } +}; + +/** +@author David Saxton + */ +class PicInfo16C55 : public PicInfo12bit +{ + public: + PicInfo16C55(); + ~PicInfo16C55(); + virtual Support gpsimSupport() const { return FullSupport; } +}; + +/** +@author David Saxton + */ +class PicInfo12C508 : public PicInfo12bit +{ + public: + PicInfo12C508(); + ~PicInfo12C508(); + virtual Support gpsimSupport() const { return FullSupport; } +}; + +/** +@author David Saxton + */ +class PicInfo12C509 : public PicInfo12C508 +{ + public: + PicInfo12C509(); + ~PicInfo12C509(); + virtual Support gpsimSupport() const { return FullSupport; } +}; + +/** +@author David Saxton + */ +class PicInfo12C671 : public PicInfo12C508 +{ + public: + PicInfo12C671(); + ~PicInfo12C671(); + virtual Support gpsimSupport() const { return NoSupport; } +}; + +/** +@author David Saxton + */ +class PicInfo12C672 : public PicInfo12C508 +{ + public: + PicInfo12C672(); + ~PicInfo12C672(); + virtual Support gpsimSupport() const { return NoSupport; } +}; + +#endif diff --git a/src/micro/picinfo14bit.cpp b/src/micro/picinfo14bit.cpp new file mode 100644 index 0000000..9e75a92 --- /dev/null +++ b/src/micro/picinfo14bit.cpp @@ -0,0 +1,446 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "micropackage.h" +#include "picinfo14bit.h" + +#include <klocale.h> +#include <kstaticdeleter.h> + +PicAsm14bit *PicAsm14bit::m_self = 0; +static KStaticDeleter<PicAsm14bit> staticDeleter; + +PicAsm14bit *PicAsm14bit::self() +{ + if ( !m_self ) staticDeleter.setObject( m_self, new PicAsm14bit() ); + return m_self; +} + + +PicInfo14bit::PicInfo14bit() + : PicInfo() +{ +} + + +PicInfo14bit::~PicInfo14bit() +{ +} + + +PicAsm14bit::PicAsm14bit() + : AsmInfo() +{ + // Byte-orientated file register operations + addInstruction( "ADDWF", 0, "000111dfffffff" ); + addInstruction( "ANDWF", 0, "000101dfffffff" ); + addInstruction( "CLRF", 0, "0000011fffffff" ); + addInstruction( "CLRW", 0, "0000010xxxxxxx" ); + addInstruction( "COMF", 0, "001001dfffffff" ); + addInstruction( "DECF", 0, "000011dfffffff" ); + addInstruction( "DECFSZ", 0, "001011dfffffff" ); + addInstruction( "INCF", 0, "001010dfffffff" ); + addInstruction( "INCFSZ", 0, "001111dfffffff" ); + addInstruction( "IORWF", 0, "000100dfffffff" ); + addInstruction( "MOVF", 0, "001000dfffffff" ); + addInstruction( "MOVWF", 0, "0000001fffffff" ); + addInstruction( "NOP", 0, "0000000xx00000" ); + addInstruction( "RLF", 0, "001101dfffffff" ); + addInstruction( "RRF", 0, "001100dfffffff" ); + addInstruction( "SUBWF", 0, "000010dfffffff" ); + addInstruction( "SWAPF", 0, "001110dfffffff" ); + addInstruction( "XORWF", 0, "000110dfffffff" ); + + // Bit-orientated file register operations + addInstruction( "BCF", 0, "0100bbbfffffff" ); + addInstruction( "BSF", 0, "0101bbbfffffff" ); + addInstruction( "BTFSC", 0, "0110bbbfffffff" ); + addInstruction( "BTFSS", 0, "0111bbbfffffff" ); + + // Literal and control operations + addInstruction( "ADDLW", 0, "11111xkkkkkkkk" ); + addInstruction( "ANDLW", 0, "111001kkkkkkkk" ); + addInstruction( "CALL", 0, "100kkkkkkkkkkk" ); + addInstruction( "CLRWDT", 0, "00000001100100" ); + addInstruction( "GOTO", 0, "101kkkkkkkkkkk" ); + addInstruction( "IORLW", 0, "111000kkkkkkkk" ); + addInstruction( "MOVLW", 0, "1100xxkkkkkkkk" ); + addInstruction( "RETFIE", 0, "00000000001001" ); + addInstruction( "RETLW", 0, "1101xxkkkkkkkk" ); + addInstruction( "RETURN", 0, "00000000001000" ); + addInstruction( "SLEEP", 0, "00000000000011" ); + addInstruction( "SUBLW", 0, "11110xkkkkkkkk" ); + addInstruction( "XORLW", 0, "111010kkkkkkkk" ); +} + +PicInfo16C8x::PicInfo16C8x() + : PicInfo14bit() +{ + delete m_package; + m_package = new MicroPackage(18); + + m_package->assignPin( 17, PicPin::type_bidir, "RA0", "PORTA", 0 ); + m_package->assignPin( 18, PicPin::type_bidir, "RA1", "PORTA", 1 ); + m_package->assignPin( 1, PicPin::type_bidir, "RA2", "PORTA", 2 ); + m_package->assignPin( 2, PicPin::type_bidir, "RA3", "PORTA", 3 ); + m_package->assignPin( 3, PicPin::type_open, "RA4", "PORTA", 4 ); + + m_package->assignPin( 6, PicPin::type_bidir, "RB0", "PORTB", 0 ); + m_package->assignPin( 7, PicPin::type_bidir, "RB1", "PORTB", 1 ); + m_package->assignPin( 8, PicPin::type_bidir, "RB2", "PORTB", 2 ); + m_package->assignPin( 9, PicPin::type_bidir, "RB3", "PORTB", 3 ); + m_package->assignPin( 10, PicPin::type_bidir, "RB4", "PORTB", 4 ); + m_package->assignPin( 11, PicPin::type_bidir, "RB5", "PORTB", 5 ); + m_package->assignPin( 12, PicPin::type_bidir, "RB6", "PORTB", 6 ); + m_package->assignPin( 13, PicPin::type_bidir, "RB7", "PORTB", 7 ); + + m_package->assignPin( 4, PicPin::type_mclr, "MCLR" ); + m_package->assignPin( 5, PicPin::type_vss, "VSS" ); + m_package->assignPin( 14, PicPin::type_vdd, "VDD" ); + m_package->assignPin( 15, PicPin::type_osc, "OSC2" ); + m_package->assignPin( 16, PicPin::type_osc, "OSC1" ); +} + +PicInfo16C8x::~PicInfo16C8x() +{ + delete m_package; + m_package = 0l; +} + +PicInfo16C84::PicInfo16C84() + : PicInfo16C8x() +{ + m_id = "P16C84"; +} +PicInfo16C84::~PicInfo16C84() +{ +} + +PicInfo16F84::PicInfo16F84() + : PicInfo16C8x() +{ + m_id = "P16F84"; +} +PicInfo16F84::~PicInfo16F84() +{ +} + +PicInfo16CR84::PicInfo16CR84() + : PicInfo16F84() +{ + m_id = "P16CR84"; +} +PicInfo16CR84::~PicInfo16CR84() +{ +} + +PicInfo16F83::PicInfo16F83() + : PicInfo16C8x() +{ + m_id = "P16F83"; +} +PicInfo16F83::~PicInfo16F83() +{ +} + +PicInfo16CR83::PicInfo16CR83() + : PicInfo16F83() +{ + m_id = "P16CR83"; +} +PicInfo16CR83::~PicInfo16CR83() +{ +} + +PicInfo16C61::PicInfo16C61() + : PicInfo16C8x() +{ + m_id = "P16C61"; +} +PicInfo16C61::~PicInfo16C61() +{ +} + +PicInfo16X6X::PicInfo16X6X() + : PicInfo14bit() +{ + m_id = "P16X6X"; +} +PicInfo16X6X::~PicInfo16X6X() +{ +} + +PicInfo16C62::PicInfo16C62() + : PicInfo16X6X() +{ + m_id = "P16C62"; + + delete m_package; + m_package = new MicroPackage(28); + + m_package->assignPin( 2, PicPin::type_bidir, "RA0", "PORTA", 0 ); + m_package->assignPin( 3, PicPin::type_bidir, "RA1", "PORTA", 1 ); + m_package->assignPin( 4, PicPin::type_bidir, "RA2", "PORTA", 2 ); + m_package->assignPin( 5, PicPin::type_bidir, "RA3", "PORTA", 3 ); + m_package->assignPin( 6, PicPin::type_open, "RA4", "PORTA", 4 ); + m_package->assignPin( 7, PicPin::type_bidir, "RA5", "PORTA", 5 ); + + m_package->assignPin( 21, PicPin::type_bidir, "RB0", "PORTB", 0 ); + m_package->assignPin( 22, PicPin::type_bidir, "RB1", "PORTB", 1 ); + m_package->assignPin( 23, PicPin::type_bidir, "RB2", "PORTB", 2 ); + m_package->assignPin( 24, PicPin::type_bidir, "RB3", "PORTB", 3 ); + m_package->assignPin( 25, PicPin::type_bidir, "RB4", "PORTB", 4 ); + m_package->assignPin( 26, PicPin::type_bidir, "RB5", "PORTB", 5 ); + m_package->assignPin( 27, PicPin::type_bidir, "RB6", "PORTB", 6 ); + m_package->assignPin( 28, PicPin::type_bidir, "RB7", "PORTB", 7 ); + + m_package->assignPin( 11, PicPin::type_bidir, "RC0", "PORTC", 0 ); + m_package->assignPin( 12, PicPin::type_bidir, "RC1", "PORTC", 1 ); + m_package->assignPin( 13, PicPin::type_bidir, "RC2", "PORTC", 2 ); + m_package->assignPin( 14, PicPin::type_bidir, "RC3", "PORTC", 3 ); + m_package->assignPin( 15, PicPin::type_bidir, "RC4", "PORTC", 4 ); + m_package->assignPin( 16, PicPin::type_bidir, "RC5", "PORTC", 5 ); + m_package->assignPin( 17, PicPin::type_bidir, "RC6", "PORTC", 6 ); + m_package->assignPin( 18, PicPin::type_bidir, "RC7", "PORTC", 7 ); + + m_package->assignPin( 1, PicPin::type_mclr, "MCLR" ); + m_package->assignPin( 8, PicPin::type_vss, "VSS" ); + m_package->assignPin( 9, PicPin::type_osc, "OSC1" ); + m_package->assignPin( 10, PicPin::type_osc, "OSC2" ); + m_package->assignPin( 19, PicPin::type_vss, "VSS" ); + m_package->assignPin( 20, PicPin::type_vdd, "VDD" ); +} +PicInfo16C62::~PicInfo16C62() +{ + delete m_package; + m_package = 0l; +} + +PicInfo16C63::PicInfo16C63() + : PicInfo16C62() +{ + m_id = "P16C63"; +} +PicInfo16C63::~PicInfo16C63() +{ +} + +PicInfo16C64::PicInfo16C64() + : PicInfo16X6X() +{ + m_id = "P16C64"; + + delete m_package; + m_package = new MicroPackage(40); + + m_package->assignPin( 2, PicPin::type_bidir, "RA0", "PORTA", 0 ); + m_package->assignPin( 3, PicPin::type_bidir, "RA1", "PORTA", 1 ); + m_package->assignPin( 4, PicPin::type_bidir, "RA2", "PORTA", 2 ); + m_package->assignPin( 5, PicPin::type_bidir, "RA3", "PORTA", 3 ); + m_package->assignPin( 6, PicPin::type_open, "RA4", "PORTA", 4 ); + m_package->assignPin( 7, PicPin::type_bidir, "RA5", "PORTB", 5 ); + + m_package->assignPin( 33, PicPin::type_bidir, "RB0", "PORTB", 0 ); + m_package->assignPin( 34, PicPin::type_bidir, "RB1", "PORTB", 1 ); + m_package->assignPin( 35, PicPin::type_bidir, "RB2", "PORTB", 2 ); + m_package->assignPin( 36, PicPin::type_bidir, "RB3", "PORTB", 3 ); + m_package->assignPin( 37, PicPin::type_bidir, "RB4", "PORTB", 4 ); + m_package->assignPin( 38, PicPin::type_bidir, "RB5", "PORTB", 5 ); + m_package->assignPin( 39, PicPin::type_bidir, "RB6", "PORTB", 6 ); + m_package->assignPin( 40, PicPin::type_bidir, "RB7", "PORTB", 7 ); + + m_package->assignPin( 15, PicPin::type_bidir, "RC0", "PORTC", 0 ); + m_package->assignPin( 16, PicPin::type_bidir, "RC1", "PORTC", 1 ); + m_package->assignPin( 17, PicPin::type_bidir, "RC2", "PORTC", 2 ); + m_package->assignPin( 18, PicPin::type_bidir, "RC3", "PORTC", 3 ); + m_package->assignPin( 23, PicPin::type_bidir, "RC4", "PORTC", 4 ); + m_package->assignPin( 24, PicPin::type_bidir, "RC5", "PORTC", 5 ); + m_package->assignPin( 25, PicPin::type_bidir, "RC6", "PORTC", 6 ); + m_package->assignPin( 26, PicPin::type_bidir, "RC7", "PORTC", 7 ); + + m_package->assignPin( 19, PicPin::type_bidir, "RD0", "PORTD", 0 ); + m_package->assignPin( 20, PicPin::type_bidir, "RD1", "PORTD", 1 ); + m_package->assignPin( 21, PicPin::type_bidir, "RD2", "PORTD", 2 ); + m_package->assignPin( 22, PicPin::type_bidir, "RD3", "PORTD", 3 ); + m_package->assignPin( 27, PicPin::type_bidir, "RD4", "PORTD", 4 ); + m_package->assignPin( 28, PicPin::type_bidir, "RD5", "PORTD", 5 ); + m_package->assignPin( 29, PicPin::type_bidir, "RD6", "PORTD", 6 ); + m_package->assignPin( 30, PicPin::type_bidir, "RD7", "PORTD", 7 ); + + m_package->assignPin( 8, PicPin::type_bidir, "RE0", "PORTE", 0 ); + m_package->assignPin( 9, PicPin::type_bidir, "RE1", "PORTE", 1 ); + m_package->assignPin( 10, PicPin::type_bidir, "RE2", "PORTE", 2 ); + + m_package->assignPin( 1, PicPin::type_mclr, "MCLR" ); + m_package->assignPin( 11, PicPin::type_vdd, "VDD" ); + m_package->assignPin( 12, PicPin::type_vss, "VSS" ); + m_package->assignPin( 13, PicPin::type_osc, "OSC1" ); + m_package->assignPin( 14, PicPin::type_osc, "OSC2" ); + m_package->assignPin( 31, PicPin::type_vss, "VSS" ); + m_package->assignPin( 32, PicPin::type_vdd, "VDD" ); +} +PicInfo16C64::~PicInfo16C64() +{ + delete m_package; + m_package = 0l; +} + +PicInfo16C65::PicInfo16C65() + : PicInfo16C64() +{ + m_id = "P16C65"; +} +PicInfo16C65::~PicInfo16C65() +{ +} + +PicInfo16F62x::PicInfo16F62x() + : PicInfo16X6X() +{ + m_id = "P16F62x"; + + delete m_package; + m_package = new MicroPackage(18); + + m_package->assignPin( 17, PicPin::type_bidir, "RA0", "PORTA", 0 ); + m_package->assignPin( 18, PicPin::type_bidir, "RA1", "PORTA", 1 ); + m_package->assignPin( 1, PicPin::type_bidir, "RA2", "PORTA", 2 ); + m_package->assignPin( 2, PicPin::type_bidir, "RA3", "PORTA", 3 ); + m_package->assignPin( 3, PicPin::type_bidir, "RA4", "PORTA", 4 ); + m_package->assignPin( 4, PicPin::type_input, "RA5", "PORTA", 5 ); + m_package->assignPin( 15, PicPin::type_bidir, "RA6", "PORTA", 6 ); + m_package->assignPin( 16, PicPin::type_bidir, "RA7", "PORTA", 7 ); + + m_package->assignPin( 6, PicPin::type_bidir, "RB0", "PORTB", 0 ); + m_package->assignPin( 7, PicPin::type_bidir, "RB1", "PORTB", 1 ); + m_package->assignPin( 8, PicPin::type_bidir, "RB2", "PORTB", 2 ); + m_package->assignPin( 9, PicPin::type_bidir, "RB3", "PORTB", 3 ); + m_package->assignPin( 10, PicPin::type_bidir, "RB4", "PORTB", 4 ); + m_package->assignPin( 11, PicPin::type_bidir, "RB5", "PORTB", 5 ); + m_package->assignPin( 12, PicPin::type_bidir, "RB6", "PORTB", 6 ); + m_package->assignPin( 13, PicPin::type_bidir, "RB7", "PORTB", 7 ); + + m_package->assignPin( 5, PicPin::type_vss, "VSS" ); + m_package->assignPin( 14, PicPin::type_vdd, "VDD" ); +} +PicInfo16F62x::~PicInfo16F62x() +{ + delete m_package; + m_package = 0l; +} + +PicInfo16F627::PicInfo16F627() + : PicInfo16F62x() +{ + m_id = "P16F627"; +} +PicInfo16F627::~PicInfo16F627() +{ +} + +PicInfo16F628::PicInfo16F628() + : PicInfo16F627() +{ + m_id = "P16F628"; +} +PicInfo16F628::~PicInfo16F628() +{ +} + +PicInfo16F648::PicInfo16F648() + : PicInfo16F628() +{ + m_id = "P16F648"; +} +PicInfo16F648::~PicInfo16F648() +{ +} + +PicInfo16C71::PicInfo16C71() + : PicInfo16C61() +{ + m_id = "P16C71"; +} +PicInfo16C71::~PicInfo16C71() +{ +} + +PicInfo16C712::PicInfo16C712() + : PicInfo16C62() +{ + m_id = "P16C712"; +} +PicInfo16C712::~PicInfo16C712() +{ +} + +PicInfo16C716::PicInfo16C716() + : PicInfo16C712() +{ + m_id = "P16C716"; +} +PicInfo16C716::~PicInfo16C716() +{ +} + +PicInfo16C72::PicInfo16C72() + : PicInfo16C62() +{ + m_id = "P16C72"; +} +PicInfo16C72::~PicInfo16C72() +{ +} + +PicInfo16C73::PicInfo16C73() + : PicInfo16C63() +{ + m_id = "P16C73"; +} +PicInfo16C73::~PicInfo16C73() +{ +} + +PicInfo16C74::PicInfo16C74() + : PicInfo16C65() +{ + m_id = "P16C74"; +} +PicInfo16C74::~PicInfo16C74() +{ +} + +PicInfo16F873::PicInfo16F873() + : PicInfo16C73() +{ + m_id = "P16F873"; +} +PicInfo16F873::~PicInfo16F873() +{ +} + +PicInfo16F874::PicInfo16F874() + : PicInfo16C74() +{ + m_id = "P16F874"; +} +PicInfo16F874::~PicInfo16F874() +{ +} + +PicInfo16F877::PicInfo16F877() + : PicInfo16F874() +{ + m_id = "P16F877"; +} +PicInfo16F877::~PicInfo16F877() +{ +} + diff --git a/src/micro/picinfo14bit.h b/src/micro/picinfo14bit.h new file mode 100644 index 0000000..4f20cb1 --- /dev/null +++ b/src/micro/picinfo14bit.h @@ -0,0 +1,330 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef PICINFO14BIT_H +#define PICINFO14BIT_H + +#include "picinfo.h" +#include "asminfo.h" + +/** +@short 14 Bit PIC Instructions +@author David Saxton + */ +class PicAsm14bit : public AsmInfo +{ + public: + static PicAsm14bit *self(); + virtual Set set() const { return AsmInfo::PIC14; } + + protected: + static PicAsm14bit *m_self; + + private: + PicAsm14bit(); +}; + +/** +@author David Saxton + */ +class PicInfo14bit : public PicInfo +{ + public: + PicInfo14bit(); + ~PicInfo14bit(); + + virtual AsmInfo* instructionSet() { return PicAsm14bit::self(); } +}; + +/** +@author David Saxton + */ +class PicInfo16C8x : public PicInfo14bit +{ + public: + PicInfo16C8x(); + ~PicInfo16C8x(); +}; + +/** +@author David Saxton + */ +class PicInfo16C84 : public PicInfo16C8x +{ + public: + PicInfo16C84(); + ~PicInfo16C84(); + virtual Support gpsimSupport() const { return FullSupport; } + virtual Support microbeSupport() const { return FullSupport; } +}; + +/** +@author David Saxton + */ +class PicInfo16F84 : public PicInfo16C8x +{ + public: + PicInfo16F84(); + ~PicInfo16F84(); + virtual Support gpsimSupport() const { return FullSupport; } + virtual Support flowcodeSupport() const { return FullSupport; } + virtual Support microbeSupport() const { return FullSupport; } +}; + +/** +@author David Saxton + */ +class PicInfo16CR84 : public PicInfo16F84 +{ + public: + PicInfo16CR84(); + ~PicInfo16CR84(); + virtual Support gpsimSupport() const { return FullSupport; } + virtual Support flowcodeSupport() const { return NoSupport; } + virtual Support microbeSupport() const { return NoSupport; } +}; + +/** +@author David Saxton + */ +class PicInfo16F83 : public PicInfo16C8x +{ + public: + PicInfo16F83(); + ~PicInfo16F83(); + virtual Support gpsimSupport() const { return FullSupport; } +}; + +/** +@author David Saxton + */ +class PicInfo16CR83 : public PicInfo16F83 +{ + public: + PicInfo16CR83(); + ~PicInfo16CR83(); + virtual Support gpsimSupport() const { return FullSupport; } +}; + +/** +@author David Saxton + */ +class PicInfo16C61 : public PicInfo16C8x +{ + public: + PicInfo16C61(); + ~PicInfo16C61(); + virtual Support gpsimSupport() const { return FullSupport; } +}; + + +/** +@author David Saxton + */ +class PicInfo16X6X : public PicInfo14bit +{ + public: + PicInfo16X6X(); + ~PicInfo16X6X(); + virtual Support gpsimSupport() const { return NoSupport; } +}; + +/** +@author David Saxton + */ +class PicInfo16C62 : public PicInfo16X6X +{ + public: + PicInfo16C62(); + ~PicInfo16C62(); + virtual Support gpsimSupport() const { return FullSupport; } +}; + +/** +@author David Saxton + */ +class PicInfo16C63 : public PicInfo16C62 +{ + public: + PicInfo16C63(); + ~PicInfo16C63(); + virtual Support gpsimSupport() const { return FullSupport; } +}; + +/** +@author David Saxton + */ +class PicInfo16C64 : public PicInfo16X6X +{ + public: + PicInfo16C64(); + ~PicInfo16C64(); + virtual Support gpsimSupport() const { return PartialSupport; } +}; + +/** +@author David Saxton + */ +class PicInfo16C65 : public PicInfo16C64 +{ + public: + PicInfo16C65(); + ~PicInfo16C65(); + virtual Support gpsimSupport() const { return PartialSupport; } +}; + +/** +@author David Saxton + */ +class PicInfo16F62x : public PicInfo16X6X +{ + public: + PicInfo16F62x(); + ~PicInfo16F62x(); + virtual Support gpsimSupport() const { return NoSupport; } +}; + +/** +@author David Saxton + */ +class PicInfo16F627 : public PicInfo16F62x +{ + public: + PicInfo16F627(); + ~PicInfo16F627(); + virtual Support gpsimSupport() const { return FullSupport; } + virtual Support flowcodeSupport() const { return PartialSupport; } + virtual Support microbeSupport() const { return FullSupport; } +}; + +/** +@author David Saxton + */ +class PicInfo16F628 : public PicInfo16F627 +{ + public: + PicInfo16F628(); + ~PicInfo16F628(); + virtual Support gpsimSupport() const { return FullSupport; } + virtual Support flowcodeSupport() const { return PartialSupport; } + virtual Support microbeSupport() const { return FullSupport; } +}; + +/** +@author David Saxton + */ +class PicInfo16F648 : public PicInfo16F628 +{ + public: + PicInfo16F648(); + ~PicInfo16F648(); + virtual Support gpsimSupport() const { return NoSupport; } +}; + +/** +@author David Saxton + */ +class PicInfo16C71 : public PicInfo16C61 +{ + public: + PicInfo16C71(); + ~PicInfo16C71(); + virtual Support gpsimSupport() const { return FullSupport; } +}; + +/** +@author David Saxton + */ +class PicInfo16C712 : public PicInfo16C62 +{ + public: + PicInfo16C712(); + ~PicInfo16C712(); + virtual Support gpsimSupport() const { return FullSupport; } +}; + +/** +@author David Saxton + */ +class PicInfo16C716 : public PicInfo16C712 +{ + public: + PicInfo16C716(); + ~PicInfo16C716(); + virtual Support gpsimSupport() const { return FullSupport; } +}; + +/** +@author David Saxton + */ +class PicInfo16C72 : public PicInfo16C62 +{ + public: + PicInfo16C72(); + ~PicInfo16C72(); + virtual Support gpsimSupport() const { return PartialSupport; } +}; + +/** +@author David Saxton + */ +class PicInfo16C73 : public PicInfo16C63 +{ + public: + PicInfo16C73(); + ~PicInfo16C73(); + virtual Support gpsimSupport() const { return PartialSupport; } +}; + +/** +@author David Saxton + */ +class PicInfo16C74 : public PicInfo16C65 +{ + public: + PicInfo16C74(); + ~PicInfo16C74(); + virtual Support gpsimSupport() const { return PartialSupport; } +}; + +/** +@author David Saxton + */ +class PicInfo16F873 : public PicInfo16C73 +{ + public: + PicInfo16F873(); + ~PicInfo16F873(); + virtual Support gpsimSupport() const { return PartialSupport; } +}; + +/** +@author David Saxton + */ +class PicInfo16F874 : public PicInfo16C74 +{ + public: + PicInfo16F874(); + ~PicInfo16F874(); + virtual Support gpsimSupport() const { return PartialSupport; } +}; + +/** +@author David Saxton + */ +class PicInfo16F877 : public PicInfo16F874 +{ + public: + PicInfo16F877(); + ~PicInfo16F877(); + virtual Support gpsimSupport() const { return PartialSupport; } +}; + +#endif diff --git a/src/micro/picinfo16bit.cpp b/src/micro/picinfo16bit.cpp new file mode 100644 index 0000000..cc7663f --- /dev/null +++ b/src/micro/picinfo16bit.cpp @@ -0,0 +1,345 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "micropackage.h" +#include "picinfo16bit.h" + +#include <klocale.h> +#include <kstaticdeleter.h> + +PicAsm16bit *PicAsm16bit::m_self = 0; +static KStaticDeleter<PicAsm16bit> staticDeleter; + +PicAsm16bit *PicAsm16bit::self() +{ + if ( !m_self ) staticDeleter.setObject( m_self, new PicAsm16bit() ); + return m_self; +} + +PicInfo16bit::PicInfo16bit() + : PicInfo() +{ +} + + +PicInfo16bit::~PicInfo16bit() +{ +} + + + +PicAsm16bit::PicAsm16bit() + : AsmInfo() +{ + // TODO 16 bit Asm instructions for PICs + + // Byte-orientated file register operations + addInstruction( "ADDWF", 0, "000111dfffffff" ); + addInstruction( "ANDWF", 0, "000101dfffffff" ); + addInstruction( "CLRF", 0, "0000011fffffff" ); + addInstruction( "CLRW", 0, "0000010xxxxxxx" ); + addInstruction( "COMF", 0, "001001dfffffff" ); + addInstruction( "DECF", 0, "000011dfffffff" ); + addInstruction( "DECFSZ", 0, "001011dfffffff" ); + addInstruction( "INCF", 0, "001010dfffffff" ); + addInstruction( "INCFSZ", 0, "001111dfffffff" ); + addInstruction( "IORWF", 0, "000100dfffffff" ); + addInstruction( "MOVF", 0, "001000dfffffff" ); + addInstruction( "MOVWF", 0, "0000001fffffff" ); + addInstruction( "NOP", 0, "0000000xx00000" ); + addInstruction( "RLF", 0, "001101dfffffff" ); + addInstruction( "RRF", 0, "001100dfffffff" ); + addInstruction( "SUBWF", 0, "000010dfffffff" ); + addInstruction( "SWAPF", 0, "001110dfffffff" ); + addInstruction( "XORWF", 0, "000110dfffffff" ); + + // Bit-orientated file register operations + addInstruction( "BCF", 0, "0100bbbfffffff" ); + addInstruction( "BSF", 0, "0101bbbfffffff" ); + addInstruction( "BTFSC", 0, "0110bbbfffffff" ); + addInstruction( "BTFSS", 0, "0111bbbfffffff" ); + + // Literal and control operations + addInstruction( "ADDLW", 0, "11111xkkkkkkkk" ); + addInstruction( "ANDLW", 0, "111001kkkkkkkk" ); + addInstruction( "CALL", 0, "100kkkkkkkkkkk" ); + addInstruction( "CLRWDT", 0, "00000001100100" ); + addInstruction( "GOTO", 0, "101kkkkkkkkkkk" ); + addInstruction( "IORLW", 0, "111000kkkkkkkk" ); + addInstruction( "MOVLW", 0, "1100xxkkkkkkkk" ); + addInstruction( "RETFIE", 0, "00000000001001" ); + addInstruction( "RETLW", 0, "1101xxkkkkkkkk" ); + addInstruction( "RETURN", 0, "00000000001000" ); + addInstruction( "SLEEP", 0, "00000000000011" ); + addInstruction( "SUBLW", 0, "11110xkkkkkkkk" ); + addInstruction( "XORLW", 0, "111010kkkkkkkk" ); +} + + +PicInfo17C7xx::PicInfo17C7xx() + : PicInfo16bit() +{ + m_id = "P17C7xx"; +} +PicInfo17C7xx::~PicInfo17C7xx() +{ +} + +PicInfo17C75x::PicInfo17C75x() + : PicInfo17C7xx() +{ + m_id = "P17C75x"; +} +PicInfo17C75x::~PicInfo17C75x() +{ +} + +PicInfo17C752::PicInfo17C752() + : PicInfo17C75x() +{ + m_id = "P17C752"; +} +PicInfo17C752::~PicInfo17C752() +{ +} + +PicInfo17C756::PicInfo17C756() + : PicInfo17C75x() +{ + m_id = "P17C756"; +} +PicInfo17C756::~PicInfo17C756() +{ +} + +PicInfo17C756A::PicInfo17C756A() + : PicInfo17C75x() +{ + m_id = "P17C756A"; +} +PicInfo17C756A::~PicInfo17C756A() +{ +} + +PicInfo17C762::PicInfo17C762() + : PicInfo17C75x() +{ + m_id = "P17C762"; +} +PicInfo17C762::~PicInfo17C762() +{ +} + +PicInfo17C766::PicInfo17C766() + : PicInfo17C75x() +{ + m_id = "P17C766"; +} +PicInfo17C766::~PicInfo17C766() +{ +} + +PicInfo18Cxx2::PicInfo18Cxx2() + : PicInfo16bit() +{ + m_id = "P18Cxx2"; +} +PicInfo18Cxx2::~PicInfo18Cxx2() +{ +} + +PicInfo18C2x2::PicInfo18C2x2() + : PicInfo16bit() +{ + m_id = "P18C2x2"; +} +PicInfo18C2x2::~PicInfo18C2x2() +{ +} + +PicInfo18C242::PicInfo18C242() + : PicInfo18C2x2() +{ + m_id = "P18C242"; +} +PicInfo18C242::~PicInfo18C242() +{ +} + +PicInfo18C252::PicInfo18C252() + : PicInfo18C242() +{ + m_id = "P18C252"; +} +PicInfo18C252::~PicInfo18C252() +{ +} + +PicInfo18C4x2::PicInfo18C4x2() + : PicInfo16bit() +{ + m_id = "P18C4x2"; + + delete m_package; + m_package = new MicroPackage(40); + + m_package->assignPin( 2, PicPin::type_bidir, "RA0", "PORTA", 0 ); + m_package->assignPin( 3, PicPin::type_bidir, "RA1", "PORTA", 1 ); + m_package->assignPin( 4, PicPin::type_bidir, "RA2", "PORTA", 2 ); + m_package->assignPin( 5, PicPin::type_bidir, "RA3", "PORTA", 3 ); + m_package->assignPin( 6, PicPin::type_open, "RA4", "PORTA", 4 ); + m_package->assignPin( 7, PicPin::type_bidir, "RA5", "PORTB", 5 ); + + m_package->assignPin( 33, PicPin::type_bidir, "RB0", "PORTB", 0 ); + m_package->assignPin( 34, PicPin::type_bidir, "RB1", "PORTB", 1 ); + m_package->assignPin( 35, PicPin::type_bidir, "RB2", "PORTB", 2 ); + m_package->assignPin( 36, PicPin::type_bidir, "RB3", "PORTB", 3 ); + m_package->assignPin( 37, PicPin::type_bidir, "RB4", "PORTB", 4 ); + m_package->assignPin( 38, PicPin::type_bidir, "RB5", "PORTB", 5 ); + m_package->assignPin( 39, PicPin::type_bidir, "RB6", "PORTB", 6 ); + m_package->assignPin( 40, PicPin::type_bidir, "RB7", "PORTB", 7 ); + + m_package->assignPin( 15, PicPin::type_bidir, "RC0", "PORTC", 0 ); + m_package->assignPin( 16, PicPin::type_bidir, "RC1", "PORTC", 1 ); + m_package->assignPin( 17, PicPin::type_bidir, "RC2", "PORTC", 2 ); + m_package->assignPin( 18, PicPin::type_bidir, "RC3", "PORTC", 3 ); + m_package->assignPin( 23, PicPin::type_bidir, "RC4", "PORTC", 4 ); + m_package->assignPin( 24, PicPin::type_bidir, "RC5", "PORTC", 5 ); + m_package->assignPin( 25, PicPin::type_bidir, "RC6", "PORTC", 6 ); + m_package->assignPin( 26, PicPin::type_bidir, "RC7", "PORTC", 7 ); + + m_package->assignPin( 19, PicPin::type_bidir, "RD0", "PORTD", 0 ); + m_package->assignPin( 20, PicPin::type_bidir, "RD1", "PORTD", 1 ); + m_package->assignPin( 21, PicPin::type_bidir, "RD2", "PORTD", 2 ); + m_package->assignPin( 22, PicPin::type_bidir, "RD3", "PORTD", 3 ); + m_package->assignPin( 27, PicPin::type_bidir, "RD4", "PORTD", 4 ); + m_package->assignPin( 28, PicPin::type_bidir, "RD5", "PORTD", 5 ); + m_package->assignPin( 29, PicPin::type_bidir, "RD6", "PORTD", 6 ); + m_package->assignPin( 30, PicPin::type_bidir, "RD7", "PORTD", 7 ); + + m_package->assignPin( 8, PicPin::type_bidir, "RE0", "PORTE", 0 ); + m_package->assignPin( 9, PicPin::type_bidir, "RE1", "PORTE", 1 ); + m_package->assignPin( 10, PicPin::type_bidir, "RE2", "PORTE", 2 ); + + m_package->assignPin( 1, PicPin::type_mclr, "MCLR" ); + m_package->assignPin( 11, PicPin::type_vdd, "VDD" ); + m_package->assignPin( 12, PicPin::type_vss, "VSS" ); + m_package->assignPin( 13, PicPin::type_osc, "OSC1" ); + m_package->assignPin( 14, PicPin::type_osc, "OSC2" ); + m_package->assignPin( 31, PicPin::type_vss, "VSS" ); + m_package->assignPin( 32, PicPin::type_vdd, "VDD" ); +} + +PicInfo18C4x2::~PicInfo18C4x2() +{ + delete m_package; + m_package = 0l; +} + +PicInfo18C442::PicInfo18C442() + : PicInfo18C4x2() +{ + m_id = "P18C442"; +} +PicInfo18C442::~PicInfo18C442() +{ +} + +PicInfo18C452::PicInfo18C452() + : PicInfo18C442() +{ + m_id = "P18C452"; +} +PicInfo18C452::~PicInfo18C452() +{ +} + +PicInfo18F442::PicInfo18F442() + : PicInfo18C442() +{ + m_id = "P18F442"; +} +PicInfo18F442::~PicInfo18F442() +{ +} + +PicInfo18F248::PicInfo18F248() + : PicInfo18F442() +{ + m_id = "P18F248"; +} +PicInfo18F248::~PicInfo18F248() +{ +} + +PicInfo18F452::PicInfo18F452() + : PicInfo18F442() +{ + m_id = "P18F452"; +} +PicInfo18F452::~PicInfo18F452() +{ +} + +PicInfo18Fxx20::PicInfo18Fxx20() + : PicInfo16bit() +{ + m_id = "P18Fxx20"; +} +PicInfo18Fxx20::~PicInfo18Fxx20() +{ +} + +PicInfo18F1220::PicInfo18F1220() + : PicInfo18Fxx20() +{ + m_id = "P18F1220"; + + delete m_package; + m_package = new MicroPackage(18); + + m_package->assignPin( 1, PicPin::type_bidir, "RA0", "PORTA", 0 ); + m_package->assignPin( 2, PicPin::type_bidir, "RA1", "PORTA", 1 ); + m_package->assignPin( 6, PicPin::type_bidir, "RA2", "PORTA", 2 ); + m_package->assignPin( 7, PicPin::type_bidir, "RA3", "PORTA", 3 ); + m_package->assignPin( 3, PicPin::type_open, "RA4", "PORTA", 4 ); + m_package->assignPin( 4, PicPin::type_open, "RA5", "PORTA", 5 ); + m_package->assignPin( 15, PicPin::type_open, "RA6", "PORTA", 6 ); + m_package->assignPin( 16, PicPin::type_open, "RA7", "PORTA", 7 ); + + m_package->assignPin( 8, PicPin::type_bidir, "RB0", "PORTB", 0 ); + m_package->assignPin( 9, PicPin::type_bidir, "RB1", "PORTB", 1 ); + m_package->assignPin( 17, PicPin::type_bidir, "RB2", "PORTB", 2 ); + m_package->assignPin( 18, PicPin::type_bidir, "RB3", "PORTB", 3 ); + m_package->assignPin( 10, PicPin::type_bidir, "RB4", "PORTB", 4 ); + m_package->assignPin( 11, PicPin::type_bidir, "RB5", "PORTB", 5 ); + m_package->assignPin( 12, PicPin::type_bidir, "RB6", "PORTB", 6 ); + m_package->assignPin( 13, PicPin::type_bidir, "RB7", "PORTB", 7 ); + + m_package->assignPin( 5, PicPin::type_vss, "VSS" ); + m_package->assignPin( 14, PicPin::type_vdd, "VDD" ); +} + +PicInfo18F1220::~PicInfo18F1220() +{ + delete m_package; + m_package = 0l; +} + +PicInfo18F1320::PicInfo18F1320() + : PicInfo18F1220() +{ + m_id = "P18F1320"; +} +PicInfo18F1320::~PicInfo18F1320() +{ +} + + diff --git a/src/micro/picinfo16bit.h b/src/micro/picinfo16bit.h new file mode 100644 index 0000000..975c23c --- /dev/null +++ b/src/micro/picinfo16bit.h @@ -0,0 +1,266 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef PICINFO16BIT_H +#define PICINFO16BIT_H + +#include "picinfo.h" +#include "asminfo.h" + +/** +@short 16 Bit PIC Instructions +@author David Saxton + */ +class PicAsm16bit : public AsmInfo +{ + public: + static PicAsm16bit *self(); + virtual Set set() const { return AsmInfo::PIC16; } + + protected: + static PicAsm16bit *m_self; + + private: + PicAsm16bit(); +}; + +/** +@author David Saxton + */ +class PicInfo16bit : public PicInfo +{ + public: + PicInfo16bit(); + ~PicInfo16bit(); + + virtual AsmInfo * instructionSet() { return PicAsm16bit::self(); } +}; + +/** +@author David Saxton + */ +class PicInfo17C7xx : public PicInfo16bit +{ + public: + PicInfo17C7xx(); + ~PicInfo17C7xx(); + virtual Support gpsimSupport() const { return NoSupport; } +}; + +/** +@author David Saxton + */ +class PicInfo17C75x : public PicInfo17C7xx +{ + public: + PicInfo17C75x(); + ~PicInfo17C75x(); + virtual Support gpsimSupport() const { return NoSupport; } +}; + +/** +@author David Saxton + */ +class PicInfo17C752 : public PicInfo17C75x +{ + public: + PicInfo17C752(); + ~PicInfo17C752(); + virtual Support gpsimSupport() const { return PartialSupport; } +}; + +/** +@author David Saxton + */ +class PicInfo17C756 : public PicInfo17C75x +{ + public: + PicInfo17C756(); + ~PicInfo17C756(); + virtual Support gpsimSupport() const { return PartialSupport; } +}; + +/** +@author David Saxton + */ +class PicInfo17C756A : public PicInfo17C75x +{ + public: + PicInfo17C756A(); + ~PicInfo17C756A(); + virtual Support gpsimSupport() const { return NoSupport; } +}; + +/** +@author David Saxton + */ +class PicInfo17C762 : public PicInfo17C75x +{ + public: + PicInfo17C762(); + ~PicInfo17C762(); + virtual Support gpsimSupport() const { return PartialSupport; } +}; + +/** +@author David Saxton + */ +class PicInfo17C766 : public PicInfo17C75x +{ + public: + PicInfo17C766(); + ~PicInfo17C766(); + virtual Support gpsimSupport() const { return PartialSupport; } +}; + +/** +@author David Saxton + */ +class PicInfo18Cxx2 : public PicInfo16bit +{ + public: + PicInfo18Cxx2(); + ~PicInfo18Cxx2(); + virtual Support gpsimSupport() const { return NoSupport; } +}; + +/** +@author David Saxton + */ +class PicInfo18C2x2 : public PicInfo16bit +{ + public: + PicInfo18C2x2(); + ~PicInfo18C2x2(); + virtual Support gpsimSupport() const { return NoSupport; } +}; + +/** +@author David Saxton + */ +class PicInfo18C242 : public PicInfo18C2x2 +{ + public: + PicInfo18C242(); + ~PicInfo18C242(); + virtual Support gpsimSupport() const { return PartialSupport; } +}; + +/** +@author David Saxton + */ +class PicInfo18C252 : public PicInfo18C242 +{ + public: + PicInfo18C252(); + ~PicInfo18C252(); + virtual Support gpsimSupport() const { return PartialSupport; } +}; + +/** +@author David Saxton + */ +class PicInfo18C4x2 : public PicInfo16bit +{ + public: + PicInfo18C4x2(); + ~PicInfo18C4x2(); + virtual Support gpsimSupport() const { return NoSupport; } +}; + +/** +@author David Saxton + */ +class PicInfo18C442 : public PicInfo18C4x2 +{ + public: + PicInfo18C442(); + ~PicInfo18C442(); + virtual Support gpsimSupport() const { return PartialSupport; } +}; + +/** +@author David Saxton + */ +class PicInfo18C452 : public PicInfo18C442 +{ + public: + PicInfo18C452(); + ~PicInfo18C452(); + virtual Support gpsimSupport() const { return PartialSupport; } +}; + +/** +@author David Saxton + */ +class PicInfo18F442 : public PicInfo18C442 +{ + public: + PicInfo18F442(); + ~PicInfo18F442(); + virtual Support gpsimSupport() const { return PartialSupport; } +}; + +/** +@author David Saxton + */ +class PicInfo18F248 : public PicInfo18F442 +{ + public: + PicInfo18F248(); + ~PicInfo18F248(); + virtual Support gpsimSupport() const { return NoSupport; } +}; + +/** +@author David Saxton + */ +class PicInfo18F452 : public PicInfo18F442 +{ + public: + PicInfo18F452(); + ~PicInfo18F452(); + virtual Support gpsimSupport() const { return PartialSupport; } +}; + +/** +@author David Saxton + */ +class PicInfo18Fxx20 : public PicInfo16bit +{ + public: + PicInfo18Fxx20(); + ~PicInfo18Fxx20(); + virtual Support gpsimSupport() const { return NoSupport; } +}; + +/** +@author David Saxton + */ +class PicInfo18F1220 : public PicInfo18Fxx20 +{ + public: + PicInfo18F1220(); + ~PicInfo18F1220(); + virtual Support gpsimSupport() const { return NoSupport; } +}; + +/** +@author David Saxton + */ +class PicInfo18F1320 : public PicInfo18F1220 +{ + public: + PicInfo18F1320(); + ~PicInfo18F1320(); + virtual Support gpsimSupport() const { return NoSupport; } +}; + +#endif diff --git a/src/microbe.xml b/src/microbe.xml new file mode 100644 index 0000000..0168c9e --- /dev/null +++ b/src/microbe.xml @@ -0,0 +1,139 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE language SYSTEM "language.dtd"> +<language name="Microbe" version="0.01" section="Sources" extensions="*.microbe;*.basic" mimetypes="application/x-microbe"> + <highlighting> + <list name="keywords"> + + <item> asm </item> + + <item> high </item> + <item> low </item> + + <item> for </item> + <item> to </item> + <item> step </item> + + <item> if </item> + <item> then </item> + <item> else </item> + + <item> while </item> + + <item> alias </item> + <item> repeat </item> + <item> until </item> + <item> end </item> + </list> + <list name="procedurekeywords"> + <item> call </item> + <item> goto </item> + <item> delay </item> + </list> + <contexts> + <context attribute="Normal Text" lineEndContext="#stay" name="Normal"> + <RegExpr attribute="Decimal" context="#stay" String="//\s*BEGIN.*$" beginRegion="Region1"/> + <RegExpr attribute="Decimal" context="#stay" String="//\s*END.*$" endRegion="Region1"/> + <keyword attribute="Keyword" context="#stay" String="keywords" /> + <keyword attribute="Extensions" context="#stay" String="extensions" /> + <keyword attribute="Function" context="#pop" String="procedurekeywords" /> + <keyword attribute="Data Type" context="#stay" String="types" /> + <Float attribute="Float" context="#stay"> + <AnyChar String="fF" attribute="Float" context="#stay"/> + </Float> + <HlCOct attribute="Octal" context="#stay"/> + <HlCHex attribute="Hex" context="#stay"/> + <Int attribute="Decimal" context="#stay"> + <StringDetect attribute="Decimal" context="#stay" String="ULL" insensitive="TRUE"/> + <StringDetect attribute="Decimal" context="#stay" String="LUL" insensitive="TRUE"/> + <StringDetect attribute="Decimal" context="#stay" String="LLU" insensitive="TRUE"/> + <StringDetect attribute="Decimal" context="#stay" String="UL" insensitive="TRUE"/> + <StringDetect attribute="Decimal" context="#stay" String="LU" insensitive="TRUE"/> + <StringDetect attribute="Decimal" context="#stay" String="LL" insensitive="TRUE"/> + <StringDetect attribute="Decimal" context="#stay" String="U" insensitive="TRUE"/> + <StringDetect attribute="Decimal" context="#stay" String="L" insensitive="TRUE"/> + </Int> + <HlCChar attribute="Char" context="#stay"/> + <DetectChar attribute="String" context="String" char="""/> + <Detect2Chars attribute="Comment" context="Commentar 1" char="/" char1="/"/> + <Detect2Chars attribute="Comment" context="Commentar 2" char="/" char1="*" beginRegion="Comment"/> + <DetectChar attribute="Symbol" context="#stay" char="{" beginRegion="Brace1" /> + <DetectChar attribute="Symbol" context="#stay" char="}" endRegion="Brace1" /> + <RegExpr attribute="Preprocessor" context="Outscoped" String="^\s*#\s*if\s+0" beginRegion="Outscoped"/> + <RegExpr attribute="Preprocessor" context="Preprocessor" String="^\s*#"/> + <RegExpr attribute="Function" context="#stay" String="\b[_\w][_\d\w]*(?=[\s]*[(])" /> + <RegExpr attribute="Symbol" context="Member" String="([.]{1,1}|[:]{2,2})" /> + <AnyChar attribute="Symbol" context="#stay" String=":!%&()+,-/.*<=>?[]{|}~^;"/> + </context> + <context attribute="String" lineEndContext="#pop" name="String"> + <LineContinue attribute="String" context="#stay"/> + <HlCStringChar attribute="String Char" context="#stay"/> + <DetectChar attribute="String" context="#pop" char="""/> + </context> + <context attribute="Normal Text" lineEndContext="#pop" name="Member" fallthrough="true" fallthroughContext="#pop"> + <RegExpr attribute="Function" context="#pop" String="\b[_\w][_\w\d]*(?=[\s]*)" /> + </context> + <context attribute="Comment" lineEndContext="#pop" name="Commentar 1"> + <keyword attribute="Decimal" context="#stay" String="attention" /> + </context> + <context attribute="Comment" lineEndContext="#stay" name="Commentar 2"> + <keyword attribute="Decimal" context="#stay" String="attention" /> + <Detect2Chars attribute="Comment" context="#pop" char="*" char1="/" endRegion="Comment"/> + </context> + <context attribute="Preprocessor" lineEndContext="#pop" name="Preprocessor"> + <LineContinue attribute="Preprocessor" context="#stay"/> + <RegExpr attribute="Preprocessor" context="Define" String="define.*((?=\\))"/> + <RegExpr attribute="Preprocessor" context="#stay" String="define.*"/> + <RangeDetect attribute="Prep. Lib" context="#stay" char=""" char1="""/> + <RangeDetect attribute="Prep. Lib" context="#stay" char="<" char1=">"/> + <Detect2Chars attribute="Comment" context="Commentar 1" char="/" char1="/"/> + <Detect2Chars attribute="Comment" context="Commentar/Preprocessor" char="/" char1="*"/> + </context> + <context attribute="Preprocessor" lineEndContext="#pop" name="Define"> + <LineContinue attribute="Preprocessor" context="#stay"/> + </context> + <context attribute="Comment" lineEndContext="#stay" name="Commentar/Preprocessor"> + <Detect2Chars attribute="Comment" context="#pop" char="*" char1="/" /> + </context> + <context attribute="Comment" lineEndContext="#stay" name="Outscoped" > + <Detect2Chars attribute="Comment" context="Commentar 2" char="/" char1="*" beginRegion="Comment"/> + <keyword attribute="Decimal" context="#stay" String="attention" /> + <RegExpr attribute="Comment" context="Outscoped intern" String="^\s*#\s*if" beginRegion="Outscoped"/> + <RegExpr attribute="Preprocessor" context="#pop" String="^\s*#\s*(endif|else|elif)" endRegion="Outscoped"/> + </context> + <context attribute="Comment" lineEndContext="#stay" name="Outscoped intern"> + <Detect2Chars attribute="Comment" context="Commentar 2" char="/" char1="*" beginRegion="Comment"/> + <RegExpr attribute="Comment" context="Outscoped intern" String="^\s*#\s*if" beginRegion="Outscoped"/> + <RegExpr attribute="Comment" context="#pop" String="^\s*#\s*endif" endRegion="Outscoped"/> + </context> + <context attribute="Function" lineEndContext="#stay" name="Procedure call"> + + </context> + </contexts> + <itemDatas> + <itemData name="Normal Text" defStyleNum="dsNormal"/> + <itemData name="Keyword" defStyleNum="dsKeyword"/> + <itemData name="Extensions" defStyleNum="dsKeyword" color="#0095ff" selColor="#ffffff" bold="1" italic="0"/> + <itemData name="Function" defStyleNum="dsKeyword" color="#000080" selColor="#ffffff" bold="0" italic="0"/> + <itemData name="Data Type" defStyleNum="dsDataType"/> + <itemData name="Decimal" defStyleNum="dsDecVal"/> + <itemData name="Octal" defStyleNum="dsBaseN"/> + <itemData name="Hex" defStyleNum="dsBaseN"/> + <itemData name="Float" defStyleNum="dsFloat"/> + <itemData name="Char" defStyleNum="dsChar"/> + <itemData name="String" defStyleNum="dsString"/> + <itemData name="String Char" defStyleNum="dsChar"/> + <itemData name="Comment" defStyleNum="dsComment"/> + <itemData name="Symbol" defStyleNum="dsNormal"/> + <itemData name="Preprocessor" defStyleNum="dsOthers"/> + <itemData name="Prep. Lib" defStyleNum="dsOthers"/> + </itemDatas> + </highlighting> + <general> + <comments> + <comment name="singleLine" start="//" /> + <comment name="multiLine" start="/*" end="*/" /> + </comments> + <keywords casesensitive="1" /> + </general> +</language> + diff --git a/src/microsettings.cpp b/src/microsettings.cpp new file mode 100644 index 0000000..4554dc9 --- /dev/null +++ b/src/microsettings.cpp @@ -0,0 +1,338 @@ +/*************************************************************************** + * Copyright (C) 2003-2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "itemdocumentdata.h" +#include "microinfo.h" +#include "micropackage.h" +#include "pinmapping.h" + + +//BEGIN class VariableInfo +VariableInfo::VariableInfo() +{ + type = MicroSettings::vt_unknown; + value = QVariant(0); + initAtStart = false; + permanent = false; +} + +void VariableInfo::setValue( const QVariant & _value ) +{ + value = _value; +} + +QString VariableInfo::valueAsString() const +{ + if ( value.canCast(QVariant::String) ) return value.toString(); + if ( value.canCast(QVariant::Int) ) return QString::number(value.toInt()); + return "0"; +} +//END class VariableInfo + + + +//BEGIN class PinSettings +PinSettings::PinSettings() + : QObject() +{ + m_type = PinSettings::pt_input; + m_state = PinSettings::ps_off; + m_id = "pin"; +} + +PinSettings::PinSettings( PinSettings::pin_type _type, PinSettings::pin_state _state, const QString &id, const QString &port ) +{ + m_type = _type; + m_state = _state; + m_id = id; + m_port = port; +} + + +void PinSettings::setType( PinSettings::pin_type type ) +{ + if ( m_type == type ) + return; + m_type = type; + emit settingsChanged(); +} + + +void PinSettings::setState( PinSettings::pin_state state ) +{ + if ( m_state == state) + return; + m_state = state; + emit settingsChanged(); +} +//END class PinSettings + + + +//BEGIN class MicroSettings +MicroSettings::MicroSettings( MicroInfo * microInfo ) +{ + _microInfo = microInfo; + + QStringList portNames = _microInfo->package()->portNames(); + const QStringList::iterator portNamesEnd = portNames.end(); + for ( QStringList::iterator it = portNames.begin(); it != portNamesEnd; ++it ) + { + PinSettingsList portPins; + QStringList pinIDs; + pinIDs = _microInfo->package()->pinIDs( PicPin::type_bidir | PicPin::type_open, *it ); + pinIDs.sort(); + const int numPins = pinIDs.size(); + for ( int i=0; i<numPins; i++ ) + { + PinSettings *pinSettings = new PinSettings( PinSettings::pt_input, PinSettings::ps_off, pinIDs[i], *it ); + m_pinSettingsList.append(pinSettings); + portPins.append(pinSettings); + } + m_ports[*it] = portPins; + } +} + +MicroSettings::~MicroSettings() +{ + const PinSettingsList::iterator pinListEnd = m_pinSettingsList.end(); + for ( PinSettingsList::iterator it = m_pinSettingsList.begin(); it != pinListEnd; ++it ) + { + delete *it; + } +// delete m_variableList; +} + +void MicroSettings::setPinType( const QString &id, PinSettings::pin_type type ) +{ + PinSettings *pin = pinWithID(id); + if (pin) pin->setType(type); +} + +void MicroSettings::setPinState( const QString &id, PinSettings::pin_state state ) +{ + PinSettings *pin = pinWithID(id); + if (pin) + pin->setState(state); +} + + +PinSettings* MicroSettings::pinWithID( const QString &id ) +{ + const PinSettingsList::iterator pinListEnd = m_pinSettingsList.end(); + for ( PinSettingsList::iterator it = m_pinSettingsList.begin(); it != pinListEnd; ++it ) + { + if ( (*it)->id() == id ) return *it; + } + return 0L; +} + +int MicroSettings::portState( const QString &port ) +{ + if ( microInfo()->package()->portNames().findIndex(port) == -1 ) return -1; + + int pinPower = 1; + int num = 0; + + const PinSettingsList::iterator pinListEnd = m_pinSettingsList.end(); + for ( PinSettingsList::iterator it = m_pinSettingsList.begin(); it != pinListEnd; ++it ) + { + if ( (*it)->port() == port ) + { + if ( (*it)->state() == PinSettings::ps_on ) num += pinPower; +// cout << "(*it)->state()="<<(*it)->state()<<endl; + pinPower *= 2; + } + } + + return num; +} + +int MicroSettings::portType( const QString &port ) +{ + if ( microInfo()->package()->portNames().findIndex(port) == -1 ) return -1; + + int pinPower = 1; + int num = 0; + + const PinSettingsList::iterator pinListEnd = m_pinSettingsList.end(); + for ( PinSettingsList::iterator it = m_pinSettingsList.begin(); it != pinListEnd; ++it ) + { + if ( (*it)->port() == port ) + { + if ( (*it)->type() == PinSettings::pt_input ) num += pinPower; + pinPower *= 2; + } + } + + return num; +} + +void MicroSettings::setPortState( const QString &port, int state ) +{ + PortList::iterator plit = m_ports.find(port); + if ( plit == m_ports.end() ) return; + + const PinSettingsList::iterator plitEnd = plit.data().end(); + for ( PinSettingsList::iterator it = plit.data().begin(); it != plitEnd; ++it ) + { +// cout << "state="<<state<<endl; + (*it)->setState( (state%2 == 1) ? PinSettings::ps_on : PinSettings::ps_off ); +// cout << "(*it)->state()="<<(*it)->state()<<endl; + state /= 2; + } +} + +void MicroSettings::setPortType( const QString &port, int type ) +{ + PortList::iterator plit = m_ports.find(port); + if ( plit == m_ports.end() ) return; + + const PinSettingsList::iterator plitEnd = plit.data().end(); + for ( PinSettingsList::iterator it = plit.data().begin(); it != plitEnd; ++it ) + { + (*it)->setType( (type%2 == 1) ? PinSettings::pt_input : PinSettings::pt_output ); + type /= 2; + } +} + + +MicroData MicroSettings::microData() const +{ + MicroData data; + data.id = microInfo()->id(); + data.pinMappings = pinMappings(); + + const PinSettingsList::const_iterator pinListEnd = m_pinSettingsList.end(); + for ( PinSettingsList::const_iterator it = m_pinSettingsList.begin(); it != pinListEnd; ++it ) + { + data.pinMap[(*it)->id()].type = (*it)->type(); + data.pinMap[(*it)->id()].state= (*it)->state(); + } + + const VariableMap::const_iterator variableMapEnd = m_variableMap.end(); + for ( VariableMap::const_iterator it = m_variableMap.begin(); it != variableMapEnd; ++it ) + { + if ( it.data().permanent ) + data.variableMap[it.key()] = it.data().valueAsString(); + } + + return data; +} + + +void MicroSettings::restoreFromMicroData( const MicroData µData ) +{ + setPinMappings( microData.pinMappings ); + + const PinDataMap::const_iterator pinMapEnd = microData.pinMap.end(); + for ( PinDataMap::const_iterator it = microData.pinMap.begin(); it != pinMapEnd; ++it ) + { + PinSettings *pin = pinWithID(it.key()); + if (pin) + { + pin->setState( it.data().state ); + pin->setType( it.data().type ); + } + } + + const QStringMap::const_iterator variableMapEnd = microData.variableMap.end(); + for ( QStringMap::const_iterator it = microData.variableMap.begin(); it != variableMapEnd; ++it ) + { + setVariable( it.key(), it.data(), true ); + } +} + + +void MicroSettings::setVariable( const QString &name, QVariant value, bool permanent ) +{ + if ( name.isNull() ) return; + VariableMap::iterator it = m_variableMap.find(name); + if ( it != m_variableMap.end() ) + { + it.data().setValue(value); + it.data().permanent = permanent; + it.data().initAtStart = permanent; + } + else + { + VariableInfo info; + info.setValue(value); + info.permanent = permanent; + info.initAtStart = permanent; + m_variableMap[name] = info; + } +} + + +QStringList MicroSettings::variableNames() +{ + QStringList list; + const VariableMap::iterator variableMapEnd = m_variableMap.end(); + for ( VariableMap::iterator it = m_variableMap.begin(); it != variableMapEnd; ++it ) + { + list += it.key(); + } + return list; +} + + +VariableInfo* MicroSettings::variableInfo( const QString &name ) +{ + if ( name.isNull() ) return 0l; + VariableMap::iterator it = m_variableMap.find(name); + if ( it != m_variableMap.end() ) { + return &(it.data()); + } else { + return 0l; + } +} + + +bool MicroSettings::deleteVariable( const QString &name ) +{ + if ( name.isNull() ) return false; + VariableMap::iterator it = m_variableMap.find(name); + if ( it != m_variableMap.end() ) + { + m_variableMap.erase(it); + return true; + } else { + return false; + } +} + + +void MicroSettings::removeAllVariables() +{ + m_variableMap.clear(); +} + + +PinMapping MicroSettings::pinMapping( const QString & id ) const +{ + return m_pinMappings[id]; +} + + +void MicroSettings::setPinMappings( const PinMappingMap & pinMappings ) +{ + m_pinMappings = pinMappings; + emit pinMappingsChanged(); +} + + +PinMappingMap MicroSettings::pinMappings() const +{ + return m_pinMappings; +} +//END class MicroSettings + + diff --git a/src/microsettings.h b/src/microsettings.h new file mode 100644 index 0000000..d960f10 --- /dev/null +++ b/src/microsettings.h @@ -0,0 +1,211 @@ +/*************************************************************************** + * Copyright (C) 2003-2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef MICROSETTINGS_H +#define MICROSETTINGS_H + +#include <qobject.h> +#include <qvariant.h> + +class QString; +class QVariant; +class MicroData; +class MicroInfo; + +class VariableInfo +{ +public: + VariableInfo(); + + // Returns the value as a string + QString valueAsString() const; + + // MicroSettings::VariableType (don't rely on this just yet...) + int type; + + // Sets the value + void setValue( const QVariant & value ); + + // If true, the variable will be initialised at the start of the FlowCode + // to the given value + bool initAtStart; + + // True if the variable was "created" by the user in the variable dialog, + // as opposed to being from a variable name entry box + bool permanent; + +private: + QVariant value; +}; + + +typedef QMap< QString, VariableInfo > VariableMap; // Variable name, variable info + + +/** +@short Stores pic pin settings - type/state +@author David Saxton +*/ +class PinSettings : public QObject +{ + Q_OBJECT + public: + enum pin_type + { + pt_input, + pt_output + }; + + enum pin_state + { + ps_on, + ps_off + }; + + PinSettings(); + PinSettings( PinSettings::pin_type _type, PinSettings::pin_state _state, const QString &id, const QString &port ); + + PinSettings::pin_type type() const { return m_type; } + PinSettings::pin_state state() const { return m_state; } + QString id() const { return m_id; } + QString port() const { return m_port; } + + void setType( PinSettings::pin_type type ); + void setState( PinSettings::pin_state state ); + + signals: + /** + * Emitted when either the type or the state is changed. + */ + void settingsChanged(); + + private: + PinSettings::pin_type m_type; + PinSettings::pin_state m_state; + QString m_id; + QString m_port; +}; +typedef QValueList<PinSettings*> PinSettingsList; + +class PinMapping; +typedef QMap< QString, PinMapping > PinMappingMap; +typedef QMap< QString, PinSettingsList > PortList; + +/** +This class stores PIC settings that are specific to the PIC program being devloped. +This includes such things as port settings and variable settings. +This is different from PIC info, which includes stuff such as PIC pin names + +@short Stores Pic settings - pin settings +@author David Saxton +*/ +class MicroSettings : public QObject +{ + Q_OBJECT +public: + enum VariableType + { + vt_signedInteger, + vt_unsignedInteger, + vt_unknown + }; + MicroSettings( MicroInfo *microInfo ); + ~MicroSettings(); + /** + * Returns microdata to describe the microsettings. + * This includes ports settins and variable settings + */ + MicroData microData() const; + void restoreFromMicroData( const MicroData µData ); + /** + * Returns a pointer to the MicroInfo object for the PIC in use + */ + MicroInfo *microInfo() const { return _microInfo; } + /** + * Set the pin with the given id to the given initial type (input/output) + */ + void setPinType( const QString &id, PinSettings::pin_type type ); + /** + * Set the pin with the given id to the given initial state (on/off) + */ + void setPinState( const QString &id, PinSettings::pin_state state ); + /** + * Returns a pointer to the PinSettings for the pin with the given id, + * or null if no such pin exists. + */ + PinSettings* pinWithID( const QString &id ); + /** + * Returns the initial port state (on/off) for the given port. + * Each pin state occupies one bit of the returned integer. + */ + int portState( const QString &port ); + /** + * Sets the port with the given name to the given state + */ + void setPortState( const QString &port, int state ); + /** + * Sets the port with the given name to the given type + */ + void setPortType( const QString &port, int type ); + /** + * Returns the initial port type (intput/output) for the given port. + * Each pin type occupies one bit of the returned integer. + */ + int portType( const QString &port ); + /** + * Sets the variable "name" to the initial value "value. If the variable + * already exists, its value will be changed. Else, the variable will be + * created. + */ + void setVariable( const QString &name, QVariant value, bool permanent = true ); + /** + * Returns the list of initial variables as a QStringList, just the names + * without the values. Generated from the VariableMap m_variables. + */ + QStringList variableNames(); + /** + * Returns a pointer to the variable info with the given name, or NULL + * if the variable is not found + */ + VariableInfo *variableInfo( const QString &name ); + /** + * Deletes the variable with the given name, returns true if successul + * (i.e. a variable with that name existed), or false if not + */ + bool deleteVariable( const QString &name ); + /** + * Removes all variables + */ + void removeAllVariables(); + /** + * Sets the list of Pin Mappings to that given. + */ + void setPinMappings( const PinMappingMap & pinMappings ); + /** + * Returns the pic pin mapping with the given id. + */ + PinMapping pinMapping( const QString & id ) const; + /** + * Returns the list of different Pin Mappings; + */ + PinMappingMap pinMappings() const; + +signals: + void pinMappingsChanged(); + +private: + PinMappingMap m_pinMappings; + PinSettingsList m_pinSettingsList; + MicroInfo *_microInfo; + VariableMap m_variableMap; + PortList m_ports; +}; + +#endif diff --git a/src/node.cpp b/src/node.cpp new file mode 100644 index 0000000..9a3531d --- /dev/null +++ b/src/node.cpp @@ -0,0 +1,486 @@ +/*************************************************************************** + * Copyright (C) 2003-2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "cnitem.h" +#include "icndocument.h" +#include "connector.h" +#include "itemdocumentdata.h" +#include "node.h" + +#include <kdebug.h> + +#include <qpainter.h> + +Node::Node( ICNDocument *icnDocument, Node::node_type type, node_dir dir, const QPoint &pos, QString *id ) + : QObject(), QCanvasPolygon( icnDocument->canvas() ) +{ + p_nodeGroup = 0l; + p_parentItem = 0L; + b_deleted = false; + m_dir = dir; + m_type = type; + p_icnDocument = icnDocument; + m_level = 0; + m_selectedColor = QColor( 101, 134, 192 ); + + if (id) + { + m_id = *id; + if ( !p_icnDocument->registerUID(*id) ) + kdError() << k_funcinfo << "Could not register id " << *id << endl; + } + else m_id = p_icnDocument->generateUID("node"+QString::number(type)); + + initPoints(); + move( pos.x(), pos.y() ); + setBrush( Qt::black ); + setPen( Qt::black ); + show(); + + emit (moved(this)); +} + + +Node::~Node() +{ + p_icnDocument->unregisterUID( id() ); +} + + +int Node::rtti() const +{ + return ICNDocument::RTTI::Node; +} + + +void Node::setLevel( const int level ) +{ + m_level = level; +} + + +bool Node::acceptInput() const +{ + return type() != fp_out; +} + + +bool Node::acceptOutput() const +{ + return type() != fp_in; +} + + +void Node::setVisible( bool yes ) +{ + if ( isVisible() == yes ) + return; + + QCanvasPolygon::setVisible(yes); + + const ConnectorList::iterator inputEnd = m_inputConnectorList.end(); + for ( ConnectorList::iterator it = m_inputConnectorList.begin(); it != inputEnd; ++it ) + { + Connector *connector = *it; + if (connector) + { + if ( isVisible() ) + connector->setVisible(true); + + else + { + Node *node = connector->startNode(); + connector->setVisible( node && node->isVisible() ); + } + } + } + + const ConnectorList::iterator outputEnd = m_outputConnectorList.end(); + for ( ConnectorList::iterator it = m_outputConnectorList.begin(); it != outputEnd; ++it ) + { + Connector *connector = *it; + if (connector) + { + if ( isVisible() ) + connector->setVisible(true); + + else + { + Node *node = connector->endNode(); + connector->setVisible( node && node->isVisible() ); + } + } + } +} + + +bool Node::isConnected( Node *node, NodeList *checkedNodes ) +{ + if ( this == node ) + return true; + + bool firstNode = !checkedNodes; + if (firstNode) + checkedNodes = new NodeList(); + + else if ( checkedNodes->contains(this) ) + return false; + + + checkedNodes->append(this); + + const ConnectorList::const_iterator inputEnd = m_inputConnectorList.end(); + for ( ConnectorList::const_iterator it = m_inputConnectorList.begin(); it != inputEnd; ++it ) + { + Connector *connector = *it; + if (connector) + { + Node *startNode = connector->startNode(); + if ( startNode && startNode->isConnected( node, checkedNodes ) ) { + if (firstNode) { + delete checkedNodes; + } + return true; + } + } + } + + const ConnectorList::const_iterator outputEnd = m_outputConnectorList.end(); + for ( ConnectorList::const_iterator it = m_outputConnectorList.begin(); it != outputEnd; ++it ) + { + Connector *connector = *it; + if (connector) + { + Node *endNode = connector->endNode(); + if ( endNode && endNode->isConnected( node, checkedNodes ) ) { + if (firstNode) { + delete checkedNodes; + } + return true; + } + } + } + + if (firstNode) { + delete checkedNodes; + } + + return false; +} + + +void Node::setOrientation( node_dir dir ) +{ + if ( m_dir == dir ) + return; + + if ( dir != Node::dir_up && + dir != Node::dir_right && + dir != Node::dir_down && + dir != Node::dir_left ) + { + kdDebug() << "Node::setOrientation: Unknown node direction "<<dir<<endl; + return; + } + else + m_dir = dir; + initPoints(); +} + + +void Node::initPoints() +{ + if ( type() == ec_junction ) + { + setPoints( QPointArray( QRect( -4, -4, 8, 8 ) ) ); + return; + } + + if ( type() == fp_junction ) + { + setPoints( QPointArray( QRect( -4, -4, 9, 9 ) ) ); + return; + } + + const int length = ( type() == ec_pin ) ? 8 : -8; + + // Bounding rectangle, facing right + QPointArray pa( QRect( 0, -8, length, 16 ) ); + + double angle; + if ( m_dir == Node::dir_up ) angle = -90.; + else if ( m_dir == Node::dir_right ) angle = 0.; + else if ( m_dir == Node::dir_down ) angle = 90.; + else if ( m_dir == Node::dir_left ) angle = 180.; + else + { + kdError() << "Node::initPoints: unknown m_dir = "<<m_dir<<endl; + return; + } + + QWMatrix m; + m.rotate(angle); + pa = m.map(pa); + setPoints(pa); +} + + +QPoint Node::findConnectorDivergePoint( bool * found ) +{ + bool temp; + if (!found) + found = &temp; + *found = false; + + if ( numCon( false, false ) != 2 ) + return QPoint(0,0); + + QPointList p1; + QPointList p2; + + int inSize = m_inputConnectorList.count(); + + const ConnectorList connectors = m_inputConnectorList + m_outputConnectorList; + const ConnectorList::const_iterator end = connectors.end(); + bool gotP1 = false; + bool gotP2 = false; + int at = -1; + for ( ConnectorList::const_iterator it = connectors.begin(); it != end && !gotP2; ++it ) + { + at++; + if ( !(*it) || !(*it)->canvas() ) + continue; + + if (gotP1) + { + p2 = (*it)->connectorPoints( at < inSize ); + gotP2 = true; + } + else + { + p1 = (*it)->connectorPoints( at < inSize ); + gotP1 = true; + } + } + if ( !gotP1 || !gotP2 ) + return QPoint(0,0); + + unsigned maxLength = p1.size() > p2.size() ? p1.size() : p2.size(); + + for ( unsigned i = 1; i < maxLength; ++i ) + { + if ( p1[i] != p2[i] ) + { + *found = true; + return p1[i-1]; + } + } + return QPoint(0, 0); +} + + +void Node::setParentItem( CNItem *parentItem ) +{ + if (!parentItem) + { + kdError() << k_funcinfo << "no parent item" << endl; + return; + } + + p_parentItem = parentItem; + + setLevel(p_parentItem->level()); + + connect( p_parentItem, SIGNAL(movedBy(double, double )), this, SLOT(moveBy(double, double)) ); + connect( p_parentItem, SIGNAL(removed(Item*)), this, SLOT(removeNode(Item*)) ); +} + + +void Node::removeNode() +{ + if (b_deleted) + return; + b_deleted = true; + + emit removed(this); + p_icnDocument->appendDeleteList(this); +} + + +void Node::moveBy( double dx, double dy ) +{ + if ( dx == 0 && dy == 0 ) return; + QCanvasPolygon::moveBy( dx, dy ); + emit moved(this); +} + + +int Node::numCon( bool includeParentItem, bool includeHiddenConnectors ) const +{ + unsigned count = 0; + + const ConnectorList connectors[2] = { m_inputConnectorList, m_outputConnectorList }; + + for ( unsigned i = 0; i < 2; i++ ) + { + ConnectorList::const_iterator end = connectors[i].end(); + for ( ConnectorList::const_iterator it = connectors[i].begin(); it != end; ++it ) + { + if ( *it && (includeHiddenConnectors || (*it)->canvas()) ) + count++; + } + } + + if ( isChildNode() && includeParentItem ) + count++; + + return count; +} + + +void Node::addOutputConnector( Connector * const connector ) +{ + if ( type() == fp_in || !handleNewConnector(connector) ) + return; + + m_outputConnectorList.append(connector); + + if ( type() == fp_out || type() == fp_junction ) + { + // We can only have one output connector, so remove the others. Note + // that this code has to come *after* adding the new output connector, + // as this node will delete itself if it's an fp_junction and there are + // no output connectors. + + const ConnectorList connectors = m_outputConnectorList; + const ConnectorList::const_iterator end = connectors.end(); + for ( ConnectorList::const_iterator it = connectors.begin(); it != end; ++it ) + { + Connector * con = *it; + if ( con && con != connector ) + con->removeConnector(); + } + } + + m_outputConnectorList.remove((Connector*)0l); +} + + +void Node::addInputConnector( Connector * const connector ) +{ + if ( type() == fp_out || !handleNewConnector(connector) ) + return; + + m_inputConnectorList.append(connector); +} + + +bool Node::handleNewConnector( Connector * connector ) +{ + if (!connector) + return false; + + if ( m_inputConnectorList.contains(connector) || m_outputConnectorList.contains(connector) ) + { + kdWarning() << k_funcinfo << " Already have connector = " << connector << endl; + return false; + } + + connect( this, SIGNAL(removed(Node*)), connector, SLOT(removeConnector(Node*)) ); + connect( connector, SIGNAL(removed(Connector*)), this, SLOT(checkForRemoval(Connector*)) ); + connect( connector, SIGNAL(selected(bool)), this, SLOT(setNodeSelected(bool)) ); + + if ( !isChildNode() ) + p_icnDocument->slotRequestAssignNG(); + + return true; +} + + +Connector* Node::createInputConnector( Node * startNode ) +{ + if ( type() == fp_out || !startNode ) + return 0l; + + Connector *connector = new Connector( startNode, this, p_icnDocument ); + addInputConnector(connector); + + return connector; +} + + +void Node::removeConnector( Connector *connector ) +{ + if (!connector) + return; + + ConnectorList::iterator it; + + it = m_inputConnectorList.find(connector); + if ( it != m_inputConnectorList.end() ) + { + (*it)->removeConnector(); + (*it) = 0L; + } + + it = m_outputConnectorList.find(connector); + if ( it != m_outputConnectorList.end() ) + { + (*it)->removeConnector(); + (*it) = 0L; + } +} + +void Node::checkForRemoval( Connector *connector ) +{ + removeConnector(connector); + setNodeSelected(false); + + removeNullConnectors(); + + if (!p_parentItem) + { + int conCount = m_inputConnectorList.count() + m_outputConnectorList.count(); + if ( conCount < 2 ) + removeNode(); + } + + if ( type() == Node::fp_junction && m_outputConnectorList.isEmpty() ) + removeNode(); +} + +void Node::removeNullConnectors() +{ + m_inputConnectorList.remove((Connector*)0L); + m_outputConnectorList.remove((Connector*)0L); +} + + +NodeData Node::nodeData() const +{ + NodeData data; + data.x = x(); + data.y = y(); + return data; +} + + +void Node::setNodeSelected( bool yes ) +{ + if ( isSelected() == yes ) + return; + + QCanvasItem::setSelected(yes); + + setPen( yes ? m_selectedColor : Qt::black ); + setBrush( yes ? m_selectedColor : Qt::black ); +} + +#include "node.moc" diff --git a/src/node.h b/src/node.h new file mode 100644 index 0000000..9f440b3 --- /dev/null +++ b/src/node.h @@ -0,0 +1,222 @@ +/*************************************************************************** + * Copyright (C) 2003-2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef NODE_H +#define NODE_H + +#include <qcanvas.h> +#include <qguardedptr.h> + +class CNItem; +class Item; +class ICNDocument; +class ICNDocument; +class Connector; +class Node; +class NodeData; +class NodeGroup; +class QTimer; + +typedef QValueList<QGuardedPtr<Connector> > ConnectorList; +typedef QValueList<QGuardedPtr<Node> > NodeList; + +/** +@short A standard node that can be associated with a Connector or a CNItem +@author David Saxton +*/ +class Node : public QObject, public QCanvasPolygon +{ +Q_OBJECT +public: + /** + * Used for run-time identification of the node: + * Can be electronic node (so has values of current, voltage, etc) + * or a pic part node + */ + enum node_type + { + ec_pin, + ec_junction, + fp_in, + fp_out, + fp_junction + }; + + enum node_dir + { + dir_up = 270, + dir_right = 0, + dir_down = 90, + dir_left = 180 + }; + Node( ICNDocument *icnDocument, Node::node_type type, node_dir dir, const QPoint &pos, QString *id = 0L ); + virtual ~Node(); + + /** + * Sets the node's visibility, as well as updating the visibility of the + * attached connectors as appropriate + */ + virtual void setVisible( bool yes ); + /** + * Returns the global id, that is unique to the node + * amongst all the nodes on the canvas + */ + const QString id() const { return m_id; } + /** + * Returns the id that is internal to the CNItem to which the + * node belongs to. Returns a null QString if no parentitem + */ + const QString childId() const { return m_childId; } + /** + * Use this function to set the child-id, that is unique to the node + * amongst the other nodes associated with its parent CNItem + */ + void setChildId( const QString &id ) { m_childId = id; } + /** + * Returns the run-time-type-identifier of ICNDocument::RTTI::Node + */ + virtual int rtti() const; + /** + * Sets the "level" of the node. By default, the level is 0. The level of + * the node tells the node what CNItems it can be connected to through + * a connector. + * @see level + */ + virtual void setLevel( const int level ); + /** + * Returns the level of the nodes + * @see setLevel + */ + int level() const { return m_level; } + /** + * Use this to identify the type of node - eg ECNode or FPNode + */ + node_type type() const { return m_type; } + /** + * Returns true if the node can accept input connections. This will depend + * on the node type and number of input / output connections. + */ + bool acceptInput() const; + /** + * Returns true if the node can accept output connections. This will depend + * on the node type and number of input / output connections. + */ + bool acceptOutput() const; + /** + * Sets the orientation of the node. + */ + void setOrientation( node_dir dir ); + /** + * Associates a CNItem with the node - ie the node belongs to the CNItem, + * and hence gets deleted when the CNItem gets deleted.s + */ + virtual void setParentItem( CNItem *parentItem ); + /** + * Returns true if the node is part of a CNItem + * (i.e. not between multiple connectors) + */ + bool isChildNode() const { return (p_parentItem != 0L); } + /** + * Returns a pointer to the CNItem to which the node belongs, + * or Null if it doesn't. + */ + CNItem *parentItem() const { return p_parentItem; } + /** + * Remove a specific connector + */ + void removeConnector( Connector *connector ); + /** + * Creates a new connector, sets this as the end node to the connector + * (i.e. this node is the connector's input node), and returns a pointer + * to the connector. + */ + Connector* createInputConnector( Node * startNode ); + /** + * Registers an input connector (i.e. this is the end node) as connected + * to this node. + */ + void addInputConnector( Connector * const connector ); + /** + * Registers an input connector (i.e. this is the start node) as connected + * to this node. + */ + void addOutputConnector( Connector * const connector ); + /** + * Returns the total number of connections to the node. This is the number + * of input connectors, the number of output connectors, and the parent + * item connector if it exists and is requested. + * @param includeParentItem Count the parent item as a connector if it exists + * @param includeHiddenConnectors hidden connectors are those as e.g. part of a subcircuit + */ + int numCon( bool includeParentItem, bool includeHiddenConnectors ) const; + + NodeData nodeData() const; + + ConnectorList inputConnectorList() const { return m_inputConnectorList; } + ConnectorList outputConnectorList() const { return m_outputConnectorList; } + + void setNodeGroup( NodeGroup *ng ) { p_nodeGroup = ng; } + NodeGroup *nodeGroup() const { return p_nodeGroup; } + + /** + * Returns true if this node is connected (or is the same as) the node given + * by other connectors or nodes (although not through CNItems) + * checkedNodes is a list of nodes that have already been checked for + * being the connected nodes, and so can simply return if they are in there. + * If it is null, it will assume that it is the first ndoe & will create a list + */ + bool isConnected( Node *node, NodeList *checkedNodes = 0L ); + + void removeNullConnectors(); + /** + * Draw shape. Note that this has to remain public. + */ + virtual void drawShape( QPainter &p ) = 0; + +public slots: + void moveBy( double dx, double dy ); + void removeNode(Item*) { removeNode(); } + void removeNode(); + void checkForRemoval( Connector *connector ); + void setNodeSelected( bool yes ); + +signals: + void moved( Node *node ); + /** + * Emitted when the CNItem is removed. Normally, this signal is caught by associated + * nodes, who will remove themselves as well. + */ + void removed( Node* node ); + +protected: + /** If this node has precisely two connectors emerging from it, then this + * function will trace thw two connectors until the point where they + * diverge; this point is returned. */ + QPoint findConnectorDivergePoint( bool * found ); + void initPoints(); + bool handleNewConnector( Connector * newConnector ); + + node_type m_type; + QString m_id; + QString m_childId; + node_dir m_dir; + ICNDocument *p_icnDocument; + CNItem *p_parentItem; + ConnectorList m_inputConnectorList; + ConnectorList m_outputConnectorList; + int m_level; + NodeGroup *p_nodeGroup; + QColor m_selectedColor; + +private: + bool b_deleted; +}; + +#endif diff --git a/src/nodegroup.cpp b/src/nodegroup.cpp new file mode 100644 index 0000000..fe55293 --- /dev/null +++ b/src/nodegroup.cpp @@ -0,0 +1,572 @@ +/*************************************************************************** + * Copyright (C) 2004-2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "icndocument.h" +#include "connector.h" +#include "conrouter.h" +#include "node.h" +#include "nodegroup.h" + +#include <kdebug.h> +#include <assert.h> +#include <cmath> + +NodeGroup::NodeGroup( ICNDocument *icnDocument, const char *name ) + : QObject( icnDocument, name ) +{ + p_icnDocument = icnDocument; + b_visible = true; +} + + +NodeGroup::~NodeGroup() +{ + clearConList(); + + m_extNodeList.remove( (Node*)0l ); + const NodeList::iterator xnEnd = m_extNodeList.end(); + for ( NodeList::iterator it = m_extNodeList.begin(); it != xnEnd; ++it ) + (*it)->setNodeGroup(0l); + m_extNodeList.clear(); + + m_nodeList.remove( (Node*)0l ); + const NodeList::iterator nEnd = m_nodeList.end(); + for ( NodeList::iterator it = m_nodeList.begin(); it != nEnd; ++it ) + (*it)->setNodeGroup(0l); + m_nodeList.clear(); +} + + +void NodeGroup::setVisible( bool visible ) +{ + if ( b_visible == visible ) + return; + + b_visible = visible; + + m_nodeList.remove( (Node*)0l ); + const NodeList::iterator nEnd = m_nodeList.end(); + for ( NodeList::iterator it = m_nodeList.begin(); it != nEnd; ++it ) + (*it)->setVisible(visible); +} + + +void NodeGroup::addNode( Node *node, bool checkSurrouding ) +{ + if ( !node || node->isChildNode() || m_nodeList.contains(node) ) { + return; + } + + m_nodeList.append(node); + node->setNodeGroup(this); + node->setVisible(b_visible); + + if (checkSurrouding) + { + ConnectorList con = node->inputConnectorList(); + ConnectorList::iterator end = con.end(); + for ( ConnectorList::iterator it = con.begin(); it != end; ++it ) + { + if (*it) { + addNode( (*it)->startNode(), true ); + } + } + + con = node->outputConnectorList(); + end = con.end(); + for ( ConnectorList::iterator it = con.begin(); it != end; ++it ) + { + if (*it) { + addNode( (*it)->endNode(), true ); + } + } + } +} + + +void NodeGroup::translate( int dx, int dy ) +{ + if ( (dx == 0) && (dy == 0) ) + return; + + m_conList.remove((Connector*)0l); + m_nodeList.remove((Node*)0l); + + const ConnectorList::iterator conEnd = m_conList.end(); + for ( ConnectorList::iterator it = m_conList.begin(); it != conEnd; ++it ) + { + (*it)->updateConnectorPoints(false); + (*it)->translateRoute( dx, dy ); + } + + const NodeList::iterator nodeEnd = m_nodeList.end(); + for ( NodeList::iterator it = m_nodeList.begin(); it != nodeEnd; ++it ) + (*it)->moveBy( dx, dy ); +} + + +void NodeGroup::updateRoutes() +{ + resetRoutedMap(); + + // Basic algorithm used here: starting with the external nodes, find the + // pair with the shortest distance between them. Route the connectors + // between the two nodes appropriately. Remove that pair of nodes from the + // list, and add the nodes along the connector route (which have been spaced + // equally along the route). Repeat until all the nodes are connected. + + + const ConnectorList::iterator conEnd = m_conList.end(); + for ( ConnectorList::iterator it = m_conList.begin(); it != conEnd; ++it ) + { + if (*it) + (*it)->updateConnectorPoints(false); + } + + Node *n1, *n2; + NodeList currentList = m_extNodeList; + while ( !currentList.isEmpty() ) + { + findBestPair( ¤tList, &n1, &n2 ); + if ( n1 == 0l || n2 == 0l ) { + return; + } + NodeList route = findRoute( n1, n2 ); + currentList += route; + + ConRouter cr(p_icnDocument); + cr.mapRoute( (int)n1->x(), (int)n1->y(), (int)n2->x(), (int)n2->y() ); + QPointListList pl = cr.dividePoints( route.size()+1 ); + + const NodeList::iterator routeEnd = route.end(); + const QPointListList::iterator plEnd = pl.end(); + Node *prev = n1; + NodeList::iterator routeIt = route.begin(); + for ( QPointListList::iterator it = pl.begin(); it != plEnd; ++it ) + { + Node *next = (routeIt == routeEnd) ? n2 : (Node*)*(routeIt++); + removeRoutedNodes( ¤tList, prev, next ); + QPointList pointList = *it; + if ( prev != n1 ) + { + QPoint first = pointList.first(); + prev->moveBy( first.x() - prev->x(), first.y() - prev->y() ); + } + Connector *con = findCommonConnector( prev, next ); + if (con) + { + con->updateConnectorPoints(false); + con->setRoutePoints( pointList, false, false ); + con->updateConnectorPoints(true); +// con->conRouter()->setPoints( &pointList, con->startNode() != prev ); +// con->conRouter()->setPoints( &pointList, con->pointsAreReverse( &pointList ) ); +// con->calcBoundingPoints(); + } + prev = next; + } + } +} + + +NodeList NodeGroup::findRoute( Node *startNode, Node *endNode ) +{ + NodeList nl; + if ( !startNode || !endNode || startNode == endNode ) { + return nl; + } + + IntList temp; + IntList il = findRoute( temp, getNodePos(startNode), getNodePos(endNode) ); + + const IntList::iterator end = il.end(); + for ( IntList::iterator it = il.begin(); it != end; ++it ) + { + Node *node = getNodePtr(*it); + if (node) { + nl += node; + } + } + + nl.remove(startNode); + nl.remove(endNode); + + return nl; +} + + +IntList NodeGroup::findRoute( IntList used, int currentNode, int endNode, bool *success ) +{ + bool temp; + if (!success) { + success = &temp; + } + *success = false; + + if ( !used.contains(currentNode) ) { + used.append(currentNode); + } + + if ( currentNode == endNode ) { + *success = true; + return used; + } + + const uint n = m_nodeList.size()+m_extNodeList.size(); + for ( uint i=0; i<n; ++i ) + { + if ( b_routedMap[i*n+currentNode] && !used.contains(i) ) + { + IntList il = findRoute( used, i, endNode, success ); + if (*success) { + return il; + } + } + } + + IntList il; + return il; +} + + +Connector* NodeGroup::findCommonConnector( Node *n1, Node *n2 ) +{ + if ( !n1 || !n2 || n1==n2 ) { + return 0l; + } + + ConnectorList n1Con = n1->inputConnectorList() + n1->outputConnectorList(); + ConnectorList n2Con = n2->inputConnectorList() + n2->outputConnectorList(); + + const ConnectorList::iterator end = n1Con.end(); + for ( ConnectorList::iterator it = n1Con.begin(); it != end; ++it ) + { + if ( n2Con.contains(*it) ) { + return *it; + } + } + return 0l; +} + + +void NodeGroup::findBestPair( NodeList *list, Node **n1, Node **n2 ) +{ + *n1 = 0l; + *n2 = 0l; + + if ( list->size() < 2 ) { + return; + } + + const NodeList::iterator end = list->end(); + int shortest = 1<<30; + + // Try and find any that are aligned horizontally + for ( NodeList::iterator it1 = list->begin(); it1 != end; ++it1 ) + { + NodeList::iterator it2 = it1; + for ( ++it2; it2 != end; ++it2 ) + { + if ( *it1 != *it2 && (*it1)->y() == (*it2)->y() && canRoute( *it1, *it2 ) ) + { + const int distance = std::abs((double)( (*it1)->x()-(*it2)->x() )); + if ( distance < shortest ) + { + shortest = distance; + *n1 = *it1; + *n2 = *it2; + } + } + } + } + if (*n1) { + return; + } + + // Try and find any that are aligned vertically + for ( NodeList::iterator it1 = list->begin(); it1 != end; ++it1 ) + { + NodeList::iterator it2 = it1; + for ( ++it2; it2 != end; ++it2 ) + { + if ( *it1 != *it2 && (*it1)->x() == (*it2)->x() && canRoute( *it1, *it2 ) ) + { + const int distance = std::abs((double)( (*it1)->y()-(*it2)->y() )); + if ( distance < shortest ) + { + shortest = distance; + *n1 = *it1; + *n2 = *it2; + } + } + } + } + if (*n1) { + return; + } + + // Now, lets just find the two closest nodes + for ( NodeList::iterator it1 = list->begin(); it1 != end; ++it1 ) + { + NodeList::iterator it2 = it1; + for ( ++it2; it2 != end; ++it2 ) + { + if ( *it1 != *it2 && canRoute( *it1, *it2 ) ) + { + const int dx = (int)((*it1)->x()-(*it2)->x()); + const int dy = (int)((*it1)->y()-(*it2)->y()); + const int distance = std::abs((double)dx) + std::abs((double)dy); + if ( distance < shortest ) + { + shortest = distance; + *n1 = *it1; + *n2 = *it2; + } + } + } + } + + if (!*n1) { + kdError() << "NodeGroup::findBestPair: Could not find a routable pair of nodes!"<<endl; + } +} + + +bool NodeGroup::canRoute( Node *n1, Node *n2 ) +{ + if ( !n1 || !n2 ) { + return false; + } + + IntList reachable; + getReachable( &reachable, getNodePos(n1) ); + return reachable.contains(getNodePos(n2)); +} + + +void NodeGroup::getReachable( IntList *reachable, int node ) +{ + if ( !reachable || reachable->contains(node) ) { + return; + } + reachable->append(node); + + const uint n = m_nodeList.size() + m_extNodeList.size(); + assert( node < int(n) ); + for ( uint i=0; i<n; ++i ) + { + if ( b_routedMap[i*n + node] ) { + getReachable(reachable,i); + } + } +} + + +void NodeGroup::resetRoutedMap() +{ + const uint n = m_nodeList.size() + m_extNodeList.size(); + + b_routedMap.resize(0); + b_routedMap.resize( n*n, false ); + + const ConnectorList::iterator end = m_conList.end(); + for ( ConnectorList::iterator it = m_conList.begin(); it != end; ++it ) + { + Connector *con = *it; + if (con) + { + int n1 = getNodePos(con->startNode()); + int n2 = getNodePos(con->endNode()); + if ( n1 != -1 && n2 != -1 ) + { + b_routedMap[n1*n + n2] = b_routedMap[n2*n + n1] = true; + } + } + } +} + + +void NodeGroup::removeRoutedNodes( NodeList *nodes, Node *n1, Node *n2 ) +{ + if (!nodes) { + return; + } + + // Lets get rid of any duplicate nodes in nodes (as a general cleaning operation) + const NodeList::iterator end = nodes->end(); + for ( NodeList::iterator it = nodes->begin(); it != end; ++it ) + { + if ( nodes->contains(*it) > 1 ) { + *it = 0l; + } + } + nodes->remove((Node*)0l); + + const int n1pos = getNodePos(n1); + const int n2pos = getNodePos(n2); + + if ( n1pos == -1 || n2pos == -1 ) { + return; + } + + const uint n = m_nodeList.size() + m_extNodeList.size(); + + b_routedMap[n1pos*n + n2pos] = b_routedMap[n2pos*n + n1pos] = false; + + bool n1HasCon = false; + bool n2HasCon = false; + + for ( uint i=0; i<n; ++i ) + { + n1HasCon |= b_routedMap[n1pos*n + i]; + n2HasCon |= b_routedMap[n2pos*n + i]; + } + + if (!n1HasCon) { + nodes->remove(n1); + } + if (!n2HasCon) { + nodes->remove(n2); + } +} + + +int NodeGroup::getNodePos( Node *n ) +{ + if (!n) { + return -1; + } + int pos = m_nodeList.findIndex(n); + if ( pos != -1 ) { + return pos; + } + pos = m_extNodeList.findIndex(n); + if ( pos != -1 ) { + return pos+m_nodeList.size(); + } + return -1; +} + + +Node* NodeGroup::getNodePtr( int n ) +{ + if ( n<0 ) { + return 0l; + } + const int a = m_nodeList.size(); + if (n<a) { + return m_nodeList[n]; + } + const int b = m_extNodeList.size(); + if (n<a+b) { + return m_extNodeList[n-a]; + } + return 0l; +} + + +void NodeGroup::clearConList() +{ + const ConnectorList::iterator end = m_conList.end(); + for ( ConnectorList::iterator it = m_conList.begin(); it != end; ++it ) + { + Connector *con = *it; + if (con) + { + con->setNodeGroup(0l); + disconnect( con, SIGNAL(removed(Connector*)), this, SLOT(connectorRemoved(Connector*)) ); + } + } + m_conList.clear(); +} + + +void NodeGroup::init() +{ + NodeList::iterator xnEnd = m_extNodeList.end(); + for ( NodeList::iterator it = m_extNodeList.begin(); it != xnEnd; ++it ) + { + (*it)->setNodeGroup(0l); + } + m_extNodeList.clear(); + + clearConList(); + + // First, lets get all of our external nodes and internal connectors + const NodeList::iterator nlEnd = m_nodeList.end(); + for ( NodeList::iterator nodeIt = m_nodeList.begin(); nodeIt != nlEnd; ++nodeIt ) + { + ConnectorList inCon = (*nodeIt)->inputConnectorList(); + ConnectorList outCon = (*nodeIt)->outputConnectorList(); + ConnectorList::iterator conEnd; + + conEnd = inCon.end(); + for ( ConnectorList::iterator conIt = inCon.begin(); conIt != conEnd; ++conIt ) + { + Connector *con = *conIt; + addExtNode(con->startNode()); + if ( !m_conList.contains(con) ) { + m_conList += con; + con->setNodeGroup(this); + } + connect( con, SIGNAL(removed(Connector*)), this, SLOT(connectorRemoved(Connector*)) ); + } + + conEnd = outCon.end(); + for ( ConnectorList::iterator conIt = outCon.begin(); conIt != conEnd; ++conIt ) + { + Connector *con = *conIt; + addExtNode(con->endNode()); + if ( !m_conList.contains(con) ) { + m_conList += con; + con->setNodeGroup(this); + } + connect( con, SIGNAL(removed(Connector*)), this, SLOT(connectorRemoved(Connector*)) ); + } + + // Connect the node up to us + connect( *nodeIt, SIGNAL(removed(Node*)), this, SLOT(nodeRemoved(Node*)) ); + } + + // And connect up our external nodes + xnEnd = m_extNodeList.end(); + for ( NodeList::iterator it = m_extNodeList.begin(); it != xnEnd; ++it ) + { +// connect( *it, SIGNAL(moved(Node*)), this, SLOT(extNodeMoved()) ); + connect( *it, SIGNAL(removed(Node*)), this, SLOT(nodeRemoved(Node*)) ); + } +} + + +void NodeGroup::nodeRemoved( Node *node ) +{ + // We are probably about to get deleted by ICNDocument anyway...so no point in doing anything + m_nodeList.remove(node); + node->setNodeGroup(0l); + node->setVisible(true); + m_extNodeList.remove(node); +} + + +void NodeGroup::connectorRemoved( Connector *connector ) +{ + m_conList.remove(connector); +} + + +void NodeGroup::addExtNode( Node *node ) +{ + if ( !m_extNodeList.contains(node) && !m_nodeList.contains(node) ) + { + m_extNodeList.append(node); + node->setNodeGroup(this); + } +} + +#include "nodegroup.moc" diff --git a/src/nodegroup.h b/src/nodegroup.h new file mode 100644 index 0000000..f3f49f8 --- /dev/null +++ b/src/nodegroup.h @@ -0,0 +1,145 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef NODEGROUP_H +#define NODEGROUP_H + +#include <qguardedptr.h> +#include <qobject.h> +#include <qvaluelist.h> +#include <qvaluevector.h> + +class ICNDocument; +class Connector; +class ConRouter; +class Node; +class NodeGroup; + +class QTimer; + +typedef QValueList<int> IntList; +typedef QValueList<NodeGroup*> NodeGroupList; +typedef QValueList<QGuardedPtr<Node> > NodeList; + +/** +Controls a group of nodes who are not attached to any CNItems (poor things!) +along with their associated connectors. +@author David Saxton +*/ +class NodeGroup : public QObject +{ +Q_OBJECT +public: + NodeGroup( ICNDocument *icnDocument, const char *name = 0); + ~NodeGroup(); + /** + * Adds a node to the group (this checks to make sure that the node is not + * a child node). If checkSurrouding is true, then surrounding nodes will be + * checked to see if they are valid for inclusion - and if so, include them. + */ + void addNode( Node *node, bool checkSurrouding ); + /** + * Returns the list of internal nodes + */ + NodeList internalNodeList() const { return m_nodeList; } + /** + * Returns the list of external nodes + */ + NodeList externalNodeList() const { return m_extNodeList; } + /** + * Returns the list of connectors + */ + ConnectorList connectorList() const { return m_conList; } + /** + * Translates the routes by the given amount + */ + void translate( int dx, int dy ); + void init(); + /** + * @returns true if node is an internal node to this group + */ + bool contains( Node *node ) const { return m_nodeList.contains(node); } + /** + * Reroute the NodeGroup. This function should only ever be called by + * ICNDocument::rerouteInvalidatedConnectors(), as it is important that + * there is only ever one entity controlling the routing of connectors. + */ + void updateRoutes(); + /** + * Sets the visibility of all nodes in the group. + */ + void setVisible( bool visible ); + +public slots: + /** + * Called when an internal or external node is deleted + */ + void nodeRemoved( Node *node ); + /** + * Called when a connector is removed + */ + void connectorRemoved( Connector *connector ); + + +protected: + void clearConList(); + /** + * Finds the common connector between two nodes + */ + Connector* findCommonConnector( Node *n1, Node *n2 ); + /** + * Find the best pair of nodes in the given list to route between. These + * will be nodes that give a ncie path (e.g. if they're aligned horizontally + * or vertically), or otherwise the closest such pair. The two nodes will be + * returned in n1 and n2. + */ + void findBestPair( NodeList *list, Node **n1, Node **n2 ); + /** + * Finds the nodes along the route with the given start and end nodes (which + * will be unique). The end nodes are not included in the returned list. + */ + NodeList findRoute( Node *startNode, Node *endNode ); + + ConnectorList m_conList; + NodeList m_nodeList; + NodeList m_extNodeList; + ICNDocument *p_icnDocument; + QValueVector<bool> b_routedMap; // Routes between different nodes + bool b_visible; + +private: + IntList findRoute( IntList used, int currentNode, int endNode, bool *success = 0l ); + void resetRoutedMap(); + /** + * Looks at b_routedMap as well as the connectors coming out of nodes, and + * removes the nodes from the given list that have all of their connectors + * routed. + */ + void removeRoutedNodes( NodeList *nodes, Node *n1, Node *n2 ); + void addExtNode( Node *node ); + /** + * Looks at b_mappedRoute to see if there is a completely unrouted set of + * connectors between the two given nodes; + */ + bool canRoute( Node *n1, Node *n2 ); + void getReachable( IntList *reachable, int node ); + /** + * Either: position of node in m_nodeList, + * or: (position of node in m_extNodeList) + m_nodeList.size() + * or: -1 + */ + int getNodePos( Node *n ); + /** + * Essentially the inverse of getNodePos + */ + Node* getNodePtr( int n ); +}; + +#endif diff --git a/src/oscilloscopedata.cpp b/src/oscilloscopedata.cpp new file mode 100644 index 0000000..cc1e289 --- /dev/null +++ b/src/oscilloscopedata.cpp @@ -0,0 +1,171 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "oscilloscopedata.h" +#include "oscilloscope.h" + +//BEGIN class ProbeData +ProbeData::ProbeData( int id ) + : m_id(id) +{ + m_resetTime = Simulator::self()->time(); +// b_isPaused = false; + m_color = Qt::black; + m_drawPosition = 0.5; + m_insertPos = 0; +} + + +ProbeData::~ProbeData() +{ + unregisterProbe(m_id); +} + + +void ProbeData::setColor( QColor color ) +{ + m_color = color; + emit displayAttributeChanged(); +} +//END class ProbeData + + +//BEGIN class LogicProbeData +LogicProbeData::LogicProbeData( int id ) + : ProbeData(id) +{ +} + + +void LogicProbeData::eraseData() +{ + bool lastValue = false; + bool hasLastValue = m_insertPos > 0; + if (hasLastValue) + lastValue = m_data[m_insertPos-1].value; + + m_data.reset(); + m_insertPos = 0; + m_resetTime = Simulator::self()->time(); + + if (hasLastValue) + addDataPoint( LogicDataPoint( lastValue, m_resetTime ) ); +} + + +ullong LogicProbeData::findPos( llong time ) const +{ + if ( time <= 0 ) + return 0; + + for ( int a = m_data.allocatedUpTo()-1; a >= 0; a-- ) + { + DCArray<LogicDataPoint> * dcArray = m_data.dcArray(a); + + // We're only interested in this if the earliest recorded data point in this dcArray is <= time + if ( m_data.toPos( a, 0, 0 ) >= m_insertPos || (dcArray->chunk(0)->data[0].time > ullong(time)) ) + continue; + + // Cool, somewhere in this dcArray.... + for ( int b = dcArray->allocatedUpTo()-1; b >= 0; b-- ) + { + // Done check if the data we'd be accessing is beyond that set + if ( m_data.toPos( a, b, 0 ) >= m_insertPos || dcArray->chunk(b)->data[0].time > ullong(time) ) + continue; + + // Soon... + for ( int c = DATA_CHUNK_SIZE-1; c >= 0; c-- ) + { + ullong pos = m_data.toPos( a, b, c ); + + if ( pos >= m_insertPos || dcArray->chunk(b)->data[c].time > ullong(time) ) + continue; + + // Wee! + return pos; + } + } + } + + // Either we have no data points, or the one closest to the given time will be the one at the start + return 0; +} +//END class LogicProbeData + + +//BEGIN class FloatingProbeData +FloatingProbeData::FloatingProbeData( int id ) + : ProbeData(id) +{ + m_scaling = Linear; + m_upperAbsValue = 10.0; + m_lowerAbsValue = 0.1; +} + + +void FloatingProbeData::eraseData() +{ + m_data.reset(); + m_insertPos = 0; + m_resetTime = Simulator::self()->time(); +} + + +ullong FloatingProbeData::findPos( llong time ) const +{ + if ( time <= 0 || ullong(time) <= m_resetTime || m_insertPos == 0 ) + return 0; + + ullong at = ullong((time-m_resetTime)*double(LINEAR_UPDATE_RATE)/double(LOGIC_UPDATE_RATE)); + + if ( at >= m_insertPos ) + at = m_insertPos-1; + + return at; +} + + +ullong FloatingProbeData::toTime( ullong at ) const +{ + return ullong(m_resetTime + (at*LOGIC_UPDATE_RATE/LINEAR_UPDATE_RATE)); +} + + +void FloatingProbeData::setScaling( Scaling scaling ) +{ + if ( m_scaling == scaling ) + return; + + m_scaling = scaling; + emit displayAttributeChanged(); +} + + +void FloatingProbeData::setUpperAbsValue( double upperAbsValue ) +{ + if ( m_upperAbsValue == upperAbsValue ) + return; + + m_upperAbsValue = upperAbsValue; + emit displayAttributeChanged(); +} + + +void FloatingProbeData::setLowerAbsValue( double lowerAbsValue ) +{ + if ( m_lowerAbsValue == lowerAbsValue ) + return; + + m_lowerAbsValue = lowerAbsValue; + emit displayAttributeChanged(); +} +//END class FloatingProbeData + + diff --git a/src/oscilloscopedata.h b/src/oscilloscopedata.h new file mode 100644 index 0000000..cbabdac --- /dev/null +++ b/src/oscilloscopedata.h @@ -0,0 +1,365 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef OSCILLOSCOPEDATA_H +#define OSCILLOSCOPEDATA_H + +#include <qcolor.h> +#include <qobject.h> + +typedef long long llong; +typedef unsigned long long ullong; +typedef unsigned int uint; + +#define DATA_CHUNK_SIZE (8192/sizeof(T)) +#define DATA_CHUNK_ARRAY_SIZE ((8192-sizeof(uint))/sizeof(DataChunk<T>*)) + +// Allow a minimum of 64 megabytes of stored data (67108864 bytes) +/// \todo The maximum allowed amount of stored data should be configurable or +/// more intelligent (e.g. taking into account the number of probes or the +/// amount of physical memory on the system). +#define DCARRAY_ARRAY_SIZE ((67108864/(8192*DATA_CHUNK_ARRAY_SIZE))+1) + + +/** +For use in LogicProbe: Every time the input changes state, the new input state +is recorded in value, along with the simulator time that it occurs at. + */ +class LogicDataPoint +{ + public: + LogicDataPoint() + { + value = 0; + time = 0; + } + LogicDataPoint( bool v, ullong t ) + { + value = v; + time = t; + } + bool value : 1; + ullong time : 63; +}; + + +template <typename T> +class DataChunk +{ + public: + DataChunk() { memset( data, 0, DATA_CHUNK_SIZE*sizeof(T) ); } + + T data[ DATA_CHUNK_SIZE ]; + + private: + // We don't want to accidently copy a shedload of data + DataChunk( const DataChunk & ); +}; + +typedef DataChunk<LogicDataPoint> LogicChunk; +typedef DataChunk<float> FloatingChunk; + + +template <typename T> +class DCArray +{ + public: + DCArray() + { + memset( m_data, 0, DATA_CHUNK_ARRAY_SIZE*sizeof(DataChunk<T> *) ); + m_allocatedUpTo = 0; + } + ~DCArray() + { + for ( uint i=0; i<m_allocatedUpTo; ++i) + delete m_data[i]; + } + + inline DataChunk<T> * chunk( uint i ) + { + if ( i >= m_allocatedUpTo ) + allocateUpTo(i+1024); + + if ( i >= DATA_CHUNK_ARRAY_SIZE ) + return 0l; + + return m_data[i]; + } + uint allocatedUpTo() const { return m_allocatedUpTo; } + + + protected: + void allocateUpTo( uint upTo ) + { + if ( upTo > DATA_CHUNK_ARRAY_SIZE ) + upTo = DATA_CHUNK_ARRAY_SIZE; + + for ( uint i=m_allocatedUpTo; i<upTo; ++i ) + m_data[i] = new DataChunk<T>; + m_allocatedUpTo = upTo; + } + + DataChunk<T> * m_data[DATA_CHUNK_ARRAY_SIZE]; + uint m_allocatedUpTo; + + private: + // We don't want to accidently copy a shedload of data + DCArray( const DCArray & ); +}; + + +template <typename T> +class StoredData +{ + public: + StoredData() + { + memset( m_data, 0, DCARRAY_ARRAY_SIZE*sizeof(DCArray<T> *) ); + m_allocatedUpTo = 0; + } + ~StoredData() + { + reset(); + } + + inline T & operator[]( ullong i ) + { + return dataAt(i); + } + inline T & dataAt( ullong i, ullong * insertPos = 0 ) + { + ullong c = i % DATA_CHUNK_SIZE; + ullong b = ullong((i-c)/DATA_CHUNK_SIZE) % DATA_CHUNK_ARRAY_SIZE; + ullong a = ullong((ullong((i-c)/DATA_CHUNK_SIZE)-b)/DATA_CHUNK_ARRAY_SIZE); + + if ( a >= m_allocatedUpTo ) + allocateUpTo(a+1); + + if ( a >= DCARRAY_ARRAY_SIZE ) + { + a = DCARRAY_ARRAY_SIZE - 1; + if ( insertPos ) + *insertPos = toPos( a, b, c ); + } + + return m_data[a]->chunk(b)->data[c]; + } + + ullong toPos( ullong a, ullong b, ullong c ) const + { + return (((a*DATA_CHUNK_ARRAY_SIZE)+b)*DATA_CHUNK_SIZE)+c; + } + + uint allocatedUpTo() const { return m_allocatedUpTo; } + + DCArray<T> * dcArray( unsigned pos ) const + { + return (pos < m_allocatedUpTo) ? m_data[pos] : 0l; + } + + /** + * Initialises all data to 0 + */ + void reset() + { + for ( uint i=0; i<m_allocatedUpTo; ++i) + delete m_data[i]; + m_allocatedUpTo = 0; + } + + protected: + void allocateUpTo( uint upTo ) + { + if ( upTo >= DCARRAY_ARRAY_SIZE ) + { + // Shuffle all data (getting rid of the oldest data) + delete m_data[0]; + for ( unsigned i = 1; i < m_allocatedUpTo; ++i ) + m_data[i-1] = m_data[i]; + + upTo = DCARRAY_ARRAY_SIZE; + m_allocatedUpTo--; + } + + for ( unsigned i = m_allocatedUpTo; i < upTo; ++i ) + m_data[i] = new DCArray<T>; + + m_allocatedUpTo = upTo; + } + DCArray<T> * m_data[DCARRAY_ARRAY_SIZE]; + + uint m_allocatedUpTo; + + private: + // We don't want to accidently copy a shedload of data + StoredData( const StoredData<T> & ); +}; + + +/** +@author David Saxton + */ +class ProbeData : public QObject +{ + Q_OBJECT; + public: + ProbeData( int id ); + ~ProbeData(); + + /** + * @returns unique id for oscilloscope, set on construction + */ + int id() const { return m_id; } + /** + * Set the proportion (0 = top, 1 = bottom) of the way down the + * oscilloscope view that the probe output is drawn. If the proportion + * is out of range ( <0, or >1), then the drawPosition is set to 0/1 + */ + void setDrawPosition( float drawPosition ) { m_drawPosition = drawPosition; } + /** + * Returns the draw position. Default is 0.5. + * @see setDrawPosition + */ + float drawPosition() const { return m_drawPosition; } + /** + * Set the colour that is used to display the probe in the oscilloscope. + * Default is black. + */ + void setColor( QColor color ); + /** + * @returns the colour that is used to display the probe in the oscilloscope + */ + QColor color() const { return m_color; } +// /** +// * Will not record any data when paused +// */ +// void setPaused( bool isPaused ) { b_isPaused = isPaused; } + /** + * Returns the time (in Simulator time) that this probe was created at, + * or last reset. + */ + ullong resetTime() const { return m_resetTime; } + /** + * Erases all recorded data, and sets m_resetTime to the current + * simulator time. + */ + virtual void eraseData() = 0; + /** + * Searches for and returns the position of the last DataPoint that was + * added before or at the given Simulator time. If no DataPoints were + * were recorded before the given time, then will return the one closest + * to the given time. Will return 0 if no DataPoints have been recorded + * yet. + */ + virtual ullong findPos( llong time ) const = 0; + + ullong insertPos() const { return m_insertPos; } + + signals: + /** + * Emitted when an attribute that affects how the probe is drawn in the + * oscilloscope is changed. + */ + void displayAttributeChanged(); + + protected: + const int m_id; + float m_drawPosition; + ullong m_insertPos; +// bool b_isPaused; + ullong m_resetTime; + QColor m_color; +}; + + +/** +@author David Saxton +*/ +class LogicProbeData : public ProbeData +{ + public: + LogicProbeData( int id ); + + /** + * Appends the data point to the set of data. + */ + void addDataPoint( LogicDataPoint data ) + { + ullong next = m_insertPos++; + m_data.dataAt( next, & m_insertPos ) = data; + } + + virtual void eraseData(); + virtual ullong findPos( llong time ) const; + + protected: + StoredData<LogicDataPoint> m_data; + friend class OscilloscopeView; +}; + + +/** +@author David Saxton +*/ +class FloatingProbeData : public ProbeData +{ + public: + enum Scaling { Linear, Logarithmic }; + + FloatingProbeData( int id ); + + /** + * Appends the data point to the set of data. + */ + void addDataPoint( float data ) { m_data[m_insertPos++] = data; } + /** + * Converts the insert position to a Simulator time. + */ + ullong toTime( ullong at ) const; + /** + * Sets the scaling to use in the oscilloscope display. + */ + void setScaling( Scaling scaling ); + /** + * @return the scaling used for the oscilloscope display. + */ + Scaling scaling() const { return m_scaling; } + /** + * Sets the value to use as the upper absolute value in the display. + */ + void setUpperAbsValue( double upperAbsValue ); + /** + * @return the upper absolute value to use in the display. + */ + double upperAbsValue() const { return m_upperAbsValue; } + /** + * Sets the value to use as the lower absolute value in the display + * (this is only used with logarithmic scaling). + */ + void setLowerAbsValue( double lowerAbsValue ); + /** + * @return the lower absolute value to use in the display (this is + * only used with logarithmic scaling). + */ + double lowerAbsValue() const { return m_lowerAbsValue; } + + virtual void eraseData(); + virtual ullong findPos( llong time ) const; + + protected: + Scaling m_scaling; + double m_upperAbsValue; + double m_lowerAbsValue; + StoredData<float> m_data; + friend class OscilloscopeView; +}; + + +#endif diff --git a/src/picitem.cpp b/src/picitem.cpp new file mode 100644 index 0000000..cbd7f1f --- /dev/null +++ b/src/picitem.cpp @@ -0,0 +1,397 @@ +/*************************************************************************** + * Copyright (C) 2003-2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "canvasitemparts.h" +#include "flowcodedocument.h" +#include "microinfo.h" +#include "microsettings.h" +#include "microsettingsdlg.h" +#include "micropackage.h" +#include "picitem.h" + +#include <kdebug.h> +#include <kiconloader.h> +#include <klocale.h> +#include <qpainter.h> + +static const int InnerWidth = 88; +static const int SidePadding = 24; +static const int TopPadding = 36; +static const int BottomPadding = 42; +static const int PinWidth = 12; +static const int PinSeparation = 16 - PinWidth; +static const int PinLength = 8; +static const int ArcWidth = 22; +static const int PinDirArrow = 3; + + +//BEGIN class PinItem +PinItem::PinItem( FlowCodeDocument* _view, QPoint position, bool _onLeft, PinSettings * pinSettings ) + : QCanvasRectangle(0) +{ + m_pinSettings = pinSettings; + view = _view; + onLeft = _onLeft; + + connect( m_pinSettings, SIGNAL(settingsChanged()), this, SLOT(updateDrawing()) ); + + if ( QFontInfo(m_font).pixelSize() > 11 ) // It has to be > 11, not > 12, as (I think) pixelSize() rounds off the actual size + m_font.setPixelSize(12); + + setCanvas( view->canvas() ); + + move ( position.x(), position.y() ); + initItem(); + setZ( (ICNDocument::Z::RaisedItem + ICNDocument::Z::ResizeHandle)/2 + 1 ); // Hackish, but whatever +} + + +int PinItem::rtti() const +{ + return ItemDocument::RTTI::Pin; +} + + +void PinItem::updateDrawing() +{ + update(); +} + + +void PinItem::initItem() +{ + setSize( PinLength, PinWidth ); + setSelected(false); + setPen( Qt::black ); + calcTextRect(); + show(); +} + + +void PinItem::drawShape( QPainter& p ) +{ + if (!m_pinSettings) + return; + + if ( m_pinSettings->state() == PinSettings::ps_on ) + { + if ( m_pinSettings->type() == PinSettings::pt_output ) + setBrush( QColor( 255, 127, 127 ) ); + else + setBrush( QColor( 255, 191, 191 ) ); + } + else + setBrush( Qt::white ); + + p.drawRect(rect()); + + p.setFont(m_font); + p.setBrush( Qt::NoBrush ); + QRect r = m_textRect; + if ( onLeft ) + p.drawText( r, Qt::AlignLeft, m_pinSettings->id() ); + else + p.drawText( r, Qt::AlignRight, m_pinSettings->id() ); + QRect br = p.boundingRect( r, Qt::AlignLeft, m_pinSettings->id() ); + + int left; + int right; + if ( onLeft ) + { + right = (int)x(); + left = right - 8; + } + else + { + left = (int)x() + PinLength; + right = left + 8; + } + + int midY = (int)y() + PinWidth/2; + QPointArray pa(3); + int midLeft = left + (8-PinDirArrow)/2; + int midRight = left + (8+PinDirArrow)/2; + + if ( onLeft ) + { + midLeft--; + midRight--; + } + else + { + midLeft++; + midRight++; + } + + p.setBrush( Qt::black ); + + // Right facing arrow + if ( (m_pinSettings->type() == PinSettings::pt_input && onLeft) || + (m_pinSettings->type() == PinSettings::pt_output && !onLeft) ) + { + pa[0] = QPoint::QPoint( midRight, midY ); + pa[1] = QPoint::QPoint( midLeft, midY - PinDirArrow ); + pa[2] = QPoint::QPoint( midLeft, midY + PinDirArrow ); + p.drawPolygon(pa); + p.drawLine ( left, midY, right, midY ); + } + else // Left facing arrow + { + pa[0] = QPoint::QPoint( midLeft, midY ); + pa[1] = QPoint::QPoint( midRight, midY - PinDirArrow ); + pa[2] = QPoint::QPoint( midRight, midY + PinDirArrow ); + p.drawPolygon(pa); + p.drawLine ( left, midY, right, midY ); + } +} + + +QRect PinItem::boundingRect () const +{ + QRect r = m_textRect; + if ( onLeft ) + r.setLeft( (int)x() - 10 ); + else + r.setRight( (int)x() + PinLength + 10 ); + + return r; +} + + +QString PinItem::id() +{ + return m_pinSettings->id(); +} + + +void PinItem::switchState() +{ + if ( m_pinSettings->state() == PinSettings::ps_on ) + m_pinSettings->setState(PinSettings::ps_off); + else + m_pinSettings->setState(PinSettings::ps_on); + + update(); +} + + +void PinItem::dragged( int dx ) +{ + if ( (onLeft && dx > 0) || + (!onLeft && dx < 0) ) + { + m_pinSettings->setType(PinSettings::pt_input); + } + else + m_pinSettings->setType(PinSettings::pt_output); + + update(); +} + + +void PinItem::moveBy ( double dx, double dy ) +{ + QCanvasRectangle::moveBy( dx, dy ); + calcTextRect(); +} + + +void PinItem::calcTextRect() +{ + m_textRect = rect(); + m_textRect.moveTop( m_textRect.top()-2 ); + QRect br; + + QWidget tmpWidget; + QPainter p(&tmpWidget); + + p.setFont(m_font); + + if (!m_pinSettings) + { + kdDebug() << "PinItem::textRect: No pinSettings!"<<endl; + return; + } + if ( onLeft ) + { + m_textRect.setLeft( (int)x() + PinLength + 2 ); + m_textRect.setRight( (int)x() + InnerWidth/2 ); + br = p.boundingRect( m_textRect, Qt::AlignLeft, m_pinSettings->id() ); + } + else + { + m_textRect.setLeft( m_textRect.right() - InnerWidth/2 ); + m_textRect.setRight( (int)x() - 2 ); + br = p.boundingRect( m_textRect, Qt::AlignRight, m_pinSettings->id() ); + } +} +//END class PinItem + + + +//BEGIN class PicItem +PicItem::PicItem( ICNDocument *icnDocument, bool newItem, const char *id, MicroSettings *_microSettings ) + : CNItem( icnDocument, newItem, id ? id : "picitem" ) +{ + m_name = "PIC"; + m_type = typeString(); + p_icnDocument = icnDocument; + icnDocument->registerItem(this); + + microSettings = _microSettings; + const int numPins = microSettings->microInfo()->package()->pinCount( PicPin::type_bidir | PicPin::type_input | PicPin::type_open ); + const int numSide = (numPins/2) + (numPins%2); + + m_bExpanded = true; + m_innerHeight = (numSide+2)*PinWidth + (numSide-1)*PinSeparation; + updateVisibility(); + + addButton( "settings", QRect( SidePadding-8, m_innerHeight+TopPadding+(BottomPadding-24)/2-1, InnerWidth+16, 24 ), i18n("Advanced...") ); + addButton( "expandBtn", QRect( (TopPadding-22)/2, (TopPadding-22)/2, 22, 22 ), KGlobal::iconLoader()->loadIcon( "down", KIcon::Small ), true ); + button("expandBtn")->setState(true); + + move( 12, 12 ); + + QStringList pinIDs = microSettings->microInfo()->package()->pinIDs( PicPin::type_bidir | PicPin::type_input | PicPin::type_open ); + QStringList::iterator it = pinIDs.begin(); + + for ( int i=0; i < numSide; ++i, ++it ) + { + QPoint position( int(this->x()) + SidePadding - PinLength+1, int(y()) + TopPadding + (i+1)*PinWidth + i*PinSeparation ); + const QString id = *it; + PinSettings *settings = microSettings->pinWithID(id); + m_pinItemList.append( new PinItem( dynamic_cast<FlowCodeDocument*>(icnDocument), position, true, settings ) ); + } + + for ( int i=0; i < numPins/2; ++i, ++it ) + { + QPoint position( int(this->x()) + SidePadding + InnerWidth-1, int(y()) + TopPadding + m_innerHeight - ( (i+2)*PinWidth + i*PinSeparation ) ); + const QString id = *it; + PinSettings *settings = microSettings->pinWithID(id); + m_pinItemList.append( new PinItem( dynamic_cast<FlowCodeDocument*>(icnDocument), position, false, settings ) ); + } + + setSelected(false); + setPen( Qt::black ); + updateZ(-1); + update(); + show(); +} + + +PicItem::~PicItem() +{ + const PinItemList::iterator end = m_pinItemList.end(); + for ( PinItemList::iterator it = m_pinItemList.begin(); it != end; ++it ) + delete *it; + + m_pinItemList.clear(); +} + + +void PicItem::updateZ( int baseZ ) +{ + (void)baseZ; + setZ( (ICNDocument::Z::RaisedItem + ICNDocument::Z::ResizeHandle)/2 ); // Hackish, but whatever + button("settings")->setZ( z()+1 ); + button("expandBtn")->setZ( z()+1 ); +} + + +void PicItem::drawShape( QPainter & p ) +{ + int _x = int(x()); + int _y = int(y()); + + p.setBrush( QColor( 0xef, 0xff, 0xef ) ); + p.setFont( font() ); + + p.drawRoundRect( _x, _y, width(), height(), 2000/width(), 2000/height() ); + + p.drawText( _x+TopPadding-2, _y, width()-TopPadding+2, TopPadding, Qt::AlignVCenter, i18n("PIC Settings") ); + + if ( !m_bExpanded ) + return; + + // Draw rectangle to cut off pins + p.setBrush( QColor( 239, 255, 255 ) ); + QRect r( _x+SidePadding, _y+TopPadding, InnerWidth, m_innerHeight ); + p.drawRect(r); + + // Draw dimple thingy at end of pic + p.drawArc( r.x()+(r.width()-ArcWidth)/2, r.y()+1-ArcWidth/2, ArcWidth, ArcWidth, 180*16, 180*16 ); + + // Draw vertical text centered in PIC + p.translate( r.width()/2 + r.x(), r.height()/2 + r.y() ); + p.rotate(90); + QRect textRect( r.width()/-2, r.height()/-2, r.width(), r.height() ); + p.drawText( textRect, Qt::AlignCenter, microSettings->microInfo()->id() ); + + p.rotate(-90); + p.translate( r.width()/-2 - r.x(), r.height()/-2 - r.y() ); +} + + +void PicItem::buttonStateChanged( const QString &id, bool state ) +{ + if ( id == "expandBtn" ) + { + m_bExpanded = state; + updateVisibility(); + } + + else if ( id == "settings" ) + { + if (!state) + return; + + // Redraw button + button("settings")->setState(false); + update(); + + MicroSettingsDlg *dlg = new MicroSettingsDlg( microSettings, 0L, "microSettingsDlg" ); + connect( dlg, SIGNAL(okClicked()), this, SLOT(slotMicroSettingsDlgAccepted()) ); + connect( dlg, SIGNAL(applyClicked()), this, SLOT(slotMicroSettingsDlgAccepted()) ); + dlg->show(); + // At this point the PIC is selected but this does not appear to the + // user so we must deselect it when done. + p_icnDocument->unselectAll(); + } +} + + +void PicItem::updateVisibility() +{ + if (m_bExpanded) + setSize( 0, 0, InnerWidth+(2*SidePadding), m_innerHeight+TopPadding+BottomPadding, true ); + + else + setSize( 0, 0, InnerWidth+(2*SidePadding), TopPadding, true ); + + const PinItemList::iterator end = m_pinItemList.end(); + for ( PinItemList::iterator it = m_pinItemList.begin(); it != end; ++it ) + (*it)->setVisible(m_bExpanded); + + if ( Button * btn = button("settings") ) + btn->setVisible(m_bExpanded); +} + + +void PicItem::slotMicroSettingsDlgAccepted() +{ + const PinItemList::iterator end = m_pinItemList.end(); + for ( PinItemList::iterator it = m_pinItemList.begin(); it != end; ++it ) + canvas()->setChanged( (*it)->boundingRect() ); + + p_icnDocument->requestStateSave(); +} +//END class PicItem + +#include "picitem.moc" diff --git a/src/picitem.h b/src/picitem.h new file mode 100644 index 0000000..0474ba8 --- /dev/null +++ b/src/picitem.h @@ -0,0 +1,94 @@ +/*************************************************************************** + * Copyright (C) 2003,2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef PICITEM_H +#define PICITEM_H + +#include "cnitem.h" + +#include <qobject.h> + +class MicroSettings; +class FlowCodeDocument; +class PinSettings; + +/** +@short Allows visual setting of pin type/state +@author David Saxton +*/ +class PinItem : public QObject, public QCanvasRectangle +{ + Q_OBJECT +public: + PinItem( FlowCodeDocument* _view, QPoint position, bool _onLeft, PinSettings *_pinSettings ); + + int rtti() const; + QRect boundingRect () const; + void switchState(); + + QString id(); + + /** + * Called from ICNDocument when the pin item was dragged + */ + void dragged( int dx ); + + virtual void moveBy ( double dx, double dy ); + +public slots: + void updateDrawing(); + +private: + void initItem(); + void drawShape( QPainter& p ); + void calcTextRect(); + + FlowCodeDocument *view; // Pointer to canvas view that the component item is currently on + bool onLeft; + PinSettings * m_pinSettings; + QRect m_textRect; + QFont m_font; +}; +typedef QValueList<PinItem*> PinItemList; + + +/** +Allows visual editing of inital PIC settings +@author David Saxton +*/ +class PicItem : public CNItem +{ + Q_OBJECT + public: + PicItem( ICNDocument *icnDocument, bool newItem, const char *id, MicroSettings *_microSettings ); + ~PicItem(); + + void drawShape( QPainter &p ); + + virtual void buttonStateChanged( const QString &id, bool state ); + virtual bool isMovable() const { return false; } + + static QString typeString() { return "microitem"; } + virtual void updateZ( int baseZ ); + + protected slots: + void slotMicroSettingsDlgAccepted(); + + protected: + void updateVisibility(); + + MicroSettings *microSettings; + PinItemList m_pinItemList; + ICNDocument *p_icnDocument; + bool m_bExpanded; + int m_innerHeight; +}; + +#endif diff --git a/src/projectmanager.cpp b/src/projectmanager.cpp new file mode 100644 index 0000000..dfef6b3 --- /dev/null +++ b/src/projectmanager.cpp @@ -0,0 +1,1256 @@ +/*************************************************************************** + * Copyright (C) 2003-2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "core/ktlconfig.h" +#include "docmanager.h" +#include "document.h" +#include "language.h" +#include "languagemanager.h" +#include "ktechlab.h" +#include "microselectwidget.h" +#include "programmerdlg.h" +#include "projectdlgs.h" +#include "projectmanager.h" +#include "recentfilesaction.h" + +#include <kdebug.h> +#include <kfiledialog.h> +#include <kiconloader.h> +#include <kio/netaccess.h> +#include <klocale.h> +#include <kmessagebox.h> +#include <kmimetype.h> +#include <kstandarddirs.h> +#include <qdom.h> +#include <qpopupmenu.h> +#include <qwhatsthis.h> + +#include <assert.h> + +//BEGIN class LinkerOptions +LinkerOptions::LinkerOptions() +{ + m_hexFormat = HexFormat::inhx32; + m_bOutputMapFile = false; +} + + +QDomElement LinkerOptions::toDomElement( QDomDocument & doc, const KURL & baseURL ) const +{ + QDomElement node = doc.createElement("linker"); + + node.setAttribute( "hex-format", hexFormatToString(hexFormat()) ); + node.setAttribute( "output-map-file", outputMapFile() ); + node.setAttribute( "library-dir", libraryDir() ); + node.setAttribute( "linker-script", linkerScript() ); + node.setAttribute( "other", linkerOther() ); + + QStringList::const_iterator end = m_linkedInternal.end(); + for ( QStringList::const_iterator it = m_linkedInternal.begin(); it != end; ++it ) + { + QDomElement child = doc.createElement("linked-internal"); + node.appendChild(child); + child.setAttribute( "url", KURL::relativeURL( baseURL, *it ) ); + } + + end = m_linkedExternal.end(); + for ( QStringList::const_iterator it = m_linkedExternal.begin(); it != end; ++it ) + { + QDomElement child = doc.createElement("linked-external"); + node.appendChild(child); + child.setAttribute( "url", *it ); + } + + return node; +} + + +void LinkerOptions::domElementToLinkerOptions( const QDomElement & element, const KURL & baseURL ) +{ + setHexFormat( stringToHexFormat( element.attribute( "hex-format", QString::null ) ) ); + setOutputMapFile( element.attribute( "output-map-file", "0" ).toInt() ); + setLibraryDir( element.attribute( "library-dir", QString::null ) ); + setLinkerScript( element.attribute( "linker-script", QString::null ) ); + setLinkerOther( element.attribute( "other", QString::null ) ); + + m_linkedInternal.clear(); + m_linkedExternal.clear(); + + QDomNode node = element.firstChild(); + while ( !node.isNull() ) + { + QDomElement childElement = node.toElement(); + if ( !childElement.isNull() ) + { + const QString tagName = childElement.tagName(); + + if ( tagName == "linked-internal" ) + m_linkedInternal << KURL( baseURL, childElement.attribute( "url", QString::null ) ).url(); + + else if ( tagName == "linked-external" ) + m_linkedExternal << childElement.attribute( "url", QString::null ); + + else + kdError() << k_funcinfo << "Unrecognised element tag name: "<<tagName<<endl; + } + + node = node.nextSibling(); + } +} + + +QString LinkerOptions::hexFormatToString( HexFormat::type hexFormat ) +{ + switch ( hexFormat ) + { + case HexFormat::inhx32: + return "inhx32"; + + case HexFormat::inhx8m: + return "inhx8m"; + + case HexFormat::inhx8s: + return "inhx8s"; + + case HexFormat::inhx16: + return "inhx16"; + } + + // Default hex format is inhx32 + return "inhx32"; +} + + +LinkerOptions::HexFormat::type LinkerOptions::stringToHexFormat( const QString & hexFormat ) +{ + if ( hexFormat == "inhx8m" ) + return HexFormat::inhx8m; + + if ( hexFormat == "inhx8s" ) + return HexFormat::inhx8s; + + if ( hexFormat == "inhx16" ) + return HexFormat::inhx16; + + return HexFormat::inhx32; +} +//END class LinkerOptions + + + +//BEGIN class ProcessingOptions +ProcessingOptions::ProcessingOptions() +{ + m_bUseParentMicroID = false; + m_microID = "P16F84"; +} + + +ProcessingOptions::~ProcessingOptions() +{ +} + + +QDomElement ProcessingOptions::toDomElement( QDomDocument & doc, const KURL & baseURL ) const +{ + QDomElement node = doc.createElement("processing"); + + node.setAttribute( "output", KURL::relativeURL( baseURL, outputURL().url() ) ); + node.setAttribute( "micro", m_microID ); + + return node; +} + + +void ProcessingOptions::domElementToProcessingOptions( const QDomElement & element, const KURL & baseURL ) +{ + setOutputURL( KURL( baseURL, element.attribute( "output", QString::null ) ) ); + setMicroID( element.attribute("micro", QString::null ) ); +} +//END class ProcessingOptions + + + +//BEGIN class ProjectItem +ProjectItem::ProjectItem( ProjectItem * parent, Type type, ProjectManager * projectManager, KTechlab * ktechlab ) + : QObject() +{ + m_pParent = parent; + m_pILVItem = 0l; + m_pProjectManager = projectManager; + p_ktechlab = ktechlab; + m_type = type; +} + + +ProjectItem::~ProjectItem() +{ + m_children.remove( (ProjectItem*)0l ); + ProjectItemList::iterator end = m_children.end(); + for ( ProjectItemList::iterator it = m_children.begin(); it != end; ++it ) + (*it)->deleteLater(); + m_children.clear(); + + delete m_pILVItem; + m_pILVItem = 0l; +} + + +void ProjectItem::setILVItem( ILVItem * ilvItem ) +{ + m_pILVItem = ilvItem; + ilvItem->setOpen(true); + ilvItem->setText( 0, name() ); + ilvItem->setProjectItem(this); + updateILVItemPixmap(); +} + + +void ProjectItem::updateILVItemPixmap() +{ + if ( !m_pILVItem ) + return; + + switch ( type() ) + { + case ProjectType: + { + // ?! - We shouldn't have an ilvitem for this. + break; + } + + case ProgramType: + { + QPixmap pm; + pm.load( locate( "appdata", "icons/project_program.png" ) ); + m_pILVItem->setPixmap( 0, pm ); + break; + } + + case LibraryType: + { + QPixmap pm; + pm.load( locate( "appdata", "icons/project_library.png" ) ); + m_pILVItem->setPixmap( 0, pm ); + break; + } + + case FileType: + { + KMimeType::Ptr m = KMimeType::findByPath( url().path() ); + m_pILVItem->setPixmap( 0, m->pixmap( KIcon::Small ) ); + break; + } + } +} + + +void ProjectItem::addChild( ProjectItem * child ) +{ + if ( !child || m_children.contains(child) ) + return; + + m_children << child; + + child->setILVItem( m_pILVItem ? + new ILVItem( m_pILVItem, child->name() ) : + new ILVItem( m_pProjectManager, name() ) ); + + updateControlChildMicroIDs(); +} + + +void ProjectItem::updateControlChildMicroIDs() +{ + bool control = false; + switch ( type() ) + { + case ProjectItem::ProjectType: + case ProjectItem::LibraryType: + case ProjectItem::ProgramType: + control = !microID().isEmpty(); + break; + + case ProjectItem::FileType: + control = true; + break; + } + + m_children.remove( (ProjectItem*)0l ); + ProjectItemList::iterator end = m_children.end(); + for ( ProjectItemList::iterator it = m_children.begin(); it != end; ++it ) + (*it)->setUseParentMicroID( control ); +} + + +void ProjectItem::setName( const QString & name ) +{ + m_name = name; + if (m_pILVItem) + m_pILVItem->setText( 0, name ); +} + + +void ProjectItem::setURL( const KURL & url ) +{ + m_url = url; + + if ( m_name.isEmpty() ) + setName( url.fileName() ); + + if ( type() != FileType ) + { + // The output url *is* our url + setOutputURL(url); + } + else if ( outputURL().isEmpty() ) + { + // Try and guess what the output url should be... + QString newExtension; + + switch ( outputType() ) + { + case ProgramOutput: + newExtension = ".hex"; + break; + + case ObjectOutput: + newExtension = ".o"; + break; + + case LibraryOutput: + newExtension = ".o"; + break; + + case UnknownOutput: + break; + } + + if ( !newExtension.isEmpty() ) + { + const QString fileName = url.url(); + QString extension = fileName.right( fileName.length() - fileName.findRev('.') ); + setOutputURL( QString(fileName).replace( extension, newExtension ) ); + } + } + + updateILVItemPixmap(); +} + + +QString ProjectItem::microID() const +{ + if ( !m_bUseParentMicroID ) + return m_microID; + + return m_pParent ? m_pParent->microID() : QString::null; +} + + +void ProjectItem::setMicroID( const QString & id ) +{ + ProcessingOptions::setMicroID(id); + updateControlChildMicroIDs(); +} + + +ProjectItem::OutputType ProjectItem::outputType() const +{ + if ( !m_pParent ) + return UnknownOutput; + + switch ( m_pParent->type() ) + { + case ProjectItem::ProjectType: + { + // We're a top level build target, so look at our own type + switch ( type() ) + { + case ProjectItem::ProjectType: + kdWarning() << k_funcinfo << "Parent item and this item are both project items" << endl; + return UnknownOutput; + + case ProjectItem::FileType: + case ProjectItem::ProgramType: + return ProgramOutput; + + case ProjectItem::LibraryType: + return LibraryOutput; + } + return UnknownOutput; + } + + case ProjectItem::FileType: + { + kdWarning() << k_funcinfo << "Don't know how to handle parent item being a file" << endl; + return UnknownOutput; + } + + case ProjectItem::ProgramType: + case ProjectItem::LibraryType: + return ObjectOutput; + } + + return UnknownOutput; +} + + +bool ProjectItem::build( ProcessOptionsList * pol ) +{ + if ( !pol ) + return false; + + // Check to see that we aren't already in the ProcessOptionstList; + ProcessOptionsList::iterator polEnd = pol->end(); + for ( ProcessOptionsList::iterator it = pol->begin(); it != polEnd; ++it ) + { + if ( (*it).targetFile() == outputURL().path() ) + return true; + } + + ProjectInfo * projectInfo = ProjectManager::self()->currentProject(); + assert(projectInfo); + + if ( outputURL().isEmpty() ) + { + KMessageBox::sorry( 0l, i18n("Don't know how to build \"%1\" (output url is empty).").arg(name()) ); + return false; + } + + // Build all internal libraries that we depend on + QStringList::iterator send = m_linkedInternal.end(); + for ( QStringList::iterator it = m_linkedInternal.begin(); it != send; ++it ) + { + ProjectItem * lib = projectInfo->findItem( projectInfo->directory() + *it ); + if ( !lib ) + { + KMessageBox::sorry( 0l, i18n("Don't know how to build \"%1\" (library does not exist in project).").arg(*it) ); + return false; + } + + if ( !lib->build(pol) ) + return false; + } + + + // Build all children + m_children.remove( (ProjectItem*)0l ); + ProjectItemList::iterator cend = m_children.end(); + for ( ProjectItemList::iterator it = m_children.begin(); it != cend; ++it ) + { + if ( ! (*it)->build(pol) ) + return false; + } + + + // Now build ourself + ProcessOptions po; + po.b_addToProject = false; + po.setTargetFile( outputURL().path() ); + po.m_picID = microID(); + + ProcessOptions::ProcessPath::MediaType typeTo; + + switch ( outputType() ) + { + case UnknownOutput: + KMessageBox::sorry( 0l, i18n("Don't know how to build \"%1\" (unknown output type).").arg(name()) ); + return false; + + case ProgramOutput: + typeTo = ProcessOptions::ProcessPath::Program; + break; + + case ObjectOutput: + typeTo = ProcessOptions::ProcessPath::Object; + break; + + case LibraryOutput: + typeTo = ProcessOptions::ProcessPath::Library; + break; + } + + switch ( type() ) + { + case ProjectType: + // Nothing to do + return true; + + case FileType: + po.setInputFiles( url().path() ); + po.setProcessPath( ProcessOptions::ProcessPath::path( ProcessOptions::guessMediaType( url().url() ), typeTo ) ); + break; + + case ProgramType: + case LibraryType: + // Build up a list of input urls + QStringList inputFiles; + + // Link child objects + m_children.remove( (ProjectItem*)0l ); + ProjectItemList::iterator cend = m_children.end(); + for ( ProjectItemList::iterator it = m_children.begin(); it != cend; ++it ) + inputFiles << (*it)->outputURL().path(); + + po.setInputFiles(inputFiles); + po.setProcessPath( ProcessOptions::ProcessPath::path( ProcessOptions::ProcessPath::Object, typeTo ) ); + break; + } + + po.m_hexFormat = hexFormatToString( hexFormat() ); + po.m_bOutputMapFile = outputMapFile(); + po.m_libraryDir = libraryDir(); + po.m_linkerScript = linkerScript(); + po.m_linkOther = linkerOther(); + + // Link against libraries + QStringList::iterator lend = m_linkedInternal.end(); + for ( QStringList::iterator it = m_linkedInternal.begin(); it != lend; ++it ) + po.m_linkLibraries += projectInfo->directory() + *it; + lend = m_linkedExternal.end(); + for ( QStringList::iterator it = m_linkedExternal.begin(); it != lend; ++it ) + po.m_linkLibraries += *it; + + // Save our working file (if open) and append to the build list + Document * currentDoc = DocManager::self()->findDocument( url() ); + if (currentDoc) + currentDoc->fileSave(); + pol->append(po); + + return true; +} + + +void ProjectItem::upload( ProcessOptionsList * pol ) +{ + build( pol ); + + ProgrammerDlg * dlg = new ProgrammerDlg( microID(), (QWidget*)p_ktechlab, "Programmer Dlg" ); + + dlg->exec(); + if ( !dlg->isAccepted() ) + { + dlg->deleteLater(); + return; + } + + ProcessOptions po; + dlg->initOptions( & po ); + po.b_addToProject = false; + po.setInputFiles( outputURL().path() ); + po.setProcessPath( ProcessOptions::ProcessPath::Program_PIC ); + + pol->append( po ); + + dlg->deleteLater(); +} + + +QDomElement ProjectItem::toDomElement( QDomDocument & doc, const KURL & baseURL ) const +{ + QDomElement node = doc.createElement("item"); + + node.setAttribute( "type", typeToString() ); + node.setAttribute( "name", m_name ); + node.setAttribute( "url", KURL::relativeURL( baseURL, m_url.url() ) ); + + node.appendChild( LinkerOptions::toDomElement( doc, baseURL ) ); + node.appendChild( ProcessingOptions::toDomElement( doc, baseURL ) ); + + + ProjectItemList::const_iterator end = m_children.end(); + for ( ProjectItemList::const_iterator it = m_children.begin(); it != end; ++it ) + { + if (*it) + node.appendChild( (*it)->toDomElement( doc, baseURL ) ); + } + + return node; +} + + +KURL::List ProjectItem::childOutputURLs( unsigned types, unsigned outputTypes ) const +{ + KURL::List urls; + + ProjectItemList::const_iterator end = m_children.end(); + for ( ProjectItemList::const_iterator it = m_children.begin(); it != end; ++it ) + { + if (!*it) + continue; + + if ( ((*it)->type() & types) && ((*it)->outputType() & outputTypes) ) + urls += (*it)->outputURL().prettyURL(); + + urls += (*it)->childOutputURLs(types); + } + + return urls; +} + + +ProjectItem * ProjectItem::findItem( const KURL & url ) +{ + if ( this->url() == url ) + return this; + + ProjectItemList::const_iterator end = m_children.end(); + for ( ProjectItemList::const_iterator it = m_children.begin(); it != end; ++it ) + { + if (!*it) + continue; + + ProjectItem * found = (*it)->findItem(url); + if (found) + return found; + } + + return 0l; +} + + +bool ProjectItem::closeOpenFiles() +{ + Document * doc = DocManager::self()->findDocument(m_url); + if ( doc && !doc->fileClose() ) + return false; + + m_children.remove( (ProjectItem*)0l ); + ProjectItemList::iterator end = m_children.end(); + for ( ProjectItemList::iterator it = m_children.begin(); it != end; ++it ) + { + if ( !(*it)->closeOpenFiles() ) + return false; + } + + return true; +} + + +void ProjectItem::addFiles() +{ + KURL::List urls = p_ktechlab->getFileURLs(); + const KURL::List::iterator end = urls.end(); + for ( KURL::List::iterator it = urls.begin(); it != end; ++ it) + addFile(*it); +} + + +void ProjectItem::addCurrentFile() +{ + Document *document = DocManager::self()->getFocusedDocument(); + if (!document) + return; + + // If the file isn't saved yet, we must do that + // before it is added to the project. + if( document->url().isEmpty() ) + { + document->fileSaveAs(); + // If the user pressed cancel then just give up, + // otherwise the file can now be added. + } + + if( !document->url().isEmpty() ) + addFile( document->url() ); +} + + +void ProjectItem::addFile( const KURL & url ) +{ + if ( url.isEmpty() ) + return; + + m_children.remove( (ProjectItem*)0l ); + ProjectItemList::iterator end = m_children.end(); + for ( ProjectItemList::iterator it = m_children.begin(); it != end; ++it ) + { + if ( (*it)->type() == FileType && (*it)->url() == url ) + return; + } + + ProjectItem * item = new ProjectItem( this, FileType, m_pProjectManager, p_ktechlab ); + item->setURL(url); + addChild(item); +} + + +QString ProjectItem::typeToString() const +{ + switch (m_type) + { + case ProjectType: + return "Project"; + + case FileType: + return "File"; + + case ProgramType: + return "Program"; + + case LibraryType: + return "Library"; + } + return QString::null; +} + + +ProjectItem::Type ProjectItem::stringToType( const QString & type ) +{ + if ( type == "Project" ) + return ProjectType; + + if ( type == "File" ) + return FileType; + + if ( type == "Program" ) + return ProgramType; + + if ( type == "Library" ) + return LibraryType; + + return FileType; +} + + +void ProjectItem::domElementToItem( const QDomElement & element, const KURL & baseURL ) +{ + Type type = stringToType( element.attribute( "type", QString::null ) ); + QString name = element.attribute( "name", QString::null ); + KURL url( baseURL, element.attribute( "url", QString::null ) ); + + ProjectItem * createdItem = new ProjectItem( this, type, m_pProjectManager, p_ktechlab ); + createdItem->setName( name ); + createdItem->setURL( url ); + + addChild( createdItem ); + + QDomNode node = element.firstChild(); + while ( !node.isNull() ) + { + QDomElement childElement = node.toElement(); + if ( !childElement.isNull() ) + { + const QString tagName = childElement.tagName(); + + if ( tagName == "linker" ) + createdItem->domElementToLinkerOptions( childElement, baseURL ); + + else if ( tagName == "processing" ) + createdItem->domElementToProcessingOptions( childElement, baseURL ); + + else if ( tagName == "item" ) + createdItem->domElementToItem( childElement, baseURL ); + + else + kdError() << k_funcinfo << "Unrecognised element tag name: "<<tagName<<endl; + } + + node = node.nextSibling(); + } +} +//END class ProjectItem + + + +//BEGIN class ProjectInfo +ProjectInfo::ProjectInfo( ProjectManager * projectManager, KTechlab * ktechlab ) + : ProjectItem( 0l, ProjectItem::ProjectType, projectManager, ktechlab ) +{ + m_microID = QString::null; +} + + +ProjectInfo::~ ProjectInfo() +{ +} + + +bool ProjectInfo::open( const KURL & url ) +{ + QString target; + if ( !KIO::NetAccess::download( url, target, 0l ) ) + { + // If the file could not be downloaded, for example does not + // exist on disk, NetAccess will tell us what error to use + KMessageBox::error( 0l, KIO::NetAccess::lastErrorString() ); + + return false; + } + + QFile file(target); + if ( !file.open( IO_ReadOnly ) ) + { + KMessageBox::sorry( 0l, i18n("Could not open %1 for reading").arg(target) ); + return false; + } + + m_url = url; + + QString xml; + QTextStream textStream( &file ); + while ( !textStream.eof() ) + xml += textStream.readLine() + '\n'; + + file.close(); + + QDomDocument doc( "KTechlab" ); + QString errorMessage; + if ( !doc.setContent( xml, &errorMessage ) ) + { + KMessageBox::sorry( 0l, i18n("Couldn't parse xml:\n%1").arg(errorMessage) ); + return false; + } + + QDomElement root = doc.documentElement(); + + QDomNode node = root.firstChild(); + while ( !node.isNull() ) + { + QDomElement element = node.toElement(); + if ( !element.isNull() ) + { + const QString tagName = element.tagName(); + + if ( tagName == "linker" ) + domElementToLinkerOptions( element, m_url ); + + else if ( tagName == "processing" ) + domElementToProcessingOptions( element, m_url ); + + else if ( tagName == "file" || tagName == "item" ) + domElementToItem( element, m_url ); + + else + kdWarning() << k_funcinfo << "Unrecognised element tag name: "<<tagName<<endl; + } + + node = node.nextSibling(); + } + + updateControlChildMicroIDs(); + return true; +} + + +bool ProjectInfo::save() +{ + QFile file( m_url.path() ); + if ( file.open(IO_WriteOnly) == false ) + { + KMessageBox::sorry( NULL, i18n("Project could not be saved to \"%1\"").arg(m_url.path()), i18n("Saving Project") ); + return false; + } + + QDomDocument doc("KTechlab"); + + QDomElement root = doc.createElement("project"); + doc.appendChild(root); + +// root.appendChild( LinkerOptions::toDomElement(doc) ); +// root.appendChild( ProcessingOptions::toDomElement(doc) ); + + m_children.remove( (ProjectItem*)0l ); + ProjectItemList::const_iterator end = m_children.end(); + for ( ProjectItemList::const_iterator it = m_children.begin(); it != end; ++it ) + root.appendChild( (*it)->toDomElement( doc, m_url ) ); + + QTextStream stream(&file); + stream << doc.toString(); + file.close(); + + (static_cast<RecentFilesAction*>(p_ktechlab->action("project_open_recent")))->addURL(m_url); + + return true; +} + + +bool ProjectInfo::saveAndClose() +{ + if (!save()) + return false; + + if (!closeOpenFiles()) + return false; + + return true; +} +//END class ProjectInfo + + + +//BEGIN class ProjectManager +ProjectManager * ProjectManager::m_pSelf = 0l; + +ProjectManager * ProjectManager::self( KTechlab * ktl, KateMDI::ToolView * parent ) +{ + if ( !m_pSelf ) + { + assert(ktl); + assert(parent); + m_pSelf = new ProjectManager( ktl, parent ); + } + return m_pSelf; +} + + +ProjectManager::ProjectManager( KTechlab * ktl, KateMDI::ToolView * parent ) + : ItemSelector( parent, "Project Manager" ), + m_pCurrentProject(0l), + p_ktechlab(ktl) +{ + QWhatsThis::add( this, i18n("Displays the list of files in the project.\nTo open or close a project, use the \"Project\" menu. Right click on a file to remove it from the project") ); + + setListCaption( i18n("File") ); + setCaption( i18n("Project Manager") ); + + connect( this, SIGNAL(clicked(QListViewItem*)), this, SLOT(slotItemClicked(QListViewItem*)) ); +} + + +ProjectManager::~ProjectManager() +{ +} + + +void ProjectManager::slotNewProject() +{ + if ( !slotCloseProject() ) + return; + + NewProjectDlg *newProjectDlg = new NewProjectDlg(this); + newProjectDlg->exec(); + + if ( newProjectDlg->accepted() ) + { + m_pCurrentProject = new ProjectInfo( this, p_ktechlab ); + m_pCurrentProject->setName( newProjectDlg->projectName() ); + m_pCurrentProject->setURL( newProjectDlg->location() + m_pCurrentProject->name().lower() + ".ktechlab" ); + + QDir dir; + if ( !dir.mkdir( m_pCurrentProject->directory() ) ) + kdDebug() << "Error in creating directory " << m_pCurrentProject->directory() << endl; + + m_pCurrentProject->save(); + updateActions(); + + emit projectCreated(); + } + + delete newProjectDlg; +} + + +void ProjectManager::slotProjectOptions() +{ +} + + +void ProjectManager::slotOpenProject() +{ + KURL url = KFileDialog::getOpenURL(QString::null, + "*.ktechlab|KTechlab Project(*.ktechlab)\n*|All Files", this, i18n("Open Location")); + + if ( url.isEmpty() ) + return; + + slotOpenProject(url); +} + + +void ProjectManager::slotOpenProject( const KURL & url ) +{ + if ( m_pCurrentProject && m_pCurrentProject->url() == url ) + return; + + if ( !slotCloseProject() ) + return; + + m_pCurrentProject = new ProjectInfo( this, p_ktechlab ); + + if ( !m_pCurrentProject->open(url) ) + { + m_pCurrentProject->deleteLater(); + m_pCurrentProject = 0l; + return; + } + + RecentFilesAction * rfa = static_cast<RecentFilesAction*>(p_ktechlab->action("project_open_recent")); + rfa->addURL( m_pCurrentProject->url() ); + + if ( KTLConfig::raiseItemSelectors() ) + p_ktechlab->showToolView( p_ktechlab->toolView( toolViewIdentifier() ) ); + + updateActions(); + emit projectOpened(); +} + + +bool ProjectManager::slotCloseProject() +{ + if ( !m_pCurrentProject ) + return true; + + if ( !m_pCurrentProject->saveAndClose() ) + return false; + + m_pCurrentProject->deleteLater(); + m_pCurrentProject = 0l; + updateActions(); + emit projectClosed(); + return true; +} + + +void ProjectManager::slotCreateSubproject() +{ + if ( !currentProject() ) + return; + + CreateSubprojectDlg * dlg = new CreateSubprojectDlg(this); + dlg->exec(); + + if ( dlg->accepted() ) + { + ProjectItem::Type type = ProjectItem::ProgramType; + switch ( dlg->type() ) + { + case CreateSubprojectDlg::ProgramType: + type = ProjectItem::ProgramType; + break; + + case CreateSubprojectDlg::LibraryType: + type = ProjectItem::LibraryType; + break; + } + + ProjectItem * subproject = new ProjectItem( currentProject(), type, this, p_ktechlab ); + subproject->setURL( dlg->targetFile() ); + + currentProject()->addChild(subproject); + currentProject()->save(); + + emit subprojectCreated(); + } + + delete dlg; +} + + +void ProjectManager::updateActions() +{ + bool projectIsOpen = m_pCurrentProject; + + p_ktechlab->action("project_create_subproject")->setEnabled( projectIsOpen ); + p_ktechlab->action("project_export_makefile")->setEnabled( projectIsOpen ); + p_ktechlab->action("subproject_add_existing_file")->setEnabled( projectIsOpen ); + p_ktechlab->action("subproject_add_current_file")->setEnabled( projectIsOpen ); +// p_ktechlab->action("project_options")->setEnabled( projectIsOpen ); + p_ktechlab->action("project_close")->setEnabled( projectIsOpen ); + p_ktechlab->action("project_add_existing_file")->setEnabled( projectIsOpen ); + p_ktechlab->action("project_add_current_file")->setEnabled( projectIsOpen ); +} + + +void ProjectManager::slotAddFile() +{ + if ( !currentProject() ) + return; + + currentProject()->addFiles(); + emit filesAdded(); +} + + +void ProjectManager::slotAddCurrentFile() +{ + if ( !currentProject() ) + return; + currentProject()->addCurrentFile(); + emit filesAdded(); +} + + +void ProjectManager::slotSubprojectAddExistingFile() +{ + ILVItem * currentItem = dynamic_cast<ILVItem*>(selectedItem()); + if ( !currentItem || !currentItem->projectItem() ) + return; + + currentItem->projectItem()->addFiles(); + emit filesAdded(); +} + + +void ProjectManager::slotSubprojectAddCurrentFile() +{ + ILVItem * currentItem = dynamic_cast<ILVItem*>(selectedItem()); + if ( !currentItem || !currentItem->projectItem() ) + return; + + currentItem->projectItem()->addCurrentFile(); + emit filesAdded(); +} + + +void ProjectManager::slotItemBuild() +{ + ILVItem * currentItem = dynamic_cast<ILVItem*>(selectedItem()); + if ( !currentItem || !currentItem->projectItem() ) + return; + + ProcessOptionsList pol; + currentItem->projectItem()->build(&pol); + LanguageManager::self()->compile(pol); +} + + +void ProjectManager::slotItemUpload() +{ + ILVItem * currentItem = dynamic_cast<ILVItem*>(selectedItem()); + if ( !currentItem || !currentItem->projectItem() ) + return; + + ProcessOptionsList pol; + currentItem->projectItem()->upload(&pol); + LanguageManager::self()->compile(pol); +} + + +void ProjectManager::slotRemoveSelected() +{ + ILVItem *currentItem = dynamic_cast<ILVItem*>(selectedItem()); + if ( !currentItem ) + return; + + int choice = KMessageBox::questionYesNo( this, i18n("Do you really want to remove \"%1\"?").arg( currentItem->text(0) ), i18n("Remove Project File?"), KGuiItem(i18n("Remove")), KGuiItem(i18n("Cancel")) ); + + if ( choice == KMessageBox::No ) + return; + + currentItem->projectItem()->deleteLater(); + emit filesRemoved(); +} + + +void ProjectManager::slotExportToMakefile() +{ +} + + +void ProjectManager::slotSubprojectLinkerOptions() +{ + ILVItem * currentItem = dynamic_cast<ILVItem*>(selectedItem()); + if ( !currentItem || !currentItem->projectItem() ) + return; + + LinkerOptionsDlg * dlg = new LinkerOptionsDlg( currentItem->projectItem(), this ); + dlg->exec(); + currentProject()->save(); + + // The dialog sets the options for us if it was accepted, so we don't need to do anything + delete dlg; +} + + +void ProjectManager::slotItemProcessingOptions() +{ + ILVItem * currentItem = dynamic_cast<ILVItem*>(selectedItem()); + if ( !currentItem || !currentItem->projectItem() ) + return; + + ProcessingOptionsDlg * dlg = new ProcessingOptionsDlg( currentItem->projectItem(), this ); + dlg->exec(); + currentProject()->save(); + + // The dialog sets the options for us if it was accepted, so we don't need to do anything + delete dlg; +} + + +void ProjectManager::slotItemClicked( QListViewItem * item ) +{ + ILVItem * ilvItem = dynamic_cast<ILVItem*>(item); + if ( !ilvItem ) + return; + + ProjectItem * projectItem = ilvItem->projectItem(); + if ( !projectItem || projectItem->type() != ProjectItem::FileType ) + return; + + DocManager::self()->openURL( projectItem->url() ); +} + + +void ProjectManager::slotContextMenuRequested( QListViewItem * item, const QPoint& pos, int /*col*/ ) +{ + QString popupName; + ILVItem * ilvItem = dynamic_cast<ILVItem*>(item); + KAction * linkerOptionsAct = p_ktechlab->action("project_item_linker_options"); + linkerOptionsAct->setEnabled(false); + + if ( !m_pCurrentProject ) + popupName = "project_none_popup"; + + else if ( !ilvItem ) + popupName = "project_blank_popup"; + + else + { + ProcessOptions::ProcessPath::MediaType mediaType = ProcessOptions::guessMediaType( ilvItem->projectItem()->url().url() ); + + switch ( ilvItem->projectItem()->type() ) + { + case ProjectItem::FileType: + if ( mediaType == ProcessOptions::ProcessPath::Unknown ) + popupName = "project_file_other_popup"; + else + popupName = "project_file_popup"; + break; + + case ProjectItem::ProgramType: + popupName = "project_program_popup"; + break; + + case ProjectItem::LibraryType: + popupName = "project_library_popup"; + break; + + case ProjectItem::ProjectType: + return; + } + switch ( ilvItem->projectItem()->outputType() ) + { + case ProjectItem::ProgramOutput: + linkerOptionsAct->setEnabled(true); + break; + + case ProjectItem::ObjectOutput: + case ProjectItem::LibraryOutput: + case ProjectItem::UnknownOutput: + linkerOptionsAct->setEnabled(false); + break; + } + + // Only have linking options for SDCC files + linkerOptionsAct->setEnabled( mediaType == ProcessOptions::ProcessPath::C ); + } + + bool haveFocusedDocument = DocManager::self()->getFocusedDocument(); + p_ktechlab->action("subproject_add_current_file")->setEnabled( haveFocusedDocument ); + p_ktechlab->action("project_add_current_file")->setEnabled( haveFocusedDocument ); + + QPopupMenu *pop = static_cast<QPopupMenu*>(p_ktechlab->factory()->container( popupName, p_ktechlab )); + if (pop) + pop->popup(pos); +} +//END class ProjectManager + +#include "projectmanager.moc" diff --git a/src/projectmanager.h b/src/projectmanager.h new file mode 100644 index 0000000..81620cb --- /dev/null +++ b/src/projectmanager.h @@ -0,0 +1,345 @@ +/*************************************************************************** + * Copyright (C) 2003-2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef PROJECTMANAGER_H +#define PROJECTMANAGER_H + +#include "itemselector.h" + +#include <kurl.h> +#include <qguardedptr.h> +#include <qvaluelist.h> + +class Document; +class ILVItem; +class KTechlab; +class ProcessOptions; +class ProjectInfo; +class ProjectItem; +class ProjectManager; +class QDomDocument; +class QDomElement; +class QStringList; +namespace KateMDI { class ToolView; } + +typedef QValueList<ProcessOptions> ProcessOptionsList; +typedef QValueList< QGuardedPtr<ProjectItem> > ProjectItemList; + + +class LinkerOptions +{ + public: + LinkerOptions(); + + class HexFormat + { + public: + enum type { inhx32, inhx8m, inhx8s, inhx16 }; + }; + + HexFormat::type hexFormat() const { return m_hexFormat; } + void setHexFormat( HexFormat::type hexFormat ) { m_hexFormat = hexFormat; } + + bool outputMapFile() const { return m_bOutputMapFile; } + void setOutputMapFile( bool outputMapFile ) { m_bOutputMapFile = outputMapFile; } + + QString libraryDir() const { return m_libraryDir; } + void setLibraryDir( const QString & libraryDir ) { m_libraryDir = libraryDir; } + + QString linkerScript() const { return m_linkerScript; } + void setLinkerScript( const QString & linkerScript ) { m_linkerScript = linkerScript; } + + QString linkerOther() const { return m_other; } + void setLinkerOther( const QString & other ) { m_other = other; } + + /** + * Used for linkable ProjectItems. Returns a list of urls of files + * inside the project to link against. Each url is relative to the + * project directory. + */ + QStringList linkedInternal() const { return m_linkedInternal; } + void setLinkedInternal( const QStringList & linkedInternal ) { m_linkedInternal = linkedInternal; } + + /** + * Used for linkable ProjectItems. Returns a list of urls of files + * outside the project to link against. Each url is absolute. + */ + QStringList linkedExternal() const { return m_linkedExternal; } + void setLinkedExternal( const QStringList & linkedExternal ) { m_linkedExternal = linkedExternal; } + + QDomElement toDomElement( QDomDocument & doc, const KURL & baseURL ) const; + + static QString hexFormatToString( HexFormat::type format ); + static HexFormat::type stringToHexFormat( const QString & hexFormat ); + + protected: + void domElementToLinkerOptions( const QDomElement & element, const KURL & baseURL ); + + QStringList m_linkedInternal; + QStringList m_linkedExternal; + HexFormat::type m_hexFormat; + bool m_bOutputMapFile; + QString m_libraryDir; + QString m_linkerScript; + QString m_other; +}; + + +class ProcessingOptions +{ + public: + ProcessingOptions(); + virtual ~ProcessingOptions(); + + /** + * Sets the output url that this item will be built into (if this is a + * buildable item). + */ + void setOutputURL( const KURL & url ) { m_outputURL = url; } + KURL outputURL() const { return m_outputURL; } + + /** + * Set the microprocessor id that this project item is being built for + * (when applicable). + */ + virtual void setMicroID( const QString & id ) { m_microID = id; } + virtual QString microID() const { return m_microID; } + + QDomElement toDomElement( QDomDocument & doc, const KURL & baseURL ) const; + + void setUseParentMicroID( bool useParentMicroID ) { m_bUseParentMicroID = useParentMicroID; } + bool useParentMicroID() const { return m_bUseParentMicroID; } + + protected: + void domElementToProcessingOptions( const QDomElement & element, const KURL & baseURL ); + + KURL m_outputURL; + QString m_microID; + bool m_bUseParentMicroID; +}; + + +/** +@author David Saxton +*/ +class ProjectItem : public QObject, public LinkerOptions, public ProcessingOptions +{ + public: + enum Type + { + ProjectType = 1 << 0, + FileType = 1 << 1, + ProgramType = 1 << 2, + LibraryType = 1 << 3 + }; + enum { AllTypes = ProjectType | FileType | ProgramType | LibraryType }; + + enum OutputType + { + ProgramOutput = 1 << 0, + ObjectOutput = 1 << 1, + LibraryOutput = 1 << 2, + UnknownOutput = 1 << 3 + }; + enum { AllOutputs = ProgramOutput | ObjectOutput | LibraryOutput | UnknownOutput }; + + ProjectItem( ProjectItem * parent, Type type, ProjectManager * projectManager, KTechlab * ktechlab ); + virtual ~ProjectItem(); + + Type type() const { return m_type; } + QString typeToString() const; + static Type stringToType( const QString & type ); + + void setILVItem( ILVItem * ilvItem ); + + /** + * Adds the child to the list of children, and creates an ILVItem for it + * in the project tree view. + */ + void addChild( ProjectItem * child ); + ProjectItemList children() const { return m_children; } + + void setName( const QString & name ); + QString name() const { return m_name; } + + /** + * Sets the (input) url that this project item refers to. If the output + * url has not yet been set, then this project item will set the output + * url based on this (input) url. + */ + void setURL( const KURL & url ); + KURL url() const { return m_url; } + + OutputType outputType() const; + + /** + * Returns a list of output urls of the children and their recursively + * contained children (does not include the url for this project item). + * @param types An OR'ed list of ProjectItem::Type values for the + * children. + * @param outputTypes An OR'ed list of ProjectItem::OutputType values + * for the children. + */ + KURL::List childOutputURLs( unsigned types = AllTypes, unsigned outputTypes = AllOutputs ) const; + + /** + * Creates a new ProjectItem for the given url and adds it as a child. + */ + void addFile( const KURL & url ); + /** + * Queries the user for a list of urls to add, and then calls addFile + * for each url. + */ + void addFiles(); + + void addCurrentFile(); + bool closeOpenFiles(); + QDomElement toDomElement( QDomDocument & doc, const KURL & baseURL ) const; + + bool build( ProcessOptionsList * pol ); + void upload( ProcessOptionsList * pol ); + + virtual void setMicroID( const QString & id ); + virtual QString microID() const; + + /** + * Searches this item and the children for an item for the given url, + * return null if no such item could be found. + */ + ProjectItem * findItem( const KURL & url ); + + protected: + void domElementToItem( const QDomElement & element, const KURL & baseURL ); + void updateILVItemPixmap(); + void updateControlChildMicroIDs(); + + KURL m_url; + QString m_name; + ProjectItemList m_children; + Type m_type; + + KTechlab * p_ktechlab; + QGuardedPtr<ILVItem> m_pILVItem; + ProjectManager * m_pProjectManager; + ProjectItem * m_pParent; +}; + + +/** +@author David Saxton +*/ +class ProjectInfo : public ProjectItem +{ + Q_OBJECT + + public: + ProjectInfo( ProjectManager * projectManager, KTechlab * ktechlab ); + ~ProjectInfo(); + + /** + * Returns the directory that the project is saved in + */ + QString directory() const { return m_url.directory(false); } + + /** + * Saves the project information to file, and attempts to close all + * open project files. + * @return true iff succesful + */ + bool saveAndClose(); + bool save(); + + bool open( const KURL & url ); +}; + +/** +@short Project Management +@author David Saxton +*/ +class ProjectManager : public ItemSelector +{ + Q_OBJECT + public: + ~ProjectManager(); + static ProjectManager * self( KTechlab * ktl = 0l, KateMDI::ToolView * parent = 0l ); + + static QString toolViewIdentifier() { return "ProjectManager"; } + + /** + * @return the currently open project, or NULL if no project is open. + */ + ProjectInfo * currentProject() const { return m_pCurrentProject; } + + void updateActions(); + + signals: + /** + * Emitted when an existing project is opened. + */ + void projectOpened(); + /** + * Emitted when the current project is closed. + */ + void projectClosed(); + /** + * Emitted when a new project is created. + */ + void projectCreated(); + /** + * Emitted when a subproject is created. + */ + void subprojectCreated(); + /** + * Emitted when file(s) are added to the project or a subproject. + */ + void filesAdded(); + /** + * Emitted when file(s) are removed from the project or a subproject. + */ + void filesRemoved(); + + public slots: + void slotNewProject(); + void slotOpenProject(); + void slotOpenProject( const KURL &url ); + bool slotCloseProject(); + void slotCreateSubproject(); + void slotAddFile(); + void slotAddCurrentFile(); + void slotSubprojectAddExistingFile(); + void slotSubprojectAddCurrentFile(); + void slotItemBuild(); + void slotItemUpload(); + void slotItemProcessingOptions(); + void slotRemoveSelected(); + void slotExportToMakefile(); + void slotSubprojectLinkerOptions(); + /** + * Pops ups a project configuration dialog + */ + void slotProjectOptions(); + + private slots: + void slotContextMenuRequested( QListViewItem *item, const QPoint &pos, int col ); + /** + * Called when a user clicks on any item in the project view + */ + void slotItemClicked( QListViewItem * item ); + + protected: + ProjectInfo * m_pCurrentProject; + KTechlab * const p_ktechlab; + + private: + ProjectManager( KTechlab * ktl, KateMDI::ToolView * parent ); + static ProjectManager * m_pSelf; +}; + +#endif diff --git a/src/recentfilesaction.cpp b/src/recentfilesaction.cpp new file mode 100644 index 0000000..ac38d6b --- /dev/null +++ b/src/recentfilesaction.cpp @@ -0,0 +1,173 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "recentfilesaction.h" + +#include <kconfig.h> +#include <kpopupmenu.h> +#include <kstandarddirs.h> +#include <kurl.h> + +RecentFilesAction::RecentFilesAction( const QString & configGroupName, const QString& text, const QObject* receiver, const char* slot, QObject* parent, const char* name ) + : KSelectAction( text, 0/*pix*/, parent, name ) +{ + m_configGroupName = configGroupName; + m_maxItems = 10; + + m_popup = new KPopupMenu; + connect(m_popup, SIGNAL(aboutToShow()), this, SLOT(menuAboutToShow())); + connect(m_popup, SIGNAL(activated(int)), this, SLOT(menuItemActivated(int))); + connect( this, SIGNAL( activated( const QString& ) ), + this, SLOT( itemSelected( const QString& ) ) ); + + setMenuAccelsEnabled( false ); + + if ( receiver ) + connect( this, SIGNAL(urlSelected(const KURL &)), receiver, slot ); +} + + +RecentFilesAction::~RecentFilesAction() +{ + delete m_popup; +} + +void RecentFilesAction::addURL( const KURL& url ) +{ + if ( url.isLocalFile() && !KGlobal::dirs()->relativeLocation("tmp", url.path()).startsWith("/")) + return; + + QString file; + if ( url.isLocalFile() && url.ref().isNull() && url.query().isNull() ) + file = url.path(); + else + file = url.prettyURL(); + + QStringList lst = items(); + + // remove file if already in list + lst.remove( file ); + + // remove last item if already maxitems in list + if( lst.count() == m_maxItems ) + { + // remove last item + lst.remove( lst.last() ); + } + + // add file to list + lst.prepend( file ); + setItems( lst ); + + saveEntries(); +} + + +void RecentFilesAction::loadEntries() +{ + KConfig * config = KGlobal::config(); + + QString key; + QString value; + QString oldGroup; + QStringList lst; + + oldGroup = config->group(); + + config->setGroup( m_configGroupName ); + + // read file list + for( unsigned int i = 1 ; i <= m_maxItems ; i++ ) + { + key = QString( "File%1" ).arg( i ); + value = config->readPathEntry( key ); + + if (!value.isNull()) + lst.append( value ); + } + + // set file + setItems( lst ); + + config->setGroup( oldGroup ); +} + +void RecentFilesAction::saveEntries() +{ + KConfig * config = KGlobal::config(); + + QString key; + QString value; + QString oldGroup; + QStringList lst = items(); + + oldGroup = config->group(); + + config->deleteGroup( m_configGroupName, true ); + config->setGroup( m_configGroupName ); + + // write file list + for( unsigned int i = 1 ; i <= lst.count() ; i++ ) + { + key = QString( "File%1" ).arg( i ); + value = lst[ i - 1 ]; + config->writePathEntry( key, value ); + } + + config->setGroup( oldGroup ); + + config->sync(); +} + +void RecentFilesAction::itemSelected( const QString& text ) +{ + emit urlSelected( KURL( text ) ); +} + +void RecentFilesAction::menuItemActivated( int id ) +{ + emit urlSelected( KURL(m_popup->text(id)) ); +} + +void RecentFilesAction::menuAboutToShow() +{ + KPopupMenu *menu = m_popup; + menu->clear(); + QStringList list = items(); + QStringList::iterator end = list.end(); + for ( QStringList::Iterator it = list.begin(); it != end; ++it ) + menu->insertItem(*it); +} + +void RecentFilesAction::slotClicked() +{ + KAction::slotActivated(); +} + +void RecentFilesAction::slotActivated(const QString& text) +{ + KSelectAction::slotActivated(text); +} + + +void RecentFilesAction::slotActivated(int id) +{ + KSelectAction::slotActivated(id); +} + + +void RecentFilesAction::slotActivated() +{ + emit activated( currentItem() ); + emit activated( currentText() ); +} + + +#include "recentfilesaction.moc" diff --git a/src/recentfilesaction.h b/src/recentfilesaction.h new file mode 100644 index 0000000..158e945 --- /dev/null +++ b/src/recentfilesaction.h @@ -0,0 +1,74 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef RECENTFILESACTION_H +#define RECENTFILESACTION_H + +#include <kactionclasses.h> + +/** +Taken mainly from kdelibs/kdeui/kactionclasses.[cpp/h], author Michael Koch. +Adapted to work around strange bug occuring. +*/ +class RecentFilesAction : public KSelectAction +{ + Q_OBJECT + public: + RecentFilesAction( const QString & configGroupName, const QString & text, const QObject * receiver, const char* slot, QObject* parent, const char * name ); + + ~RecentFilesAction(); + + /** + * Loads the recent files entries from a given KConfig object. + * You can provide the name of the group used to load the entries. + * If the groupname is empty, entries are load from a group called 'RecentFiles' + * + * This method does not effect the active group of KConfig. + */ + void loadEntries(); + /** + * Saves the current recent files entries to a given KConfig object. + * You can provide the name of the group used to load the entries. + * If the groupname is empty, entries are saved to a group called 'RecentFiles' + * + * This method does not effect the active group of KConfig. + */ + void saveEntries(); + /** + * Add URL to recent files list. + * + * @param url The URL of the file + */ + void addURL( const KURL& url ); + + signals: + /** + * This signal gets emited when the user selects an URL. + * + * @param url The URL thats the user selected. + */ + void urlSelected( const KURL& url ); + + protected slots: + void itemSelected( const QString& string ); + void menuAboutToShow(); + void menuItemActivated( int id ); + void slotClicked(); + virtual void slotActivated(int); + virtual void slotActivated(const QString& ); + virtual void slotActivated(); + + protected: + unsigned m_maxItems; + KPopupMenu * m_popup; + QString m_configGroupName; +}; + +#endif diff --git a/src/resizeoverlay.cpp b/src/resizeoverlay.cpp new file mode 100644 index 0000000..9d714bd --- /dev/null +++ b/src/resizeoverlay.cpp @@ -0,0 +1,746 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "itemdocument.h" +#include "mechanicsitem.h" +#include "resizeoverlay.h" + +#include <kdebug.h> +#include <qpainter.h> + + +//BEGIN class ResizeOverlay +ResizeOverlay::ResizeOverlay( Item *parent ) + : QObject(parent) +{ + b_showResizeHandles = false; + b_visible = true; + p_item = parent; +} + + +ResizeOverlay::~ResizeOverlay() +{ + const ResizeHandleMap::iterator end = m_resizeHandleMap.end(); + for ( ResizeHandleMap::iterator it = m_resizeHandleMap.begin(); it != end; ++it ) + { + if (it.data()) + it.data()->setCanvas(0l); + delete (ResizeHandle*)it.data(); + } + m_resizeHandleMap.clear(); +} + + +void ResizeOverlay::showResizeHandles( bool show ) +{ + b_showResizeHandles = show; + const ResizeHandleMap::iterator end = m_resizeHandleMap.end(); + for ( ResizeHandleMap::iterator it = m_resizeHandleMap.begin(); it != end; ++it ) + { + it.data()->setVisible(b_showResizeHandles && b_visible); + } +} + + +void ResizeOverlay::setVisible( bool visible ) +{ + b_visible = visible; + const ResizeHandleMap::iterator end = m_resizeHandleMap.end(); + for ( ResizeHandleMap::iterator it = m_resizeHandleMap.begin(); it != end; ++it ) + { + it.data()->setVisible(b_showResizeHandles && b_visible); + } +} + + +ResizeHandle *ResizeOverlay::createResizeHandle( int id, ResizeHandle::DrawType drawType, int xsnap, int ysnap ) +{ + ResizeHandleMap::iterator it = m_resizeHandleMap.find(id); + if ( it != m_resizeHandleMap.end() ) + return it.data(); + + ResizeHandle *newResizeHandle = new ResizeHandle( this, id, drawType, xsnap, ysnap ); + m_resizeHandleMap[id] = newResizeHandle; + connect( newResizeHandle, SIGNAL(rhMovedBy(int, double, double )), this, SLOT(slotResizeHandleMoved(int, double, double )) ); + return newResizeHandle; +} + + +void ResizeOverlay::removeResizeHandle( int id ) +{ + ResizeHandleMap::iterator it = m_resizeHandleMap.find(id); + if ( it == m_resizeHandleMap.end() ) + return; + + ResizeHandle *rh = it.data(); + disconnect( rh, SIGNAL(rhMovedBy(int, double, double )), this, SLOT(slotResizeHandleMoved(int, double, double )) ); + delete rh; + m_resizeHandleMap.erase(it); +} + + +ResizeHandle *ResizeOverlay::resizeHandle( int id ) +{ + ResizeHandleMap::iterator it = m_resizeHandleMap.find(id); + if ( it != m_resizeHandleMap.end() ) + return it.data(); + return 0l; +} + + +void ResizeOverlay::slotMoveAllResizeHandles( double dx, double dy ) +{ + const ResizeHandleMap::iterator end = m_resizeHandleMap.end(); + for ( ResizeHandleMap::iterator it = m_resizeHandleMap.begin(); it != end; ++it ) + { + it.data()->moveBy( dx, dy ); + } +} + + +void ResizeOverlay::syncX( ResizeHandle *rh1, ResizeHandle *rh2, ResizeHandle *rh3 ) +{ + syncX( rh1, rh2 ); + syncX( rh1, rh3 ); + syncX( rh2, rh3 ); +} +void ResizeOverlay::syncY( ResizeHandle *rh1, ResizeHandle *rh2, ResizeHandle *rh3 ) +{ + syncY( rh1, rh2 ); + syncY( rh1, rh3 ); + syncY( rh2, rh3 ); +} +void ResizeOverlay::syncX( ResizeHandle *rh1, ResizeHandle *rh2 ) +{ + if ( !rh1 || !rh2 ) + return; + connect( rh1, SIGNAL(rhMovedByX(double )), rh2, SLOT(slotMoveByX(double )) ); + connect( rh2, SIGNAL(rhMovedByX(double )), rh1, SLOT(slotMoveByX(double )) ); +} +void ResizeOverlay::syncY( ResizeHandle *rh1, ResizeHandle *rh2 ) +{ + if ( !rh1 || !rh2 ) + return; + connect( rh1, SIGNAL(rhMovedByY(double )), rh2, SLOT(slotMoveByY(double )) ); + connect( rh2, SIGNAL(rhMovedByY(double )), rh1, SLOT(slotMoveByY(double )) ); +} +//END class ResizeOverlay + + + +//BEGIN class MechanicsItemOverlay +MechanicsItemOverlay::MechanicsItemOverlay( MechanicsItem *parent ) + : ResizeOverlay(parent) +{ + p_mechanicsItem = parent; + connect( parent, SIGNAL(moved()), this, SLOT(slotUpdateResizeHandles()) ); + connect( parent, SIGNAL(resized()), this, SLOT(slotUpdateResizeHandles()) ); + + m_tl = createResizeHandle( ResizeHandle::rhp_topLeft, ResizeHandle::dt_resize_backwardsDiagonal ); + m_tm = createResizeHandle( ResizeHandle::rhp_topMiddle, ResizeHandle::dt_resize_vertical ); + m_tr = createResizeHandle( ResizeHandle::rhp_topRight, ResizeHandle::dt_resize_forwardsDiagonal ); + m_mr = createResizeHandle( ResizeHandle::rhp_middleRight, ResizeHandle::dt_resize_horizontal ); + m_br = createResizeHandle( ResizeHandle::rhp_bottomRight, ResizeHandle::dt_resize_backwardsDiagonal ); + m_bm = createResizeHandle( ResizeHandle::rhp_bottomMiddle, ResizeHandle::dt_resize_vertical ); + m_bl = createResizeHandle( ResizeHandle::rhp_bottomLeft, ResizeHandle::dt_resize_forwardsDiagonal ); + m_ml = createResizeHandle( ResizeHandle::rhp_middleLeft, ResizeHandle::dt_resize_horizontal ); + m_mm = createResizeHandle( ResizeHandle::rhp_center, ResizeHandle::dt_point_crosshair ); + + slotUpdateResizeHandles(); +} + +MechanicsItemOverlay::~MechanicsItemOverlay() +{ +} + +void MechanicsItemOverlay::slotUpdateResizeHandles() +{ + const PositionInfo absPos = p_mechanicsItem->absolutePosition(); + const QRect sizeRect = p_mechanicsItem->sizeRect(); + + QPointArray pa(9); + pa[0] = sizeRect.topLeft(); + pa[2] = sizeRect.topRight(); + pa[1] = (pa[0]+pa[2])/2; + pa[4] = sizeRect.bottomRight(); + pa[3] = (pa[2]+pa[4])/2; + pa[6] = sizeRect.bottomLeft(); + pa[5] = (pa[4]+pa[6])/2; + pa[7] = (pa[6]+pa[0])/2; + pa[8] = QPoint(0,0); + + QWMatrix m; + m.rotate(absPos.angle()*57.29577951308232); + + pa = m.map(pa); + + m_tl->move( absPos.x()+pa[0].x(), absPos.y()+pa[0].y() ); + m_tm->move( absPos.x()+pa[1].x(), absPos.y()+pa[1].y() ); + m_tr->move( absPos.x()+pa[2].x(), absPos.y()+pa[2].y() ); + m_mr->move( absPos.x()+pa[3].x(), absPos.y()+pa[3].y() ); + m_br->move( absPos.x()+pa[4].x(), absPos.y()+pa[4].y() ); + m_bm->move( absPos.x()+pa[5].x(), absPos.y()+pa[5].y() ); + m_bl->move( absPos.x()+pa[6].x(), absPos.y()+pa[6].y() ); + m_ml->move( absPos.x()+pa[7].x(), absPos.y()+pa[7].y() ); + m_mm->move( absPos.x()+pa[8].x(), absPos.y()+pa[8].y() ); +} + +void MechanicsItemOverlay::slotResizeHandleMoved( int id, double dx, double dy ) +{ + Q_UNUSED(id); + Q_UNUSED(dx); + Q_UNUSED(dy); + + switch (id) + { + case ResizeHandle::rhp_topLeft: + break; + case ResizeHandle::rhp_topMiddle: + break; + case ResizeHandle::rhp_topRight: + break; + case ResizeHandle::rhp_middleRight: + break; + case ResizeHandle::rhp_bottomRight: + break; + case ResizeHandle::rhp_bottomMiddle: + break; + case ResizeHandle::rhp_bottomLeft: + break; + case ResizeHandle::rhp_middleLeft: + break; + case ResizeHandle::rhp_center: + break; + default: + kdError() << k_funcinfo << "Unknown resize handle id " << id << endl; + break; + } +} +//END class MechanicsItemOverlay + + + +//BEGIN class RectangularOverlay +RectangularOverlay::RectangularOverlay( Item *parent, int xsnap, int ysnap ) + : ResizeOverlay(parent) +{ + connect( parent, SIGNAL(resized()), this, SLOT(slotUpdateResizeHandles()) ); + connect( parent, SIGNAL(movedBy(double, double )), this, SLOT(slotMoveAllResizeHandles(double, double )) ); + + m_tl = createResizeHandle( ResizeHandle::rhp_topLeft, ResizeHandle::dt_resize_backwardsDiagonal, xsnap, ysnap ); + m_tm = createResizeHandle( ResizeHandle::rhp_topMiddle, ResizeHandle::dt_resize_vertical, xsnap, ysnap ); + m_tr = createResizeHandle( ResizeHandle::rhp_topRight, ResizeHandle::dt_resize_forwardsDiagonal, xsnap, ysnap ); + m_mr = createResizeHandle( ResizeHandle::rhp_middleRight, ResizeHandle::dt_resize_horizontal, xsnap, ysnap ); + m_br = createResizeHandle( ResizeHandle::rhp_bottomRight, ResizeHandle::dt_resize_backwardsDiagonal, xsnap, ysnap ); + m_bm = createResizeHandle( ResizeHandle::rhp_bottomMiddle, ResizeHandle::dt_resize_vertical, xsnap, ysnap ); + m_bl = createResizeHandle( ResizeHandle::rhp_bottomLeft, ResizeHandle::dt_resize_forwardsDiagonal, xsnap, ysnap ); + m_ml = createResizeHandle( ResizeHandle::rhp_middleLeft, ResizeHandle::dt_resize_horizontal, xsnap, ysnap ); + + syncX( m_tl, m_ml, m_bl ); + syncX( m_tr, m_mr, m_br ); + syncY( m_tl, m_tm, m_tr ); + syncY( m_bl, m_bm, m_br ); + + slotUpdateResizeHandles(); +} + + +void RectangularOverlay::removeTopMiddle() +{ + if (!m_tm) + return; + removeResizeHandle( m_tm->id() ); + m_tm = 0l; +} + + +void RectangularOverlay::removeBotMiddle() +{ + if (!m_bm) + return; + removeResizeHandle( m_bm->id() ); + m_bm = 0l; +} + + +void RectangularOverlay::slotUpdateResizeHandles() +{ + const QRect sizeRect = p_item->sizeRect(); + + int x1 = sizeRect.left() + int(p_item->x()); + int x2 = x1 + sizeRect.width(); + + int y1 = sizeRect.top() + int(p_item->y()); + int y2 = y1 + sizeRect.height(); + + m_tl->move( x1, y1 ); + if (m_tm) + m_tm->move( (x1+x2)/2, y1 ); + m_tr->move( x2, y1 ); + m_mr->move( x2, (y1+y2)/2 ); + m_br->move( x2, y2 ); + if (m_bm) + m_bm->move( (x1+x2)/2, y2 ); + m_bl->move( x1, y2 ); + m_ml->move( x1, (y1+y2)/2 ); +} + + +bool RectangularOverlay::isValidXPos( ResizeHandle *rh ) +{ + Q_UNUSED(rh); + bool ok; + getSizeRect( 0l, &ok, 0l ); + return ok; +} + + +bool RectangularOverlay::isValidYPos( ResizeHandle *rh ) +{ + Q_UNUSED(rh); + bool ok; + getSizeRect( 0l, 0l, &ok ); + return ok; +} + + +void RectangularOverlay::slotResizeHandleMoved( int id, double dx, double dy ) +{ + Q_UNUSED(id); + Q_UNUSED(dx); + Q_UNUSED(dy); + + bool ok; + QRect sizeRect = getSizeRect(&ok); + if (!ok) + return; + + p_item->setSize(sizeRect); + slotUpdateResizeHandles(); +} + + +QRect RectangularOverlay::getSizeRect( bool *ok, bool *widthOk, bool *heightOk ) const +{ + bool t1,t2,t3; + if (!ok) + ok = &t1; + if (!widthOk) + widthOk = &t2; + if (!heightOk) + heightOk = &t3; + + int width = int(m_br->x() - m_tl->x()); + int height = int(m_br->y() - m_tl->y()); + + QRect sizeRect( int(m_tl->x() - p_item->x()), + int(m_tl->y() - p_item->y()), + width, height ); + + *widthOk = sizeRect.width() >= p_item->minimumSize().width(); + *heightOk = sizeRect.height() >= p_item->minimumSize().height(); + *ok = *widthOk && *heightOk; + + return sizeRect; +} +//END class RectangularOverlay + + + +//BEGIN class LineOverlay +LineOverlay::LineOverlay( Item * parent ) + : ResizeOverlay(parent) +{ + connect( parent, SIGNAL(resized()), this, SLOT(slotUpdateResizeHandles()) ); + connect( parent, SIGNAL(movedBy(double, double )), this, SLOT(slotMoveAllResizeHandles(double, double )) ); + + m_pStart = createResizeHandle( ResizeHandle::rhp_start, ResizeHandle::dt_point_rect ); + m_pEnd = createResizeHandle( ResizeHandle::rhp_end, ResizeHandle::dt_point_rect ); + + slotUpdateResizeHandles(); +} + + +QPoint LineOverlay::startPoint() const +{ + return QPoint( int(m_pStart->x()), int(m_pStart->y()) ); +} +QPoint LineOverlay::endPoint() const +{ + return QPoint( int(m_pEnd->x()), int(m_pEnd->y()) ); +} + + +void LineOverlay::slotUpdateResizeHandles() +{ + int _x = int(p_item->x() + p_item->offsetX()); + int _y = int(p_item->y() + p_item->offsetY()); + + m_pStart->move( _x, _y ); + m_pEnd->move( _x+p_item->width(), _y+p_item->height() ); +} + + +void LineOverlay::slotResizeHandleMoved( int id, double dx, double dy ) +{ + Q_UNUSED(id); + Q_UNUSED(dx); + Q_UNUSED(dy); + + p_item->setSize( int(m_pStart->x()-p_item->x()), int(m_pStart->y()-p_item->y()), + int(m_pEnd->x()-m_pStart->x()), int(m_pEnd->y()-m_pStart->y()) ); +} +//END class LineOverlay + + + +//BEGIN class ResizeHandle +ResizeHandle::ResizeHandle( ResizeOverlay *resizeOverlay, int id, DrawType drawType, int xsnap, int ysnap ) + : QObject(), QCanvasRectangle( 0, 0, 13, 13, resizeOverlay->parentItem()->canvas() ) +{ + p_resizeOverlay = resizeOverlay; + m_drawType = drawType; + m_id = id; + b_hover = false; + m_xsnap = xsnap; + m_ysnap = ysnap; + setZ( ItemDocument::Z::ResizeHandle ); +} + +ResizeHandle::~ResizeHandle() +{ + hide(); +} + +int ResizeHandle::rtti() const +{ + return ItemDocument::RTTI::ResizeHandle; +} + +void ResizeHandle::setHover( bool hover ) +{ + if ( b_hover == hover ) + return; + + b_hover = hover; + canvas()->setChanged( QRect( int(x())-8, int(y())-8, 15, 15 ) ); +} + +QPointArray ResizeHandle::areaPoints() const +{ +// QPointArray pa = QCanvasRectangle::areaPoints(); +// pa.translate( -7, -7 ); +// return pa; + return QPointArray( QRect( int(x())-8, int(y())-8, 15, 15 ) ); +} + +void ResizeHandle::moveRH( double _x, double _y ) +{ + double dx = int((_x-4)/m_xsnap)*m_xsnap+4 - x(); + double dy = int((_y-4)/m_ysnap)*m_ysnap+4 - y(); + if ( (dx == 0) && (dy == 0) ) + return; + + //BEGIN Move and check + moveBy( dx, dy ); + if ( dx != 0 ) + emit rhMovedByX(dx); + if ( dy != 0 ) + emit rhMovedByY(dy); + + bool xOk = p_resizeOverlay->isValidXPos(this); + bool yOk = p_resizeOverlay->isValidYPos(this); + + if (!xOk) + { + moveBy( -dx, 0 ); + emit rhMovedByX(-dx); + dx = 0; + } + if (!yOk) + { + moveBy( 0, -dy ); + emit rhMovedByY(-dy); + dy = 0; + } + + if ( !xOk && !yOk ) + return; + //END Move and check + + emit rhMovedBy( id(), dx, dy ); +} + +void ResizeHandle::setDrawType( DrawType drawType ) +{ + m_drawType = drawType; + canvas()->setChanged(boundingRect()); +} + +void ResizeHandle::drawShape( QPainter &p ) +{ + p.drawPixmap( rect().topLeft()-QPoint( 7, 7 ), handlePixmap( m_drawType, b_hover ) ); +} + +const QPixmap& ResizeHandle::handlePixmap( DrawType drawType, bool hover ) +{ + const char * resize_forwardsDiagonal_hover_xpm[] = { + "13 13 3 1", + " c None", + ". c #000000", + "+ c #8EA5D0", + " ", + " ....... ", + " ..+++. ", + " .++++. ", + " .+++++. ", + " . .+++++.. ", + " ...+++++... ", + " ..+++++. . ", + " .+++++. ", + " .++++. ", + " .+++.. ", + " ....... ", + " "}; + static QPixmap pixmap_forwardsDiagonal_hover(resize_forwardsDiagonal_hover_xpm); + + const char * resize_forwardsDiagonal_nohover_xpm[] = { + "13 13 2 1", + " c None", + ". c #000000", + " ", + " ....... ", + " ...... ", + " ...... ", + " ....... ", + " . ........ ", + " ........... ", + " ........ . ", + " ....... ", + " ...... ", + " ...... ", + " ....... ", + " "}; + static QPixmap pixmap_forwardsDiagonal_nohover(resize_forwardsDiagonal_nohover_xpm); + + + const char * resize_backwardsDiagonal_hover_xpm[] = { + "13 13 3 1", + " c None", + ". c #000000", + "+ c #8EA5D0", + " ", + " ....... ", + " .+++.. ", + " .++++. ", + " .+++++. ", + " ..+++++. . ", + " ...+++++... ", + " . .+++++.. ", + " .+++++. ", + " .++++. ", + " ..+++. ", + " ....... ", + " "}; + static QPixmap pixmap_backwardsDiagonal_hover(resize_backwardsDiagonal_hover_xpm); + + const char * resize_backwardsDiagonal_nohover_xpm[] = { + "13 13 2 1", + " c None", + ". c #000000", + " ", + " ....... ", + " ...... ", + " ...... ", + " ....... ", + " ........ . ", + " ........... ", + " . ........ ", + " ....... ", + " ...... ", + " ...... ", + " ....... ", + " "}; + static QPixmap pixmap_backwardsDiagonal_nohover(resize_backwardsDiagonal_nohover_xpm); + + + const char * resize_vertical_hover_xpm[] = { + "13 13 3 1", + " c None", + ". c #000000", + "+ c #8EA5D0", + " . ", + " ... ", + " ..+.. ", + " ..+++.. ", + " ..+++++.. ", + " .+++. ", + " .+++. ", + " .+++. ", + " ..+++++.. ", + " ..+++.. ", + " ..+.. ", + " ... ", + " . "}; + static QPixmap pixmap_vertical_hover(resize_vertical_hover_xpm); + + const char * resize_vertical_nohover_xpm[] = { + "13 13 2 1", + " c None", + ". c #000000", + " . ", + " ... ", + " ..... ", + " ....... ", + " ......... ", + " ..... ", + " ..... ", + " ..... ", + " ......... ", + " ....... ", + " ..... ", + " ... ", + " . "}; + static QPixmap pixmap_vertical_nohover(resize_vertical_nohover_xpm); + + + const char * resize_horizontal_hover_xpm[] = { + "13 13 3 1", + " c None", + ". c #000000", + "+ c #8EA5D0", + " ", + " ", + " . . ", + " .. .. ", + " ..+...+.. ", + " ..+++++++.. ", + "..+++++++++..", + " ..+++++++.. ", + " ..+...+.. ", + " .. .. ", + " . . ", + " ", + " "}; + static QPixmap pixmap_horizontal_hover(resize_horizontal_hover_xpm); + + const char * resize_horizontal_nohover_xpm[] = { + "13 13 2 1", + " c None", + ". c #000000", + " ", + " ", + " . . ", + " .. .. ", + " ......... ", + " ........... ", + ".............", + " ........... ", + " ......... ", + " .. .. ", + " . . ", + " ", + " "}; + static QPixmap pixmap_horizontal_nohover(resize_horizontal_nohover_xpm); + + const char * point_rect_hover_xpm[] = { + "13 13 3 1", + " c None", + ". c #000000", + "+ c #8EA5D0", + " ", + " ", + " ", + " ", + " ", + " ..... ", + " .+++. ", + " .+++. ", + " .+++. ", + " ..... ", + " ", + " ", + " "}; + static QPixmap pixmap_point_rect_hover(point_rect_hover_xpm); + + const char * point_rect_nohover_xpm[] = { + "13 13 3 1", + " c None", + ". c #000000", + "+ c #FFFFFF", + " ", + " ", + " ", + " ", + " ", + " ..... ", + " .+++. ", + " .+++. ", + " .+++. ", + " ..... ", + " ", + " ", + " "}; + static QPixmap pixmap_point_rect_nohover(point_rect_nohover_xpm); + + if (hover) + { + switch(drawType) + { + case ResizeHandle::dt_resize_forwardsDiagonal: + return pixmap_forwardsDiagonal_hover; + case ResizeHandle::dt_resize_backwardsDiagonal: + return pixmap_backwardsDiagonal_hover; + case ResizeHandle::dt_resize_vertical: + return pixmap_vertical_hover; + case ResizeHandle::dt_resize_horizontal: + return pixmap_horizontal_hover; + case ResizeHandle::dt_point_rect: + return pixmap_point_rect_hover; + + case ResizeHandle::dt_point_crosshair: + case ResizeHandle::dt_rotate_topLeft: + case ResizeHandle::dt_rotate_topRight: + case ResizeHandle::dt_rotate_bottomRight: + case ResizeHandle::dt_rotate_bottomLeft: + kdWarning() << k_funcinfo << "ResizeHandle of type " << drawType << " does not have an image." << endl; + } + } + else + { + switch(drawType) + { + case ResizeHandle::dt_resize_forwardsDiagonal: + return pixmap_forwardsDiagonal_nohover; + case ResizeHandle::dt_resize_backwardsDiagonal: + return pixmap_backwardsDiagonal_nohover; + case ResizeHandle::dt_resize_vertical: + return pixmap_vertical_nohover; + case ResizeHandle::dt_resize_horizontal: + return pixmap_horizontal_nohover; + case ResizeHandle::dt_point_rect: + return pixmap_point_rect_nohover; + + case ResizeHandle::dt_point_crosshair: + case ResizeHandle::dt_rotate_topLeft: + case ResizeHandle::dt_rotate_topRight: + case ResizeHandle::dt_rotate_bottomRight: + case ResizeHandle::dt_rotate_bottomLeft: + kdWarning() << k_funcinfo << "ResizeHandle of type " << drawType << " does not have an image." << endl; + } + } + + static QPixmap blank; + return blank; +} +//END class ResizeHandle + +#include "resizeoverlay.moc" diff --git a/src/resizeoverlay.h b/src/resizeoverlay.h new file mode 100644 index 0000000..c71e6de --- /dev/null +++ b/src/resizeoverlay.h @@ -0,0 +1,276 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef RESIZEOVERLAY_H +#define RESIZEOVERLAY_H + +// This file contains class definitions for different types of resizing and rotating + +#include <qcanvas.h> +#include <qguardedptr.h> +#include <qmap.h> +#include <qobject.h> +#include <qvaluelist.h> + +class MechanicsItem; +class ResizeHandle; +class ResizeOverlay; +class QEvent; + +typedef QMap< int, QGuardedPtr<ResizeHandle> > ResizeHandleMap; + +/** +@author David Saxton +*/ +class ResizeHandle : public QObject, public QCanvasRectangle +{ + Q_OBJECT +public: + /** + * Convenience enumeration for resize handle positioning. Note: this class + * does not make use of the values in this enumeration - it is just + * provided here for use by other classes. + */ + enum ResizeHandlePosition + { + rhp_none, + rhp_topLeft, + rhp_topMiddle, + rhp_topRight, + rhp_middleRight, + rhp_bottomRight, + rhp_bottomMiddle, + rhp_bottomLeft, + rhp_middleLeft, + rhp_center, + rhp_start, + rhp_end + }; + + enum DrawType + { + // Draws a simple rectangle + dt_point_rect, + + // Crosshair + dt_point_crosshair, + + // Straight arrows in various directions + dt_resize_forwardsDiagonal, + dt_resize_backwardsDiagonal, + dt_resize_vertical, + dt_resize_horizontal, + + // Arrows as part of an arc + dt_rotate_topLeft, + dt_rotate_topRight, + dt_rotate_bottomRight, + dt_rotate_bottomLeft + }; + + ResizeHandle( ResizeOverlay *resizeOverlay, int id, DrawType drawType, int xsnap, int ysnap ); + ~ResizeHandle(); + + int id() const { return m_id; } + int rtti() const; + + void setDrawType( DrawType drawType ); + void moveRH( double x, double y ); + void setHover( bool hover ); + + static const QPixmap& handlePixmap( DrawType drawType, bool hover ); + + virtual QPointArray areaPoints () const; + +public slots: + void slotMoveByX( double dx ) { moveBy( dx, 0 ); } + void slotMoveByY( double dy ) { moveBy( 0, dy ); } + +signals: + void rhMovedBy( int id, double dx, double dy ); + void rhMovedByX( double dx ); + void rhMovedByY( double dy ); + +protected: + virtual void drawShape( QPainter &p ); + DrawType m_drawType; + bool b_hover; // If true, then paint resize handle for mouse hovering over + int m_id; + int m_xsnap; + int m_ysnap; + ResizeOverlay *p_resizeOverlay; + +}; +typedef QValueList<ResizeHandle*> ResizeHandleList; + +/** +@author David Saxton +*/ +class ResizeOverlay : public QObject +{ + Q_OBJECT +public: + ResizeOverlay( Item *parent ); + ~ResizeOverlay(); + + Item *parentItem() const { return p_item; } + + /** + * Shows / hides the resize handles. They are hidden by default. + */ + void showResizeHandles( bool show ); + /** + * Sets the visibility. Visibility is true by default. + */ + void setVisible( bool visible ); + /** + * Reinherit this function to determine whether the X coordinate of the spot + * that the resize handle has moved into is valid or not + */ + virtual bool isValidXPos( ResizeHandle *rh ) { Q_UNUSED(rh); return true; } + /** + * Reinherit this function to determine whether the Y coordinate of the spot + * that the resize handle has moved into is valid or not + */ + virtual bool isValidYPos( ResizeHandle *rh ) { Q_UNUSED(rh); return true; } + +public slots: + void slotMoveAllResizeHandles( double dx, double dy ); + +protected slots: + virtual void slotResizeHandleMoved( int id, double dx, double dy ) = 0; + +protected: + /** + * Connects up the given resize handles so that they are always kept at the + * same horizontal coordinate + */ + void syncX( ResizeHandle *rh1, ResizeHandle *rh2 ); + void syncX( ResizeHandle *rh1, ResizeHandle *rh2, ResizeHandle *rh3 ); + /** + * Connects up the given resize handles so that they are always kept at the + * same vertical coordinate + */ + void syncY( ResizeHandle *rh1, ResizeHandle *rh2 ); + void syncY( ResizeHandle *rh1, ResizeHandle *rh2, ResizeHandle *rh3 ); + /** + * Returns a pointer to the ResizeHandle with the given id, or 0 if no such + * handle exists + */ + ResizeHandle *resizeHandle( int id ); + /** + * Creates and attaches the resize handle with the given DrawType. If a + * ResizeHandle with the given id exists, will return a pointer to that + * instead + */ + ResizeHandle *createResizeHandle( int id, ResizeHandle::DrawType drawType, int xsnap = 1, int ysnap = 1 ); + /** + * Removes the resize handle with the given id + */ + void removeResizeHandle( int id ); + + Item *p_item; + ResizeHandleMap m_resizeHandleMap; + bool b_showResizeHandles; + bool b_visible; +}; + + +/** +@author David Saxton +*/ +class MechanicsItemOverlay : public ResizeOverlay +{ +Q_OBJECT +public: + MechanicsItemOverlay( MechanicsItem *parent ); + ~MechanicsItemOverlay(); + +public slots: + void slotUpdateResizeHandles(); + +protected slots: + virtual void slotResizeHandleMoved( int id, double dx, double dy ); + +protected: + ResizeHandle *m_tl; + ResizeHandle *m_tm; + ResizeHandle *m_tr; + ResizeHandle *m_mr; + ResizeHandle *m_br; + ResizeHandle *m_bm; + ResizeHandle *m_bl; + ResizeHandle *m_ml; + ResizeHandle *m_mm; + MechanicsItem *p_mechanicsItem; +}; + + +/** +@author David Saxton +*/ +class RectangularOverlay : public ResizeOverlay +{ +Q_OBJECT +public: + RectangularOverlay( Item *item, int xsnap = 1, int ysnap = 1 ); + void removeTopMiddle(); + void removeBotMiddle(); + /** + * Get the size rectangle from the position of the handles. If the size + * is invalid (e.g. the parent Item does not consider it a valid size, + * then *ok is set to false; otherwise to true. + * @returns the sizerect, regardless of whether or not it is valid + */ + QRect getSizeRect( bool *ok = 0l, bool *widthOk = 0l, bool *heightOk = 0l ) const; + virtual bool isValidXPos( ResizeHandle *rh ); + virtual bool isValidYPos( ResizeHandle *rh ); + +public slots: + void slotUpdateResizeHandles(); + +protected slots: + virtual void slotResizeHandleMoved( int id, double dx, double dy ); + +protected: + ResizeHandle *m_tl; + ResizeHandle *m_tm; + ResizeHandle *m_tr; + ResizeHandle *m_mr; + ResizeHandle *m_br; + ResizeHandle *m_bm; + ResizeHandle *m_bl; + ResizeHandle *m_ml; +}; + + +/** +@author David Saxton +*/ +class LineOverlay : public ResizeOverlay +{ + Q_OBJECT + public: + LineOverlay( Item * parent ); + QPoint startPoint() const; + QPoint endPoint() const; + + public slots: + void slotUpdateResizeHandles(); + + protected slots: + virtual void slotResizeHandleMoved( int id, double dx, double dy ); + + protected: + ResizeHandle * m_pStart; + ResizeHandle * m_pEnd; +}; + +#endif diff --git a/src/simulator.cpp b/src/simulator.cpp new file mode 100644 index 0000000..c0f406d --- /dev/null +++ b/src/simulator.cpp @@ -0,0 +1,469 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "component.h" +#include "gpsimprocessor.h" +#include "pin.h" +#include "simulator.h" +#include "switch.h" + +#include <kstaticdeleter.h> +#include <qtimer.h> + + +//BEGIN class Simulator +Simulator * Simulator::m_pSelf = 0l; +static KStaticDeleter<Simulator> staticSimulatorDeleter; + +Simulator * Simulator::self() +{ + if (!m_pSelf) + staticSimulatorDeleter.setObject( m_pSelf, new Simulator() ); + return m_pSelf; +} + + +Simulator::Simulator() +{ + m_currentChain = 0; + m_llNumber = 0; + m_stepNumber = 0; + m_bIsSimulating = true; + m_gpsimProcessors = 0l; + m_componentCallbacks = 0l; + m_components = 0l; + m_ordinaryCircuits = 0l; + m_switches = 0l; + + unsigned max = unsigned(LOGIC_UPDATE_RATE/LINEAR_UPDATE_RATE); + for ( unsigned i = 0; i < max; i++ ) + { + m_pStartStepCallback[i] = 0l; + m_pNextStepCallback[i] = 0l; + } + + LogicConfig lc; + m_pChangedLogicStart = new LogicOut( lc, false ); + m_pChangedLogicLast = m_pChangedLogicStart; + + m_pChangedCircuitStart = new Circuit; + m_pChangedCircuitLast = m_pChangedCircuitStart; + + QTimer * stepTimer = new QTimer(this); + connect( stepTimer, SIGNAL(timeout()), this, SLOT(step()) ); + stepTimer->start(1); +} + + +Simulator::~Simulator() +{ + delete m_pChangedLogicStart; + delete m_pChangedCircuitStart; + + detachAll(m_gpsimProcessors); + detachAll(m_components); + detachAll(m_componentCallbacks); + detachAll(m_ordinaryCircuits); + detachAll(m_switches); +} + + +void Simulator::step() +{ + if (!m_bIsSimulating) + return; + + // We are called a thousand times a second (the maximum allowed by QTimer), + // so divide the LINEAR_UPDATE_RATE by 1e3 for the number of loops we need + // to do. + const unsigned maxSteps = unsigned(LINEAR_UPDATE_RATE/1e3); + for ( unsigned i = 0; i < maxSteps; ++i ) + { + m_llNumber = 0; + m_stepNumber++; + + // Update the non-logic parts of the simulation + LinkedList<Component> * component = m_components; + while (component) + { + component->data()->stepNonLogic(); + component = component->m_pNext; + } + LinkedList<Circuit> * circuit = m_ordinaryCircuits; + while (circuit) + { + circuit->data()->doNonLogic(); + circuit = circuit->m_pNext; + } + LinkedList<Switch> * sw = m_switches; + while (sw) + { + sw->data()->bounce(); + sw = sw->m_pNext; + } + + // Update the logic parts of our simulation + const unsigned max = unsigned(LOGIC_UPDATE_RATE/LINEAR_UPDATE_RATE); + for ( m_llNumber = 0; m_llNumber < max; ++m_llNumber ) + { + // Update the logic components + LinkedList<ComponentCallback> * callback = m_componentCallbacks; + while (callback) + { + callback->data()->callback(); + callback = callback->m_pNext; + } + + callback = m_pStartStepCallback[m_llNumber]; + while (callback) + { + LinkedList<ComponentCallback> * next = callback->m_pNext; + callback->m_pNext = 0l; + callback->data()->callback(); + callback = next; + } + m_pStartStepCallback[m_llNumber] = 0l; + +#ifndef NO_GPSIM + // Update the gpsim processors + LinkedList<GpsimProcessor> * gpsimProcessor = m_gpsimProcessors; + while (gpsimProcessor) + { + gpsimProcessor->data()->executeNext(); + gpsimProcessor = gpsimProcessor->m_pNext; + } +#endif + + + int prevChain = m_currentChain; + m_currentChain = 1 - m_currentChain; + + + // Update the non-logic circuits + if ( Circuit * changed = m_pChangedCircuitStart->nextChanged(prevChain) ) + { + for ( Circuit * circuit = changed; circuit; circuit = circuit->nextChanged(prevChain) ) + circuit->setCanAddChanged(true); + + m_pChangedCircuitStart->setNextChanged( 0l, prevChain ); + m_pChangedCircuitLast = m_pChangedCircuitStart; + + do + { + Circuit * next = changed->nextChanged(prevChain); + changed->setNextChanged( 0l, prevChain ); + changed->doLogic(); + changed = next; + } + while (changed); + } + + // Call the logic callbacks + if (LogicOut * changed = m_pChangedLogicStart->nextChanged(prevChain)) + { + for ( LogicOut * out = changed; out; out = out->nextChanged(prevChain) ) + out->setCanAddChanged(true); + + m_pChangedLogicStart->setNextChanged( 0l, prevChain ); + m_pChangedLogicLast = m_pChangedLogicStart; + do + { + LogicOut * next = changed->nextChanged(prevChain); + changed->setNextChanged( 0l, prevChain ); + + double v = changed->isHigh() ? changed->outputHighVoltage() : 0.0; + + for ( PinList::iterator it = changed->pinListBegin; it != changed->pinListEnd; ++it ) + { + if ( Pin * pin = *it ) + pin->setVoltage(v); + } + + LogicIn * logicCallback = changed; + while (logicCallback) + { + logicCallback->callCallback(); + logicCallback = logicCallback->nextLogic(); + } + + changed = next; + } + while (changed); + } + } + } +} + + +void Simulator::slotSetSimulating( bool simulate ) +{ + if ( m_bIsSimulating == simulate ) + return; + + m_bIsSimulating = simulate; + emit simulatingStateChanged(simulate); +} + + +void Simulator::createLogicChain( LogicOut * logicOut, const LogicInList & logicInList, const PinList & pinList ) +{ + if (!logicOut) + return; + + bool state = logicOut->outputState(); + + logicOut->setUseLogicChain(true); + logicOut->pinList = pinList; + logicOut->pinListBegin = logicOut->pinList.begin(); + logicOut->pinListEnd = logicOut->pinList.end(); + + LogicIn * last = logicOut; + const LogicInList::const_iterator end = logicInList.end(); + for ( LogicInList::const_iterator it = logicInList.begin(); it != end; ++it ) + { + LogicIn * next = *it; + last->setNextLogic(next); + last->setLastState(state); + last = next; + } + last->setNextLogic(0l); + last->setLastState(state); + + // Mark it as changed, if it isn't already changed... + LogicOut * changed = m_pChangedLogicStart->nextChanged(m_currentChain); + while (changed) + { + if ( changed == logicOut ) + return; + changed = changed->nextChanged(m_currentChain); + } + addChangedLogic(logicOut); + logicOut->setCanAddChanged(false); + + if ( !m_logicChainStarts.contains( logicOut ) ) + m_logicChainStarts << logicOut; +} + + +template <typename T> +void Simulator::attach( LinkedList<T> ** start, T * data ) +{ + if (!data) + return; + + while ( *start && (*start)->m_pNext ) + { + if ( (*start)->data() == data ) + return; + start = & (*start)->m_pNext; + } + + if (*start) + (*start)->m_pNext = new LinkedList<T>(data); + else + *start = new LinkedList<T>(data); +} + + +template <typename T> +void Simulator::detach( LinkedList<T> ** start, T * data ) +{ + if (!data) + return; + + while (*start) + { + if ( (*start)->data() == data ) + { + LinkedList<T> * toDelete = *start; + *start = (*start)->m_pNext; + delete toDelete; + return; + } + + start = & (*start)->m_pNext; + } +} + + +template <typename T> +void Simulator::detachAll( LinkedList<T> * list ) +{ + while (list) + { + LinkedList<T> * next = list->m_pNext; + delete list; + list = next; + } +} + + +void Simulator::attachGpsimProcessor( GpsimProcessor * cpu ) +{ + attach( & m_gpsimProcessors, cpu ); +} + + +void Simulator::detachGpsimProcessor( GpsimProcessor * cpu ) +{ + detach( & m_gpsimProcessors, cpu ); +} + + +void Simulator::attachComponentCallback( Component * component, VoidCallbackPtr function ) +{ + attach( & m_componentCallbacks, new ComponentCallback( component, function ) ); +} + + +void Simulator::attachComponent( Component * component ) +{ + if ( !component || !component->doesStepNonLogic() ) + return; + + attach( & m_components, component ); +} + + +void Simulator::detachComponent( Component * component ) +{ + detach( & m_components, component ); + detachComponentCallbacks(component); +} + + +void Simulator::attachSwitch( Switch * sw ) +{ + attach( & m_switches, sw ); +} + + +void Simulator::detachSwitch( Switch * sw ) +{ + detach( & m_switches, sw ); +} + + +void Simulator::detachComponentCallbacks( Component * component ) +{ + LinkedList<ComponentCallback> * callback = m_componentCallbacks; + while (callback) + { + LinkedList<ComponentCallback> * next = callback->m_pNext; + ComponentCallback * data = callback->data(); + if ( data->component() == component ) + { + detach( & m_componentCallbacks, data ); + delete data; + } + callback = next; + } +} + + +void Simulator::attachCircuit( Circuit * circuit ) +{ + if (!circuit) + return; + attach( & m_ordinaryCircuits, circuit ); + addChangedCircuit(circuit); + circuit->setCanAddChanged(false); +} + + +void Simulator::removeLogicInReferences( LogicIn * logicIn ) +{ + if ( !logicIn ) + return; + + QValueList<LogicOut*>::iterator end = m_logicChainStarts.end(); + for ( QValueList<LogicOut*>::iterator it = m_logicChainStarts.begin(); it != end; ++it ) + { + LogicIn * logicCallback = *it; + while (logicCallback) + { + if ( logicCallback->nextLogic() == logicIn ) + logicCallback->setNextLogic( logicCallback->nextLogic()->nextLogic() ); + logicCallback = logicCallback->nextLogic(); + } + } +} + + +void Simulator::removeLogicOutReferences( LogicOut * logic ) +{ + m_logicChainStarts.remove( logic ); + + // Any changes to the code below will probably also apply to Simulator::detachCircuit + + if ( m_pChangedLogicLast == logic ) + { + LogicOut * previous_1 = 0l; + LogicOut * previous_2 = 0l; + for ( LogicOut * logic = m_pChangedLogicStart; logic; ) + { + if (previous_1) + previous_2 = previous_1; + previous_1 = logic; + logic = logic->nextChanged( m_currentChain ); + } + + m_pChangedLogicLast = previous_2; + } + + for ( unsigned chain = 0; chain < 2; ++chain ) + { + for ( LogicOut * prevChanged = m_pChangedLogicStart; prevChanged; prevChanged = prevChanged->nextChanged( chain ) ) + { + LogicOut * nextChanged = prevChanged->nextChanged( chain ); + if ( nextChanged == logic ) + prevChanged->setNextChanged( nextChanged->nextChanged( chain ), chain ); + } + } +} + + +void Simulator::detachCircuit( Circuit * circuit ) +{ + if (!circuit) + return; + + detach( & m_ordinaryCircuits, circuit ); + + // Any changes to the code below will probably also apply to Simulator::removeLogicOutReferences + + if ( m_pChangedCircuitLast == circuit ) + { + Circuit * previous_1 = 0l; + Circuit * previous_2 = 0l; + for ( Circuit * circuit = m_pChangedCircuitStart; circuit; ) + { + if (previous_1) + previous_2 = previous_1; + previous_1 = circuit; + circuit = circuit->nextChanged( m_currentChain ); + } + + m_pChangedCircuitLast = previous_2; + } + + for ( unsigned chain = 0; chain < 2; ++chain ) + { + for ( Circuit * prevChanged = m_pChangedCircuitStart; prevChanged; prevChanged = prevChanged->nextChanged( chain ) ) + { + Circuit * nextChanged = prevChanged->nextChanged( chain ); + if ( nextChanged == circuit ) + prevChanged->setNextChanged( nextChanged->nextChanged( chain ), chain ); + } + } +} +//END class Simulator + +#include "simulator.moc" diff --git a/src/simulator.h b/src/simulator.h new file mode 100644 index 0000000..a6ade3a --- /dev/null +++ b/src/simulator.h @@ -0,0 +1,247 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef SIMULATOR_H +#define SIMULATOR_H + +#include "circuit.h" +#include "logic.h" + +/** +This should be a multiple of 1000. It is the number of times a second that +linear elements are updated. +*/ +const int LINEAR_UPDATE_RATE = int(1e4); + +/** +This should be a multiple of 1000. It is the number of times a second that +logic elements are updated. +*/ +const int LOGIC_UPDATE_RATE = int(1e6); + + +class Circuit; +class CircuitDocument; +class Component; +class ComponentCallback; +class ECNode; +class GpsimProcessor; +class LogicIn; +class LogicOut; +class Switch; +class Wire; + +typedef QValueList<ECNode*> ECNodeList; +typedef QValueList<LogicIn*> LogicInList; + +typedef void(Component::*VoidCallbackPtr)(); + +template <typename T> +class LinkedList +{ + public: + LinkedList( T * data ) { m_pData = data; m_pNext = 0l; } + T * data() const { return m_pData; } + + LinkedList<T> * m_pNext; + + protected: + T * m_pData; +}; + + +class ComponentCallback +{ + public: + ComponentCallback( Component * component, VoidCallbackPtr function ) + { + m_pComponent = component; + m_pFunction = function; + } + + void callback() { (m_pComponent->*m_pFunction)(); } + Component * component() const { return m_pComponent; } + + protected: + Component * m_pComponent; + VoidCallbackPtr m_pFunction; +}; + + +/** +This singleton class oversees all simulation (keeping in sync linear, nonlinear, +logic, external simulators (such as gpsim), mechanical simulation, etc). +@author David Saxton +*/ +class Simulator : public QObject +{ + Q_OBJECT + public: + static Simulator * self(); + ~Simulator(); + + /** + * Number of (1/LOGIC_UPDATE_RATE) intervals that the simulator has been + * stepping for. + */ + long long time() const { return m_stepNumber*(long long)(LOGIC_UPDATE_RATE/LINEAR_UPDATE_RATE) + m_llNumber; } + /** + * Initializes a new logic chain. + */ + void createLogicChain( LogicOut * logicOut, const LogicInList & logicInList, const PinList & pinList ); + /** + * Adds the given LogicOut to the list of changed LogicOuts + */ + void addChangedLogic( LogicOut * changed ) + { + m_pChangedLogicLast->setNextChanged( changed, m_currentChain ); + m_pChangedLogicLast = changed; + } + /** + * Remove pointers to the given LogicOut, called when it is deleted for + * safety reasons. + */ + void removeLogicOutReferences( LogicOut * logic ); + /** + * Remove pointers to the given LogicIn, called when it is deleted for + * safety reasons. Simulator does not have any references to LogicIns + * itself - instead, they are removed from logic chains which are + * currently marked as changed. + */ + void removeLogicInReferences( LogicIn * logic ); + /** + * Adds the given Circuit to the list of changed Circuits + */ + void addChangedCircuit( Circuit * changed ) + { + m_pChangedCircuitLast->setNextChanged( changed, m_currentChain ); + m_pChangedCircuitLast = changed; + } + inline void addStepCallback( int at, LinkedList<ComponentCallback> * ccb ); + /** + * Add the given processor to the simulator. GpsimProcessor::step will + * be called while present in the simulator (it is at GpsimProcessor's + * disgression whether to actually step, depending on its running + * status). + * @see detachGpsimProcessor( GpsimProcessor * cpu ); + */ + void attachGpsimProcessor( GpsimProcessor * cpu ); + /** + * Remove the given processor from the simulation loop + */ + void detachGpsimProcessor( GpsimProcessor * cpu ); + /** + * Attach the component callback to the simulator. This will be called + * during the logic update loop, at LOGIC_UPDATE_RATE times per second (so + * make sure the function passed is an efficient one!). + */ + void attachComponentCallback( Component * component, VoidCallbackPtr function ); + /** + * Removes the callbacks for the given component from the simulator. + */ + void detachComponentCallbacks( Component * component ); + /** + * Attach the component to the simulator. + */ + void attachComponent( Component * component ); + /** + * Detaches the component from the simulator. + */ + void detachComponent( Component * component ); + /** + * Attach a circuit to the simulator + */ + void attachCircuit( Circuit * circuit ); + /** + * Detach a circuit from the simulator. + */ + void detachCircuit( Circuit * circuit ); + /** + * Attaches the switch to the simulator (only needed when the switch has + * started bouncing. + */ + void attachSwitch( Switch * sw ); + /** + * Detaches the switch from the simulator (called when the switch has + * stopped bouncing). + */ + void detachSwitch( Switch * sw ); + /** + * @return whether or not we are currently simulating stuff + * @see slotSetSimulating + */ + bool isSimulating() const { return m_bIsSimulating; } + + public slots: + /** + * Set whether or not to simulate at the moment. + * @see isSimulating + */ + void slotSetSimulating( bool simulate ); + + signals: + /** + * Emitted when the simulating state changes. + * @see slotSetSimulating + */ + void simulatingStateChanged( bool isSimulating ); + + private slots: + void step(); + + protected: + template <typename T> + void attach( LinkedList<T> ** start, T * data ); + template <typename T> + void detach( LinkedList<T> ** start, T * data ); + template <typename T> + void detachAll( LinkedList<T> * list ); + + bool m_bIsSimulating; + static Simulator * m_pSelf; + + ///List of LogicOuts that are at the start of a LogicChain + QValueList<LogicOut*> m_logicChainStarts; + + LogicOut * m_pChangedLogicStart; + LogicOut * m_pChangedLogicLast; + + Circuit * m_pChangedCircuitStart; + Circuit * m_pChangedCircuitLast; + + LinkedList<GpsimProcessor> * m_gpsimProcessors; + LinkedList<Component> * m_components; + LinkedList<ComponentCallback> * m_componentCallbacks; + LinkedList<Circuit> * m_ordinaryCircuits; + LinkedList<Switch> * m_switches; + + LinkedList<ComponentCallback> * m_pStartStepCallback[LOGIC_UPDATE_RATE/LINEAR_UPDATE_RATE]; + LinkedList<ComponentCallback> * m_pNextStepCallback[LOGIC_UPDATE_RATE/LINEAR_UPDATE_RATE]; + + private: + Simulator(); + long m_llNumber; + long long m_stepNumber; + unsigned char m_currentChain; +}; + + +inline void Simulator::addStepCallback( int at, LinkedList<ComponentCallback> * ccb ) +{ + if ( !m_pStartStepCallback[at] ) + m_pStartStepCallback[at] = ccb; + + else + m_pNextStepCallback[at]->m_pNext = ccb; + + m_pNextStepCallback[at] = ccb; +} + +#endif diff --git a/src/textdocument.cpp b/src/textdocument.cpp new file mode 100644 index 0000000..ba03c04 --- /dev/null +++ b/src/textdocument.cpp @@ -0,0 +1,1146 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "asmformatter.h" +#include "asminfo.h" +#include "asmparser.h" +#include "debugmanager.h" +#include "docmanager.h" +#include "documentiface.h" +#include "filemetainfo.h" +#include "gpsimprocessor.h" +#include "ktechlab.h" +#include "language.h" +#include "languagemanager.h" +#include "microselectwidget.h" +#include "programmerdlg.h" +#include "symbolviewer.h" +#include "textdocument.h" +#include "textview.h" + +// #include <kate/katedocument.h> + +#include <kdebug.h> +#include <klibloader.h> +#include <klocale.h> +#include <kmessagebox.h> +#include <ktempfile.h> + + +TextDocument *TextDocument::constructTextDocument( const QString& caption, KTechlab *parent, const char *name ) +{ + TextDocument *textDocument = new TextDocument( caption, parent, name); + if( textDocument->m_constructorSuccessful ) + return textDocument; + delete textDocument; + return 0L; +} + + +TextDocument::TextDocument( const QString &caption, KTechlab *ktechlab, const char *name ) + : Document( caption, ktechlab, name ), + m_doc(0) +{ + m_constructorSuccessful = false; + +#ifndef NO_GPSIM + m_bOwnDebugger = false; + b_lockSyncBreakpoints = false; + m_lastDebugLineAt = -1; + m_pDebugger = 0l; +#endif + + m_pLastTextOutputTarget = 0l; + m_guessedCodeType = TextDocument::ct_unknown; + m_type = Document::dt_text; + m_bookmarkActions.setAutoDelete(true); + m_pDocumentIface = new TextDocumentIface(this); + + KLibFactory *factory = KLibLoader::self()->factory("libkatepart"); + if(!factory) + { + KMessageBox::sorry( ktechlab, i18n("Libkatepart not available for constructing editor") ); + return; + } + m_doc = (Kate::Document*)(KTextEditor::Document *)factory->create( 0L, "kate", "KTextEditor::Document"); + + guessScheme(); + + connect( m_doc, SIGNAL(undoChanged()), this, SIGNAL(undoRedoStateChanged()) ); + connect( m_doc, SIGNAL(undoChanged()), this, SLOT(slotSyncModifiedStates()) ); + connect( m_doc, SIGNAL(textChanged()), this, SLOT(slotSyncModifiedStates()) ); + connect( m_doc, SIGNAL(marksChanged()), this, SLOT(slotUpdateMarksInfo()) ); + connect( m_doc, SIGNAL(selectionChanged()), this, SLOT(slotSelectionmChanged()) ); + + m_doc->setDescription((KTextEditor::MarkInterface::MarkTypes)Breakpoint, i18n("Breakpoint")); + m_doc->setPixmap((KTextEditor::MarkInterface::MarkTypes)Breakpoint, *inactiveBreakpointPixmap()); + m_doc->setPixmap((KTextEditor::MarkInterface::MarkTypes)ActiveBreakpoint, *activeBreakpointPixmap()); + m_doc->setPixmap((KTextEditor::MarkInterface::MarkTypes)ReachedBreakpoint, *reachedBreakpointPixmap()); + m_doc->setPixmap((KTextEditor::MarkInterface::MarkTypes)DisabledBreakpoint, *disabledBreakpointPixmap()); + m_doc->setPixmap((KTextEditor::MarkInterface::MarkTypes)ExecutionPoint, *executionPointPixmap()); + m_doc->setMarksUserChangable( Bookmark | Breakpoint ); + + m_constructorSuccessful = true; +} + + +TextDocument::~TextDocument() +{ + if( !m_constructorSuccessful ) + return; + + debugStop(); + + ViewList::iterator end = m_viewList.end(); + for ( ViewList::iterator it = m_viewList.begin(); it != end; ++it ) + { + if ( TextView * tv = dynamic_cast<TextView*>( (View*)*it) ) + { + Kate::View * kv = tv->kateView(); + p_ktechlab->factory()->removeClient( kv ); + } + } + + delete m_doc; + m_doc = 0l; + + delete m_pDocumentIface; + m_pDocumentIface = 0l; +} + + +bool TextDocument::fileClose() +{ + const QString path = url().prettyURL(); + if ( !path.isEmpty() ) + fileMetaInfo()->grabMetaInfo( path, this ); + + return Document::fileClose(); +} + + +TextView* TextDocument::textView() const +{ + return static_cast<TextView*>(activeView()); +} + + +View * TextDocument::createView( ViewContainer *viewContainer, uint viewAreaId, const char *name ) +{ + TextView * textView = new TextView( this, viewContainer, viewAreaId, name ); + + fileMetaInfo()->initializeFromMetaInfo( url().prettyURL(), textView ); + + handleNewView(textView); + return textView; +} + + +Kate::View* TextDocument::createKateView( QWidget *parent, const char *name ) +{ + return static_cast<Kate::View*>((m_doc->createView( parent, name ))->qt_cast("Kate::View")); +} + + +void TextDocument::cut() +{ + if (textView()) + textView()->cut(); +} +void TextDocument::copy() +{ + if (textView()) + textView()->copy(); +} +void TextDocument::paste() +{ + if (textView()) + textView()->paste(); +} + + +void TextDocument::setText( const QString & text, bool asInitial ) +{ + if ( asInitial ) + { + disconnect( m_doc, SIGNAL(undoChanged()), this, SIGNAL(undoRedoStateChanged()) ); + disconnect( m_doc, SIGNAL(undoChanged()), this, SLOT(slotSyncModifiedStates()) ); + disconnect( m_doc, SIGNAL(textChanged()), this, SLOT(slotSyncModifiedStates()) ); + } + + const ViewList::iterator end = m_viewList.end(); + for ( ViewList::iterator it = m_viewList.begin(); it != end; ++it ) + (static_cast<TextView*>((View*)*it))->saveCursorPosition(); + + m_doc->setText(text); + + for ( ViewList::iterator it = m_viewList.begin(); it != end; ++it ) + (static_cast<TextView*>((View*)*it))->restoreCursorPosition(); + + if ( asInitial ) + { + m_doc->clearUndo(); + m_doc->clearRedo(); + setModified(false); + + connect( m_doc, SIGNAL(undoChanged()), this, SIGNAL(undoRedoStateChanged()) ); + connect( m_doc, SIGNAL(undoChanged()), this, SLOT(slotSyncModifiedStates()) ); + connect( m_doc, SIGNAL(textChanged()), this, SLOT(slotSyncModifiedStates()) ); + } +} + + +void TextDocument::undo() +{ + m_doc->undo(); + slotSyncModifiedStates(); +} + + +void TextDocument::redo() +{ + m_doc->redo(); + slotSyncModifiedStates(); +} + + +void TextDocument::slotSyncModifiedStates() +{ + setModified( m_doc->isModified() ); +} +void TextDocument::setModified( bool modified ) +{ + if ( (modified == b_modified) && (modified == isModified()) ) { + return; + } + m_doc->setModified(modified); + b_modified = modified; + + emit modifiedStateChanged(); +} + + +void TextDocument::guessScheme( bool allowDisable ) +{ + // And specific file actions depending on the current type of file + QString fileName = url().fileName(); + QString extension = fileName.right( fileName.length() - fileName.findRev('.') - 1 ); + + if ( extension == "asm" || extension == "src" || extension == "inc" ) + slotInitLanguage(ct_asm); + + else if ( extension == "hex" ) + slotInitLanguage(ct_hex); + + else if ( extension == "basic" || extension == "microbe" ) + slotInitLanguage(ct_microbe); + + else if ( extension == "c" ) + slotInitLanguage(ct_c); + + else if ( m_guessedCodeType != TextDocument::ct_unknown ) + slotInitLanguage(m_guessedCodeType); + + else if ( allowDisable && activeView() ) + textView()->disableActions(); +} + + +void TextDocument::slotInitLanguage( CodeType type ) +{ + QString hlName; + + switch (type) + { + case ct_asm: + hlName = "PicAsm"; + break; + + case ct_c: + hlName = "C"; + break; + + case ct_hex: + break; + + case ct_microbe: + hlName = "Microbe"; + break; + + case ct_unknown: + break; + } + + if ( !hlName.isEmpty() ) + { + int i = 0; + int hlModeCount = m_doc->hlModeCount(); + while ( i<hlModeCount && m_doc->hlModeName(i) != hlName ) + i++; + + m_doc->setHlMode(i); + } + + m_guessedCodeType = type; + + ViewList::iterator end = m_viewList.end(); + for ( ViewList::iterator it = m_viewList.begin(); it != end; ++it ) + { + if ( TextView * tv = dynamic_cast<TextView*>( (View*)*it ) ) + tv->initCodeActions(); + } +} + + +void TextDocument::formatAssembly() +{ + AsmFormatter formatter; + QStringList lines = QStringList::split( "\n", m_doc->text(), true ); + setText( formatter.tidyAsm(lines), false ); + setModified(true); +} + + +void TextDocument::fileSave( const KURL& url ) +{ + if ( m_doc->url().path() != url.path() ) + { + kdError() << k_funcinfo << "Error: Kate::View url and passed url do not match; cannot save." << endl; + return; + } + + if ( activeView() && (textView()->save() == Kate::View::SAVE_OK ) ) + saveDone(); +} + + +void TextDocument::fileSaveAs() +{ + if ( activeView() && (textView()->saveAs() == Kate::View::SAVE_OK) ) + saveDone(); + + // Our modified state may not have changed, but we emit this to force the + // main window to update our caption. + emit modifiedStateChanged(); +} + + +void TextDocument::saveDone() +{ + setURL( m_doc->url() ); + guessScheme(false); + setModified(false); + emit modifiedStateChanged(); +} + + +bool TextDocument::openURL( const KURL& url ) +{ + m_doc->openURL(url); + setURL(url); + + fileMetaInfo()->initializeFromMetaInfo( url.prettyURL(), this ); + guessScheme(); + +#ifndef NO_GPSIM + DebugManager::self()->urlOpened( this ); +#endif + + return true; +} + + +void TextDocument::setLastTextOutputTarget( TextDocument * target ) +{ + m_pLastTextOutputTarget = target; +} + + +QString TextDocument::outputFilePath( const QString &ext ) +{ + QString filePath = url().path(); + if ( filePath.isEmpty() ) + { + KTempFile f( QString::null, ext ); + (*f.textStream()) << m_doc->text(); + f.close(); + DocManager::self()->associateDocument( f.name(), this ); + return f.name(); + } + if ( isModified() ) + { + fileSave(); + } + return filePath; +} + + +void TextDocument::slotConvertTo( int target ) +{ + switch ( (ConvertToTarget)target ) + { + case TextDocument::MicrobeOutput: + break; + + case TextDocument::AssemblyOutput: + convertToAssembly(); + break; + + case TextDocument::HexOutput: + convertToHex(); + break; + + case TextDocument::PICOutput: + convertToPIC(); + break; + } +} + + +void TextDocument::convertToAssembly() +{ + QString filePath; + bool showPICSelect = false; + ProcessOptions::ProcessPath::MediaType toType; + + if ( m_guessedCodeType == TextDocument::ct_microbe ) + { + toType = ProcessOptions::ProcessPath::AssemblyAbsolute; + filePath = outputFilePath(".microbe"); + } + + else if ( m_guessedCodeType == TextDocument::ct_hex ) + { + toType = ProcessOptions::ProcessPath::Disassembly; + filePath = outputFilePath(".hex"); + } + + else if ( m_guessedCodeType == TextDocument::ct_c ) + { + toType = ProcessOptions::ProcessPath::AssemblyRelocatable; + filePath = outputFilePath(".c"); + showPICSelect = true; + } + + else + { + kdError() << "Could not get file type for converting to assembly!"<<endl; + return; + } + + OutputMethodDlg dlg( i18n("Assembly Code Output"), url(), showPICSelect, p_ktechlab ); + + if ( m_guessedCodeType == TextDocument::ct_c ) + dlg.microSelect()->setAllowedAsmSet( AsmInfo::PIC14 | AsmInfo::PIC16 ); + + dlg.setOutputExtension(".asm"); + dlg.setFilter("*.asm *.src *.inc|Assembly Code (*.asm, *.src, *.inc)\n*|All Files"); + dlg.exec(); + if (!dlg.isAccepted()) + return; + + ProcessOptions o( dlg.info() ); + o.setTextOutputTarget( m_pLastTextOutputTarget, this, SLOT(setLastTextOutputTarget( TextDocument* )) ); + o.setInputFiles(filePath); + o.setProcessPath( ProcessOptions::ProcessPath::path( ProcessOptions::guessMediaType(filePath), toType ) ); + LanguageManager::self()->compile(o); +} + + +void TextDocument::convertToHex() +{ + QString filePath; + bool showPICSelect = false; + + if ( m_guessedCodeType == TextDocument::ct_microbe ) + filePath = outputFilePath(".microbe"); + + else if ( m_guessedCodeType == TextDocument::ct_asm ) + filePath = outputFilePath(".asm"); + + else if ( m_guessedCodeType == TextDocument::ct_c ) + { + filePath = outputFilePath(".c"); + showPICSelect = true; + } + + else + { + kdError() << "Could not get file type for converting to hex!"<<endl; + return; + } + + OutputMethodDlg dlg( i18n("Hex Code Output"), url(), showPICSelect, p_ktechlab ); + dlg.setOutputExtension(".hex"); + dlg.setFilter("*.hex|Hex (*.hex)\n*|All Files"); + + if ( m_guessedCodeType == TextDocument::ct_c ) + dlg.microSelect()->setAllowedAsmSet( AsmInfo::PIC14 | AsmInfo::PIC16 ); + + dlg.exec(); + if (!dlg.isAccepted()) + return; + + ProcessOptions o( dlg.info() ); + o.setTextOutputTarget( m_pLastTextOutputTarget, this, SLOT(setLastTextOutputTarget( TextDocument* )) ); + o.setInputFiles(filePath); + o.setProcessPath( ProcessOptions::ProcessPath::path( ProcessOptions::guessMediaType(filePath), ProcessOptions::ProcessPath::Program ) ); + LanguageManager::self()->compile(o); +} + + +void TextDocument::convertToPIC() +{ + QString filePath; + + QString picID; + + switch ( m_guessedCodeType ) + { + case ct_microbe: + filePath = outputFilePath(".microbe"); + break; + + case ct_asm: + { + filePath = outputFilePath(".asm"); + AsmParser p( filePath ); + p.parse(); + picID = p.picID(); + break; + } + + case ct_c: + filePath = outputFilePath(".c"); + break; + + case ct_hex: + filePath = outputFilePath(".hex"); + break; + + case ct_unknown: + kdError() << "Could not get file type for converting to hex!"<<endl; + return; + } + + ProgrammerDlg * dlg = new ProgrammerDlg( picID, (QWidget*)p_ktechlab, "Programmer Dlg" ); + + if ( m_guessedCodeType == TextDocument::ct_c ) + dlg->microSelect()->setAllowedAsmSet( AsmInfo::PIC14 | AsmInfo::PIC16 ); + + dlg->exec(); + if ( !dlg->isAccepted() ) + { + dlg->deleteLater(); + return; + } + + ProcessOptions o; + dlg->initOptions( & o ); + o.setInputFiles( filePath ); + o.setProcessPath( ProcessOptions::ProcessPath::path( ProcessOptions::guessMediaType(filePath), ProcessOptions::ProcessPath::Pic ) ); + LanguageManager::self()->compile( o ); + + dlg->deleteLater(); +} + + +void TextDocument::print() +{ + KTextEditor::printInterface(m_doc)->print (); +} + + +void TextDocument::slotSelectionmChanged() +{ + p_ktechlab->action( "edit_cut" )->setEnabled( m_doc->hasSelection () ); + p_ktechlab->action( "edit_copy" )->setEnabled( m_doc->hasSelection () ); +} + + +IntList TextDocument::bookmarkList() const +{ + IntList bookmarkList; + + typedef QPtrList<KTextEditor::Mark> MarkList; + MarkList markList = m_doc->marks(); + + // Find out what marks need adding to our internal lists + for ( KTextEditor::Mark * mark = markList.first(); mark; mark = markList.next() ) + { + if ( mark->type & Bookmark ) + bookmarkList += mark->line; + } + + return bookmarkList; +} + + +void TextDocument::slotUpdateMarksInfo() +{ + if ( activeView() ) + textView()->slotUpdateMarksInfo(); + +#ifndef NO_GPSIM + syncBreakpoints(); +#endif + + // Update our list of bookmarks in the menu + p_ktechlab->unplugActionList("bookmark_actionlist"); + m_bookmarkActions.clear(); + + QPtrList<KTextEditor::Mark> markList = m_doc->marks(); + + // Find out what marks need adding to our internal lists + for ( KTextEditor::Mark * mark = markList.first(); mark; mark = markList.next() ) + { + if ( mark->type & Bookmark ) + { + KAction * a = new KAction( i18n("%1 - %2").arg( QString::number( mark->line+1 ) ).arg( m_doc->textLine(mark->line) ), + 0, this, SLOT(slotBookmarkRequested()), this, + QString("bookmark_%1").arg(QString::number(mark->line).ascii()) ); + m_bookmarkActions.append(a); + } + } + + p_ktechlab->plugActionList( "bookmark_actionlist", m_bookmarkActions ); +} + + +void TextDocument::slotBookmarkRequested() +{ + const QObject * s = sender(); + if (!s) return; + + QString name = s->name(); + if ( !name.startsWith("bookmark_") ) + return; + + name.remove("bookmark_"); + int line = -1; + bool ok; + line = name.toInt(&ok); + if ( ok && line >= 0 && activeView() ) + (static_cast<TextView*>(activeView()))->gotoLine(line); +} + + +void TextDocument::setBookmarks( const IntList &lines ) +{ + clearBookmarks(); + const IntList::const_iterator end = lines.end(); + for ( IntList::const_iterator it = lines.begin(); it != end; ++it ) + setBookmark( *it, true ); +} + + +void TextDocument::clearBookmarks() +{ + QPtrList<KTextEditor::Mark> markList = m_doc->marks(); + + // Find out what marks need adding to our internal lists + for ( KTextEditor::Mark * mark = markList.first(); mark; mark = markList.next() ) + { + if ( mark->type & Bookmark ) + m_doc->removeMark( mark->line, Bookmark ); + } + + slotUpdateMarksInfo(); +} + + +void TextDocument::setBookmark( uint line, bool isBookmark ) +{ + if (isBookmark) + m_doc->addMark( line, Bookmark ); + + else + m_doc->removeMark( line, Bookmark ); +} + + +void TextDocument::setBreakpoints( const IntList &lines ) +{ +#ifndef NO_GPSIM + clearBreakpoints(); + const IntList::const_iterator end = lines.end(); + for ( IntList::const_iterator it = lines.begin(); it != end; ++it ) + setBreakpoint( *it, true ); +#endif // !NO_GPSIM +} + + +IntList TextDocument::breakpointList() const +{ + IntList breakpointList; + +#ifndef NO_GPSIM + typedef QPtrList<KTextEditor::Mark> MarkList; + MarkList markList = m_doc->marks(); + + // Find out what marks need adding to our internal lists + for ( KTextEditor::Mark * mark = markList.first(); mark; mark = markList.next() ) + { + if ( mark->type & Breakpoint ) + breakpointList += mark->line; + } +#endif // !NO_GPSIM + + return breakpointList; +} + + +void TextDocument::setBreakpoint( uint line, bool isBreakpoint ) +{ +#ifndef NO_GPSIM + if (isBreakpoint) + { + m_doc->addMark( line, Breakpoint ); + if (m_pDebugger) + m_pDebugger->setBreakpoint( m_debugFile, line, true ); + } + else + { + m_doc->removeMark( line, Breakpoint ); + if (m_pDebugger) + m_pDebugger->setBreakpoint( m_debugFile, line, false ); + } +#endif // !NO_GPSIM +} + + +void TextDocument::debugRun() +{ +#ifndef NO_GPSIM + if (m_pDebugger) + { + m_pDebugger->gpsim()->setRunning(true); + slotInitDebugActions(); + return; + } + + switch ( guessedCodeType() ) + { + case ct_unknown: + KMessageBox::sorry( 0l, i18n("Unknown code type."), i18n("Cannot debug") ); + return; + + case ct_hex: + KMessageBox::sorry( 0l, i18n("Cannot debug hex."), i18n("Cannot debug") ); + return; + + case ct_microbe: + m_bLoadDebuggerAsHLL = true; + m_debugFile = outputFilePath(".microbe"); + break; + + case ct_asm: + m_bLoadDebuggerAsHLL = false; + m_debugFile = outputFilePath(".asm"); + break; + + case ct_c: + m_bLoadDebuggerAsHLL = true; + m_debugFile = outputFilePath(".c"); + break; + } + + m_symbolFile = GpsimProcessor::generateSymbolFile( m_debugFile, this, SLOT(slotCODCreationSucceeded()), SLOT(slotCODCreationFailed()) ); +#endif // !NO_GPSIM +} + + +void TextDocument::debugInterrupt() +{ +#ifndef NO_GPSIM + if (!m_pDebugger) + return; + + m_pDebugger->gpsim()->setRunning(false); + slotInitDebugActions(); +#endif // !NO_GPSIM +} + + +void TextDocument::debugStop() +{ +#ifndef NO_GPSIM + if ( !m_pDebugger || !m_bOwnDebugger ) + return; + + m_pDebugger->gpsim()->deleteLater(); + m_pDebugger = 0l; + slotDebugSetCurrentLine( SourceLine() ); + slotInitDebugActions(); +#endif // !NO_GPSIM +} + + +void TextDocument::debugStep() +{ +#ifndef NO_GPSIM + if (!m_pDebugger) + return; + + m_pDebugger->stepInto(); +#endif // !NO_GPSIM +} + + +void TextDocument::debugStepOver() +{ +#ifndef NO_GPSIM + if (!m_pDebugger) + return; + + m_pDebugger->stepOver(); +#endif // !NO_GPSIM +} + + +void TextDocument::debugStepOut() +{ +#ifndef NO_GPSIM + if (!m_pDebugger) + return; + + m_pDebugger->stepOut(); +#endif // !NO_GPSIM +} + + +void TextDocument::slotDebugSetCurrentLine( const SourceLine & line ) +{ +#ifndef NO_GPSIM + int textLine = line.line(); + + if ( DocManager::self()->findDocument( line.fileName() ) != this ) + textLine = -1; + + m_doc->removeMark( m_lastDebugLineAt, ExecutionPoint ); + m_doc->addMark( textLine, ExecutionPoint ); + + if ( activeView() ) + textView()->setCursorPosition( textLine, 0 ); + + m_lastDebugLineAt = textLine; +#endif // !NO_GPSIM +} + + +void TextDocument::slotInitDebugActions() +{ +#ifndef NO_GPSIM + if ( m_pDebugger ) + { + if ( m_pDebugger->gpsim()->isRunning() ) + slotDebugSetCurrentLine( SourceLine() ); + else + slotDebugSetCurrentLine( m_pDebugger->currentLine() ); + } + + if ( activeView() ) + textView()->slotInitDebugActions(); +#endif // !NO_GPSIM +} + + +void TextDocument::slotCODCreationSucceeded() +{ +#ifndef NO_GPSIM + GpsimProcessor * gpsim = new GpsimProcessor( m_symbolFile, this ); + + if (m_bLoadDebuggerAsHLL) + gpsim->setDebugMode( GpsimDebugger::HLLDebugger ); + else + gpsim->setDebugMode( GpsimDebugger::AsmDebugger ); + + setDebugger( gpsim->currentDebugger(), true ); +#endif // !NO_GPSIM +} +void TextDocument::slotCODCreationFailed() +{ +#ifndef NO_GPSIM + m_debugFile = QString::null; + m_symbolFile = QString::null; +#endif // !NO_GPSIM +} + + +void TextDocument::slotDebuggerDestroyed() +{ +#ifndef NO_GPSIM + slotDebugSetCurrentLine( SourceLine() ); + m_pDebugger = 0l; + m_debugFile = QString::null; + slotInitDebugActions(); +#endif // !NO_GPSIM +} + + +#ifndef NO_GPSIM +void TextDocument::clearBreakpoints() +{ + QPtrList<KTextEditor::Mark> markList = m_doc->marks(); + + // Find out what marks need adding to our internal lists + for ( KTextEditor::Mark * mark = markList.first(); mark; mark = markList.next() ) + { + if ( mark->type & Bookmark ) + m_doc->removeMark( mark->line, Breakpoint ); + } + + slotUpdateMarksInfo(); +} + + +void TextDocument::syncBreakpoints() +{ + if (b_lockSyncBreakpoints) + return; + + // We don't really care about synching marks if we aren't debugging / aren't able to take use of the marks + if (!m_pDebugger) + return; + + b_lockSyncBreakpoints = true; + + typedef QPtrList<KTextEditor::Mark> MarkList; + MarkList markList = m_doc->marks(); + + IntList bpList; + + // Find out what marks need adding to our internal lists + for ( KTextEditor::Mark * mark = markList.first(); mark; mark = markList.next() ) + { + const int line = mark->line; + + if ( mark->type & Breakpoint ) + bpList.append(line); + + if ( mark->type == ExecutionPoint ) + m_lastDebugLineAt = line; + } + + m_pDebugger->setBreakpoints( m_debugFile, bpList ); + + b_lockSyncBreakpoints = false; +} + + +bool TextDocument::debuggerIsRunning() const +{ + return m_pDebugger; +} + + +bool TextDocument::debuggerIsStepping() const +{ + return m_pDebugger && !m_pDebugger->gpsim()->isRunning(); +} + + +void TextDocument::setDebugger( GpsimDebugger * debugger, bool ownDebugger ) +{ + if ( debugger == m_pDebugger ) + return; + + // If we create a gpsim, then we may get called by DebugManager, which will + // try to claim we don't own it. So if we have a symbol file waiting, thne + // wait until we are called from its successful creation + if ( !m_symbolFile.isEmpty() && !ownDebugger ) + return; + + // Reset it for use next time + m_symbolFile = QString::null; + + if (m_bOwnDebugger) + delete m_pDebugger; + + m_pDebugger = debugger; + m_bOwnDebugger = ownDebugger; + + if (!m_pDebugger) + return; + + if ( m_debugFile.isEmpty() ) + m_debugFile = url().path(); + + connect( m_pDebugger, SIGNAL(destroyed()), this, SLOT(slotDebuggerDestroyed()) ); + connect( m_pDebugger->gpsim(), SIGNAL(runningStatusChanged(bool )), this, SLOT(slotInitDebugActions()) ); + connect( m_pDebugger, SIGNAL(lineReached(const SourceLine &)), this, SLOT(slotDebugSetCurrentLine(const SourceLine &)) ); + m_pDebugger->setBreakpoints( m_debugFile, breakpointList() ); + + slotInitDebugActions(); + if ( !m_pDebugger->gpsim()->isRunning() ) + slotDebugSetCurrentLine( m_pDebugger->currentLine() ); + + if ( this == dynamic_cast<TextDocument*>(DocManager::self()->getFocusedDocument()) ) + SymbolViewer::self()->setContext( m_pDebugger->gpsim() ); +} +#endif // !NO_GPSIM + + +const QPixmap* TextDocument::inactiveBreakpointPixmap() +{ + const char*breakpoint_gr_xpm[]={ + "11 16 6 1", + "c c #c6c6c6", + "d c #2c2c2c", + "# c #000000", + ". c None", + "a c #ffffff", + "b c #555555", + "...........", + "...........", + "...#####...", + "..#aaaaa#..", + ".#abbbbbb#.", + "#abbbbbbbb#", + "#abcacacbd#", + "#abbbbbbbb#", + "#abcacacbd#", + "#abbbbbbbb#", + ".#bbbbbbb#.", + "..#bdbdb#..", + "...#####...", + "...........", + "...........", + "..........."}; + static QPixmap pixmap( breakpoint_gr_xpm ); + return &pixmap; +} + + +const QPixmap* TextDocument::activeBreakpointPixmap() +{ + const char* breakpoint_xpm[]={ + "11 16 6 1", + "c c #c6c6c6", + ". c None", + "# c #000000", + "d c #840000", + "a c #ffffff", + "b c #ff0000", + "...........", + "...........", + "...#####...", + "..#aaaaa#..", + ".#abbbbbb#.", + "#abbbbbbbb#", + "#abcacacbd#", + "#abbbbbbbb#", + "#abcacacbd#", + "#abbbbbbbb#", + ".#bbbbbbb#.", + "..#bdbdb#..", + "...#####...", + "...........", + "...........", + "..........."}; + static QPixmap pixmap( breakpoint_xpm ); + return &pixmap; +} + + + +const QPixmap* TextDocument::reachedBreakpointPixmap() +{ + const char*breakpoint_bl_xpm[]={ + "11 16 7 1", + "a c #c0c0ff", + "# c #000000", + "c c #0000c0", + "e c #0000ff", + "b c #dcdcdc", + "d c #ffffff", + ". c None", + "...........", + "...........", + "...#####...", + "..#ababa#..", + ".#bcccccc#.", + "#acccccccc#", + "#bcadadace#", + "#acccccccc#", + "#bcadadace#", + "#acccccccc#", + ".#ccccccc#.", + "..#cecec#..", + "...#####...", + "...........", + "...........", + "..........."}; + static QPixmap pixmap( breakpoint_bl_xpm ); + return &pixmap; +} + + +const QPixmap* TextDocument::disabledBreakpointPixmap() +{ + const char*breakpoint_wh_xpm[]={ + "11 16 7 1", + "a c #c0c0ff", + "# c #000000", + "c c #0000c0", + "e c #0000ff", + "b c #dcdcdc", + "d c #ffffff", + ". c None", + "...........", + "...........", + "...#####...", + "..#ddddd#..", + ".#ddddddd#.", + "#ddddddddd#", + "#ddddddddd#", + "#ddddddddd#", + "#ddddddddd#", + "#ddddddddd#", + ".#ddddddd#.", + "..#ddddd#..", + "...#####...", + "...........", + "...........", + "..........."}; + static QPixmap pixmap( breakpoint_wh_xpm ); + return &pixmap; +} + + +const QPixmap* TextDocument::executionPointPixmap() +{ + const char*exec_xpm[]={ + "11 16 4 1", + "a c #00ff00", + "b c #000000", + ". c None", + "# c #00c000", + "...........", + "...........", + "...........", + "#a.........", + "#aaa.......", + "#aaaaa.....", + "#aaaaaaa...", + "#aaaaaaaaa.", + "#aaaaaaa#b.", + "#aaaaa#b...", + "#aaa#b.....", + "#a#b.......", + "#b.........", + "...........", + "...........", + "..........."}; + static QPixmap pixmap( exec_xpm ); + return &pixmap; +} + +#include "textdocument.moc" diff --git a/src/textdocument.h b/src/textdocument.h new file mode 100644 index 0000000..1876473 --- /dev/null +++ b/src/textdocument.h @@ -0,0 +1,245 @@ +/*************************************************************************** + * Copyright (C) 2004-2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef TEXTDOCUMENT_H +#define TEXTDOCUMENT_H + +#include "config.h" +#include "document.h" + +#include <qguardedptr.h> + +#include <kate/document.h> + +class GpsimDebugger; +class SourceLine; +class TextView; + +typedef QValueList<int> IntList; + +/** +@author David Saxton +*/ +class TextDocument : public Document +{ +Q_OBJECT +public: + ~TextDocument(); + + enum CodeType + { + ct_unknown, + ct_asm, + ct_c, + ct_hex, + ct_microbe + }; + + enum MarkType { + Bookmark = KTextEditor::MarkInterface::markType01, + Breakpoint = KTextEditor::MarkInterface::markType02, + ActiveBreakpoint = KTextEditor::MarkInterface::markType03, + ReachedBreakpoint = KTextEditor::MarkInterface::markType04, + DisabledBreakpoint = KTextEditor::MarkInterface::markType05, + ExecutionPoint = KTextEditor::MarkInterface::markType06 + }; + + virtual View *createView( ViewContainer *viewContainer, uint viewAreaId, const char *name = 0l ); + + /** + * Attempts to construct a new TextDocument object and returns a pointer to + * it if successful, or 0 if it failed. + * @returns pointer to constructed object, or 0 if there was a problem + */ + static TextDocument *constructTextDocument( const QString& caption, KTechlab *parent, const char *name = 0L ); + /** + * @returns the guessed code type that this file is + */ + CodeType guessedCodeType() const { return m_guessedCodeType; } + /** + * Set the given lines as all bookmarks + */ + void setBookmarks( const IntList &lines ); + /** + * Set the given line to a bookmark (or not) + */ + void setBookmark( uint line, bool isBookmark ); + /** + * @return List of bookmarks + */ + IntList bookmarkList() const; + + /** + * Set the given lines as all breakpoints + */ + void setBreakpoints( const IntList &lines ); + /** + * Set the given line to a breakpoint (or not ) + */ + void setBreakpoint( uint line, bool isBreakpoint ); + /** + * @return List of breakpoints + */ + IntList breakpointList() const; + +#ifndef NO_GPSIM + /** + * Attach ourselves to the given debugger. + * @param ownDebugger whether we have permission to delete it. + */ + void setDebugger( GpsimDebugger * debugger, bool ownDebugger ); + GpsimDebugger * debugger() const { return m_pDebugger; } + bool ownDebugger() const { return m_bOwnDebugger; } + /** + * Returns true if the debugger is running (this includes when we are in + * stepping mode) + */ + bool debuggerIsRunning() const; + /** + * Returns true if we are in stepping more + */ + bool debuggerIsStepping() const; + QString debugFile() const { return m_debugFile; } + virtual void clearBreakpoints(); +#endif + + virtual bool openURL(const KURL& url); + void fileSave(const KURL& url); + /** + * Set the document to the given text, making the document unmodified, and + * reseting the undo/redo history/ + * @param asInitial whether the next should be treated as if we had just + * opened the file (no undo/redo history, and unmodified). + */ + void setText( const QString & text, bool asInitial ); + /** + * Attempts to guess the filetype from the file extension, and load an + * appropriate highlighting/etc + * @param allowDisable If false, will simply keep the old scheme if nothing + * appropriate is found + */ + void guessScheme( bool allowDisable = true ); + + virtual void fileSave() { fileSave(url()); } + virtual void fileSaveAs(); + virtual void print(); + virtual void setModified( bool modified ); + + Kate::View* createKateView( QWidget *parent, const char *name = 0l ); + + virtual void undo(); + virtual void redo(); + virtual void cut(); + virtual void copy(); + virtual void paste(); + + virtual bool isModified() const { return m_doc->isModified(); } + virtual bool isUndoAvailable() const { return (m_doc->undoCount() != 0); } + virtual bool isRedoAvailable() const { return (m_doc->redoCount() != 0); } + + void clearBookmarks(); + virtual bool fileClose(); + + static const QPixmap* inactiveBreakpointPixmap(); + static const QPixmap* activeBreakpointPixmap(); + static const QPixmap* reachedBreakpointPixmap(); + static const QPixmap* disabledBreakpointPixmap(); + static const QPixmap* executionPointPixmap(); + /** + * Returns a TextView pointer to the active TextView (if there is one) + */ + TextView *textView() const; + + enum ConvertToTarget + { + MicrobeOutput, // (not used) + AssemblyOutput, + HexOutput, + PICOutput + }; + + Kate::Document * kateDocument() const { return m_doc; } + +public slots: + /** + * @param target as ConvertToTarget + */ + void slotConvertTo( int target ); + void convertToAssembly(); + void convertToHex(); + void convertToPIC(); + void formatAssembly(); + void debugRun(); + void debugInterrupt(); + void debugStep(); + void debugStepOver(); + void debugStepOut(); + void debugStop(); + void slotInitLanguage( CodeType type ); + /** + * Called when change line / toggle marks + */ + void slotUpdateMarksInfo(); + + void slotDebugSetCurrentLine( const SourceLine & line ); + /** + * Initialize the actions appropriate for when the debugger is running + * or stepping + */ + void slotInitDebugActions(); + +protected: + /** + * Returns a filepath with the editor's contents in. If the url of this file + * is non-empty (i.e. the user has already saved the file), then that url is + * returned. Otherwise, a temporary file with the given extension (ext) is + * created, and the location of this file is returned. + */ + QString outputFilePath( const QString &ext ); + void saveDone(); +#ifndef NO_GPSIM + /** + * Looks at the list of marks returned by Kate, and syncs them with the + * marks that we know about + */ + void syncBreakpoints(); + + int m_lastDebugLineAt; // Last line with a debug point reached mark + bool m_bLoadDebuggerAsHLL; +#endif + + Kate::Document *m_doc; + QGuardedPtr<TextDocument> m_pLastTextOutputTarget; + +private slots: + void setLastTextOutputTarget( TextDocument * target ); + void slotSyncModifiedStates(); + void slotCODCreationSucceeded(); + void slotCODCreationFailed(); + void slotDebuggerDestroyed(); + void slotBookmarkRequested(); + void slotSelectionmChanged(); + +private: + TextDocument( const QString& caption, KTechlab *parent, const char *name = 0L ); + bool m_constructorSuccessful; + CodeType m_guessedCodeType; + QPtrList<KAction> m_bookmarkActions; + +#ifndef NO_GPSIM + bool b_lockSyncBreakpoints; // Used to avoid calling syncMarks() when we are currently doing so + bool m_bOwnDebugger; + QGuardedPtr<GpsimDebugger> m_pDebugger; + QString m_symbolFile; + QString m_debugFile; +#endif +}; + +#endif diff --git a/src/textview.cpp b/src/textview.cpp new file mode 100644 index 0000000..49a1946 --- /dev/null +++ b/src/textview.cpp @@ -0,0 +1,496 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#define protected public +#include <kxmlguiclient.h> +#undef protected + +#include "asmformatter.h" +#include "config.h" +#include "filemetainfo.h" +#include "gpsimprocessor.h" +#include "ktechlab.h" +#include "symbolviewer.h" +#include "textdocument.h" +#include "textview.h" +#include "variablelabel.h" +#include "viewiface.h" + +#include <ktexteditor/editinterface.h> +#include <ktexteditor/texthintinterface.h> + +// #include "kateview.h" +#include <kdebug.h> +#include <kiconloader.h> +#include <klocale.h> +#include <kpopupmenu.h> +#include <kstandarddirs.h> + +#include <qapplication.h> +#include <qcursor.h> +#include <qobjectlist.h> +#include <qtimer.h> + + +//BEGIN class TextView +TextView::TextView( TextDocument * textDocument, ViewContainer *viewContainer, uint viewAreaId, const char *name ) + : View( textDocument, viewContainer, viewAreaId, name ) +{ + m_view = textDocument->createKateView(this); + m_view->insertChildClient(this); + + KActionCollection * ac = actionCollection(); + + + //BEGIN Convert To * Actions + KToolBarPopupAction * pa = new KToolBarPopupAction( i18n("Convert to"), "fork", 0, 0, 0, ac, "program_convert" ); + pa->setDelayed(false); + + KPopupMenu * m = pa->popupMenu(); + + m->insertTitle( i18n("Convert to ...") ); + m->insertItem( KGlobal::iconLoader()->loadIcon( "convert_to_microbe", KIcon::Small ), i18n("Microbe"), TextDocument::MicrobeOutput ); + m->insertItem( KGlobal::iconLoader()->loadIcon( "convert_to_assembly", KIcon::Small ), i18n("Assembly"), TextDocument::AssemblyOutput ); + m->insertItem( KGlobal::iconLoader()->loadIcon( "convert_to_hex", KIcon::Small ), i18n("Hex"), TextDocument::HexOutput ); + m->insertItem( KGlobal::iconLoader()->loadIcon( "convert_to_pic", KIcon::Small ), i18n("PIC (upload)"), TextDocument::PICOutput ); + connect( m, SIGNAL(activated(int)), textDocument, SLOT(slotConvertTo(int)) ); + + m->setItemEnabled( TextDocument::MicrobeOutput, false ); + //END Convert To * Actions + + + new KAction( i18n("Format Assembly Code"), "", Qt::Key_F12, textDocument, SLOT(formatAssembly()), ac, "format_asm" ); + + +#ifndef NO_GPSIM + //BEGIN Debug Actions + new KAction( i18n("Set &Breakpoint"), 0, 0, this, SLOT(toggleBreakpoint()), ac, "debug_toggle_breakpoint" ); + new KAction( i18n("Run"), "dbgrun", 0, textDocument, SLOT(debugRun()), ac, "debug_run" ); + new KAction( i18n("Interrupt"), "player_pause", 0, textDocument, SLOT(debugInterrupt()), ac, "debug_interrupt" ); + new KAction( i18n("Stop"), "stop", 0, textDocument, SLOT(debugStop()), ac, "debug_stop" ); + new KAction( i18n("Step"), "dbgstep", Qt::CTRL|Qt::ALT|Qt::Key_Right, textDocument, SLOT(debugStep()), ac, "debug_step" ); + new KAction( i18n("Step Over"), "dbgnext", 0, textDocument, SLOT(debugStepOver()), ac, "debug_step_over" ); + new KAction( i18n("Step Out"), "dbgstepout", 0, textDocument, SLOT(debugStepOut()), ac, "debug_step_out" ); + //END Debug Actions +#endif + + + setXMLFile( "ktechlabtextui.rc" ); + m_view->setXMLFile( locate( "appdata", "ktechlabkateui.rc" ) ); + + m_savedCursorLine = 0; + m_savedCursorColumn = 0; + m_pViewIface = new TextViewIface(this); + + setAcceptDrops(true); + + m_statusBar->insertItem( "", ViewStatusBar::LineCol ); + + m_view->installPopup( static_cast<QPopupMenu*>( p_ktechlab->factory()->container( "ktexteditor_popup", p_ktechlab ) ) ); + + connect( m_view, SIGNAL(cursorPositionChanged()), this, SLOT(slotCursorPositionChanged()) ); + connect( m_view, SIGNAL(gotFocus(Kate::View*)), this, SLOT(setFocused()) ); + + m_layout->insertWidget( 0, m_view ); + + slotCursorPositionChanged(); + slotInitDebugActions(); + initCodeActions(); + +#ifndef NO_GPSIM + m_pTextViewLabel = new VariableLabel( this ); + m_pTextViewLabel->hide(); + + TextViewEventFilter * eventFilter = new TextViewEventFilter( this ); + connect( eventFilter, SIGNAL(wordHoveredOver( const QString&, int, int )), this, SLOT(slotWordHoveredOver( const QString&, int, int )) ); + connect( eventFilter, SIGNAL(wordUnhovered()), this, SLOT(slotWordUnhovered()) ); + + QObject * internalView = m_view->child( 0, "KateViewInternal" ); + internalView->installEventFilter( eventFilter ); +#endif +} + + +TextView::~TextView() +{ + if ( p_ktechlab ) + { + if ( KXMLGUIFactory * f = m_view->factory() ) + f->removeClient( m_view ); + + p_ktechlab->addNoRemoveGUIClient( m_view ); + } + + delete m_pViewIface; + m_pViewIface = 0l; +} + + +bool TextView::closeView() +{ + if ( textDocument() ) + { + const QString path = textDocument()->url().prettyURL(); + if ( !path.isEmpty() ) + fileMetaInfo()->grabMetaInfo( path, this ); + } + + bool doClose = View::closeView(); + if (doClose) + p_ktechlab->factory()->removeClient(m_view); + return View::closeView(); +} + + +TextDocument *TextView::textDocument() const +{ + return static_cast<TextDocument*>(document()); +} + + +void TextView::disableActions() +{ + KPopupMenu * tb = (dynamic_cast<KToolBarPopupAction*>(action("program_convert")))->popupMenu(); + + tb->setItemEnabled( TextDocument::AssemblyOutput, false ); + tb->setItemEnabled( TextDocument::HexOutput, false ); + tb->setItemEnabled( TextDocument::PICOutput, false ); + action("format_asm")->setEnabled(false); + +#ifndef NO_GPSIM + action("debug_toggle_breakpoint")->setEnabled(false); +#endif +} + + +void TextView::setFocused() +{ + View::setFocused(); + +#ifndef NO_GPSIM + GpsimDebugger * debugger = textDocument()->debugger(); + if ( !debugger || !debugger->gpsim() ) + return; + + SymbolViewer::self()->setContext( debugger->gpsim() ); +#endif +} + + +void TextView::initCodeActions() +{ + disableActions(); + + KPopupMenu * tb = (dynamic_cast<KToolBarPopupAction*>(action("program_convert")))->popupMenu(); + + switch ( textDocument()->guessedCodeType() ) + { + case TextDocument::ct_asm: + { + tb->setItemEnabled( TextDocument::HexOutput, true ); + tb->setItemEnabled( TextDocument::PICOutput, true ); + action("format_asm")->setEnabled(true); +#ifndef NO_GPSIM + action("debug_toggle_breakpoint")->setEnabled(true); + slotInitDebugActions(); +#endif + break; + } + case TextDocument::ct_c: + { + tb->setItemEnabled( TextDocument::AssemblyOutput, true ); + tb->setItemEnabled( TextDocument::HexOutput, true ); + tb->setItemEnabled( TextDocument::PICOutput, true ); + break; + } + case TextDocument::ct_hex: + { + tb->setItemEnabled( TextDocument::AssemblyOutput, true ); + tb->setItemEnabled( TextDocument::PICOutput, true ); + break; + } + case TextDocument::ct_microbe: + { + tb->setItemEnabled( TextDocument::AssemblyOutput, true ); + tb->setItemEnabled( TextDocument::HexOutput, true ); + tb->setItemEnabled( TextDocument::PICOutput, true ); + break; + } + case TextDocument::ct_unknown: + { + break; + } + } +} + + +unsigned TextView::currentLine() +{ + unsigned l,c ; + m_view->cursorPosition( &l, &c ); + return l; +} +unsigned TextView::currentColumn() +{ + unsigned l,c ; + m_view->cursorPosition( &l, &c ); + return c; +} + + +void TextView::saveCursorPosition() +{ + m_view->cursorPosition( &m_savedCursorLine, &m_savedCursorColumn ); +} + + +void TextView::restoreCursorPosition() +{ + m_view->setCursorPosition( m_savedCursorLine, m_savedCursorColumn ); +} + + +void TextView::slotCursorPositionChanged() +{ + uint line, column; + m_view->cursorPosition( &line, &column ); + + m_statusBar->changeItem( i18n(" Line: %1 Col: %2 ").arg(QString::number(line+1)).arg(QString::number(column+1)), ViewStatusBar::LineCol ); + + slotUpdateMarksInfo(); +} + + +void TextView::slotUpdateMarksInfo() +{ +#ifndef NO_GPSIM + uint l,c ; + m_view->cursorPosition( &l, &c ); + + if ( m_view->getDoc()->mark(l) & TextDocument::Breakpoint ) + action("debug_toggle_breakpoint")->setText( i18n("Clear &Breakpoint") ); + else + action("debug_toggle_breakpoint")->setText( i18n("Set &Breakpoint") ); +#endif +} + + +void TextView::slotInitDebugActions() +{ +#ifndef NO_GPSIM + bool isRunning = textDocument()->debuggerIsRunning(); + bool isStepping = textDocument()->debuggerIsStepping(); + bool ownDebugger = textDocument()->ownDebugger(); + + action("debug_run")->setEnabled( !isRunning || isStepping ); + action("debug_interrupt")->setEnabled(isRunning && !isStepping); + action("debug_stop")->setEnabled(isRunning && ownDebugger); + action("debug_step")->setEnabled(isRunning && isStepping); + action("debug_step_over")->setEnabled(isRunning && isStepping); + action("debug_step_out")->setEnabled(isRunning && isStepping); +#endif // !NO_GPSIM +} + + +void TextView::toggleBreakpoint() +{ +#ifndef NO_GPSIM + uint l,c ; + m_view->cursorPosition( &l, &c ); + textDocument()->setBreakpoint( l, !(m_view->getDoc()->mark(l) & TextDocument::Breakpoint) ); +#endif // !NO_GPSIM +} + + +void TextView::slotWordHoveredOver( const QString & word, int line, int col ) +{ +#ifndef NO_GPSIM + // We're only interested in popping something up if we currently have a debugger running + GpsimProcessor * gpsim = textDocument()->debugger() ? textDocument()->debugger()->gpsim() : 0l; + if ( !gpsim ) + { + m_pTextViewLabel->hide(); + return; + } + + // Find out if the word that we are hovering over is the operand data + KTextEditor::EditInterface * e = (KTextEditor::EditInterface*)textDocument()->kateDocument()->qt_cast("KTextEditor::EditInterface"); + InstructionParts parts( e->textLine( unsigned(line) ) ); + if ( !parts.operandData().contains( word ) ) + return; + + if ( RegisterInfo * info = gpsim->registerMemory()->fromName( word ) ) + m_pTextViewLabel->setRegister( info, info->name() ); + + else + { + int operandAddress = textDocument()->debugger()->programAddress( textDocument()->debugFile(), line ); + if ( operandAddress == -1 ) + { + m_pTextViewLabel->hide(); + return; + } + + int regAddress = gpsim->operandRegister( operandAddress ); + + if ( regAddress != -1 ) + m_pTextViewLabel->setRegister( gpsim->registerMemory()->fromAddress( regAddress ), word ); + + else + { + m_pTextViewLabel->hide(); + return; + } + } + + m_pTextViewLabel->move( mapFromGlobal( QCursor::pos() ) + QPoint( 0, 20 ) ); + m_pTextViewLabel->show(); +#endif // !NO_GPSIM +} + + +void TextView::slotWordUnhovered() +{ +#ifndef NO_GPSIM + m_pTextViewLabel->hide(); +#endif // !NO_GPSIM +} +//END class TextView + + + +//BEGIN class TextViewEventFilter +TextViewEventFilter::TextViewEventFilter( TextView * textView ) +{ + m_hoverStatus = Sleeping; + m_pTextView = textView; + m_lastLine = m_lastCol = -1; + + ((KTextEditor::TextHintInterface*)textView->kateView()->qt_cast("KTextEditor::TextHintInterface"))->enableTextHints(0); + connect( textView->kateView(), SIGNAL(needTextHint(int, int, QString &)), this, SLOT(slotNeedTextHint( int, int, QString& )) ); + + m_pHoverTimer = new QTimer( this ); + connect( m_pHoverTimer, SIGNAL(timeout()), this, SLOT(hoverTimeout()) ); + + m_pSleepTimer = new QTimer( this ); + connect( m_pSleepTimer, SIGNAL(timeout()), this, SLOT(gotoSleep()) ); + + m_pNoWordTimer = new QTimer( this ); + connect( m_pNoWordTimer, SIGNAL(timeout()), this, SLOT(slotNoWordTimeout()) ); +} + + +bool TextViewEventFilter::eventFilter( QObject *, QEvent * e ) +{ + if ( e->type() == QEvent::MouseMove ) + { + if ( !m_pNoWordTimer->isActive() ) + m_pNoWordTimer->start( 10 ); + return false; + } + + if ( e->type() == QEvent::FocusOut || e->type() == QEvent::FocusIn || e->type() == QEvent::MouseButtonPress || e->type() == QEvent::Leave || e->type() == QEvent::Wheel ) + { + // user moved focus somewhere - hide the tip and sleep + if ( ((QFocusEvent*)e)->reason() != QFocusEvent::Popup ) + updateHovering( 0, -1, -1 ); + } + + return false; +} + + +void TextViewEventFilter::hoverTimeout() +{ + m_pSleepTimer->stop(); + m_hoverStatus = Active; + emit wordHoveredOver( m_lastWord, m_lastLine, m_lastCol ); +} + + +void TextViewEventFilter::gotoSleep() +{ + m_hoverStatus = Sleeping; + m_lastWord = QString::null; + emit wordUnhovered(); + m_pHoverTimer->stop(); +} + + +void TextViewEventFilter::slotNoWordTimeout() +{ + updateHovering( 0, -1, -1 ); +} + + +void TextViewEventFilter::updateHovering( const QString & currentWord, int line, int col ) +{ + if ( (currentWord == m_lastWord) && (line == m_lastLine) ) + return; + + m_lastWord = currentWord; + m_lastLine = line; + m_lastCol = col; + + if ( currentWord.isEmpty() ) + { + if ( m_hoverStatus == Active ) + m_hoverStatus = Hidden; + + emit wordUnhovered(); + if ( !m_pSleepTimer->isActive() ) + m_pSleepTimer->start( 2000, true ); + + return; + } + + if ( m_hoverStatus != Sleeping ) + emit wordHoveredOver( currentWord, line, col ); + else + m_pHoverTimer->start( 700, true ); +} + + +static inline bool isWordLetter( const QString & s ) { return (s.length() == 1) && (s[0].isLetterOrNumber() || s[0] == '_'); } + + +void TextViewEventFilter::slotNeedTextHint( int line, int col, QString & ) +{ + m_pNoWordTimer->stop(); + + KTextEditor::EditInterface * e = (KTextEditor::EditInterface*)m_pTextView->textDocument()->kateDocument()->qt_cast("KTextEditor::EditInterface"); + + // Return if we aren't currently in a word + if ( !isWordLetter( e->text( line, col, line, col+1 ) ) ) + { + updateHovering( QString::null, line, col ); + return; + } + + // Find the start of the word + int wordStart = col; + do wordStart--; + while ( wordStart > 0 && isWordLetter( e->text( line, wordStart, line, wordStart+1 ) ) ); + wordStart++; + + // Find the end of the word + int wordEnd = col; + do wordEnd++; + while ( isWordLetter( e->text( line, wordEnd, line, wordEnd+1 ) ) ); + + QString t = e->text( line, wordStart, line, wordEnd ); + + updateHovering( t, line, col ); +} +//END class TextViewEventFilter + + + +#include "textview.moc" diff --git a/src/textview.h b/src/textview.h new file mode 100644 index 0000000..8903843 --- /dev/null +++ b/src/textview.h @@ -0,0 +1,190 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef TEXTVIEW_H +#define TEXTVIEW_H + +#include "config.h" +#include "view.h" + +#include <kate/view.h> +#include <qguardedptr.h> +#include <qlabel.h> + +class QMouseEvent; +class RegisterInfo; +class TextDocument; +class TextView; +class VariableLabel; + + +/** +@author David Saxton +*/ +class TextView : public View +{ + Q_OBJECT + public: + TextView( TextDocument *textDocument, ViewContainer *viewContainer, uint viewAreaId, const char *name = 0 ); + ~TextView(); + virtual bool closeView(); + /** + * Brings up the goto line dialog. + */ + bool gotoLine( const int line ) { return m_view->setCursorPosition( line, 0/*m_view->cursorColumn()*/ ); } + /** + * Returns a pointer to the document as a text document + */ + TextDocument *textDocument() const; + void cut() { m_view->cut(); } + void copy() { m_view->copy(); } + void paste() { m_view->paste(); } + void saveCursorPosition(); + void restoreCursorPosition(); + /** + * Enable code actions depending on the type of code being edited + */ + void initCodeActions(); + void setCursorPosition( uint line, uint col ) { m_view->setCursorPosition( line, col ); } + unsigned currentLine(); + unsigned currentColumn(); + void disableActions(); + + Kate::View * kateView() const { return m_view; } + + Kate::View::saveResult save() { return m_view->save(); } + Kate::View::saveResult saveAs() { return m_view->saveAs(); } + + virtual void setFocused(); + + public slots: + /** + * Called when change line / toggle marks + */ + void slotUpdateMarksInfo(); + void slotCursorPositionChanged(); + void toggleBreakpoint(); + /** + * Initialize the actions appropriate for when the debugger is running + * or stepping + */ + void slotInitDebugActions(); + + protected slots: + void slotWordHoveredOver( const QString & word, int line, int col ); + void slotWordUnhovered(); + + protected: + Kate::View *m_view; +#ifndef NO_GPSIM + VariableLabel * m_pTextViewLabel; ///< Pops up when the user hovers his mouse over a word +#endif + + private: + uint m_savedCursorLine; + uint m_savedCursorColumn; +}; + + +/** +This class is an event filter to be installed in the kate view, and is used to +do stuff like poping up menus and telling TextView that a word is being hovered +over (used in the debugger). + +@author David Saxton +*/ +class TextViewEventFilter : public QObject +{ + Q_OBJECT + public: + TextViewEventFilter( TextView * textView ); + + bool eventFilter( QObject * watched, QEvent * e ); + + signals: + /** + * When the user hovers the mouse for more than 700 milliseconds over a + * word, "hover mode" is entered. When the user presses a key, clicks + * mouse, etc, this mode is left. During the mode, any word that is + * under the mouse cursor will be emitted as hoveredOver( word ). + */ + void wordHoveredOver( const QString & word, int line, int col ); + /** + * Emitted when focus is lost, the mouse moves to a different word, etc. + */ + void wordUnhovered(); + + protected slots: + void slotNeedTextHint( int line, int col, QString & text ); + /** + * Called when we are not in hover mode, but the user has had his mouse + * in the same position for some time. + */ + void hoverTimeout(); + /** + * Called (from m_pSleepTimer) when we are in hover mode, but no word + * has been hovered over for some time. + */ + void gotoSleep(); + /** + * @see m_pNoWordTimer + */ + void slotNoWordTimeout(); + + protected: + enum HoverStatus + { + /** + * We are currently hovering - wordHoveredOver was emitted, and + * wordUnhovered hasn't been emitted yet. + */ + Active, + /** + * A word was unhovered recently. We will go straight to PoppedUp + * mode if a word is hovered over again. + */ + Hidden, + /** + * A word was not unhovered recentely. There will be a short display + * before going to PoppedUp mode if a word is hovered over. + */ + Sleeping, + }; + + /** + * Starts / stops timers, emits signals, etc. See other functions for an + * idea of what this does. + */ + void updateHovering( const QString & currentWord, int line, int col ); + /** + * Started when the user moves his mouse over a word, and we are in + * Sleeping mode. Reset when the user moves his mouse, etc. + */ + QTimer * m_pHoverTimer; + /** + * Started when a word is unhovered. When this timeouts, we will go to + * Sleeping mode. + */ + QTimer * m_pSleepTimer; + /** + * Activated by the user moving the mouse. Reset by a call to + * slotNeedTextHint. This timer is needed as KateViewInternal doesn't + * bother updating us if the mouse cursor isn't over text. + */ + QTimer * m_pNoWordTimer; + + TextView * m_pTextView; + QString m_lastWord; + int m_lastLine; + int m_lastCol; + HoverStatus m_hoverStatus; +}; + +#endif diff --git a/src/variablelabel.cpp b/src/variablelabel.cpp new file mode 100644 index 0000000..a6a6ed1 --- /dev/null +++ b/src/variablelabel.cpp @@ -0,0 +1,91 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "config.h" +#ifndef NO_GPSIM + +#include "gpsimprocessor.h" +#include "symbolviewer.h" +#include "textview.h" +#include "variablelabel.h" + + +//BEGIN class VariableLabel +VariableLabel::VariableLabel( TextView * parent ) + : QLabel( parent, "toolTipTip", WStyle_StaysOnTop | WStyle_Customize | WStyle_NoBorder | WStyle_Tool | WX11BypassWM ) +{ + m_value = -1; + + setMargin(1); + setAutoMask( FALSE ); + setFrameStyle( QFrame::Plain | QFrame::Box ); + setLineWidth( 1 ); + setAlignment( AlignAuto | AlignTop ); + setIndent(0); + polish(); + adjustSize(); +} + + +void VariableLabel::setRegister( RegisterInfo * info, const QString & name ) +{ + disconnectRegisterInfo(); + + if ( !info ) + return; + + m_value = -1; + m_pRegisterInfo = info; + m_registerName = name; + + connect( m_pRegisterInfo, SIGNAL(destroyed()), this, SLOT(hide()) ); + connect( m_pRegisterInfo, SIGNAL(valueChanged(unsigned)), this, SLOT(updateText()) ); + + updateText(); +} + + +void VariableLabel::disconnectRegisterInfo() +{ + if ( !m_pRegisterInfo ) + return; + + disconnect( m_pRegisterInfo, SIGNAL(destroyed()), this, SLOT(hide()) ); + disconnect( m_pRegisterInfo, SIGNAL(valueChanged(unsigned)), this, SLOT(updateText()) ); + + m_pRegisterInfo = 0l; + m_registerName = QString::null; +} + + +void VariableLabel::setValue( unsigned value ) +{ + disconnectRegisterInfo(); + m_value = value; + + updateText(); +} + + +void VariableLabel::updateText() +{ + if ( m_pRegisterInfo ) + setText( QString("%1 = %2").arg( m_registerName ).arg( SymbolViewer::self()->toDisplayString( m_pRegisterInfo->value() ) ) ); + + else if ( m_value != -1 ) + setText( QString::number( m_value ) ); + + adjustSize(); +} +//END class VariableLabel + +#include "variablelabel.moc" + +#endif // !NO_GPSIM diff --git a/src/variablelabel.h b/src/variablelabel.h new file mode 100644 index 0000000..ba3850e --- /dev/null +++ b/src/variablelabel.h @@ -0,0 +1,60 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "config.h" +#ifndef NO_GPSIM + +#ifndef VARIABLELABEL_H +#define VARIABLELABEL_H + +#include <qguardedptr.h> +#include <qlabel.h> + +class TextView; + + +/** +Used for displaying the value of a variable when the user hovers his mouse over +a variable while debugging. + +@author David Saxton + */ +class VariableLabel : public QLabel +{ + Q_OBJECT + public: + VariableLabel( TextView * parent ); + /** + * Sets the register that this label is displaying the value of. + */ + void setRegister( RegisterInfo * info, const QString & name ); + /** + * Sets the value that this label is displaying. This is an alternative + * to setRegister. + */ + void setValue( unsigned value ); + + protected slots: + /** + * Updates what is displayed from m_pRegisterInfo. + */ + void updateText(); + + protected: + void disconnectRegisterInfo(); + + QGuardedPtr<RegisterInfo> m_pRegisterInfo; + QString m_registerName; + int m_value; +}; + +#endif + +#endif // !NO_GPSIM diff --git a/src/variant.cpp b/src/variant.cpp new file mode 100644 index 0000000..10422cd --- /dev/null +++ b/src/variant.cpp @@ -0,0 +1,139 @@ +/*************************************************************************** + * Copyright (C) 2003-2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "colorcombo.h" +#include "cnitem.h" + +#include <cmath> +#include <klocale.h> + +Variant::Variant( Type::Value type ) + : QObject() +{ + m_type = type; + m_bSetDefault = false; + m_bHidden = false; + m_bAdvanced = false; + m_minValue = 1e-6; + m_maxValue = 1e9; + m_minAbsValue = 1e-6; + m_colorScheme = ColorCombo::QtStandard; + if ( type == Type::Color ) + { + // this value is taken from ColorCombo and should ideally be put somewhere... + m_defaultValue = "#f62a2a"; + m_value = "#f62a2a"; + } +} + + +Variant::~Variant() +{ +} + + +void Variant::setType( Type::Value type ) +{ + m_type = type; +} + + +void Variant::appendAllowed(QString string) +{ + if ( !m_allowed.contains(string) ) { + m_allowed.append(string); + } +} + + +void Variant::setAllowed(QStringList stringList) +{ + // Ideally, we should check for duplicates in whatever is setting the + // allowed strings, but it is a lot easier and permanent to do it here + m_allowed.clear(); + const QStringList::iterator end = stringList.end(); + for ( QStringList::iterator it = stringList.begin(); it != end; ++it ) + { + if ( !m_allowed.contains(*it) ) { + m_allowed.append(*it); + } + } +} + + +void Variant::setMinValue( const double value ) +{ + m_minValue = value; + if ( std::abs(value) < m_minAbsValue && value != 0. ) + { + m_minAbsValue = std::abs(value); + } +} + +void Variant::setMaxValue( const double value ) +{ + m_maxValue = value; + if ( std::abs(value) < m_minAbsValue && value != 0. ) + { + m_minAbsValue = std::abs(value); + } +} + +QString Variant::displayString() const +{ + switch(type()) + { + case Variant::Type::Double: + { + double numValue = m_value.toDouble(); + return QString::number( numValue / CNItem::getMultiplier(numValue) ) + " " + CNItem::getNumberMag(numValue) + m_unit; + } + + case Variant::Type::Int: + return m_value.toString()+" "+m_unit; + + case Variant::Type::Bool: + return m_value.toBool() ? i18n("True") : i18n("False"); + + default: + return m_value.toString(); + } +} + +void Variant::setValue( const QVariant& val ) +{ + if (!m_bSetDefault) + setDefaultValue(val); + + if ( m_value == val ) + return; + + const QVariant old = m_value; + m_value = val; + emit( valueChanged( val, old ) ); +} + +void Variant::setDefaultValue( QVariant val ) +{ + m_defaultValue = val; + m_bSetDefault = true; +} + +void Variant::resetToDefault() +{ + setValue( defaultValue() ); +} + +void Variant::setMinAbsValue( double val ) +{ + m_minAbsValue = val; +} + +#include "variant.moc" diff --git a/src/variant.h b/src/variant.h new file mode 100644 index 0000000..8367026 --- /dev/null +++ b/src/variant.h @@ -0,0 +1,175 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef VARIANT_H +#define VARIANT_H + +#include <qobject.h> +#include <qvariant.h> + +class QColor; +class QString; + +/** +For information: +QVariant::type() returns an enum for the current data type +contained. e.g. returns QVariant::Color or QVariant::Rect +@author Daniel Clarke +@author David Saxton +*/ +class Variant : public QObject +{ +Q_OBJECT +public: + class Type + { + public: + enum Value + { + None, + Int, // Integer + Raw, // QByteArray + Double, // Real number + String, // Editable string + Multiline, // String that may contain linebreaks + Select, // Selection of strings + Combo, // Editable combination of strings + FileName, // Filename + Color, // Color + Bool, // Boolean + VarName, // Variable name + Port, // Port name + Pin, // Pin name + PenStyle, // Pen Style + PenCapStyle, // Pen Cap Style + SevenSegment, // Pin Map for Seven Segment Display + KeyPad // Pin Map for Keypad + }; + }; + + Variant( Type::Value type ); + virtual ~Variant(); + + /** + * Returns the type of Variant (see Variant::Type::Value) + */ + Variant::Type::Value type() const { return m_type; } + /** + * Sets the variant type + */ + void setType( Type::Value type ); + /** + * Returns the filter used for file dialogs (if this is of type Type::FileName) + */ + QString filter() const { return m_filter; } + void setFilter( const QString & filter ) { m_filter = filter; } + /** + * The selection of colours to be used in the combo box - e.g. + * ColorCombo::LED. + * @see ColorCombo::ColorScheme + */ + int colorScheme() const { return m_colorScheme; } + void setColorScheme( int colorScheme ) { m_colorScheme = colorScheme; } + /** + * This function is for convenience; it sets both the toolbar and editor + * caption. + */ + void setCaption( const QString & caption ) { setToolbarCaption(caption); setEditorCaption(caption); } + /** + * This text is displayed to the left of the entry widget in the toolbar + */ + QString toolbarCaption() const { return m_toolbarCaption; } + void setToolbarCaption( const QString & caption ) { m_toolbarCaption = caption; } + /** + * This text is displayed to the left of the entry widget in the item editor + */ + QString editorCaption() const { return m_editorCaption; } + void setEditorCaption( const QString & caption ) { m_editorCaption = caption; } + /** + * Unit of number, (e.g. V (volts) / F (farads)) + */ + QString unit() const { return m_unit; } + void setUnit( const QString & unit ) { m_unit = unit; } + /** + * The smallest (as in negative, not absoluteness) value that the user can + * set this to. + */ + double minValue() const { return m_minValue; } + void setMinValue( double value ); + /** + * The largest (as in positive, not absoluteness) value that the user can + * set this to. + */ + double maxValue() const { return m_maxValue; } + void setMaxValue( double value ); + /** + * The smallest absolute value that the user can set this to, before the + * value is considered zero. + */ + double minAbsValue() const { return m_minAbsValue; } + void setMinAbsValue( double val ); + QVariant defaultValue() const { return m_defaultValue; } + void setDefaultValue( QVariant val ); + /** + * If this data is marked as advanced, it will only display in the item + * editor (and not in the toolbar) + */ + void setAdvanced( bool advanced ) { m_bAdvanced = advanced; } + bool isAdvanced() const { return m_bAdvanced; } + /** + * If this data is marked as hidden, it will not be editable from anywhere + * in the user interface + */ + void setHidden( bool hidden ) { m_bHidden = hidden; } + bool isHidden() const { return m_bHidden; } + /** + * Returns the best possible attempt at representing the data in a string + * for display. Used by the properties list view. + */ + QString displayString() const; + /** + * The list of values that the data is allowed to take (if it is string) + */ + QStringList allowed() const { return m_allowed; } + void setAllowed(QStringList stringList); + void appendAllowed(QString string); + + QVariant value() const { return m_value; } + void setValue( const QVariant& val ); + void resetToDefault(); + +signals: + /** + * Emitted when the value changes. + * NOTE: The order of data given is the new value, and then the old value + * This is done so that slots that don't care about the old value don't + * have to accept it + */ + void valueChanged( QVariant newValue, QVariant oldValue ); + +private: + QVariant m_value; // the actual data + QVariant m_defaultValue; + QString m_unit; + double m_minAbsValue; + double m_minValue; + double m_maxValue; + QString m_toolbarCaption; // Short description shown in e.g. properties dialog + QString m_editorCaption; // Text displayed before the data entry widget in the toolbar + bool m_bAdvanced; // If advanced, only display data in item editor + bool m_bHidden; // If hidden, do not allow user to change data + QString m_filter; // If type() == Type::FileName this is the filter used in file dialogs. + bool m_bSetDefault; // If false, then the default will be set to the first thing this variant is set to + Type::Value m_type; + QStringList m_allowed; + int m_colorScheme; +}; + +#endif diff --git a/src/view.cpp b/src/view.cpp new file mode 100644 index 0000000..07e81ec --- /dev/null +++ b/src/view.cpp @@ -0,0 +1,227 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "document.h" +#include "iteminterface.h" +#include "ktechlab.h" +#include "view.h" +#include "viewiface.h" +#include "viewcontainer.h" + +#include <kdebug.h> +#include <kiconloader.h> +#include <klocale.h> +#include <ksqueezedtextlabel.h> +#include <qpainter.h> + +//BEGIN KVSSBSep +// Taken from kdebase-3.4.0/kate/app/kateviewspace.cpp, Copyright Anders Lund <anders.lund@lund.tdcadsl.dk> +/* + "KateViewSpaceStatusBarSeparator" + A 2 px line to separate the statusbar from the view. + It is here to compensate for the lack of a frame in the view, + I think Kate looks very nice this way, as QScrollView with frame + looks slightly clumsy... + Slight 3D effect. I looked for suitable QStyle props or methods, + but found none, though maybe it should use QStyle::PM_DefaultFrameWidth + for height (TRY!). + It does look a bit funny with flat styles (Light, .Net) as is, + but there are on methods to paint panel lines separately. And, + those styles tends to look funny on their own, as a light line + in a 3D frame next to a light contents widget is not functional. + Also, QStatusBar is up to now completely ignorant to style. + -anders +*/ +class KVSSBSep : public QWidget { + public: + KVSSBSep( View * parent=0) : QWidget(parent) + { + setFixedHeight( 2 ); + } + protected: + void paintEvent( QPaintEvent *e ) + { + QPainter p( this ); + p.setPen( colorGroup().shadow() ); + p.drawLine( e->rect().left(), 0, e->rect().right(), 0 ); + p.setPen( ((View*)parentWidget())->isFocused() ? colorGroup().light() : colorGroup().midlight() ); + p.drawLine( e->rect().left(), 1, e->rect().right(), 1 ); + } +}; +//END KVSSBSep + + + +//BEGIN class View +View::View( Document *document, ViewContainer *viewContainer, uint viewAreaId, const char *name ) + : QWidget( viewContainer->viewArea(viewAreaId), name ? name : (const char *)("view_" + QString::number(viewAreaId)) ), + KXMLGUIClient() +{ + m_dcopID = 0; + m_viewAreaId = viewAreaId; + m_pDocument = document; + p_ktechlab = document->ktechlab(); + p_viewContainer = viewContainer; + m_pViewIface = 0l; + + if ( ViewArea * viewArea = viewContainer->viewArea(viewAreaId) ) + viewArea->setView(this); + + else + kdDebug() << k_funcinfo << " viewArea = " << viewArea <<endl; + + b_isFocused = false; + + m_layout = new QVBoxLayout(this); + + // Don't bother creating statusbar if no ktechlab as we are not a main ktechlab tab + if (p_ktechlab) + { + m_statusBar = new ViewStatusBar(this); + + m_layout->addWidget( new KVSSBSep(this) ); + m_layout->addWidget( m_statusBar ); + + connect( p_ktechlab, SIGNAL(configurationChanged()), this, SLOT(slotUpdateConfiguration()) ); + } +} + + +View::~View() +{ + if (p_ktechlab) + p_ktechlab->factory()->removeClient(this); +} + + +KAction * View::action( const QString & name ) const +{ + KAction * action = actionCollection()->action(name); + if ( !action ) + kdError() << k_funcinfo << "No such action: " << name << endl; + return action; +} + + +DCOPObject * View::dcopObject( ) const +{ + return m_pViewIface; +} + + +bool View::closeView() +{ + return p_viewContainer->closeViewArea( viewAreaId() ); +} + + +void View::setFocused() +{ + b_isFocused = true; + p_viewContainer->setActiveViewArea( viewAreaId() ); + + if ( p_ktechlab ) + { + p_ktechlab->action("file_save")->setEnabled(true); + p_ktechlab->action("file_save_as")->setEnabled(true); + p_ktechlab->action("file_close")->setEnabled(true); + p_ktechlab->action("file_print")->setEnabled(true); + p_ktechlab->action("edit_paste")->setEnabled(true); + p_ktechlab->action("view_split_leftright")->setEnabled(true); + p_ktechlab->action("view_split_topbottom")->setEnabled(true); + + ItemInterface::self()->updateItemActions(); + } + + emit viewFocused(this); +} + + +void View::setUnfocused() +{ + b_isFocused = false; + emit viewUnfocused(); +} + + +void View::setDCOPID( unsigned id ) +{ + if ( m_dcopID == id ) + return; + + m_dcopID = id; + if ( m_pViewIface ) + { + QCString docID; + docID.setNum( document()->dcopID() ); + + QCString viewID; + viewID.setNum( dcopID() ); + + m_pViewIface->setObjId( "View#" + docID + "." + viewID ); + } +} +//END class View + + + +//BEGIN class ViewStatusBar +ViewStatusBar::ViewStatusBar( View *view ) + : KStatusBar(view) +{ + p_view = view; + + m_modifiedLabel = new QLabel(this); + addWidget( m_modifiedLabel, 0, false ); + m_fileNameLabel = new KSqueezedTextLabel(this); + addWidget( m_fileNameLabel, 1, false ); + + m_modifiedPixmap = KGlobal::iconLoader()->loadIcon( "filesave", KIcon::Small ); + m_unmodifiedPixmap = KGlobal::iconLoader()->loadIcon( "null", KIcon::Small ); + + connect( view->document(), SIGNAL(modifiedStateChanged()), this, SLOT(slotModifiedStateChanged()) ); + connect( view->document(), SIGNAL(fileNameChanged(const KURL& )), this, SLOT(slotFileNameChanged(const KURL& )) ); + + connect( view, SIGNAL(viewFocused(View* )), this, SLOT(slotViewFocused(View* )) ); + connect( view, SIGNAL(viewUnfocused()), this, SLOT(slotViewUnfocused()) ); + + slotModifiedStateChanged(); + slotFileNameChanged( view->document()->url() ); +} + + +void ViewStatusBar::slotModifiedStateChanged() +{ + m_modifiedLabel->setPixmap( p_view->document()->isModified() ? m_modifiedPixmap : m_unmodifiedPixmap ); +} + + +void ViewStatusBar::slotFileNameChanged( const KURL &url ) +{ + m_fileNameLabel->setText( url.isEmpty() ? i18n("Untitled") : url.fileName(true) ); +} + + +void ViewStatusBar::slotViewFocused( View * ) +{ + setPalette(p_view->palette()); +} + + +void ViewStatusBar::slotViewUnfocused() +{ + QPalette pal( p_view->palette() ); + pal.setColor( QColorGroup::Background, pal.active().mid() ); + pal.setColor( QColorGroup::Light, pal.active().midlight() ); + setPalette(pal); +} +//END class ViewStatusBar + +#include "view.moc" diff --git a/src/view.h b/src/view.h new file mode 100644 index 0000000..d501363 --- /dev/null +++ b/src/view.h @@ -0,0 +1,155 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef VIEW_H +#define VIEW_H + +#include <kstatusbar.h> +#include <kurl.h> +#include <kxmlguiclient.h> +#include <qguardedptr.h> +#include <qpixmap.h> + +class DCOPObject; +class Document; +class KSqueezedTextLabel; +class KTechlab; +class View; +class ViewContainer; +class ViewIface; +class QVBoxLayout; + +class ViewStatusBar : public KStatusBar +{ +Q_OBJECT +public: + ViewStatusBar( View *view ); + + enum InfoId + { + SimulationState, + LineCol, + InsertMode, + SelectionMode + }; + +public slots: + void slotModifiedStateChanged(); + void slotFileNameChanged( const KURL &url ); + void slotViewFocused( View * ); + void slotViewUnfocused(); + +protected: + View *p_view; + QLabel* m_modifiedLabel; + KSqueezedTextLabel* m_fileNameLabel; + QPixmap m_modifiedPixmap; + QPixmap m_unmodifiedPixmap; +}; + +/** +@author David Saxton +*/ +class View : public QWidget, public KXMLGUIClient +{ +Q_OBJECT +public: + View( Document *document, ViewContainer *viewContainer, uint viewAreaId, const char *name = 0 ); + virtual ~View(); + + KAction * action( const QString & name ) const; + bool isFocused() const { return b_isFocused; } + /** + * Pointer to the parent document + */ + Document *document() const { return m_pDocument; } + /** + * Returns the DCOP object from this view + */ + DCOPObject * dcopObject() const; + /** + * Returns the dcop suffix for this view - a unique ID for the current the + * view within all views associated with the parent document. DCOP name + * will become "View#docID#viewID". + */ + unsigned dcopID() const { return m_dcopID; } + /** + * Sets the dcop suffix. The DCOP object for this view will be renamed. + * @see dcopID + */ + void setDCOPID( unsigned id ); + /** + * Pointer to the ViewContainer that we're in + */ + ViewContainer *viewContainer() const { return p_viewContainer; } + /** + * Tells the view container which contains this view to close this view, + * returning true if successful (i.e. not both last view and unsaved, etc) + */ + virtual bool closeView(); + /** + * Returns the unique (for the view container) view area id associated with this view + */ + uint viewAreaId() const { return m_viewAreaId; } + /** + * Zoom in + */ + virtual void viewZoomIn() {}; + /** + * Zoom out + */ + virtual void viewZoomOut() {}; + virtual bool canZoomIn() const { return true; } + virtual bool canZoomOut() const { return true; } + /** + * Restore view to actual size + */ + virtual void actualSize() {}; + + virtual void toggleBreakpoint() {}; + /** + * Called by ktechlab when it has entered its destructor to avoid calls to + * it (such as from the TextView destructor). + */ + void setKTechlabDeleted() { p_ktechlab = 0l; } + +public slots: + /** + * Called when the view is to be focused (enables actions, etc) + */ + virtual void setFocused(); + /** + * Called when the view is to be unfocused (disables actions, etc) + */ + virtual void setUnfocused(); + +protected slots: + /** + * Called when the user changes the configuration. + */ + virtual void slotUpdateConfiguration() {}; + +signals: + void viewFocused( View *view ); + void viewUnfocused(); + +protected: + QGuardedPtr<Document> m_pDocument; + KTechlab * p_ktechlab; + QGuardedPtr<ViewContainer> p_viewContainer; + uint m_viewAreaId; + bool b_isFocused; + ViewStatusBar *m_statusBar; + QVBoxLayout *m_layout; + ViewIface * m_pViewIface; + unsigned m_dcopID; +}; + +#endif diff --git a/src/viewcontainer.cpp b/src/viewcontainer.cpp new file mode 100644 index 0000000..4f145dd --- /dev/null +++ b/src/viewcontainer.cpp @@ -0,0 +1,610 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "docmanager.h" +#include "document.h" +#include "ktechlab.h" +#include "view.h" +#include "viewcontainer.h" + +#include <kconfig.h> +#include <kdebug.h> +#include <kglobalsettings.h> +#include <klocale.h> +#include <ktabwidget.h> +#include <qobjectlist.h> + +ViewContainer::ViewContainer( const QString & caption, KTechlab * ktechlab, QWidget * parent ) + : QWidget( ktechlab ? ktechlab->tabWidget() : parent ) +{ + b_deleted = false; + p_ktechlab = ktechlab; + if (ktechlab) + connect( ktechlab, SIGNAL(needUpdateCaptions()), this, SLOT(updateCaption()) ); + + QHBoxLayout *layout = new QHBoxLayout(this); + m_baseViewArea = new ViewArea( this, this, 0, "viewarea_0" ); + connect( m_baseViewArea, SIGNAL(destroyed(QObject* )), this, SLOT(baseViewAreaDestroyed(QObject* )) ); + + layout->addWidget(m_baseViewArea); + + m_activeViewArea = 0; + b_focused = false; + + if (ktechlab) + ktechlab->tabWidget()->addTab( this, caption ); + + show(); + + if (ktechlab) + ktechlab->tabWidget()->setCurrentPage( ktechlab->tabWidget()->indexOf(this) ); +} + + +ViewContainer::~ViewContainer() +{ + b_deleted = true; +} + + +void ViewContainer::setFocused() +{ + if (b_focused) + return; + b_focused = true; + + View *view = activeView(); + if (view) + view->setFocused(); +} + + +void ViewContainer::setUnfocused() +{ + if (!b_focused) + return; + b_focused = false; + + View *view = activeView(); + if (view) + view->setUnfocused(); +} + + +void ViewContainer::setActiveViewArea( uint id ) +{ + if ( m_activeViewArea == int(id) ) + return; + + View *oldView = view(m_activeViewArea); + if (oldView) + oldView->setUnfocused(); + + m_activeViewArea = id; + + ViewArea *va = viewArea(id); + if ( va && b_focused ) + va->setFocus(); + + View *newView = view(id); + if ( newView && b_focused ); + { + if (newView) + { + setCaption( newView->caption() ); + newView->setFocused(); + } + } +} + + +View *ViewContainer::view( uint id ) const +{ + ViewArea *va = viewArea(id); + if (!va) + return 0l; + + // We do not want a recursive search as ViewAreas also hold other ViewAreas + QObjectList *l = va->queryList( "View", 0, false, false ); + View *view = 0l; + if ( !l->isEmpty() ) + view = dynamic_cast<View*>(l->first()); + delete l; + + return view; +} + + +ViewArea *ViewContainer::viewArea( uint id ) const +{ + if ( !m_viewAreaMap.contains(id) ) + return 0l; + + return m_viewAreaMap[id]; +} + + +bool ViewContainer::closeViewContainer() +{ + bool didClose = true; + while ( didClose && !m_viewAreaMap.isEmpty() ) + { + didClose = closeViewArea( m_viewAreaMap.begin().key() ); + } + + return m_viewAreaMap.isEmpty(); +} + + +bool ViewContainer::closeViewArea( uint id ) +{ + ViewArea *va = viewArea(id); + if ( !va ) + return true; + + bool doClose = false; + View *v = view(id); + if ( v && v->document() ) + { + doClose = v->document()->numberOfViews() > 1; + if (!doClose) + doClose = v->document()->fileClose(); + } + else + doClose = true; + + if (!doClose) + return false; + + m_viewAreaMap.remove(id); + va->deleteLater(); + + if ( m_activeViewArea == int(id) ) + { + m_activeViewArea = -1; + findActiveViewArea(); + } + + return true; +} + + +int ViewContainer::createViewArea( int relativeViewArea, ViewArea::Position position ) +{ + if ( relativeViewArea == -1 ) + relativeViewArea = activeViewArea(); + + ViewArea *relative = viewArea(relativeViewArea); + if (!relative) + { + kdError() << k_funcinfo << "Could not find relative view area" << endl; + return -1; + } + + uint id = uniqueNewId(); +// setActiveViewArea(id); + + ViewArea *viewArea = relative->createViewArea( position, id ); +// ViewArea *viewArea = new ViewArea( m_splitter, id, (const char*)("viewarea_"+QString::number(id)) ); + viewArea->show(); // remove? + + return id; +} + + +void ViewContainer::setViewAreaId( ViewArea *viewArea, uint id ) +{ + m_viewAreaMap[id] = viewArea; + m_usedIDs.append(id); +} + + +void ViewContainer::setViewAreaRemoved( uint id ) +{ + if (b_deleted) + return; + + ViewAreaMap::iterator it = m_viewAreaMap.find(id); + if ( it == m_viewAreaMap.end() ) + return; + + m_viewAreaMap.erase(it); + + if ( m_activeViewArea == int(id) ) + findActiveViewArea(); +} + + +void ViewContainer::findActiveViewArea() +{ + if ( m_viewAreaMap.isEmpty() ) + return; + + setActiveViewArea( (--m_viewAreaMap.end()).key() ); +} + + +void ViewContainer::baseViewAreaDestroyed( QObject *obj ) +{ + if (!obj) + return; + + if (!b_deleted) + { + b_deleted = true; + close(); + deleteLater(); + } +} + + +ViewContainer * ViewContainer::duplicateViewContainer() +{ + ViewContainer *viewContainer = new ViewContainer( caption(), p_ktechlab ); + copyViewContainerIntoExisting(viewContainer); + + p_ktechlab->addWindow(viewContainer); + + return viewContainer; +} + + +void ViewContainer::copyViewContainerIntoExisting( ViewContainer *viewContainer ) +{ + if (!viewContainer) + return; + + const ViewAreaMap::iterator end = m_viewAreaMap.end(); + for ( ViewAreaMap::iterator it = m_viewAreaMap.begin(); it != end; ++it ) + { + View *oldView = view(it.key()); + if (!oldView) + continue; + + // See if there is an empty view container to be inserted into with id 0... + uint newId; + if ( viewContainer->view(0) ) + newId = viewContainer->createViewArea( 0, ViewArea::Right ); + else + newId = 0; + + oldView->document()->createView( viewContainer, newId ); + } +} + + +bool ViewContainer::canSaveUsefulStateInfo() const +{ + return m_baseViewArea && m_baseViewArea->canSaveUsefulStateInfo(); +} + + +void ViewContainer::saveState( KConfig *config ) +{ + if (!m_baseViewArea) + return; + + config->writeEntry( "BaseViewArea", m_baseViewArea->id() ); + m_baseViewArea->saveState(config); +} + + +void ViewContainer::restoreState( KConfig *config, const QString &groupName ) +{ + config->setGroup(groupName); + int baseAreaId = config->readNumEntry("BaseViewArea"); + m_baseViewArea->restoreState( config, baseAreaId, groupName ); +} + + +int ViewContainer::uniqueParentId() +{ + int lowest = -1; + const IntList::iterator end = m_usedIDs.end(); + for ( IntList::iterator it = m_usedIDs.begin(); it != end; ++it ) + { + if ( *it < lowest ) + lowest = *it; + } + int newId = lowest-1; + m_usedIDs.append(newId); + return newId; +} + + +int ViewContainer::uniqueNewId() +{ + int highest = 0; + const IntList::iterator end = m_usedIDs.end(); + for ( IntList::iterator it = m_usedIDs.begin(); it != end; ++it ) + { + if ( *it > highest ) + highest = *it; + } + int newId = highest+1; + m_usedIDs.append(newId); + return newId; +} + + +void ViewContainer::setIdUsed( int id ) +{ + m_usedIDs.append(id); +} + + +void ViewContainer::updateCaption() +{ + QString caption; + + if ( !activeView() || !activeView()->document() ) + caption = i18n("(empty)"); + + else + { + Document * doc = activeView()->document(); + caption = doc->url().isEmpty() ? doc->caption() : doc->url().fileName(); + if ( viewCount() > 1 ) + caption += " ..."; + } + + setCaption(caption); + p_ktechlab->tabWidget()->setTabLabel( this, caption ); +} + + +void ViewContainer::setKTechlabDeleted() +{ + p_ktechlab = 0l; + ViewAreaMap::iterator end = m_viewAreaMap.end(); + for ( ViewAreaMap::iterator it = m_viewAreaMap.begin(); it != end; ++it ) + { + if ( *it ) + (*it)->setKTechlabDeleted(); + } +} + + + + + +ViewArea::ViewArea( QWidget *parent, ViewContainer *viewContainer, int id, const char *name ) + : QSplitter( parent, name ) +{ + p_viewContainer = viewContainer; + m_id = id; + p_view = 0l; + p_viewArea1 = 0l; + p_viewArea2 = 0l; + if (id >= 0) + p_viewContainer->setViewAreaId( this, uint(id) ); + p_viewContainer->setIdUsed(id); + setOpaqueResize(KGlobalSettings::opaqueResize()); +} + + +ViewArea::~ViewArea() +{ + if ( m_id >= 0 ) + p_viewContainer->setViewAreaRemoved( uint(m_id) ); +} + + +ViewArea *ViewArea::createViewArea( Position position, uint id ) +{ + if (p_viewArea1 || p_viewArea2) + { + kdError() << k_funcinfo << "Attempting to create ViewArea when already containing ViewAreas!" << endl; + return 0l; + } + if (!p_view) + { + kdError() << k_funcinfo << "We don't have a view yet, so creating a new ViewArea is redundant" << endl; + return 0l; + } + + setOrientation( ( position == Right ) ? Qt::Horizontal : Qt::Vertical ); + + p_viewArea1 = new ViewArea( this, p_viewContainer, m_id, (const char*)("viewarea_"+QString::number(m_id)) ); + p_viewArea2 = new ViewArea( this, p_viewContainer, id, (const char*)("viewarea_"+QString::number(id)) ); + + connect( p_viewArea1, SIGNAL(destroyed(QObject* )), this, SLOT(viewAreaDestroyed(QObject* )) ); + connect( p_viewArea2, SIGNAL(destroyed(QObject* )), this, SLOT(viewAreaDestroyed(QObject* )) ); + + p_view->reparent( p_viewArea1, QPoint(), true ); + p_viewArea1->setView(p_view); + p_view = 0l; + m_id = p_viewContainer->uniqueParentId(); + + QValueList<int> splitPos; + int pos = ((orientation() == Qt::Horizontal) ? width()/2 : height()/2); + splitPos << pos << pos; + setSizes(splitPos); + + p_viewArea1->show(); + p_viewArea2->show(); + return p_viewArea2; +} + + +void ViewArea::viewAreaDestroyed( QObject *obj ) +{ + ViewArea *viewArea = static_cast<ViewArea*>(obj); + + if ( viewArea == p_viewArea1 ) + p_viewArea1 = 0l; + + if ( viewArea == p_viewArea2 ) + p_viewArea2 = 0l; + + if ( !p_viewArea1 && !p_viewArea2 ) + deleteLater(); +} + + +void ViewArea::setView( View *view ) +{ + if (!view) + return; + if (p_view) + { + kdError() << k_funcinfo << "Attempting to set already contained view!" << endl; + return; + } + p_view = view; + connect( view, SIGNAL(destroyed()), this, SLOT(viewDestroyed()) ); +} + + +void ViewArea::viewDestroyed() +{ + if ( !p_view && !p_viewArea1 && !p_viewArea2 ) + deleteLater(); +} + + +void ViewArea::setKTechlabDeleted() +{ + if ( p_view ) + p_view->setKTechlabDeleted(); +} + + +bool ViewArea::canSaveUsefulStateInfo() const +{ + if ( p_viewArea1 && p_viewArea1->canSaveUsefulStateInfo() ) + return true; + + if ( p_viewArea2 && p_viewArea2->canSaveUsefulStateInfo() ) + return true; + + if ( p_view && p_view->document() && !p_view->document()->url().isEmpty() ) + return true; + + return false; +} + + +void ViewArea::saveState( KConfig *config ) +{ + bool va1Ok = p_viewArea1 && p_viewArea1->canSaveUsefulStateInfo(); + bool va2Ok = p_viewArea2 && p_viewArea2->canSaveUsefulStateInfo(); + + if ( va1Ok || va2Ok ) + { + config->writeEntry( orientationKey(m_id), (orientation() == Qt::Horizontal) ? "LeftRight" : "TopBottom" ); + + QValueList<int> contains; + if (va1Ok) + contains << p_viewArea1->id(); + if (va2Ok) + contains << p_viewArea2->id(); + + config->writeEntry( containsKey(m_id), contains ); + if (va1Ok) + p_viewArea1->saveState(config); + if (va2Ok) + p_viewArea2->saveState(config); + } + else if ( p_view && !p_view->document()->url().isEmpty() ) + { + config->writePathEntry( fileKey(m_id), p_view->document()->url().prettyURL() ); + } +} + + +void ViewArea::restoreState( KConfig *config, int id, const QString &groupName ) +{ + if (!config) + return; + + if ( id != m_id ) + { + if ( m_id >= 0 ) + p_viewContainer->setViewAreaRemoved( uint(m_id) ); + + m_id = id; + + if ( m_id >= 0 ) + p_viewContainer->setViewAreaId( this, uint(m_id) ); + + p_viewContainer->setIdUsed(id); + } + + config->setGroup(groupName); + if ( config->hasKey( orientationKey(id) ) ) + { + QString orientation = config->readEntry( orientationKey(m_id) ); + setOrientation( (orientation == "LeftRight") ? Qt::Horizontal : Qt::Vertical ); + } + + config->setGroup(groupName); + if ( config->hasKey( containsKey(m_id) ) ) + { + typedef QValueList<int> IntList; + IntList contains = config->readIntListEntry( containsKey(m_id) ); + + if ( contains.isEmpty() || contains.size() > 2 ) + kdError() << k_funcinfo << "Contained list has wrong size of " << contains.size() << endl; + + else + { + if ( contains.size() >= 1 ) + { + int viewArea1Id = contains[0]; + p_viewArea1 = new ViewArea( this, p_viewContainer, viewArea1Id, (const char*)("viewarea_"+QString::number(viewArea1Id)) ); + connect( p_viewArea1, SIGNAL(destroyed(QObject* )), this, SLOT(viewAreaDestroyed(QObject* )) ); + p_viewArea1->restoreState( config, viewArea1Id, groupName ); + p_viewArea1->show(); + } + + if ( contains.size() >= 2 ) + { + int viewArea2Id = contains[1]; + p_viewArea2 = new ViewArea( this, p_viewContainer, viewArea2Id, (const char*)("viewarea_"+QString::number(viewArea2Id)) ); + connect( p_viewArea2, SIGNAL(destroyed(QObject* )), this, SLOT(viewAreaDestroyed(QObject* )) ); + p_viewArea2->restoreState( config, viewArea2Id, groupName ); + p_viewArea2->show(); + } + } + } + + config->setGroup(groupName); + if ( config->hasKey( fileKey(m_id) ) ) + { + bool openedOk = DocManager::self()->openURL( config->readPathEntry( fileKey(m_id) ), this ); + if (!openedOk) + deleteLater(); + } +} + +QString ViewArea::fileKey( int id ) +{ + return QString("ViewArea ") + QString::number(id) + QString(" file"); +} +QString ViewArea::containsKey( int id ) +{ + return QString("ViewArea ") + QString::number(id) + QString(" contains"); +} +QString ViewArea::orientationKey( int id ) +{ + return QString("ViewArea ") + QString::number(id) + QString(" orientation"); +} + + + + +ViewContainerDrag::ViewContainerDrag( ViewContainer *viewContainer ) + : QStoredDrag( "dontcare", viewContainer) +{ + p_viewContainer = viewContainer; +} + +#include "viewcontainer.moc" diff --git a/src/viewcontainer.h b/src/viewcontainer.h new file mode 100644 index 0000000..c798530 --- /dev/null +++ b/src/viewcontainer.h @@ -0,0 +1,249 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef VIEWCONTAINER_H +#define VIEWCONTAINER_H + +#include <qdragobject.h> +#include <qmap.h> +#include <qsplitter.h> +#include <qvaluelist.h> + +class KTechlab; +class View; +class ViewArea; +class ViewContainer; + +class KConfig; +class QHBoxLayout; +class QLayout; +class QSplitter; + +typedef QMap< uint, ViewArea* > ViewAreaMap; +typedef QValueList<int> IntList; + +class ViewContainerDrag : public QStoredDrag +{ +public: + ViewContainerDrag( ViewContainer *viewContainer ); + + ViewContainer *viewContainer() const { return p_viewContainer; } + +protected: + ViewContainer *p_viewContainer; +}; + + +/** +Contains either exactly one View, or two ViewAreas, seperated by a QSplitter. +If it contains one view, then the value returned in id() is that of the view. +If it contains two ViewAreas, then the value returned by id() is -1. +@author David Saxton +*/ +class ViewArea : public QSplitter +{ +Q_OBJECT +public: + enum Position + { + Right, + Bottom + }; + + ViewArea( QWidget *parent, ViewContainer *viewContainer, int id, const char *name = 0 ); + ~ViewArea(); + + ViewContainer *viewContainer() const { return p_viewContainer; } + int id() const { return m_id; } + /** + * Splits this ViewArea into two, and returns a pointer to the new ViewArea + */ + ViewArea *createViewArea( Position position, uint id ); + /** + * Adds the given View to the main part of the layout + */ + void setView( View *view ); + /** + * Saves the state of this ViewArea and any contained ViewAreas + */ + void saveState( KConfig *config ); + /** + * Restores the state of this ViewArea and any contained ViewAreas + * @param groupName e.g. "ViewContainer 1" + */ + void restoreState( KConfig *config, int id, const QString &groupName ); + /** + * Returns true if this ViewArea can save useful information as to its state + * (i.e. it's children can save useful information about their state, or has + * a view with a url in it) + */ + bool canSaveUsefulStateInfo() const; + /** + * Calls View::setKTechlabDeleted. + */ + void setKTechlabDeleted(); + + static QString fileKey( int id ); + static QString containsKey( int id ); + static QString orientationKey( int id ); + +protected slots: + void viewAreaDestroyed( QObject *obj ); + void viewDestroyed(); + +protected: + int m_id; + QGuardedPtr<View> p_view; + ViewArea *p_viewArea1; + ViewArea *p_viewArea2; + ViewContainer *p_viewContainer; +}; + +/** +@author David Saxton +*/ +class ViewContainer : public QWidget +{ +Q_OBJECT +public: + /** + * Constructs a new ViewContainer, along with a default ViewArea ready for + * parenting a View, with an id of 0. parent is only used if ktechlab is + * null; otherwise the parent widget is ktechlab's tabWidget() + */ + ViewContainer( const QString & caption, KTechlab * ktechlab, QWidget * parent = 0 ); + ~ViewContainer(); + + /** + * Returns the view in the ViewArea with the given id + */ + View *view( uint id ) const; + /** + * Returns the ViewArea with the given id + */ + ViewArea *viewArea( uint id ) const; + /** + * The active view area is the one that is focused. + */ + void setActiveViewArea( uint id ); + /** + * Returns the id of the active ViewArea + */ + uint activeViewArea() const { return m_activeViewArea; } + /** + * Returns a pointer to the view of the active view area + */ + View *activeView() const { return view( activeViewArea() ); } + /** + * Attempts to close the given viewarea, returning true if successful (i.e + * if the user did not object to the close request ) + */ + bool closeViewArea( uint id ); + /** + * Creates a view area (parent QWidget, splitter et al) ready for inclusion + * of a view. + * @param relativeViewArea the viewarea to position the new viewarea next to, if -1 then is taken to be the active view area + * @param position Top, Right, Bottom or Left of given relativeViewArea + * @returns id of the the view area, or -1 if unsucessful + */ + int createViewArea( int relativeViewArea, ViewArea::Position position = ViewArea::Right ); + /** + * Attempts to close each view area, returning false if any fail to be + * closed + */ + bool closeViewContainer(); + /** + * @returns number of views in this view container + */ + uint viewCount() const { return m_viewAreaMap.size(); } + /** + * Called when the view container is focused (e.g. user clicks on the tab + * for this view container) + */ + void setFocused(); + /** + * Called when the view container is unfocused (e.g. user clicks on another + * tab). + */ + void setUnfocused(); + /** + * Create a new view container, and make a new instance of each view in + * that view container. + */ + ViewContainer* duplicateViewContainer(); + /** + * Copies each view in this ViewContainer into the given ViewContainer + */ + void copyViewContainerIntoExisting( ViewContainer *viewContainer ); + /** + * Sets the pointer to the view area with the given id + */ + void setViewAreaId( ViewArea *viewArea, uint id ); + /** + * Removes a ViewArea from internal lists + */ + void setViewAreaRemoved( uint id ); + /** + * Sets the id to be "used" + */ + void setIdUsed( int id ); + /** + * Writes the state of the View Container (layout of views and view URLs) + * to the given KConfig. Doesn't change the group - so preset it if + * needed! + */ + void saveState( KConfig *config ); + /** + * Reads in the saved config state (as written by saveState), and restores + * the ViewContainer with all appropriate views open + * @param groupName e.g. "ViewContainer 1" + */ + void restoreState( KConfig *config, const QString &groupName ); + /** + * Returns a unique id (negative) for a ViewArea that is now a Parent of other ViewAreas + */ + int uniqueParentId(); + /** + * Returns a unique id (positive) for a new ViewArea + */ + int uniqueNewId(); + /** + * Returns true if this ViewArea can save useful information as to its state + * (i.e. it's children can save useful information about their state, or has + * a view with a url in it) + */ + bool canSaveUsefulStateInfo() const; + /** + * Calls setKTechlabDeleted for each ViewArea. + */ + void setKTechlabDeleted(); + +public slots: + /** + * Sets the tab caption et al from the contents of this ViewContainer + */ + void updateCaption(); + +protected slots: + void baseViewAreaDestroyed( QObject *obj ); + +protected: + void restoreViewArea( KConfig *config, int id, ViewArea *viewArea ); + void findActiveViewArea(); + int m_activeViewArea; + ViewArea *m_baseViewArea; + ViewAreaMap m_viewAreaMap; + IntList m_usedIDs; + KTechlab *p_ktechlab; + bool b_focused; + bool b_deleted; +}; + +#endif diff --git a/src/viewiface.cpp b/src/viewiface.cpp new file mode 100644 index 0000000..592b0fe --- /dev/null +++ b/src/viewiface.cpp @@ -0,0 +1,142 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#include "circuitview.h" +#include "document.h" +#include "flowcodeview.h" +#include "mechanicsview.h" +#include "textview.h" +#include "viewiface.h" + + +//BEGIN class ViewIface +ViewIface::ViewIface( View * view ) + : DCOPObject("View") +{ + m_pView = view; +} + +ViewIface::~ ViewIface( ) +{ +} + +DCOPRef ViewIface::document( ) +{ + return DCOPRef( m_pView->document()->dcopObject() ); +} + +bool ViewIface::isFocused( ) +{ + return m_pView->isFocused(); +} + +bool ViewIface::close( ) +{ + return m_pView->closeView(); +} + +void ViewIface::zoomIn( ) +{ + m_pView->viewZoomIn(); +} + +void ViewIface::zoomOut( ) +{ + m_pView->viewZoomOut(); +} + +bool ViewIface::canZoomIn( ) +{ + return m_pView->canZoomIn(); +} + +bool ViewIface::canZoomOut( ) +{ + return m_pView->canZoomOut(); +} + +void ViewIface::actualSize( ) +{ + m_pView->actualSize(); +} +//END class ViewIface + + + +//BEGIN class TextViewIface +TextViewIface::TextViewIface( TextView * view ) + : ViewIface(view) +{ + m_pTextView = view; +} + +void TextViewIface::toggleBreakpoint( ) +{ + m_pTextView->toggleBreakpoint(); +} + +bool TextViewIface::gotoLine( const int line ) +{ + return m_pTextView->gotoLine(line); +} +//END class TextViewIface + + + +//BEGIN class ItemViewIface +ItemViewIface::ItemViewIface( ItemView * view ) + : ViewIface(view) +{ + m_pItemView = view; +} + +double ItemViewIface::zoomLevel( ) +{ + return m_pItemView->zoomLevel(); +} +//END class ItemViewIface + + + +//BEGIN class MechanicsViewIface +MechanicsViewIface::MechanicsViewIface( MechanicsView * view ) + : ItemViewIface(view) +{ + m_pMechanicsView = view; +} +//END class ICNViewIface + + + +//BEGIN class ICNViewIface +ICNViewIface::ICNViewIface( ICNView * view ) + : ItemViewIface(view) +{ + m_pICNView = view; +} +//END class ICNViewIface + + + +//BEGIN class CircuitViewIface +CircuitViewIface::CircuitViewIface( CircuitView * view ) + : ICNViewIface(view) +{ + m_pCircuitView = view; +} +//END class CircuitViewIface + + +//BEGIN class FlowCodeViewIface +FlowCodeViewIface::FlowCodeViewIface( FlowCodeView * view ) + : ICNViewIface(view) +{ +} +//END class FlowCodeViewIface diff --git a/src/viewiface.h b/src/viewiface.h new file mode 100644 index 0000000..71290b5 --- /dev/null +++ b/src/viewiface.h @@ -0,0 +1,123 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef VIEWIFACE_H +#define VIEWIFACE_H + +#include <dcopobject.h> +#include <dcopref.h> + +class CircuitView; +class FlowCodeView; +class ICNView; +class ItemView; +class MechanicsView; +class TextView; +class View; + +/** +@author David Saxton +*/ +class ViewIface : public DCOPObject +{ + K_DCOP + + public: + ViewIface( View * view ); + virtual ~ViewIface(); + + k_dcop: + DCOPRef document(); + bool isFocused(); + bool close(); + void zoomIn(); + void zoomOut(); + bool canZoomIn(); + bool canZoomOut(); + void actualSize(); + + protected: + View * m_pView; +}; + +class TextViewIface : public ViewIface +{ + K_DCOP + + public: + TextViewIface( TextView * view ); + + k_dcop: + void toggleBreakpoint(); + bool gotoLine( const int line ); + + protected: + TextView * m_pTextView; +}; + +class ItemViewIface : public ViewIface +{ + K_DCOP + + public: + ItemViewIface( ItemView * view ); + + k_dcop: + double zoomLevel(); + + protected: + ItemView * m_pItemView; +}; + +class MechanicsViewIface : public ItemViewIface +{ + K_DCOP + + public: + MechanicsViewIface( MechanicsView * view ); + + protected: + MechanicsView * m_pMechanicsView; +}; + +class ICNViewIface : public ItemViewIface +{ + K_DCOP + + public: + ICNViewIface( ICNView * view ); + + protected: + ICNView * m_pICNView; +}; + +class CircuitViewIface : public ICNViewIface +{ + K_DCOP + + public: + CircuitViewIface( CircuitView * view ); + + protected: + CircuitView * m_pCircuitView; +}; + +class FlowCodeViewIface : public ICNViewIface +{ + K_DCOP + + public: + FlowCodeViewIface( FlowCodeView * view ); + + protected: + FlowCodeView * m_pFlowCodeView; +}; + +#endif diff --git a/src/x-circuit.desktop b/src/x-circuit.desktop new file mode 100644 index 0000000..75b97d4 --- /dev/null +++ b/src/x-circuit.desktop @@ -0,0 +1,7 @@ +[Desktop Entry] +Encoding=UTF-8 +Type=MimeType +MimeType=application/x-circuit +Icon=ktechlab_circuit +Patterns=*.circuit +Comment=KTechlab Circuit
\ No newline at end of file diff --git a/src/x-flowcode.desktop b/src/x-flowcode.desktop new file mode 100644 index 0000000..6283e40 --- /dev/null +++ b/src/x-flowcode.desktop @@ -0,0 +1,7 @@ +[Desktop Entry] +Encoding=UTF-8 +Type=MimeType +MimeType=application/x-flowcode +Icon=ktechlab_flowcode +Patterns=*.flowcode +Comment=KTechlab FlowCode
\ No newline at end of file diff --git a/src/x-ktechlab.desktop b/src/x-ktechlab.desktop new file mode 100644 index 0000000..82f6fb2 --- /dev/null +++ b/src/x-ktechlab.desktop @@ -0,0 +1,7 @@ +[Desktop Entry] +Encoding=UTF-8 +Type=MimeType +MimeType=application/x-ktechlab +Icon=ktechlab +Patterns=*.ktechlab +Comment=KTechlab Project
\ No newline at end of file diff --git a/src/x-microbe.desktop b/src/x-microbe.desktop new file mode 100644 index 0000000..be36ee4 --- /dev/null +++ b/src/x-microbe.desktop @@ -0,0 +1,7 @@ +[Desktop Entry] +Encoding=UTF-8 +Type=MimeType +MimeType=application/x-microbe +Icon=ktechlab_microbe +Patterns=*.microbe;*.basic +Comment=KTechlab Microbe |