TelescopeControl.cpp   TelescopeControl.cpp 
skipping to change at line 28 skipping to change at line 28
* This program is distributed in the hope that it will be useful, * This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software * along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA. * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA.
*/ */
#include "StelUtils.hpp"
#include "TelescopeControl.hpp" #include "TelescopeControl.hpp"
#include "TelescopeClient.hpp" #include "TelescopeClient.hpp"
#include "TelescopeDialog.hpp" #include "TelescopeDialog.hpp"
#include "SlewDialog.hpp" #include "SlewDialog.hpp"
#include "LogFile.hpp" #include "LogFile.hpp"
#include "StelApp.hpp" #include "StelApp.hpp"
#include "StelCore.hpp" #include "StelCore.hpp"
#include "StelFileMgr.hpp" #include "StelFileMgr.hpp"
#include "StelGui.hpp" #include "StelGui.hpp"
#include "StelGuiItems.hpp" #include "StelGuiItems.hpp"
#include "StelIniParser.hpp" #include "StelIniParser.hpp"
#include "StelLocaleMgr.hpp" #include "StelLocaleMgr.hpp"
#include "StelModuleMgr.hpp" #include "StelModuleMgr.hpp"
#include "StelMovementMgr.hpp" #include "StelMovementMgr.hpp"
#include "StelObject.hpp" #include "StelObject.hpp"
#include "StelObjectMgr.hpp" #include "StelObjectMgr.hpp"
#include "StelPainter.hpp"
#include "StelProjector.hpp" #include "StelProjector.hpp"
#include "StelShortcutMgr.hpp"
#include "StelStyle.hpp" #include "StelStyle.hpp"
#include "renderer/StelGeometryBuilder.hpp" #include "StelTextureMgr.hpp"
#include "renderer/StelRenderer.hpp" #include "StelActionMgr.hpp"
#include "renderer/StelTextureNew.hpp"
#include <QAction>
#include <QDateTime> #include <QDateTime>
#include <QDir> #include <QDir>
#include <QFileInfo> #include <QFileInfo>
#include <QMapIterator> #include <QMapIterator>
#include <QSettings> #include <QSettings>
#include <QString> #include <QString>
#include <QStringList> #include <QStringList>
#include <QDir> #include <QDir>
#include <QSignalMapper>
#include <QDebug> #include <QDebug>
/////////////////////////////////////////////////////////////////////////// ///// /////////////////////////////////////////////////////////////////////////// /////
// //
StelModule* TelescopeControlStelPluginInterface::getStelModule() const StelModule* TelescopeControlStelPluginInterface::getStelModule() const
{ {
return new TelescopeControl(); return new TelescopeControl();
} }
skipping to change at line 82 skipping to change at line 82
{ {
// Allow to load the resources when used as a static plugin // Allow to load the resources when used as a static plugin
Q_INIT_RESOURCE(TelescopeControl); Q_INIT_RESOURCE(TelescopeControl);
StelPluginInfo info; StelPluginInfo info;
info.id = "TelescopeControl"; info.id = "TelescopeControl";
info.displayedName = N_("Telescope Control"); info.displayedName = N_("Telescope Control");
info.authors = "Bogdan Marinov, Johannes Gajdosik"; info.authors = "Bogdan Marinov, Johannes Gajdosik";
info.contact = "http://stellarium.org"; info.contact = "http://stellarium.org";
info.description = N_("This plug-in allows Stellarium to send \"slew \" commands to a telescope on a computerized mount (a \"GoTo telescope\")." ); info.description = N_("This plug-in allows Stellarium to send \"slew \" commands to a telescope on a computerized mount (a \"GoTo telescope\")." );
info.version = TELESCOPE_CONTROL_VERSION;
return info; return info;
} }
Q_EXPORT_PLUGIN2(TelescopeControl, TelescopeControlStelPluginInterface)
/////////////////////////////////////////////////////////////////////////// ///// /////////////////////////////////////////////////////////////////////////// /////
// Constructor and destructor // Constructor and destructor
TelescopeControl::TelescopeControl() TelescopeControl::TelescopeControl()
: pixmapHover(NULL) : toolbarButton(NULL)
, pixmapOnIcon(NULL) , useTelescopeServerLogs(false)
, pixmapOffIcon(NULL) , useServerExecutables(false)
, reticleTexture(NULL)
, selectionTexture(NULL)
, telescopeDialog(NULL) , telescopeDialog(NULL)
, slewDialog(NULL) , slewDialog(NULL)
, actionGroupId("PluginTelescopeControl") , actionGroupId("PluginTelescopeControl")
, moveToSelectedActionId("actionMove_Telescope_To_Selection_%1") , moveToSelectedActionId("actionMove_Telescope_To_Selection_%1")
, moveToCenterActionId("actionSlew_Telescope_To_Direction_%1") , moveToCenterActionId("actionSlew_Telescope_To_Direction_%1")
{ {
setObjectName("TelescopeControl"); setObjectName("TelescopeControl");
connectionTypeNames.insert(ConnectionVirtual, "virtual"); connectionTypeNames.insert(ConnectionVirtual, "virtual");
connectionTypeNames.insert(ConnectionInternal, "internal"); connectionTypeNames.insert(ConnectionInternal, "internal");
connectionTypeNames.insert(ConnectionLocal, "local"); connectionTypeNames.insert(ConnectionLocal, "local");
connectionTypeNames.insert(ConnectionRemote, "remote"); connectionTypeNames.insert(ConnectionRemote, "remote");
} }
TelescopeControl::~TelescopeControl() TelescopeControl::~TelescopeControl()
{ {
} }
/////////////////////////////////////////////////////////////////////////// ///// /////////////////////////////////////////////////////////////////////////// /////
// Methods inherited from the StelModule class // Methods inherited from the StelModule class
// init(), update(), draw(), getCallOrder() // init(), update(), draw(), getCallOrder()
void TelescopeControl::init() void TelescopeControl::init()
{ {
//TODO: I think I've overdone the try/catch... //TODO: I think I've overdone the try/catch...
try try
{ {
skipping to change at line 152 skipping to change at line 148
/*If the alsoDelete parameter is set to true, Stellarium cra shes with a /*If the alsoDelete parameter is set to true, Stellarium cra shes with a
segmentation fault when an object is selected. TODO: Find out why. segmentation fault when an object is selected. TODO: Find out why.
unloadModule() didn't work prior to revision 5058: t he module unloaded unloadModule() didn't work prior to revision 5058: t he module unloaded
normally, but Stellarium crashed later with a segmen tation fault, normally, but Stellarium crashed later with a segmen tation fault,
because LandscapeMgr::getCallOrder() depended on the module's because LandscapeMgr::getCallOrder() depended on the module's
existence to return a value.*/ existence to return a value.*/
//Load and start all telescope clients //Load and start all telescope clients
loadTelescopes(); loadTelescopes();
StelGui* gui = dynamic_cast<StelGui*>(StelApp::getInstance() //Load OpenGL textures
.getGui()); reticleTexture = StelApp::getInstance().getTextureManager().
StelShortcutMgr* shMgr = StelApp::getInstance().getStelShort createTexture(":/telescopeControl/telescope_reticle.png");
cutManager(); selectionTexture = StelApp::getInstance().getTextureManager(
).createTexture(StelFileMgr::getInstallationDir()+"/textures/pointeur2.png"
);
//Create telescope key bindings //Create telescope key bindings
/* QAction-s with these key bindings existed in Stellarium p rior to /* StelAction-s with these key bindings existed in Stellariu m prior to
revision 6311. Any future backports should account f or that. */ revision 6311. Any future backports should account f or that. */
for (int i = MIN_SLOT_NUMBER; i <= MAX_SLOT_NUMBER; i++) for (int i = MIN_SLOT_NUMBER; i <= MAX_SLOT_NUMBER; i++)
{ {
// "Slew to object" commands // "Slew to object" commands
QString name = moveToSelectedActionId.arg(i); QString name = moveToSelectedActionId.arg(i);
QString shortcut = QString("Ctrl+%1").arg(i); QString shortcut = QString("Ctrl+%1").arg(i);
QAction* action = shMgr->addGuiAction(name, true, "" QString text;
, text = q_("Move telescope #%1 to selected object").a
shortcut, "", rg(i);
actionGroupId, addAction(name, N_("Telescope Control"), text, "slew
false); TelescopeToSelectedObject()", shortcut);
connect(action, SIGNAL(triggered()),
this, SLOT(slewTelescopeToSelectedObject()))
;
// "Slew to the center of the screen" commands // "Slew to the center of the screen" commands
name = moveToCenterActionId.arg(i); name = moveToCenterActionId.arg(i);
shortcut = QString("Alt+%1").arg(i); shortcut = QString("Alt+%1").arg(i);
action = shMgr->addGuiAction(name, true, "", text = q_("Move telescope #%1 to the point currently
shortcut, "", actionGro in the center of the screen").arg(i);
upId, addAction(name, N_("Telescope Control"), text, "slew
false, false); TelescopeToViewDirection()", shortcut);
connect(action, SIGNAL(triggered()), this,
SLOT(slewTelescopeToViewDirection()));
} }
// Also updates descriptions if the actions have been loaded
from file
translateActionDescriptions();
connect(&StelApp::getInstance(), SIGNAL(languageChanged()), connect(&StelApp::getInstance(), SIGNAL(languageChanged()),
this, SLOT(translateActionDescriptions())); this, SLOT(translateActionDescriptions()));
//Create and initialize dialog windows //Create and initialize dialog windows
telescopeDialog = new TelescopeDialog(); telescopeDialog = new TelescopeDialog();
slewDialog = new SlewDialog(); slewDialog = new SlewDialog();
connect(shMgr->getGuiAction("actionShow_Slew_Window"), SIGNA addAction("actionShow_Slew_Window", N_("Telescope Control"),
L(toggled(bool)), slewDialog, SLOT(setVisible(bool))); N_("Move a telescope to a given set of coordinates"), slewDialog, "visible
connect(slewDialog, SIGNAL(visibleChanged(bool)), shMgr->get ", "Ctrl+0");
GuiAction("actionShow_Slew_Window"), SLOT(setChecked(bool)));
//Create toolbar button //Create toolbar button
pixmapHover = new QPixmap(":/graphicGui/glow32x32.png"); StelGui* gui = dynamic_cast<StelGui*>(StelApp::getInstance()
pixmapOnIcon = new QPixmap(":/telescopeControl/button_Slew_ .getGui());
Dialog_on.png"); if (gui!=NULL)
pixmapOffIcon = new QPixmap(":/telescopeControl/button_Slew_ {
Dialog_off.png"); toolbarButton = new StelButton(NULL,
toolbarButton = new StelButton(NULL, *pixmapOnIcon, *pixmapOffIcon, QPixmap(":/telescopeC
*pixmapHover, gui->getGuiAction("actionShow_Slew_Window")); ontrol/button_Slew_Dialog_on.png"),
gui->getButtonBar()->addButton(toolbarButton, "065-pluginsGr QPixmap(":/telescopeC
oup"); ontrol/button_Slew_Dialog_off.png"),
QPixmap(":/graphicGui
/glow32x32.png"),
"actionShow_Slew_Wind
ow");
gui->getButtonBar()->addButton(toolbarButton, "065-p
luginsGroup");
}
} }
catch (std::runtime_error &e) catch (std::runtime_error &e)
{ {
qWarning() << "TelescopeControl::init() error: " << e.what() ; qWarning() << "TelescopeControl::init() error: " << e.what() ;
return; return;
} }
GETSTELMODULE(StelObjectMgr)->registerStelObjectMgr(this); GETSTELMODULE(StelObjectMgr)->registerStelObjectMgr(this);
//Initialize style, as it is not called at startup: //Initialize style, as it is not called at startup:
//(necessary to initialize the reticle/label/circle colors) //(necessary to initialize the reticle/label/circle colors)
setStelStyle(StelApp::getInstance().getCurrentStelStyle()); setStelStyle(StelApp::getInstance().getCurrentStelStyle());
connect(&StelApp::getInstance(), SIGNAL(colorSchemeChanged(const QSt ring&)), this, SLOT(setStelStyle(const QString&))); connect(&StelApp::getInstance(), SIGNAL(colorSchemeChanged(const QSt ring&)), this, SLOT(setStelStyle(const QString&)));
} }
void TelescopeControl::translateActionDescriptions()
{
StelActionMgr* actionMgr = StelApp::getInstance().getStelActionManag
er();
for (int i = MIN_SLOT_NUMBER; i <= MAX_SLOT_NUMBER; i++)
{
QString name;
QString description;
name = moveToSelectedActionId.arg(i);
description = q_("Move telescope #%1 to selected object").ar
g(i);
actionMgr->findAction(name)->setText(description);
name = moveToCenterActionId.arg(i);
description = q_("Move telescope #%1 to the point currently
in the center of the screen").arg(i);
actionMgr->findAction(name)->setText(description);
}
}
void TelescopeControl::deinit() void TelescopeControl::deinit()
{ {
//Destroy all clients first in order to avoid displaying a TCP error //Destroy all clients first in order to avoid displaying a TCP error
deleteAllTelescopes(); deleteAllTelescopes();
QHash<int, QProcess*>::const_iterator iterator = telescopeServerProc ess.constBegin(); QHash<int, QProcess*>::const_iterator iterator = telescopeServerProc ess.constBegin();
while(iterator != telescopeServerProcess.constEnd()) while(iterator != telescopeServerProcess.constEnd())
{ {
int slotNumber = iterator.key(); int slotNumber = iterator.key();
#ifdef Q_OS_WIN32 #ifdef Q_OS_WIN32
skipping to change at line 232 skipping to change at line 245
#else #else
telescopeServerProcess[slotNumber]->terminate(); telescopeServerProcess[slotNumber]->terminate();
#endif #endif
telescopeServerProcess[slotNumber]->waitForFinished(); telescopeServerProcess[slotNumber]->waitForFinished();
delete telescopeServerProcess[slotNumber]; delete telescopeServerProcess[slotNumber];
qDebug() << "TelescopeControl::deinit(): Server process at s lot" << slotNumber << "terminated successfully."; qDebug() << "TelescopeControl::deinit(): Server process at s lot" << slotNumber << "terminated successfully.";
++iterator; ++iterator;
} }
if(NULL != reticleTexture) {delete reticleTexture;}
if(NULL != selectionTexture) {delete selectionTexture;}
if(NULL != telescopeDialog) {delete telescopeDialog;}
if(NULL != slewDialog) {delete slewDialog;}
if(NULL != pixmapHover) {delete pixmapHover;}
if(NULL != pixmapOnIcon) {delete pixmapOnIcon;}
if(NULL != pixmapOffIcon) {delete pixmapOffIcon;}
reticleTexture = selectionTexture = NULL;
telescopeDialog = NULL;
slewDialog = NULL;
pixmapHover = pixmapOnIcon = pixmapOffIcon;
//TODO: Decide if it should be saved on change //TODO: Decide if it should be saved on change
//Save the configuration on exit //Save the configuration on exit
saveConfiguration(); saveConfiguration();
} }
void TelescopeControl::update(double deltaTime) void TelescopeControl::update(double deltaTime)
{ {
labelFader.update((int)(deltaTime*1000)); labelFader.update((int)(deltaTime*1000));
reticleFader.update((int)(deltaTime*1000)); reticleFader.update((int)(deltaTime*1000));
circleFader.update((int)(deltaTime*1000)); circleFader.update((int)(deltaTime*1000));
// communicate with the telescopes: // communicate with the telescopes:
communicate(); communicate();
} }
void TelescopeControl::draw(StelCore* core, StelRenderer* renderer) void TelescopeControl::draw(StelCore* core)
{ {
const StelProjectorP prj = core->getProjection(StelCore::FrameJ2000) ; const StelProjectorP prj = core->getProjection(StelCore::FrameJ2000) ;
renderer->setFont(labelFont); StelPainter sPainter(prj);
if(NULL == reticleTexture) sPainter.setFont(labelFont);
{ glEnable(GL_TEXTURE_2D);
Q_ASSERT_X(NULL == selectionTexture, Q_FUNC_INFO, "Textures glEnable(GL_BLEND);
should be created simultaneously"); reticleTexture->bind();
reticleTexture = renderer->createTexture(":/telescopeContr glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // Normal transpa
ol/telescope_reticle.png"); rency mode
selectionTexture = renderer->createTexture("textures/pointeu
r2.png");
}
foreach (const TelescopeClientP& telescope, telescopeClients) foreach (const TelescopeClientP& telescope, telescopeClients)
{ {
if (telescope->isConnected() && telescope->hasKnownPosition( )) if (telescope->isConnected() && telescope->hasKnownPosition( ))
{ {
Vec3d XY; Vec3d XY;
if (prj->projectCheck(telescope->getJ2000EquatorialP os(core), XY)) if (prj->projectCheck(telescope->getJ2000EquatorialP os(core), XY))
{ {
//Telescope circles appear synchronously wit h markers //Telescope circles appear synchronously wit h markers
if (circleFader.getInterstate() >= 0) if (circleFader.getInterstate() >= 0)
{ {
renderer->setGlobalColor(circleColor sPainter.setColor(circleColor[0], ci
[0], circleColor[1], circleColor[2], rcleColor[1], circleColor[2], circleFader.getInterstate());
circleFader glDisable(GL_TEXTURE_2D);
.getInterstate());
renderer->setBlendMode(BlendMode_Non
e);
StelVertexBuffer<VertexP2>* circleBu
ffer =
renderer->createVertexBuffer
<VertexP2>(PrimitiveType_LineStrip);
foreach (double circle, telescope->g etOculars()) foreach (double circle, telescope->g etOculars())
{ {
StelGeometryBuilder() sPainter.drawCircle(XY[0], X
.buildCircle(circleB Y[1], 0.5 * prj->getPixelPerRadAtCenter() * (M_PI/180) * (circle));
uffer, XY[0], XY[1],
0.5f *
prj->getPixelPerRadAtCenter() * (M_PI / 180.0f) * circle);
renderer->drawVertexBuffer(c
ircleBuffer);
circleBuffer->unlock();
circleBuffer->clear();
} }
delete circleBuffer; glEnable(GL_TEXTURE_2D);
} }
if (reticleFader.getInterstate() >= 0) if (reticleFader.getInterstate() >= 0)
{ {
renderer->setBlendMode(BlendMode_Alp sPainter.setColor(reticleColor[0], r
ha); eticleColor[1], reticleColor[2], reticleFader.getInterstate());
reticleTexture->bind(); sPainter.drawSprite2dMode(XY[0],XY[1
renderer->setGlobalColor(reticleColo ],15.f);
r[0], reticleColor[1], reticleColor[2],
reticleFade
r.getInterstate());
renderer->drawTexturedRect(XY[0] - 1
5.0f, XY[1] - 15.0f, 30.0f, 30.0f);
} }
if (labelFader.getInterstate() >= 0) if (labelFader.getInterstate() >= 0)
{ {
renderer->setGlobalColor(labelColor[ sPainter.setColor(labelColor[0], lab
0], labelColor[1], labelColor[2], elColor[1], labelColor[2], labelFader.getInterstate());
labelFader.
getInterstate());
//TODO: Different position of the la bel if circles are shown? //TODO: Different position of the la bel if circles are shown?
//TODO: Remove magic number (text sp acing) //TODO: Remove magic number (text sp acing)
renderer->drawText(TextParams(XY[0], sPainter.drawText(XY[0], XY[1], tele
XY[1], telescope->getNameI18n()) scope->getNameI18n(), 0, 6 + 10, -4, false);
.shift(6 + 10, -
4).useGravity());
//Same position as the other objects : doesn't work, telescope label overlaps object label //Same position as the other objects : doesn't work, telescope label overlaps object label
//sPainter.drawText(XY[0], XY[1], sc ope->getNameI18n(), 0, 10, 10, false); //sPainter.drawText(XY[0], XY[1], sc ope->getNameI18n(), 0, 10, 10, false);
reticleTexture->bind();
} }
} }
} }
} }
if(GETSTELMODULE(StelObjectMgr)->getFlagSelectedObjectPointer()) if(GETSTELMODULE(StelObjectMgr)->getFlagSelectedObjectPointer())
{ drawPointer(prj, core, sPainter);
drawPointer(prj, core, renderer);
}
} }
void TelescopeControl::setStelStyle(const QString& section) void TelescopeControl::setStelStyle(const QString& section)
{ {
if (section == "night_color") if (section == "night_color")
{ {
setLabelColor(labelNightColor); setLabelColor(labelNightColor);
setReticleColor(reticleNightColor); setReticleColor(reticleNightColor);
setCircleColor(circleNightColor); setCircleColor(circleNightColor);
} }
skipping to change at line 505 skipping to change at line 489
Vec3d objectPosition = selectObject->getJ2000EquatorialPos(StelApp:: getInstance().getCore()); Vec3d objectPosition = selectObject->getJ2000EquatorialPos(StelApp:: getInstance().getCore());
telescopeGoto(slotNumber, objectPosition); telescopeGoto(slotNumber, objectPosition);
} }
void TelescopeControl::slewTelescopeToViewDirection() void TelescopeControl::slewTelescopeToViewDirection()
{ {
// Find out for which telescope is the command // Find out for which telescope is the command
if (sender() == NULL) if (sender() == NULL)
return; return;
// XXX: we could use a QSignalMapper instead of this trick.
int slotNumber = sender()->objectName().right(1).toInt(); int slotNumber = sender()->objectName().right(1).toInt();
// Find out the coordinates of the target // Find out the coordinates of the target
Vec3d centerPosition = GETSTELMODULE(StelMovementMgr)->getViewDirect ionJ2000(); Vec3d centerPosition = GETSTELMODULE(StelMovementMgr)->getViewDirect ionJ2000();
telescopeGoto(slotNumber, centerPosition); telescopeGoto(slotNumber, centerPosition);
} }
void TelescopeControl::drawPointer(const StelProjectorP& prj, const StelCor e* core, StelRenderer* renderer) void TelescopeControl::drawPointer(const StelProjectorP& prj, const StelCor e* core, StelPainter& sPainter)
{ {
#ifndef COMPATIBILITY_001002 #ifndef COMPATIBILITY_001002
//Leaves this whole routine empty if this is the backport version. //Leaves this whole routine empty if this is the backport version.
//Otherwise, there will be two concentric selection markers drawn ar ound the telescope pointer. //Otherwise, there will be two concentric selection markers drawn ar ound the telescope pointer.
//In 0.10.3, the plug-in unloads the module that draws the surplus m arker. //In 0.10.3, the plug-in unloads the module that draws the surplus m arker.
const QList<StelObjectP> newSelected = GETSTELMODULE(StelObjectMgr)- >getSelectedObject("Telescope"); const QList<StelObjectP> newSelected = GETSTELMODULE(StelObjectMgr)- >getSelectedObject("Telescope");
if (!newSelected.empty()) if (!newSelected.empty())
{ {
const StelObjectP obj = newSelected[0]; const StelObjectP obj = newSelected[0];
Vec3d pos = obj->getJ2000EquatorialPos(core); Vec3d pos = obj->getJ2000EquatorialPos(core);
Vec3d screenpos; Vec3d screenpos;
// Compute 2D pos and return if outside screen // Compute 2D pos and return if outside screen
if (!prj->project(pos, screenpos)) if (!prj->project(pos, screenpos))
return; return;
const Vec3f& c(obj->getInfoColor()); const Vec3f& c(obj->getInfoColor());
renderer->setGlobalColor(c[0], c[1], c[2]); sPainter.setColor(c[0], c[1], c[2]);
selectionTexture->bind(); selectionTexture->bind();
renderer->setBlendMode(BlendMode_Alpha); glEnable(GL_TEXTURE_2D);
renderer->drawTexturedRect(screenpos[0] - 25.0f, screenpos[1 glEnable(GL_BLEND);
] - 25.0f, 50.0f, 50.0f, glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // Normal
StelApp::getInstance().getTotalRu transparency mode
nTime() * 40.0f); sPainter.drawSprite2dMode(screenpos[0], screenpos[1], 25., S
telApp::getInstance().getTotalRunTime() * 40.);
} }
#endif //COMPATIBILITY_001002 #endif //COMPATIBILITY_001002
} }
void TelescopeControl::telescopeGoto(int slotNumber, const Vec3d &j2000Pos) void TelescopeControl::telescopeGoto(int slotNumber, const Vec3d &j2000Pos)
{ {
//TODO: See the original code. I think that something is wrong here. .. //TODO: See the original code. I think that something is wrong here. ..
if(telescopeClients.contains(slotNumber)) if(telescopeClients.contains(slotNumber))
telescopeClients.value(slotNumber)->telescopeGoto(j2000Pos); telescopeClients.value(slotNumber)->telescopeGoto(j2000Pos);
} }
skipping to change at line 658 skipping to change at line 644
circleNightColor = StelUtils::strToVec3f(settings->value("night_colo r_telescope_circles", "0.5,0,0").toString()); circleNightColor = StelUtils::strToVec3f(settings->value("night_colo r_telescope_circles", "0.5,0,0").toString());
//Load server executables flag and directory //Load server executables flag and directory
useServerExecutables = settings->value("flag_use_server_executables" , false).toBool(); useServerExecutables = settings->value("flag_use_server_executables" , false).toBool();
serverExecutablesDirectoryPath = settings->value("server_executables _path").toString(); serverExecutablesDirectoryPath = settings->value("server_executables _path").toString();
//If no directory is specified in the configuration file, try to fin d the default one //If no directory is specified in the configuration file, try to fin d the default one
if(serverExecutablesDirectoryPath.isEmpty() || !QDir(serverExecutabl esDirectoryPath).exists()) if(serverExecutablesDirectoryPath.isEmpty() || !QDir(serverExecutabl esDirectoryPath).exists())
{ {
//Find out if the default server directory exists //Find out if the default server directory exists
QString serverDirectoryPath; QString serverDirectoryPath = StelFileMgr::findFile("servers
try ", StelFileMgr::Directory);
{ if (serverDirectoryPath.isEmpty())
serverDirectoryPath = StelFileMgr::findFile("servers
", StelFileMgr::Directory);
}
catch(std::runtime_error &e)
{ {
//qDebug() << "TelescopeControl: No telescope server s directory detected."; //qDebug() << "TelescopeControl: No telescope server s directory detected.";
useServerExecutables = false; useServerExecutables = false;
serverDirectoryPath = StelFileMgr::getUserDir() + "/ servers"; serverDirectoryPath = StelFileMgr::getUserDir() + "/ servers";
} }
if(!serverDirectoryPath.isEmpty()) else
{ {
serverExecutablesDirectoryPath = serverDirectoryPath ; serverExecutablesDirectoryPath = serverDirectoryPath ;
} }
} }
//Load logging flag //Load logging flag
useTelescopeServerLogs = settings->value("flag_enable_telescope_logs ", false).toBool(); useTelescopeServerLogs = settings->value("flag_enable_telescope_logs ", false).toBool();
settings->endGroup(); settings->endGroup();
} }
skipping to change at line 720 skipping to change at line 702
} }
//Save logging flag //Save logging flag
settings->setValue("flag_enable_telescope_logs", useTelescopeServerL ogs); settings->setValue("flag_enable_telescope_logs", useTelescopeServerL ogs);
settings->endGroup(); settings->endGroup();
} }
void TelescopeControl::saveTelescopes() void TelescopeControl::saveTelescopes()
{ {
try //Open/create the JSON file
QString telescopesJsonPath = StelFileMgr::findFile("modules/Telescop
eControl", (StelFileMgr::Flags)(StelFileMgr::Directory|StelFileMgr::Writabl
e)) + "/telescopes.json";
if (telescopesJsonPath.isEmpty())
{ {
//Open/create the JSON file qWarning() << "TelescopeControl: Error saving telescopes";
QString telescopesJsonPath = StelFileMgr::findFile("modules/ return;
TelescopeControl", (StelFileMgr::Flags)(StelFileMgr::Directory|StelFileMgr:
:Writable)) + "/telescopes.json";
QFile telescopesJsonFile(telescopesJsonPath);
if(!telescopesJsonFile.open(QFile::WriteOnly|QFile::Text))
{
qWarning() << "TelescopeControl: Telescopes can not
be saved. A file can not be open for writing:" << QDir::toNativeSeparators(
telescopesJsonPath);
return;
}
//Add the version:
telescopeDescriptions.insert("version", QString(TELESCOPE_CO
NTROL_VERSION));
//Convert the tree to JSON
StelJsonParser::write(telescopeDescriptions, &telescopesJson
File);
telescopesJsonFile.flush();//Is this necessary?
telescopesJsonFile.close();
} }
catch(std::runtime_error &e) QFile telescopesJsonFile(telescopesJsonPath);
if(!telescopesJsonFile.open(QFile::WriteOnly|QFile::Text))
{ {
qWarning() << "TelescopeControl: Error saving telescopes: " << e.what(); qWarning() << "TelescopeControl: Telescopes can not be saved . A file can not be open for writing:" << QDir::toNativeSeparators(telescop esJsonPath);
return; return;
} }
//Add the version:
telescopeDescriptions.insert("version", QString(TELESCOPE_CONTROL_VE
RSION));
//Convert the tree to JSON
StelJsonParser::write(telescopeDescriptions, &telescopesJsonFile);
telescopesJsonFile.flush();//Is this necessary?
telescopesJsonFile.close();
} }
void TelescopeControl::loadTelescopes() void TelescopeControl::loadTelescopes()
{ {
QVariantMap result; QVariantMap result;
try
QString telescopesJsonPath = StelFileMgr::findFile("modules/Telescop
eControl", (StelFileMgr::Flags)(StelFileMgr::Directory|StelFileMgr::Writabl
e)) + "/telescopes.json";
if (telescopesJsonPath.isEmpty())
{
qWarning() << "TelescopeControl: Error loading telescopes";
return;
}
if(!QFileInfo(telescopesJsonPath).exists())
{ {
QString telescopesJsonPath = StelFileMgr::findFile("modules/ qWarning() << "TelescopeControl::loadTelescopes(): No telesc
TelescopeControl", (StelFileMgr::Flags)(StelFileMgr::Directory|StelFileMgr: opes loaded. File is missing:" << QDir::toNativeSeparators(telescopesJsonPa
:Writable)) + "/telescopes.json"; th);
telescopeDescriptions = result;
return;
}
if(!QFileInfo(telescopesJsonPath).exists()) QFile telescopesJsonFile(telescopesJsonPath);
{
qWarning() << "TelescopeControl::loadTelescopes(): N
o telescopes loaded. File is missing:" << QDir::toNativeSeparators(telescop
esJsonPath);
telescopeDescriptions = result;
return;
}
QFile telescopesJsonFile(telescopesJsonPath); QVariantMap map;
QVariantMap map; if(!telescopesJsonFile.open(QFile::ReadOnly))
{
qWarning() << "TelescopeControl: No telescopes loaded. Can't
open for reading" << QDir::toNativeSeparators(telescopesJsonPath);
telescopeDescriptions = result;
return;
}
else
{
map = StelJsonParser::parse(&telescopesJsonFile).toMap();
telescopesJsonFile.close();
}
//File contains any telescopes?
if(map.isEmpty())
{
telescopeDescriptions = result;
return;
}
if(!telescopesJsonFile.open(QFile::ReadOnly)) QString version = map.value("version", "0.0.0").toString();
if(version < QString(TELESCOPE_CONTROL_VERSION))
{
QString newName = telescopesJsonPath + ".backup." + QDateTim
e::currentDateTime().toString("yyyy-MM-dd-hh-mm-ss");
if(telescopesJsonFile.rename(newName))
{ {
qWarning() << "TelescopeControl: No telescopes loade qWarning() << "TelescopeControl: The existing versio
d. Can't open for reading" << QDir::toNativeSeparators(telescopesJsonPath); n of telescopes.json is obsolete. Backing it up as " << QDir::toNativeSepar
ators(newName);
qWarning() << "TelescopeControl: A blank telescopes.
json file will have to be created.";
telescopeDescriptions = result; telescopeDescriptions = result;
return; return;
} }
else else
{ {
map = StelJsonParser::parse(&telescopesJsonFile).toM qWarning() << "TelescopeControl: The existing versio
ap(); n of telescopes.json is obsolete. Unable to rename.";
telescopesJsonFile.close(); telescopeDescriptions = result;
return;
} }
}
map.remove("version");//Otherwise it will try to read it as a telesc
ope
//Make sure that there are no telescope clients yet
deleteAllTelescopes();
//File contains any telescopes? //Read telescopes, if any
if(map.isEmpty()) int telescopesCount = 0;
QMapIterator<QString, QVariant> node(map);
bool ok;
while(node.hasNext())
{
node.next();
QString key = node.key();
//If this is not a valid slot number, remove the node
int slot = key.toInt(&ok);
if(!ok || !isValidSlotNumber(slot))
{ {
telescopeDescriptions = result; qDebug() << "TelescopeControl::loadTelescopes(): Del
return; eted node unrecogised as slot:" << key;
map.remove(key);
continue;
}
QVariantMap telescope = node.value().toMap();
//Essential parameters: Name, connection type, equinox
//Validation: Name
QString name = telescope.value("name").toString();
if(name.isEmpty())
{
qDebug() << "TelescopeControl: Unable to load telesc
ope: No name specified at slot" << key;
map.remove(key);
continue;
} }
QString version = map.value("version", "0.0.0").toString(); //Validation: Connection
if(version < QString(TELESCOPE_CONTROL_VERSION)) QString connection = telescope.value("connection").toString(
);
if(connection.isEmpty() || !connectionTypeNames.values().con
tains(connection))
{ {
QString newName = telescopesJsonPath + ".backup." + qDebug() << "TelescopeControl: Unable to load telesc
QDateTime::currentDateTime().toString("yyyy-MM-dd-hh-mm-ss"); ope: No valid connection type at slot" << key;
if(telescopesJsonFile.rename(newName)) map.remove(key);
{ continue;
qWarning() << "TelescopeControl: The existin
g version of telescopes.json is obsolete. Backing it up as " << QDir::toNat
iveSeparators(newName);
qWarning() << "TelescopeControl: A blank tel
escopes.json file will have to be created.";
telescopeDescriptions = result;
return;
}
else
{
qWarning() << "TelescopeControl: The existin
g version of telescopes.json is obsolete. Unable to rename.";
telescopeDescriptions = result;
return;
}
} }
map.remove("version");//Otherwise it will try to read it as a telescope ConnectionType connectionType = connectionTypeNames.key(conn ection);
//Make sure that there are no telescope clients yet QString equinox = telescope.value("equinox", "J2000").toStri
deleteAllTelescopes(); ng();
if (equinox != "J2000" && equinox != "JNow")
{
qDebug() << "TelescopeControl: Unable to load telesc
ope: Invalid equinox value at slot" << key;
map.remove(key);
continue;
}
//Read telescopes, if any QString hostName("localhost");
int telescopesCount = 0; int portTCP = 0;
QMapIterator<QString, QVariant> node(map); int delay = 0;
bool ok; QString deviceModelName;
while(node.hasNext()) QString portSerial;
{
node.next();
QString key = node.key();
//If this is not a valid slot number, remove the nod
e
int slot = key.toInt(&ok);
if(!ok || !isValidSlotNumber(slot))
{
qDebug() << "TelescopeControl::loadTelescope
s(): Deleted node unrecogised as slot:" << key;
map.remove(key);
continue;
}
QVariantMap telescope = node.value().toMap(); if (connectionType == ConnectionInternal)
{
//Serial port and device model
deviceModelName = telescope.value("device_model").to
String();
portSerial = telescope.value("serial_port").toString
();
//Essential parameters: Name, connection type, equin if(deviceModelName.isEmpty())
ox
//Validation: Name
QString name = telescope.value("name").toString();
if(name.isEmpty())
{ {
qDebug() << "TelescopeControl: Unable to loa d telescope: No name specified at slot" << key; qDebug() << "TelescopeControl: Unable to loa d telescope: No device model specified at slot" << key;
map.remove(key); map.remove(key);
continue; continue;
} }
//Validation: Connection //Do we have this server?
QString connection = telescope.value("connection").t if(!deviceModels.contains(deviceModelName))
oString();
if(connection.isEmpty() || !connectionTypeNames.valu
es().contains(connection))
{ {
qDebug() << "TelescopeControl: Unable to loa d telescope: No valid connection type at slot" << key; qWarning() << "TelescopeControl: Unable to l oad telescope at slot" << slot << "because the specified device model is mi ssing:" << deviceModelName;
map.remove(key); map.remove(key);
continue; continue;
} }
ConnectionType connectionType = connectionTypeNames. key(connection);
QString equinox = telescope.value("equinox", "J2000" if(portSerial.isEmpty() || !portSerial.startsWith(SE
).toString(); RIAL_PORT_PREFIX))
if (equinox != "J2000" && equinox != "JNow")
{ {
qDebug() << "TelescopeControl: Unable to loa d telescope: Invalid equinox value at slot" << key; qDebug() << "TelescopeControl: Unable to loa d telescope: No valid serial port specified at slot" << key;
map.remove(key); map.remove(key);
continue; continue;
} }
}
QString hostName("localhost"); if (connectionType == ConnectionRemote)
int portTCP = 0; {
int delay = 0; //Validation: Host name
QString deviceModelName; hostName = telescope.value("host_name").toString();
QString portSerial; if(hostName.isEmpty())
if (connectionType == ConnectionInternal)
{ {
//Serial port and device model qDebug() << "TelescopeControl::loadTelescope
deviceModelName = telescope.value("device_mo s(): No host name at slot" << key;
del").toString(); map.remove(key);
portSerial = telescope.value("serial_port"). continue;
toString();
if(deviceModelName.isEmpty())
{
qDebug() << "TelescopeControl: Unabl
e to load telescope: No device model specified at slot" << key;
map.remove(key);
continue;
}
//Do we have this server?
if(!deviceModels.contains(deviceModelName))
{
qWarning() << "TelescopeControl: Una
ble to load telescope at slot" << slot << "because the specified device mod
el is missing:" << deviceModelName;
map.remove(key);
continue;
}
if(portSerial.isEmpty() || !portSerial.start
sWith(SERIAL_PORT_PREFIX))
{
qDebug() << "TelescopeControl: Unabl
e to load telescope: No valid serial port specified at slot" << key;
map.remove(key);
continue;
}
} }
}
if (connectionType == ConnectionRemote) if (connectionType != ConnectionVirtual)
{
//Validation: TCP port
portTCP = telescope.value("tcp_port").toInt();
if(!telescope.contains("tcp_port") || !isValidPort(p
ortTCP))
{ {
//Validation: Host name qDebug() << "TelescopeControl: Unable to loa
hostName = telescope.value("host_name").toSt d telescope: No valid TCP port at slot" << key;
ring(); map.remove(key);
if(hostName.isEmpty()) continue;
{
qDebug() << "TelescopeControl::loadT
elescopes(): No host name at slot" << key;
map.remove(key);
continue;
}
} }
if (connectionType != ConnectionVirtual) //Validation: Delay
delay = telescope.value("delay", 0).toInt();
if(!isValidDelay(delay))
{ {
//Validation: TCP port qDebug() << "TelescopeControl: Unable to loa
portTCP = telescope.value("tcp_port").toInt( d telescope: No valid delay at slot" << key;
); map.remove(key);
if(!telescope.contains("tcp_port") || !isVal continue;
idPort(portTCP))
{
qDebug() << "TelescopeControl: Unabl
e to load telescope: No valid TCP port at slot" << key;
map.remove(key);
continue;
}
//Validation: Delay
delay = telescope.value("delay", 0).toInt();
if(!isValidDelay(delay))
{
qDebug() << "TelescopeControl: Unabl
e to load telescope: No valid delay at slot" << key;
map.remove(key);
continue;
}
} }
}
//Connect at startup //Connect at startup
bool connectAtStartup = telescope.value("connect_at_ bool connectAtStartup = telescope.value("connect_at_startup"
startup", false).toBool(); , false).toBool();
//Validation: FOV circles //Validation: FOV circles
QVariantList parsedJsonCircles = telescope.value("ci QVariantList parsedJsonCircles = telescope.value("circles").
rcles").toList(); toList();
QList<double> internalCircles; QList<double> internalCircles;
for(int i = 0; i< parsedJsonCircles.size(); i++) for(int i = 0; i< parsedJsonCircles.size(); i++)
{ {
if(i >= MAX_CIRCLE_COUNT) if(i >= MAX_CIRCLE_COUNT)
break; break;
internalCircles.append(parsedJsonCircles.val internalCircles.append(parsedJsonCircles.value(i, -1
ue(i, -1.0).toDouble()); .0).toDouble());
} }
if(internalCircles.isEmpty()) if(internalCircles.isEmpty())
{ {
//If the list is empty or invalid, make sure //If the list is empty or invalid, make sure it's no
it's no longer in the file longer in the file
telescope.remove("circles"); telescope.remove("circles");
map.insert(key, telescope); map.insert(key, telescope);
} }
else else
{ {
//Replace the existing list with the validat //Replace the existing list with the validated one
ed one QVariantList newJsonCircles;
QVariantList newJsonCircles; for(int i = 0; i < internalCircles.size(); i++)
for(int i = 0; i < internalCircles.size(); i newJsonCircles.append(internalCircles.at(i))
++) ;
newJsonCircles.append(internalCircle telescope.insert("circles", newJsonCircles);
s.at(i)); map.insert(key, telescope);
telescope.insert("circles", newJsonCircles); }
map.insert(key, telescope);
}
//Initialize a telescope client for this slot //Initialize a telescope client for this slot
//TODO: Improve the flow of control //TODO: Improve the flow of control
if(connectAtStartup) if(connectAtStartup)
{
if (connectionType == ConnectionInternal)
{ {
if (connectionType == ConnectionInternal) //Use a sever if necessary
if(deviceModels[deviceModelName].useExecutab
le)
{ {
//Use a sever if necessary if(startClientAtSlot(slot, connectio
if(deviceModels[deviceModelName].use nType, name, equinox, hostName, portTCP, delay, internalCircles))
Executable)
{ {
if(startClientAtSlot(slot, c
onnectionType, name, equinox, hostName, portTCP, delay, internalCircles))
{
if(!startServerAtSlo if(!startServerAtSlot(slot,
t(slot, deviceModelName, portTCP, portSerial)) deviceModelName, portTCP, portSerial))
{
stopClientAt
Slot(slot);
qDebug() <<
"TelescopeControl: Unable to launch a telescope server at slot" << slot;
}
}
else
{ {
qDebug() << "Telesco stopClientAtSlot(slo
peControl: Unable to create a telescope client at slot" << slot; t);
//Unnecessary due to qDebug() << "Telesco
if-else construction; peControl: Unable to launch a telescope server at slot" << slot;
//also, causes bug #
608533
//continue;
} }
} }
else else
{ {
addLogAtSlot(slot); qDebug() << "TelescopeContro
logAtSlot(slot); l: Unable to create a telescope client at slot" << slot;
if(!startClientAtSlot(slot, //Unnecessary due to if-else
connectionType, name, equinox, QString(), 0, delay, internalCircles, device construction;
ModelName, portSerial)) //also, causes bug #608533
{ //continue;
qDebug() << "Telesco
peControl: Unable to create a telescope client at slot" << slot;
//Unnecessary due to
if-else construction;
//also, causes bug #
608533
//continue;
}
} }
} }
else else
{ {
if(!startClientAtSlot(slot, connecti addLogAtSlot(slot);
onType, name, equinox, hostName, portTCP, delay, internalCircles)) logAtSlot(slot);
if(!startClientAtSlot(slot, connecti
onType, name, equinox, QString(), 0, delay, internalCircles, deviceModelNam
e, portSerial))
{ {
qDebug() << "TelescopeContro l: Unable to create a telescope client at slot" << slot; qDebug() << "TelescopeContro l: Unable to create a telescope client at slot" << slot;
//Unnecessary due to if-else construction; //Unnecessary due to if-else construction;
//also, causes bug #608533 //also, causes bug #608533
//continue; //continue;
} }
} }
} }
else
//If this line is reached, the telescope at this slo {
t has been loaded successfully if(!startClientAtSlot(slot, connectionType,
telescopesCount++; name, equinox, hostName, portTCP, delay, internalCircles))
{
qDebug() << "TelescopeControl: Unabl
e to create a telescope client at slot" << slot;
//Unnecessary due to if-else constru
ction;
//also, causes bug #608533
//continue;
}
}
} }
if(telescopesCount > 0) //If this line is reached, the telescope at this slot has be
{ en loaded successfully
result = map; telescopesCount++;
qDebug() << "TelescopeControl: Loaded successfully"
<< telescopesCount << "telescopes.";
}
} }
catch(std::runtime_error &e)
if(telescopesCount > 0)
{ {
qWarning() << "TelescopeControl: Error loading telescopes: " result = map;
<< e.what(); qDebug() << "TelescopeControl: Loaded successfully" << teles
copesCount << "telescopes.";
} }
telescopeDescriptions = result; telescopeDescriptions = result;
} }
bool TelescopeControl::addTelescopeAtSlot(int slot, ConnectionType connecti onType, QString name, QString equinox, QString host, int portTCP, int delay , bool connectAtStartup, QList<double> circles, QString deviceModelName, QS tring portSerial) bool TelescopeControl::addTelescopeAtSlot(int slot, ConnectionType connecti onType, QString name, QString equinox, QString host, int portTCP, int delay , bool connectAtStartup, QList<double> circles, QString deviceModelName, QS tring portSerial)
{ {
//Validation //Validation
if(!isValidSlotNumber(slot) || name.isEmpty() || equinox.isEmpty() | | connectionType <= ConnectionNA || connectionType >= ConnectionCount) if(!isValidSlotNumber(slot) || name.isEmpty() || equinox.isEmpty() | | connectionType <= ConnectionNA || connectionType >= ConnectionCount)
return false; return false;
skipping to change at line 1258 skipping to change at line 1235
{ {
//TODO: Add debug //TODO: Add debug
return false; return false;
} }
QString slotName = QString::number(slotNumber); QString slotName = QString::number(slotNumber);
QString serverName = deviceModels.value(deviceModelName).server; QString serverName = deviceModels.value(deviceModelName).server;
if (telescopeServers.contains(serverName)) if (telescopeServers.contains(serverName))
{ {
QString serverExecutablePath; QString serverExecutablePath = StelFileMgr::findFile(serverE
//Is the try/catch really necessary? xecutablesDirectoryPath + TELESCOPE_SERVER_PATH.arg(serverName), StelFileMg
try r::File);
{ if (serverExecutablePath.isEmpty())
serverExecutablePath = StelFileMgr::findFile(serverE
xecutablesDirectoryPath + TELESCOPE_SERVER_PATH.arg(serverName), StelFileMg
r::File);
}
catch (std::runtime_error& e)
{ {
qDebug() << "TelescopeControl: Error starting telesc ope server: Can't find executable:" << QDir::toNativeSeparators(serverExecu tablePath); qDebug() << "TelescopeControl: Error starting telesc ope server: Can't find executable:" << QDir::toNativeSeparators(serverExecu tablePath);
return false; return false;
} }
#ifdef Q_OS_WIN32 #ifdef Q_OS_WIN32
QString serialPortName; QString serialPortName;
if(portSerial.right(portSerial.size() - SERIAL_PORT_PREFIX.s ize()).toInt() > 9) if(portSerial.right(portSerial.size() - SERIAL_PORT_PREFIX.s ize()).toInt() > 9)
serialPortName = "\\\\.\\" + portSerial;//"\\.\COMxx ", not sure if it will work serialPortName = "\\\\.\\" + portSerial;//"\\.\COMxx ", not sure if it will work
else else
skipping to change at line 1674 skipping to change at line 1646
telescopeServerLogStreams.remove(slot); telescopeServerLogStreams.remove(slot);
telescopeServerLogFiles.remove(slot); telescopeServerLogFiles.remove(slot);
} }
} }
void TelescopeControl::logAtSlot(int slot) void TelescopeControl::logAtSlot(int slot)
{ {
if(telescopeServerLogStreams.contains(slot)) if(telescopeServerLogStreams.contains(slot))
log_file = telescopeServerLogStreams.value(slot); log_file = telescopeServerLogStreams.value(slot);
} }
void TelescopeControl::translateActionDescriptions()
{
StelShortcutMgr* shMgr = StelApp::getInstance().getStelShortcutManag
er();
if (!shMgr)
return;
for (int i = MIN_SLOT_NUMBER; i <= MAX_SLOT_NUMBER; i++)
{
QString name = moveToSelectedActionId.arg(i);
QString description = q_("Move telescope #%1 to selected obj
ect")
.arg(i);
shMgr->setShortcutText(name, actionGroupId, description);
name = moveToCenterActionId.arg(i);
description = q_("Move telescope #%1 to the point currently
in the center of the screen").arg(i);
shMgr->setShortcutText(name, actionGroupId, description);
}
}
 End of changes. 89 change blocks. 
408 lines changed or deleted 360 lines changed or added

This html diff was produced by rfcdiff 1.41. The latest version is available from http://tools.ietf.org/tools/rfcdiff/