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);
92 QHBoxLayout *labelLayout = new QHBoxLayout(labelWidget);
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);
119 PHBox->addWidget(labelWidget, 0, Qt::AlignTop | Qt::AlignLeft);
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{
451 QStringList menuOptions;
452 QString oneOption;
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:122
A class for adjusting the Time and Date.
Definition timedialog.h:38
QDate selectedDate(void)
QTime selectedTime(void)
QString i18nc(const char *context, const char *text, const TYPE &arg...)
QString i18n(const char *text, const TYPE &arg...)
QString name(StandardAction 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)
int day() const const
int month() const const
int year() const const
QDateTime currentDateTimeUtc()
QString toString(QStringView format, QCalendar cal) const const
virtual int exec()
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)
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 raise()
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 Nov 1 2024 18:54:15 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.