Kstars

indiproperty.cpp
1 /*
2  SPDX-FileCopyrightText: 2003 Jasem Mutlaq <[email protected]>
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 
32 extern const char *libindi_strings_context;
33 
34 /*******************************************************************
35 ** INDI Property: contains widgets, labels, and their status
36 *******************************************************************/
37 INDI_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 
51 void 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  */
76 void 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);
111  labelW->setFrameShadow(QFrame::Sunken);
112  labelW->setMargin(2);
113  labelW->setFixedWidth(PROPERTY_LABEL_WIDTH * KStars::Instance()->devicePixelRatio());
114  labelW->setTextFormat(Qt::RichText);
115  labelW->setAlignment(Qt::AlignVCenter | Qt::AlignLeft);
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 
166 void 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 
195  horSpacer = new QSpacerItem(20, 20, QSizePolicy::Expanding, QSizePolicy::Minimum);
196 
197  PHBox->addItem(horSpacer);
198 }
199 
200 void 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 
214  horSpacer = new QSpacerItem(20, 20, QSizePolicy::Expanding, QSizePolicy::Minimum);
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 
228 void 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 
242  horSpacer = new QSpacerItem(20, 20, QSizePolicy::Expanding, QSizePolicy::Minimum);
243 
244  PHBox->addItem(horSpacer);
245 
246  if (nvp->p == IP_RO)
247  return;
248 
249  setupSetButton(i18n("Set"));
250 }
251 
252 void 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 
266  horSpacer = new QSpacerItem(20, 20, QSizePolicy::Expanding, QSizePolicy::Minimum);
267 
268  PHBox->addItem(horSpacer);
269 }
270 
271 void 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 
285  horSpacer = new QSpacerItem(20, 20, QSizePolicy::Expanding, QSizePolicy::Minimum);
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 
302 void INDI_P::setBLOBOption(int state)
303 {
304  pg->getDevice()->getClientManager()->setBLOBEnabled(state == Qt::Checked, dataProp.getDeviceName(), dataProp.getName());
305 }
306 
307 void 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 
327 void 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 
340 void 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 
358 void 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 
390 void 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 
408 void 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 
449 void 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 
490  horSpacer = new QSpacerItem(20, 20, QSizePolicy::Expanding, QSizePolicy::Minimum);
491 
492  PHBox->addWidget(menuC);
493  PHBox->addItem(horSpacer);
494 }
495 
496 void INDI_P::setupSetButton(const QString &caption)
497 {
498  setB = new QPushButton(caption, this);
499  setB->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
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 
508 void INDI_P::addWidget(QWidget *w)
509 {
510  PHBox->addWidget(w);
511 }
512 
513 void INDI_P::addLayout(QHBoxLayout *layout)
514 {
515  PVBox->addLayout(layout);
516 }
517 
518 void 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 
529 void 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 
555 void 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 
609 INDI_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 
620 bool INDI_P::isRegistered() const
621 {
622  return (dataProp && dataProp.getRegistered());
623 }
void append(const T &value)
RichText
AlignVCenter
int month() const const
int year() const const
QIcon fromTheme(const QString &name)
QDateTime currentDateTimeUtc()
QByteArray toLatin1() const const
void newTime()
newTime Display dialog to set UTC date and time to the driver.
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
void addWidget(QWidget *widget, int stretch, Qt::Alignment alignment)
static KStars * Instance()
Definition: kstars.h:121
QString i18n(const char *text, const TYPE &arg...)
bool isEmpty() const const
QByteArray toUtf8() const const
QDate selectedDate(void)
Definition: timedialog.cpp:107
virtual int exec()
int hour() const const
QString & remove(int position, int n)
QString label(StandardShortcut id)
void setupSetButton(const QString &caption)
Setup the 'set' button in the property.
Dialog for adjusting the Time and Date.
Definition: timedialog.h:37
void setToolTip(const QString &)
const char * constData() const const
QString arg(qlonglong a, int fieldWidth, int base, QChar fillChar) const const
void raise()
int second() const const
QTime selectedTime(void)
Definition: timedialog.cpp:102
QString name(StandardShortcut id)
void setContentsMargins(int left, int top, int right, int bottom)
QString i18nc(const char *context, const char *text, const TYPE &arg...)
QString toString(Qt::DateFormat format) const const
int minute() const const
int day() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Wed Mar 22 2023 04:02:03 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.