You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
318 lines
9.7 KiB
318 lines
9.7 KiB
/***************************************************************************
|
|
* 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 <tdelocale.h>
|
|
#include <tqpainter.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(
|
|
TQString("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
|
|
TQPointArray 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", TQRect( -_pinOuterRadius/3-buttonRadius, _pinOuterRadius-3*buttonRadius, 2*buttonRadius, 2*buttonRadius ), "<", false );
|
|
addButton( "go_right", TQRect(_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 TQString 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( TQPainter &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 TQString & 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];
|
|
TQString pinName = TQString("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,TQString("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
|