diff --git a/kstars/indi/indicommon.h b/kstars/indi/indicommon.h index 7a3600362..3a6286281 100644 --- a/kstars/indi/indicommon.h +++ b/kstars/indi/indicommon.h @@ -1,211 +1,212 @@ /* INDI Common Defs Copyright (C) 2012 Jasem Mutlaq This application is free software; you can redistribute it and/or modify it under the terms of the 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 INDICOMMON_H #define INDICOMMON_H #include #include /*! \page INDI "INDI Overview" \tableofcontents \section Intro Introduction INDI is a simple XML-like communications protocol described for interactive and automated remote control of diverse instrumentation. INDI is small, easy to parse, and stateless. The main key concept in INDI is that devices have the ability to describe themselves. This is accomplished by using XML to describe a generic hierarchy that can represent both canonical and non-canonical devices. All devices may contain one or more properties. A property in the INDI paradigm describes a specific function of the driver. Any property may contain one or more elements. There are four types of INDI properties:
  • Text property: Property to transfer simple text information in ISO-8859-1. The text is not encoded or encrypted on transfer. If the text includes elements that can break XML syntax, a BLOB property should be used to make the transfer.
  • Number property: Property to transfer numeric information with configurable minimum, maximum, and step values. The supported number formats are decimal and sexigesmal. The property includes a GUI hint element in printf style format to enable clients to properly display numeric properties.
  • Switch property: Property to hold a group of options or selections (Represented in GUI by buttons and check boxes).
  • Light property: Property to hold a group of status indicators (Represented in GUI by colored LEDs).
  • BLOB property: BLOB is a Binary Large OBject used to transfer binary data to and from drivers.
For example, all INDI devices share the CONNECTION standard switch property. The CONNECTION property has two elements: CONNECT and DISCONNECT switches. GUI clients parse the generic XML description of properties and builds a GUI representation suitable for direct human interaction. KStars is a fully featured INDI client. INDI Data (i.e. devices and properties) and GUI (how they appear on the screen) are separated. INDI adopts is a server/client architecture where one or more servers communicate with one or more drivers, each driver can communicate with one or more actual hardware devices. Clients connect to the server, obtain a list of devices, and generate GUI to represent the devices for user interaction and control. The creation of GUI happens in run time via introspection. \section Structure Hierarchy The following is a description of some of the various classes that support INDI:
  • DriverManager: Manages local drivers as installed by INDI library. Enables users to start/stop local INDI servers (ServerManager) running one or more drivers (DriverInfo). Also enables connecting to local or remote INDI servers. For both local and remote connection, it creates a ClientManager to handle incoming data from each INDI server started, and creates a GUIManager to render the devices in INDI Control Panel. The user may also choose to only start an INDI server without a client manager and GUI.
  • ClientManager: Manages sending and receiving data from and to an INDI server. The ClientManager sends notifications (signals) when a new device or property is defined, updated, or deleted.
  • GUIManager: Handles creation of GUI interface for devices (INDI_D) and their properties and updates the interface in accord with any data emitted by the associated ClientManager. The GUI manager supports multiple ClientManagers and consolidate all devices from all the ClientManagers into a single INDI Control Panel where each device is created as a tab.
  • INDIListener: Once a ClientManager is created in DriverManager after successfully connecting to an INDI server, it is added to INDIListener where it monitors any new devices and if a new device is detected, it creates an ISD::GenericDevice to hold the data of the device. It also monitors new properties and registers them. If it detects an INDI standard property associated with a particular device family (e.g. Property EQUATORIAL_EOD_COORD is a standard property of a Telescope device), then it extends the ISD::GenericDevice to the particular specialized device type using the Decorator Pattern. All updated properties from INDI server are delegated to their respective devices.
  • ServerManager
  • Manages establishment and shutdown of local INDI servers.
  • DriverInfo
  • : Simple class that holds information on INDI drivers such as name, version, device type..etc.
  • ISD::GDInterface: Abstract class where the ISD::DeviceDecorator and ISD::GenericDevice are derived.
  • ISD::DeviceDecorator: Base class of all specialized decorators such as ISD::Telescope, ISD::Filter, and ISD::CCD devices.
  • ISD::GenericDevice: Base class for all INDI devices. It implements processes shared across all INDI devices such as handling connection/disconnection, setting of configuration..etc.. When a specialized decorator is created (e.g. ISD::Telescope), the decorator is passed an ISD::GenericDevice pointer. Since ISD::Telescope is a child of ISD::DeviceDecorator, it can override some or all the device specific functions as defined in ISD::GDInterface (e.g. ProcessNumber(INumberVectorProperty *nvp)). For any function that is not overridden, the parent ISD::DeviceDecorator will invoke the function in ISD::GenericDevice instead. Moreover, The specialized decorator device can explicitly call DeviceDecorator::ProcessNumber(INumberVectorProperty *nvp) for example to cause the ISD::DeviceDecorator to invoke the same function but as defined in ISD::GenericDevice.
\section Example Example Suppose the user wishes to control an EQMod mount with \e indi_eqmod_telescope driver. From the DriverManager GUI, the user selects the driver and \e starts INDI services. The DriverManager will create a ServerManager instance to run an INDI server with the EQMod driver executable. After establishing the server, the DriverManager will create an instance of ClientManager and set its INDI server address as the host and port of the server created in ServerManager. For local servers, the host name is \e localhost and the default INDI port is 7624. If connection to the INDI server is successful, DriverManager then adds the ClientManager to both INDIListener (to handle data), and GUIManager (to handle GUI). Since the ClientManager emits signals whenever a new, updated, or deleted device/property is received from INDI server, it affects both the data handling part as well as GUI rendering. INDIListener holds a list of all INDI devices in KStars regardless of their origin. Once INDIListener receives a new device from the ClientManager, it creates a new ISD::GenericDevice. At the GUIManager side, it creates INDI Control Panel and adds a new tab with the device name. It also creates a separate tab for each property group received. The user is presented with a basic GUI to set the connection port of EQMod and to connect/disconnect to/from the telescope. If the user clicks connect, the status of the connection property is updated, and INDI_P sends the new switch status (CONNECT=ON) to INDI server via the ClientManager. If the connection is successful at the driver side, it will start defining new properties to cover the complete functionality of the EQMod driver, one of the standard properties is EQUATORIAL_EOD_COORD which will be detected in INDIListener. Upon detection of this key signature property, INDIListener creates a new ISD::Telescope device while passing to it the ISD::GenericDevice instance created earlier. Now suppose an updated Number property arrives from INDI server, the ClientManager emits a signal indicating a number property has a new updated value and INDIListener delegates the INDI Number property to the device, which is now of type ISD::Telescope. The ISD::Telescope overridden the processNumber(INumberVectorProperty *nvp) function in ISD::DeviceDecorator because it wants to handle some telescope specific numbers such as EQUATORIAL_EOD_COORD in order to move the telescope marker on the sky map as it changes. If the received property was indeed EQUATORIAL_EOD_COORD or any property handled by the ISD::Telescope ProcessNumber() function, then there is no further action needed. But what if the property is not processed in ISD::Telescope ProcessNumber() function? In this case, the ProcessNumber() function simply calls DeviceDecorator::ProcessNumber() and it will delgate the call to ISD::GenericDevice ProcessNumber() to process. This is how the Decorator pattern work, the decorator classes implements extended functionality, but the basic class is still responsible for handling all of the basic functions. */ #define INDIVERSION 1.7 /* we support this or less */ typedef enum { PRIMARY_XML, THIRD_PARTY_XML, EM_XML, HOST_SOURCE, CUSTOM_SOURCE, GENERATED_SOURCE } DriverSource; typedef enum { SERVER_CLIENT, SERVER_ONLY } ServerMode; typedef enum { DATA_FITS, DATA_VIDEO, DATA_CCDPREVIEW, DATA_ASCII, DATA_OTHER } INDIDataTypes; typedef enum { LOAD_LAST_CONFIG, SAVE_CONFIG, LOAD_DEFAULT_CONFIG, PURGE_CONFIG } INDIConfig; typedef enum { NO_DIR = 0, RA_INC_DIR, RA_DEC_DIR, DEC_INC_DIR, DEC_DEC_DIR } GuideDirection; /* GUI layout */ #define PROPERTY_LABEL_WIDTH 100 #define ELEMENT_LABEL_WIDTH 175 +#define ELEMENT_LABEL_HEIGHT 25 #define ELEMENT_READ_WIDTH 175 #define ELEMENT_WRITE_WIDTH 175 #define ELEMENT_FULL_WIDTH 340 #define MIN_SET_WIDTH 50 #define MAX_SET_WIDTH 110 #define MED_INDI_FONT 2 #define MAX_LABEL_LENGTH 20 typedef enum { PG_NONE = 0, PG_TEXT, PG_NUMERIC, PG_BUTTONS, PG_RADIO, PG_MENU, PG_LIGHTS, PG_BLOB } PGui; /* new versions of glibc define TIME_UTC as a macro */ #undef TIME_UTC /* INDI std properties */ enum stdProperties { CONNECTION, DEVICE_PORT, TIME_UTC, TIME_LST, TIME_UTC_OFFSET, GEOGRAPHIC_COORD, /* General */ EQUATORIAL_COORD, EQUATORIAL_EOD_COORD, EQUATORIAL_EOD_COORD_REQUEST, HORIZONTAL_COORD, /* Telescope */ TELESCOPE_ABORT_MOTION, ON_COORD_SET, SOLAR_SYSTEM, TELESCOPE_MOTION_NS, /* Telescope */ TELESCOPE_MOTION_WE, TELESCOPE_PARK, /* Telescope */ CCD_EXPOSURE, CCD_TEMPERATURE_REQUEST, CCD_FRAME, /* CCD */ CCD_FRAME_TYPE, CCD_BINNING, CCD_INFO, CCD_VIDEO_STREAM, /* Video */ FOCUS_SPEED, FOCUS_MOTION, FOCUS_TIMER, /* Focuser */ FILTER_SLOT, /* Filter */ WATCHDOG_HEARTBEAT }; /* Watchdog */ /* Devices families that we explicitly support (i.e. with std properties) */ typedef enum { KSTARS_ADAPTIVE_OPTICS, KSTARS_AGENT, KSTARS_AUXILIARY, KSTARS_CCD, KSTARS_DETECTORS, KSTARS_DOME, KSTARS_FILTER, KSTARS_FOCUSER, KSTARS_ROTATOR, KSTARS_SPECTROGRAPHS, KSTARS_TELESCOPE, KSTARS_WEATHER, KSTARS_UNKNOWN } DeviceFamily; const QMap DeviceFamilyLabels = { {KSTARS_ADAPTIVE_OPTICS, "Adaptive Optics"}, {KSTARS_AGENT, "Agent"}, {KSTARS_AUXILIARY, "Auxiliary"}, {KSTARS_CCD, "CCDs"}, {KSTARS_DETECTORS, "Detectors"}, {KSTARS_DOME, "Domes"}, {KSTARS_FILTER, "Filter Wheels"}, {KSTARS_FOCUSER, "Focusers"}, {KSTARS_ROTATOR, "Rotators"}, {KSTARS_SPECTROGRAPHS, "Spectrographs"}, {KSTARS_TELESCOPE, "Telescopes"}, {KSTARS_WEATHER, "Weather"}, {KSTARS_UNKNOWN, "Unknown"}, }; typedef enum { FRAME_LIGHT, FRAME_BIAS, FRAME_DARK, FRAME_FLAT } CCDFrameType; typedef enum { SINGLE_BIN, DOUBLE_BIN, TRIPLE_BIN, QUADRAPLE_BIN } CCDBinType; typedef enum { INDI_SEND_COORDS, INDI_FIND_TELESCOPE, INDI_CENTER_LOCK, INDI_CENTER_UNLOCK, INDI_CUSTOM_PARKING, INDI_SET_PORT, INDI_CONNECT, INDI_DISCONNECT, INDI_SET_FILTER, INDI_CONFIRM_FILTER, INDI_SET_ROTATOR_TICKS, INDI_SET_ROTATOR_ANGLE } DeviceCommand; typedef enum { SOURCE_MANUAL, SOURCE_FLATCAP, SOURCE_WALL, SOURCE_DAWN_DUSK, SOURCE_DARKCAP } FlatFieldSource; typedef enum { DURATION_MANUAL, DURATION_ADU } FlatFieldDuration; #endif // INDICOMMON_H diff --git a/kstars/indi/indielement.cpp b/kstars/indi/indielement.cpp index 424380ee5..7dbec2675 100644 --- a/kstars/indi/indielement.cpp +++ b/kstars/indi/indielement.cpp @@ -1,666 +1,665 @@ /* INDI Element Copyright (C) 2003 Jasem Mutlaq (mutlaqja@ikarustech.com) This application is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. 2004-01-15 INDI element is the most basic unit of the INDI KStars client. */ #include "indielement.h" #include "indiproperty.h" #include "indigroup.h" #include "indidevice.h" #include "kstars.h" #include "ksnotification.h" #include -#include #include #include #include #include #include #include #include #include #include #include #include extern const char *libindi_strings_context; /******************************************************************* ** INDI Element *******************************************************************/ INDI_E::INDI_E(INDI_P *gProp, INDI::Property *dProp) { guiProp = gProp; dataProp = dProp; EHBox = new QHBoxLayout; EHBox->setContentsMargins(0, 0, 0, 0); } INDI_E::~INDI_E() { delete (EHBox); delete (label_w); delete (read_w); delete (write_w); delete (spin_w); delete (slider_w); delete (push_w); delete (browse_w); delete (check_w); delete (led_w); delete (hSpacer); } void INDI_E::buildSwitch(QButtonGroup *groupB, ISwitch *sw) { name = sw->name; label = i18nc(libindi_strings_context, sw->label); if (label == "(I18N_EMPTY_MESSAGE)") label = sw->label; sp = sw; if (label.isEmpty()) label = i18nc(libindi_strings_context, sw->name); if (label == "(I18N_EMPTY_MESSAGE)") label = sw->name; if (groupB == nullptr) return; switch (guiProp->getGUIType()) { case PG_BUTTONS: push_w = new QPushButton(label, guiProp->getGroup()->getContainer()); push_w->setCheckable(true); groupB->addButton(push_w); syncSwitch(); guiProp->addWidget(push_w); push_w->show(); if (sw->svp->p == IP_RO) push_w->setEnabled(sw->s == ISS_ON); break; case PG_RADIO: check_w = new QCheckBox(label, guiProp->getGroup()->getContainer()); groupB->addButton(check_w); syncSwitch(); guiProp->addWidget(check_w); check_w->show(); if (sw->svp->p == IP_RO) check_w->setEnabled(sw->s == ISS_ON); break; default: break; } } void INDI_E::buildMenuItem(ISwitch *sw) { buildSwitch(nullptr, sw); } void INDI_E::buildText(IText *itp) { name = itp->name; if (itp->label[0]) label = i18nc(libindi_strings_context, itp->label); if (label == "(I18N_EMPTY_MESSAGE)") label = itp->label; tp = itp; if (label.isEmpty()) label = i18nc(libindi_strings_context, itp->name); if (label == "(I18N_EMPTY_MESSAGE)") label = itp->name; setupElementLabel(); if (tp->text[0]) text = i18nc(libindi_strings_context, tp->text); switch (dataProp->getPermission()) { case IP_RW: setupElementRead(ELEMENT_READ_WIDTH * KStars::Instance()->devicePixelRatio()); setupElementWrite(ELEMENT_WRITE_WIDTH * KStars::Instance()->devicePixelRatio()); break; case IP_RO: setupElementRead(ELEMENT_FULL_WIDTH * KStars::Instance()->devicePixelRatio()); break; case IP_WO: setupElementWrite(ELEMENT_FULL_WIDTH * KStars::Instance()->devicePixelRatio()); break; } guiProp->addLayout(EHBox); //pp->PVBox->addLayout(EHBox); } void INDI_E::setupElementLabel() { QPalette palette; - label_w = new KSqueezedTextLabel(guiProp->getGroup()->getContainer()); - label_w->setMinimumWidth(ELEMENT_LABEL_WIDTH * KStars::Instance()->devicePixelRatio()); - label_w->setMaximumWidth(ELEMENT_LABEL_WIDTH * KStars::Instance()->devicePixelRatio()); - label_w->setFrameShape(KSqueezedTextLabel::Box); - label_w->setFrameShadow(KSqueezedTextLabel::Sunken); + label_w = new QLabel(guiProp->getGroup()->getContainer()); + label_w->setFixedWidth(ELEMENT_LABEL_WIDTH * KStars::Instance()->devicePixelRatio()); + label_w->setMinimumHeight(ELEMENT_LABEL_HEIGHT * KStars::Instance()->devicePixelRatio()); + label_w->setFrameShape(QLabel::Box); + label_w->setFrameShadow(QLabel::Sunken); label_w->setMargin(2); palette.setColor(label_w->backgroundRole(), QColor(224, 232, 238)); label_w->setPalette(palette); label_w->setTextFormat(Qt::RichText); label_w->setAlignment(Qt::AlignCenter); label_w->setWordWrap(true); if (label.length() > MAX_LABEL_LENGTH) { QFont tempFont(label_w->font()); tempFont.setPointSize(tempFont.pointSize() - MED_INDI_FONT); label_w->setFont(tempFont); } label_w->setText(label); EHBox->addWidget(label_w); } void INDI_E::syncSwitch() { QFont buttonFont; switch (guiProp->getGUIType()) { case PG_BUTTONS: if (sp->s == ISS_ON) { push_w->setChecked(true); //push_w->setDown(true); buttonFont = push_w->font(); buttonFont.setBold(true); push_w->setFont(buttonFont); if (sp->svp->p == IP_RO) push_w->setEnabled(true); } else { push_w->setChecked(false); //push_w->setDown(false); buttonFont = push_w->font(); buttonFont.setBold(false); push_w->setFont(buttonFont); if (sp->svp->p == IP_RO) push_w->setEnabled(false); } break; case PG_RADIO: if (sp->s == ISS_ON) { check_w->setChecked(true); if (sp->svp->p == IP_RO) check_w->setEnabled(true); } else { check_w->setChecked(false); if (sp->svp->p == IP_RO) check_w->setEnabled(false); } break; default: break; } } void INDI_E::syncText() { if (tp == nullptr) return; if (tp->tvp->p != IP_WO) { if (tp->text[0]) read_w->setText(i18nc(libindi_strings_context, tp->text)); else read_w->setText(tp->text); } // If write-only else { write_w->setText(tp->text); } } void INDI_E::syncNumber() { char iNumber[MAXINDIFORMAT]; if (np == nullptr || read_w == nullptr) return; numberFormat(iNumber, np->format, np->value); text = iNumber; read_w->setText(text); if (spin_w) { if (np->min != spin_w->minimum()) setMin(); if (np->max != spin_w->maximum()) setMax(); } } void INDI_E::updateTP() { if (tp == nullptr) return; IUSaveText(tp, write_w->text().toHtmlEscaped().toLatin1().constData()); } void INDI_E::updateNP() { if (np == nullptr) return; if (write_w != nullptr) { if (write_w->text().isEmpty()) return; f_scansexa(write_w->text().replace(',', '.').toLatin1().constData(), &(np->value)); return; } if (spin_w != nullptr) np->value = spin_w->value(); } void INDI_E::setText(const QString &newText) { if (tp == nullptr) return; switch (dataProp->getPermission()) { case IP_RO: read_w->setText(newText); break; case IP_WO: case IP_RW: text = newText; IUSaveText(tp, newText.toLatin1().constData()); read_w->setText(newText); write_w->setText(newText); break; } } void INDI_E::buildBLOB(IBLOB *ibp) { name = ibp->name; label = i18nc(libindi_strings_context, ibp->label); if (label == "(I18N_EMPTY_MESSAGE)") label = ibp->label; bp = ibp; if (label.isEmpty()) label = i18nc(libindi_strings_context, ibp->name); if (label == "(I18N_EMPTY_MESSAGE)") label = ibp->name; setupElementLabel(); text = i18n("INDI DATA STREAM"); switch (dataProp->getPermission()) { case IP_RW: setupElementRead(ELEMENT_READ_WIDTH * KStars::Instance()->devicePixelRatio()); setupElementWrite(ELEMENT_WRITE_WIDTH * KStars::Instance()->devicePixelRatio()); setupBrowseButton(); break; case IP_RO: setupElementRead(ELEMENT_FULL_WIDTH * KStars::Instance()->devicePixelRatio()); break; case IP_WO: setupElementWrite(ELEMENT_FULL_WIDTH * KStars::Instance()->devicePixelRatio()); setupBrowseButton(); break; } guiProp->addLayout(EHBox); } void INDI_E::buildNumber(INumber *inp) { bool scale = false; char iNumber[MAXINDIFORMAT]; name = inp->name; label = i18nc(libindi_strings_context, inp->label); if (label == "(I18N_EMPTY_MESSAGE)") label = inp->label; np = inp; if (label.isEmpty()) label = i18nc(libindi_strings_context, inp->name); if (label == "(I18N_EMPTY_MESSAGE)") label = inp->name; numberFormat(iNumber, np->format, np->value); text = iNumber; setupElementLabel(); if (np->step != 0 && (np->max - np->min) / np->step <= 100) scale = true; switch (dataProp->getPermission()) { case IP_RW: setupElementRead(ELEMENT_READ_WIDTH * KStars::Instance()->devicePixelRatio()); if (scale) setupElementScale(ELEMENT_WRITE_WIDTH * KStars::Instance()->devicePixelRatio()); else setupElementWrite(ELEMENT_WRITE_WIDTH * KStars::Instance()->devicePixelRatio()); guiProp->addLayout(EHBox); break; case IP_RO: setupElementRead(ELEMENT_READ_WIDTH * KStars::Instance()->devicePixelRatio()); guiProp->addLayout(EHBox); break; case IP_WO: if (scale) setupElementScale(ELEMENT_FULL_WIDTH * KStars::Instance()->devicePixelRatio()); else setupElementWrite(ELEMENT_FULL_WIDTH * KStars::Instance()->devicePixelRatio()); guiProp->addLayout(EHBox); break; } } void INDI_E::buildLight(ILight *ilp) { name = ilp->name; label = i18nc(libindi_strings_context, ilp->label); if (label == "(I18N_EMPTY_MESSAGE)") label = ilp->label; lp = ilp; if (label.isEmpty()) label = i18nc(libindi_strings_context, ilp->name); if (label == "(I18N_EMPTY_MESSAGE)") label = ilp->name; led_w = new KLed(guiProp->getGroup()->getContainer()); led_w->setMaximumSize(16, 16); led_w->setLook(KLed::Sunken); syncLight(); EHBox->addWidget(led_w); setupElementLabel(); guiProp->addLayout(EHBox); } void INDI_E::syncLight() { if (lp == nullptr) return; switch (lp->s) { case IPS_IDLE: led_w->setColor(Qt::gray); break; case IPS_OK: led_w->setColor(Qt::green); break; case IPS_BUSY: led_w->setColor(Qt::yellow); break; case IPS_ALERT: led_w->setColor(Qt::red); break; default: break; } } void INDI_E::setupElementScale(int length) { if (np == nullptr) return; int steps = (int)((np->max - np->min) / np->step); spin_w = new QDoubleSpinBox(guiProp->getGroup()->getContainer()); spin_w->setRange(np->min, np->max); spin_w->setSingleStep(np->step); spin_w->setValue(np->value); spin_w->setDecimals(3); slider_w = new QSlider(Qt::Horizontal, guiProp->getGroup()->getContainer()); slider_w->setRange(0, steps); slider_w->setPageStep(1); slider_w->setValue((int)((np->value - np->min) / np->step)); connect(spin_w, SIGNAL(valueChanged(double)), this, SLOT(spinChanged(double))); connect(slider_w, SIGNAL(sliderMoved(int)), this, SLOT(sliderChanged(int))); if (length == ELEMENT_FULL_WIDTH * KStars::Instance()->devicePixelRatio()) spin_w->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); else spin_w->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); spin_w->setMinimumWidth((int)(length * 0.45)); slider_w->setMinimumWidth((int)(length * 0.55)); EHBox->addWidget(slider_w); EHBox->addWidget(spin_w); } void INDI_E::spinChanged(double value) { int slider_value = (int)((value - np->min) / np->step); slider_w->setValue(slider_value); } void INDI_E::sliderChanged(int value) { double spin_value = (value * np->step) + np->min; spin_w->setValue(spin_value); } void INDI_E::setMin() { if (spin_w) { spin_w->setMinimum(np->min); spin_w->setValue(np->value); } if (slider_w) { slider_w->setMaximum((int)((np->max - np->min) / np->step)); slider_w->setMinimum(0); slider_w->setPageStep(1); slider_w->setValue((int)((np->value - np->min) / np->step)); } } void INDI_E::setMax() { if (spin_w) { spin_w->setMaximum(np->max); spin_w->setValue(np->value); } if (slider_w) { slider_w->setMaximum((int)((np->max - np->min) / np->step)); slider_w->setMinimum(0); slider_w->setPageStep(1); slider_w->setValue((int)((np->value - np->min) / np->step)); } } void INDI_E::setupElementWrite(int length) { write_w = new QLineEdit(guiProp->getGroup()->getContainer()); write_w->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); write_w->setMinimumWidth(length); write_w->setMaximumWidth(length); write_w->setText(text); //QObject::connect(write_w, SIGNAL(returnPressed(QString)), this, SLOT(updateTP())); QObject::connect(write_w, SIGNAL(returnPressed()), guiProp, SLOT(sendText())); EHBox->addWidget(write_w); } void INDI_E::setupElementRead(int length) { read_w = new QLineEdit(guiProp->getGroup()->getContainer()); read_w->setMinimumWidth(length); read_w->setFocusPolicy(Qt::NoFocus); read_w->setCursorPosition(0); read_w->setAlignment(Qt::AlignCenter); read_w->setReadOnly(true); read_w->setText(text); EHBox->addWidget(read_w); } void INDI_E::setupBrowseButton() { browse_w = new QPushButton(guiProp->getGroup()->getContainer()); browse_w->setIcon(QIcon::fromTheme("document-open")); browse_w->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); browse_w->setMinimumWidth(MIN_SET_WIDTH * KStars::Instance()->devicePixelRatio()); browse_w->setMaximumWidth(MAX_SET_WIDTH * KStars::Instance()->devicePixelRatio()); EHBox->addWidget(browse_w); QObject::connect(browse_w, SIGNAL(clicked()), this, SLOT(browseBlob())); } void INDI_E::browseBlob() { QFile fp; QString filename; QString format; int pos = 0; QUrl currentURL; currentURL = QFileDialog::getOpenFileUrl(); // if user presses cancel if (currentURL.isEmpty()) return; if (currentURL.isValid()) write_w->setText(currentURL.toLocalFile()); fp.setFileName(currentURL.toLocalFile()); if ((pos = filename.lastIndexOf(".")) != -1) format = filename.mid(pos, filename.length()); //qDebug() << "Filename is " << fp.fileName() << endl; if (!fp.open(QIODevice::ReadOnly)) { KSNotification::error( i18n("Cannot open file %1 for reading", filename)); return; } bp->bloblen = bp->size = fp.size(); bp->blob = static_cast(realloc(bp->blob, bp->size)); if (bp->blob == nullptr) { KSNotification::error( i18n("Not enough memory for file %1", filename)); fp.close(); return; } memcpy(bp->blob, fp.readAll().constData(), bp->size); blobDirty = true; } QString INDI_E::getWriteField() { if (write_w) return write_w->text(); else return nullptr; } QString INDI_E::getReadField() { if (read_w) return read_w->text(); else return nullptr; } diff --git a/kstars/indi/indielement.h b/kstars/indi/indielement.h index 7061d6af1..2947593d8 100644 --- a/kstars/indi/indielement.h +++ b/kstars/indi/indielement.h @@ -1,134 +1,146 @@ /* INDI Element Copyright (C) 2003 Jasem Mutlaq (mutlaqja@ikarustech.com) This application is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. 2004-01-15 INDI element is the most basic unit of the INDI KStars client. */ #pragma once #include "indicommon.h" #include #include #include /* Forward declaration */ class QLineEdit; class QDoubleSpinBox; class QPushButton; class QHBoxLayout; class QSpacerItem; class QCheckBox; class QButtonGroup; class QSlider; class KLed; -class KSqueezedTextLabel; +class QLabel; class INDI_P; /** * @class INDI_E * INDI_E represents an INDI GUI element (Number, Text, Switch, Light, or BLOB) within an INDI property. * It is the most basic GUI representation of property elements. * * @author Jasem Mutlaq */ class INDI_E : public QObject { - Q_OBJECT - public: - INDI_E(INDI_P *gProp, INDI::Property *dProp); - ~INDI_E(); - - const QString &getLabel() { return label; } - const QString &getName() { return name; } - - QString getWriteField(); - QString getReadField(); - - void buildSwitch(QButtonGroup *groupB, ISwitch *sw); - void buildMenuItem(ISwitch *sw); - void buildText(IText *itp); - void buildNumber(INumber *inp); - void buildLight(ILight *ilp); - void buildBLOB(IBLOB *ibp); - - // Updates GUI from data in INDI properties - void syncSwitch(); - void syncText(); - void syncNumber(); - void syncLight(); - - // Save GUI data in INDI properties - void updateTP(); - void updateNP(); - - void setText(const QString &newText); - - void setMin(); - void setMax(); - - void setupElementLabel(); - void setupElementRead(int length); - void setupElementWrite(int length); - void setupElementScale(int length); - void setupBrowseButton(); - - bool getBLOBDirty() { return blobDirty; } - void setBLOBDirty(bool isDirty) { blobDirty = isDirty; } - - public slots: - void spinChanged(double value); - void sliderChanged(int value); - void browseBlob(); - -private: - /// Name - QString name; - /// Label is the name by default, unless specified - QString label; - /// Parent DATA property - INDI::Property *dataProp { nullptr }; - /// Parent GUI property - INDI_P *guiProp { nullptr }; - /// Horizontal layout - QHBoxLayout *EHBox { nullptr }; - /// Label widget - KSqueezedTextLabel *label_w { nullptr }; - /// Read field widget - QLineEdit *read_w { nullptr }; - /// Write field widget - QLineEdit *write_w { nullptr }; - /// Light led widget - KLed *led_w { nullptr }; - /// Spinbox widget - QDoubleSpinBox *spin_w { nullptr }; - /// Slider widget - QSlider *slider_w { nullptr }; - /// Push button widget - QPushButton *push_w { nullptr }; - /// Browse button widget - QPushButton *browse_w { nullptr }; - /// Check box widget - QCheckBox *check_w { nullptr }; - /// Horizontal spacer widget - QSpacerItem *hSpacer { nullptr }; - - ISwitch *sp { nullptr }; - INumber *np { nullptr }; - IText *tp { nullptr }; - ILight *lp { nullptr }; - IBLOB *bp { nullptr }; - - bool blobDirty { false }; - /// Current text - QString text; + Q_OBJECT + public: + INDI_E(INDI_P *gProp, INDI::Property *dProp); + ~INDI_E(); + + const QString &getLabel() + { + return label; + } + const QString &getName() + { + return name; + } + + QString getWriteField(); + QString getReadField(); + + void buildSwitch(QButtonGroup *groupB, ISwitch *sw); + void buildMenuItem(ISwitch *sw); + void buildText(IText *itp); + void buildNumber(INumber *inp); + void buildLight(ILight *ilp); + void buildBLOB(IBLOB *ibp); + + // Updates GUI from data in INDI properties + void syncSwitch(); + void syncText(); + void syncNumber(); + void syncLight(); + + // Save GUI data in INDI properties + void updateTP(); + void updateNP(); + + void setText(const QString &newText); + + void setMin(); + void setMax(); + + void setupElementLabel(); + void setupElementRead(int length); + void setupElementWrite(int length); + void setupElementScale(int length); + void setupBrowseButton(); + + bool getBLOBDirty() + { + return blobDirty; + } + void setBLOBDirty(bool isDirty) + { + blobDirty = isDirty; + } + + public slots: + void spinChanged(double value); + void sliderChanged(int value); + void browseBlob(); + + private: + /// Name + QString name; + /// Label is the name by default, unless specified + QString label; + /// Parent DATA property + INDI::Property *dataProp { nullptr }; + /// Parent GUI property + INDI_P *guiProp { nullptr }; + /// Horizontal layout + QHBoxLayout *EHBox { nullptr }; + /// Label widget + QLabel *label_w { nullptr }; + /// Read field widget + QLineEdit *read_w { nullptr }; + /// Write field widget + QLineEdit *write_w { nullptr }; + /// Light led widget + KLed *led_w { nullptr }; + /// Spinbox widget + QDoubleSpinBox *spin_w { nullptr }; + /// Slider widget + QSlider *slider_w { nullptr }; + /// Push button widget + QPushButton *push_w { nullptr }; + /// Browse button widget + QPushButton *browse_w { nullptr }; + /// Check box widget + QCheckBox *check_w { nullptr }; + /// Horizontal spacer widget + QSpacerItem *hSpacer { nullptr }; + + ISwitch *sp { nullptr }; + INumber *np { nullptr }; + IText *tp { nullptr }; + ILight *lp { nullptr }; + IBLOB *bp { nullptr }; + + bool blobDirty { false }; + /// Current text + QString text; };