Kstars

indiproperty.cpp
1/*
2 SPDX-FileCopyrightText: 2003 Jasem Mutlaq <mutlaqja@ikarustech.com>
3
4 SPDX-License-Identifier: GPL-2.0-or-later
5*/
6
7#include "indiproperty.h"
8
9#include "clientmanager.h"
10#include "indidevice.h"
11#include "indielement.h"
12#include "indigroup.h"
13#include "kstars.h"
14#include "Options.h"
15#include "skymap.h"
16#include "dialogs/timedialog.h"
17
18#include <indicom.h>
19#include <indiproperty.h>
20
21#include <KLed>
22#include <KSqueezedTextLabel>
23
24#include <QAbstractButton>
25#include <QButtonGroup>
26#include <QCheckBox>
27#include <QComboBox>
28#include <QHBoxLayout>
29#include <QPushButton>
30#include <QVBoxLayout>
31
32extern const char *libindi_strings_context;
33
34/*******************************************************************
35** INDI Property: contains widgets, labels, and their status
36*******************************************************************/
37INDI_P::INDI_P(INDI_G *ipg, INDI::Property prop) : QWidget(ipg), pg(ipg), dataProp(prop)
38{
39 name = QString(prop.getName());
40
41 PHBox = new QHBoxLayout(this);
42 PHBox->setObjectName("Property Horizontal Layout");
43 PHBox->setContentsMargins(0, 0, 0, 0);
44 PVBox = new QVBoxLayout;
45 PVBox->setContentsMargins(0, 0, 0, 0);
46 PVBox->setObjectName("Property Vertical Layout");
47
48 initGUI();
49}
50
51void INDI_P::updateStateLED()
52{
53 /* set state light */
54 switch (dataProp.getState())
55 {
56 case IPS_IDLE:
57 ledStatus->setColor(Qt::gray);
58 break;
59
60 case IPS_OK:
61 ledStatus->setColor(Qt::green);
62 break;
63
64 case IPS_BUSY:
65 ledStatus->setColor(Qt::yellow);
66 break;
67
68 case IPS_ALERT:
69 ledStatus->setColor(Qt::red);
70 break;
71 }
72}
73
74/* build widgets for property pp using info in root.
75 */
76void INDI_P::initGUI()
77{
78 QString label = i18nc(libindi_strings_context, dataProp.getLabel());
79
80 if (label == "(I18N_EMPTY_MESSAGE)")
81 label = dataProp.getLabel();
82
83 /* add to GUI group */
84 ledStatus = new KLed(this);
85 ledStatus->setMaximumSize(16, 16);
86 ledStatus->setLook(KLed::Sunken);
87
88 updateStateLED();
89
90 /* Create a horizontally layout widget around light and label */
91 QWidget *labelWidget = new QWidget(this);
93 labelLayout->setContentsMargins(0, 0, 0, 0);
94
95 /* #1 First widget is the LED status indicator */
96 labelLayout->addWidget(ledStatus);
97
98 if (label.isEmpty())
99 {
100 label = i18nc(libindi_strings_context, name.toUtf8());
101 if (label == "(I18N_EMPTY_MESSAGE)")
102 label = name.toUtf8();
103
104 labelW = new KSqueezedTextLabel(label, this);
105 }
106 else
107 labelW = new KSqueezedTextLabel(label, this);
108
109 //labelW->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
110 labelW->setFrameShape(QFrame::Box);
112 labelW->setMargin(2);
113 labelW->setFixedWidth(PROPERTY_LABEL_WIDTH * KStars::Instance()->devicePixelRatio());
116 labelW->setWordWrap(true);
117
118 labelLayout->addWidget(labelW);
120
121 labelWidget->setToolTip(label);
122 ledStatus->show();
123 labelW->show();
124
125 // #3 Add the Vertical layout which may contain several elements
126 PHBox->addLayout(PVBox);
127
128 switch (dataProp.getType())
129 {
130 case INDI_SWITCH:
131 if (dataProp.getSwitch()->getRule() == ISR_NOFMANY)
132 guiType = PG_RADIO;
133 else if (dataProp.getSwitch()->count() > 4)
134 guiType = PG_MENU;
135 else
136 guiType = PG_BUTTONS;
137
138 if (guiType == PG_MENU)
139 buildMenuGUI();
140 else
141 buildSwitchGUI();
142 break;
143
144 case INDI_TEXT:
145 buildTextGUI();
146 break;
147
148 case INDI_NUMBER:
149 buildNumberGUI();
150 break;
151
152 case INDI_LIGHT:
153 buildLightGUI();
154 break;
155
156 case INDI_BLOB:
157 buildBLOBGUI();
158 break;
159
160 default:
161 break;
162 }
163 labelWidget->raise();
164}
165
166void INDI_P::buildSwitchGUI()
167{
168 auto svp = static_cast<ISwitchVectorProperty*>(dataProp.getSwitch());
169
170 if (!svp)
171 return;
172
173 groupB = new QButtonGroup(this);
174
175 if (guiType == PG_BUTTONS)
176 {
177 if (svp->r == ISR_1OFMANY)
178 groupB->setExclusive(true);
179 else
180 groupB->setExclusive(false);
181 }
182 else if (guiType == PG_RADIO)
183 groupB->setExclusive(false);
184
185 if (svp->p != IP_RO)
186 QObject::connect(groupB, SIGNAL(buttonClicked(QAbstractButton*)), this, SLOT(newSwitch(QAbstractButton*)));
187
188 for (int i = 0; i < svp->nsp; i++)
189 {
190 auto lp = new INDI_E(this, dataProp);
191 lp->buildSwitch(groupB, svp->sp + i);
192 elementList.append(lp);
193 }
194
196
197 PHBox->addItem(horSpacer);
198}
199
200void INDI_P::buildTextGUI()
201{
202 auto tvp = static_cast<ITextVectorProperty*>(dataProp.getText());
203
204 if (!tvp)
205 return;
206
207 for (int i = 0; i < tvp->ntp; i++)
208 {
209 auto lp = new INDI_E(this, dataProp);
210 lp->buildText(tvp->tp + i);
211 elementList.append(lp);
212 }
213
215
216 PHBox->addItem(horSpacer);
217
218 if (tvp->p == IP_RO)
219 return;
220
221 // INDI STD, but we use our own controls
222 if (name == "TIME_UTC")
223 setupSetButton(i18n("Time"));
224 else
225 setupSetButton(i18n("Set"));
226}
227
228void INDI_P::buildNumberGUI()
229{
230 auto nvp = static_cast<INumberVectorProperty*>(dataProp.getNumber());
231
232 if (!nvp)
233 return;
234
235 for (int i = 0; i < nvp->nnp; i++)
236 {
237 auto lp = new INDI_E(this, dataProp);
238 lp->buildNumber(nvp->np + i);
239 elementList.append(lp);
240 }
241
243
244 PHBox->addItem(horSpacer);
245
246 if (nvp->p == IP_RO)
247 return;
248
249 setupSetButton(i18n("Set"));
250}
251
252void INDI_P::buildLightGUI()
253{
254 auto lvp = static_cast<ILightVectorProperty*>(dataProp.getLight());
255
256 if (!lvp)
257 return;
258
259 for (int i = 0; i < lvp->nlp; i++)
260 {
261 auto ep = new INDI_E(this, dataProp);
262 ep->buildLight(lvp->lp + i);
263 elementList.append(ep);
264 }
265
267
268 PHBox->addItem(horSpacer);
269}
270
271void INDI_P::buildBLOBGUI()
272{
273 auto bvp = static_cast<IBLOBVectorProperty*>(dataProp.getBLOB());
274
275 if (!bvp)
276 return;
277
278 for (int i = 0; i < bvp->nbp; i++)
279 {
280 auto lp = new INDI_E(this, dataProp);
281 lp->buildBLOB(bvp->bp + i);
282 elementList.append(lp);
283 }
284
286
287 PHBox->addItem(horSpacer);
288
289 enableBLOBC = new QCheckBox();
290 enableBLOBC->setIcon(QIcon::fromTheme("network-modem"));
291 enableBLOBC->setChecked(true);
292 enableBLOBC->setToolTip(i18n("Enable binary data transfer from this property to KStars and vice-versa."));
293
294 PHBox->addWidget(enableBLOBC);
295
296 connect(enableBLOBC, SIGNAL(stateChanged(int)), this, SLOT(setBLOBOption(int)));
297
298 if (dataProp.getPermission() != IP_RO)
299 setupSetButton(i18n("Upload"));
300}
301
302void INDI_P::setBLOBOption(int state)
303{
304 pg->getDevice()->getClientManager()->setBLOBEnabled(state == Qt::Checked, dataProp.getDeviceName(), dataProp.getName());
305}
306
307void INDI_P::newSwitch(QAbstractButton *button)
308{
309 auto svp = dataProp.getSwitch();
310 QString buttonText = button->text();
311
312 if (!svp)
313 return;
314
315 buttonText.remove('&');
316
317 for (auto &el : elementList)
318 {
319 if (el->getLabel() == buttonText)
320 {
321 newSwitch(el->getName());
322 return;
323 }
324 }
325}
326
327void INDI_P::resetSwitch()
328{
329 auto svp = dataProp.getSwitch();
330
331 if (!svp)
332 return;
333
334 if (menuC != nullptr)
335 {
336 menuC->setCurrentIndex(svp->findOnSwitchIndex());
337 }
338}
339
340void INDI_P::newSwitch(int index)
341{
342 auto svp = dataProp.getSwitch();
343
344 if (!svp)
345 return;
346
347 if (index >= svp->count() || index < 0)
348 return;
349
350 auto sp = svp->at(index);
351
352 svp->reset();
353 sp->setState(ISS_ON);
354
355 sendSwitch();
356}
357
358void INDI_P::newSwitch(const QString &name)
359{
360 auto svp = dataProp.getSwitch();
361
362 if (!svp)
363 return;
364
365 auto sp = svp->findWidgetByName(name.toLatin1().constData());
366
367 if (!sp)
368 return;
369
370 if (svp->getRule() == ISR_1OFMANY)
371 {
372 svp->reset();
373 sp->setState(ISS_ON);
374 }
375 else
376 {
377 if (svp->getRule() == ISR_ATMOST1)
378 {
379 ISState prev_state = sp->getState();
380 svp->reset();
381 sp->setState(prev_state);
382 }
383
384 sp->setState(sp->getState() == ISS_ON ? ISS_OFF : ISS_ON);
385 }
386
387 sendSwitch();
388}
389
390void INDI_P::sendSwitch()
391{
392 auto svp = dataProp.getSwitch();
393
394 if (!svp)
395 return;
396
397 svp->setState(IPS_BUSY);
398
399 for (auto &el : elementList)
400 el->syncSwitch();
401
402 updateStateLED();
403
404 // Send it to server
405 pg->getDevice()->getClientManager()->sendNewProperty(svp);
406}
407
408void INDI_P::sendText()
409{
410 switch (dataProp.getType())
411 {
412 case INDI_TEXT:
413 {
414 auto tvp = dataProp.getText();
415 if (!tvp)
416 return;
417
418 tvp->setState(IPS_BUSY);
419
420 for (auto &el : elementList)
421 el->updateTP();
422
423 pg->getDevice()->getClientManager()->sendNewProperty(tvp);
424
425 break;
426 }
427
428 case INDI_NUMBER:
429 {
430 auto nvp = dataProp.getNumber();
431 if (!nvp)
432 return;
433
434 nvp->setState(IPS_BUSY);
435
436 for (auto &el : elementList)
437 el->updateNP();
438
439 pg->getDevice()->getClientManager()->sendNewProperty(nvp);
440 break;
441 }
442 default:
443 break;
444 }
445
446 updateStateLED();
447}
448
449void INDI_P::buildMenuGUI()
450{
453 int onItem = -1;
454 auto svp = dataProp.getSwitch();
455
456 if (!svp)
457 return;
458
459 menuC = new QComboBox(this);
460
461 if (svp->getPermission() == IP_RO)
462 connect(menuC, SIGNAL(activated(int)), this, SLOT(resetSwitch()));
463 else
464 connect(menuC, SIGNAL(activated(int)), this, SLOT(newSwitch(int)));
465
466 for (int i = 0; i < svp->nsp; i++)
467 {
468 auto tp = svp->at(i);
469
470 if (tp->getState() == ISS_ON)
471 onItem = i;
472
473 auto lp = new INDI_E(this, dataProp);
474
475 lp->buildMenuItem(tp);
476
477 oneOption = i18nc(libindi_strings_context, lp->getLabel().toUtf8());
478
479 if (oneOption == "(I18N_EMPTY_MESSAGE)")
480 oneOption = lp->getLabel().toUtf8();
481
482 menuOptions.append(oneOption);
483
484 elementList.append(lp);
485 }
486
487 menuC->addItems(menuOptions);
488 menuC->setCurrentIndex(onItem);
489
491
492 PHBox->addWidget(menuC);
493 PHBox->addItem(horSpacer);
494}
495
496void INDI_P::setupSetButton(const QString &caption)
497{
498 setB = new QPushButton(caption, this);
500 setB->setMinimumWidth(MIN_SET_WIDTH * KStars::Instance()->devicePixelRatio());
501 setB->setMaximumWidth(MAX_SET_WIDTH * KStars::Instance()->devicePixelRatio());
502
503 connect(setB, SIGNAL(clicked()), this, SLOT(processSetButton()));
504
505 PHBox->addWidget(setB);
506}
507
508void INDI_P::addWidget(QWidget *w)
509{
510 PHBox->addWidget(w);
511}
512
513void INDI_P::addLayout(QHBoxLayout *layout)
514{
515 PVBox->addLayout(layout);
516}
517
518void INDI_P::updateMenuGUI()
519{
520 auto svp = dataProp.getSwitch();
521
522 if (!svp)
523 return;
524
525 int currentIndex = svp->findOnSwitchIndex();
526 menuC->setCurrentIndex(currentIndex);
527}
528
529void INDI_P::processSetButton()
530{
531 switch (dataProp.getType())
532 {
533 case INDI_TEXT:
534 //if (!strcmp(dataProp.getName(), "TIME_UTC"))
535 if (dataProp.isNameMatch("TIME_UTC"))
536 newTime();
537 else
538 sendText();
539
540 break;
541
542 case INDI_NUMBER:
543 sendText();
544 break;
545
546 case INDI_BLOB:
547 sendBlob();
548 break;
549
550 default:
551 break;
552 }
553}
554
555void INDI_P::sendBlob()
556{
557 auto bvp = dataProp.getBLOB();
558
559 if (!bvp)
560 return;
561
562 bvp->setState(IPS_BUSY);
563
564 pg->getDevice()->getClientManager()->startBlob(bvp->getDeviceName(), bvp->getName(),
566
567 for (int i = 0; i < elementList.count(); i++)
568 {
569 auto bp = bvp->at(i);
570 pg->getDevice()->getClientManager()->sendOneBlob(bp);
571 }
572
573 pg->getDevice()->getClientManager()->finishBlob();
574
575 updateStateLED();
576}
577
579{
580 INDI_E *timeEle = getElement("UTC");
581 INDI_E *offsetEle = getElement("OFFSET");
582 if (!timeEle || !offsetEle)
583 return;
584
585 TimeDialog timedialog(KStars::Instance()->data()->ut(), KStars::Instance()->data()->geo(), KStars::Instance(),
586 true);
587
588 if (timedialog.exec() == QDialog::Accepted)
589 {
590 QTime newTime(timedialog.selectedTime());
591 QDate newDate(timedialog.selectedDate());
592
593 timeEle->setText(QString("%1-%2-%3T%4:%5:%6")
594 .arg(newDate.year())
595 .arg(newDate.month())
596 .arg(newDate.day())
597 .arg(newTime.hour())
598 .arg(newTime.minute())
599 .arg(newTime.second()));
600
601 offsetEle->setText(QString().setNum(KStars::Instance()->data()->geo()->TZ(), 'g', 2));
602
603 sendText();
604 }
605 else
606 return;
607}
608
609INDI_E *INDI_P::getElement(const QString &elementName) const
610{
611 for (auto *ep : elementList)
612 {
613 if (ep->getName() == elementName)
614 return ep;
615 }
616
617 return nullptr;
618}
619
620bool INDI_P::isRegistered() const
621{
622 return (dataProp && dataProp.getRegistered());
623}
INDI_E represents an INDI GUI element (Number, Text, Switch, Light, or BLOB) within an INDI property.
Definition indielement.h:42
INDI_G represents a collection of INDI properties that share a common group.
Definition indigroup.h:31
void newTime()
newTime Display dialog to set UTC date and time to the driver.
void setupSetButton(const QString &caption)
Setup the 'set' button in the property.
void setColor(const QColor &color)
void setLook(Look look)
virtual void setAlignment(Qt::Alignment)
void setMargin(int margin)
static KStars * Instance()
Definition kstars.h:123
A class for adjusting the Time and Date.
Definition timedialog.h:38
QString i18nc(const char *context, const char *text, const TYPE &arg...)
QString i18n(const char *text, const TYPE &arg...)
QString name(GameStandardAction id)
QString label(StandardShortcut id)
void setChecked(bool)
void setIcon(const QIcon &icon)
virtual void addItem(QLayoutItem *item) override
void addLayout(QLayout *layout, int stretch)
void addWidget(QWidget *widget, int stretch, Qt::Alignment alignment)
void setExclusive(bool)
const char * constData() const const
void addItems(const QStringList &texts)
void setCurrentIndex(int index)
QDateTime currentDateTimeUtc()
QString toString(QStringView format, QCalendar cal) const const
void setFrameShadow(Shadow)
void setFrameShape(Shape)
QIcon fromTheme(const QString &name)
void setTextFormat(Qt::TextFormat)
void setWordWrap(bool on)
void setContentsMargins(const QMargins &margins)
void append(QList< T > &&value)
qsizetype count() const const
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
qreal devicePixelRatio() const const
QString arg(Args &&... args) const const
bool isEmpty() const const
QString & remove(QChar ch, Qt::CaseSensitivity cs)
QByteArray toLatin1() const const
QByteArray toUtf8() const const
AlignVCenter
RichText
QWidget(QWidget *parent, Qt::WindowFlags f)
QLayout * layout() const const
void setMaximumSize(const QSize &)
void setMaximumWidth(int maxw)
void setMinimumWidth(int minw)
void setFixedWidth(int w)
void show()
void setSizePolicy(QSizePolicy)
void setToolTip(const QString &)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri Jun 7 2024 12:06:18 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.