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  ledStatus->show();
122  labelW->show();
123 
124  // #3 Add the Vertical layout which may contain several elements
125  PHBox->addLayout(PVBox);
126 
127  switch (dataProp.getType())
128  {
129  case INDI_SWITCH:
130  if (dataProp.getSwitch()->getRule() == ISR_NOFMANY)
131  guiType = PG_RADIO;
132  else if (dataProp.getSwitch()->count() > 4)
133  guiType = PG_MENU;
134  else
135  guiType = PG_BUTTONS;
136 
137  if (guiType == PG_MENU)
138  buildMenuGUI();
139  else
140  buildSwitchGUI();
141  break;
142 
143  case INDI_TEXT:
144  buildTextGUI();
145  break;
146 
147  case INDI_NUMBER:
148  buildNumberGUI();
149  break;
150 
151  case INDI_LIGHT:
152  buildLightGUI();
153  break;
154 
155  case INDI_BLOB:
156  buildBLOBGUI();
157  break;
158 
159  default:
160  break;
161  }
162 }
163 
164 void INDI_P::buildSwitchGUI()
165 {
166  auto svp = static_cast<ISwitchVectorProperty*>(dataProp.getSwitch());
167 
168  if (!svp)
169  return;
170 
171  groupB = new QButtonGroup(this);
172 
173  if (guiType == PG_BUTTONS)
174  {
175  if (svp->r == ISR_1OFMANY)
176  groupB->setExclusive(true);
177  else
178  groupB->setExclusive(false);
179  }
180  else if (guiType == PG_RADIO)
181  groupB->setExclusive(false);
182 
183  if (svp->p != IP_RO)
184  QObject::connect(groupB, SIGNAL(buttonClicked(QAbstractButton*)), this, SLOT(newSwitch(QAbstractButton*)));
185 
186  for (int i = 0; i < svp->nsp; i++)
187  {
188  auto lp = new INDI_E(this, dataProp);
189  lp->buildSwitch(groupB, svp->sp + i);
190  elementList.append(lp);
191  }
192 
193  horSpacer = new QSpacerItem(20, 20, QSizePolicy::Expanding, QSizePolicy::Minimum);
194 
195  PHBox->addItem(horSpacer);
196 }
197 
198 void INDI_P::buildTextGUI()
199 {
200  auto tvp = static_cast<ITextVectorProperty*>(dataProp.getText());
201 
202  if (!tvp)
203  return;
204 
205  for (int i = 0; i < tvp->ntp; i++)
206  {
207  auto lp = new INDI_E(this, dataProp);
208  lp->buildText(tvp->tp + i);
209  elementList.append(lp);
210  }
211 
212  horSpacer = new QSpacerItem(20, 20, QSizePolicy::Expanding, QSizePolicy::Minimum);
213 
214  PHBox->addItem(horSpacer);
215 
216  if (tvp->p == IP_RO)
217  return;
218 
219  // INDI STD, but we use our own controls
220  if (name == "TIME_UTC")
221  setupSetButton(i18n("Time"));
222  else
223  setupSetButton(i18n("Set"));
224 }
225 
226 void INDI_P::buildNumberGUI()
227 {
228  auto nvp = static_cast<INumberVectorProperty*>(dataProp.getNumber());
229 
230  if (!nvp)
231  return;
232 
233  for (int i = 0; i < nvp->nnp; i++)
234  {
235  auto lp = new INDI_E(this, dataProp);
236  lp->buildNumber(nvp->np + i);
237  elementList.append(lp);
238  }
239 
240  horSpacer = new QSpacerItem(20, 20, QSizePolicy::Expanding, QSizePolicy::Minimum);
241 
242  PHBox->addItem(horSpacer);
243 
244  if (nvp->p == IP_RO)
245  return;
246 
247  setupSetButton(i18n("Set"));
248 }
249 
250 void INDI_P::buildLightGUI()
251 {
252  auto lvp = static_cast<ILightVectorProperty*>(dataProp.getLight());
253 
254  if (!lvp)
255  return;
256 
257  for (int i = 0; i < lvp->nlp; i++)
258  {
259  auto ep = new INDI_E(this, dataProp);
260  ep->buildLight(lvp->lp + i);
261  elementList.append(ep);
262  }
263 
264  horSpacer = new QSpacerItem(20, 20, QSizePolicy::Expanding, QSizePolicy::Minimum);
265 
266  PHBox->addItem(horSpacer);
267 }
268 
269 void INDI_P::buildBLOBGUI()
270 {
271  auto bvp = static_cast<IBLOBVectorProperty*>(dataProp.getBLOB());
272 
273  if (!bvp)
274  return;
275 
276  for (int i = 0; i < bvp->nbp; i++)
277  {
278  auto lp = new INDI_E(this, dataProp);
279  lp->buildBLOB(bvp->bp + i);
280  elementList.append(lp);
281  }
282 
283  horSpacer = new QSpacerItem(20, 20, QSizePolicy::Expanding, QSizePolicy::Minimum);
284 
285  PHBox->addItem(horSpacer);
286 
287  enableBLOBC = new QCheckBox();
288  enableBLOBC->setIcon(QIcon::fromTheme("network-modem"));
289  enableBLOBC->setChecked(true);
290  enableBLOBC->setToolTip(i18n("Enable binary data transfer from this property to KStars and vice-versa."));
291 
292  PHBox->addWidget(enableBLOBC);
293 
294  connect(enableBLOBC, SIGNAL(stateChanged(int)), this, SLOT(setBLOBOption(int)));
295 
296  if (dataProp.getPermission() != IP_RO)
297  setupSetButton(i18n("Upload"));
298 }
299 
300 void INDI_P::setBLOBOption(int state)
301 {
302  pg->getDevice()->getClientManager()->setBLOBEnabled(state == Qt::Checked, dataProp.getDeviceName(), dataProp.getName());
303 }
304 
305 void INDI_P::newSwitch(QAbstractButton *button)
306 {
307  auto svp = dataProp.getSwitch();
308  QString buttonText = button->text();
309 
310  if (!svp)
311  return;
312 
313  buttonText.remove('&');
314 
315  for (auto &el : elementList)
316  {
317  if (el->getLabel() == buttonText)
318  {
319  newSwitch(el->getName());
320  return;
321  }
322  }
323 }
324 
325 void INDI_P::resetSwitch()
326 {
327  auto svp = dataProp.getSwitch();
328 
329  if (!svp)
330  return;
331 
332  if (menuC != nullptr)
333  {
334  menuC->setCurrentIndex(svp->findOnSwitchIndex());
335  }
336 }
337 
338 void INDI_P::newSwitch(int index)
339 {
340  auto svp = dataProp.getSwitch();
341 
342  if (!svp)
343  return;
344 
345  if (index >= svp->count() || index < 0)
346  return;
347 
348  auto sp = svp->at(index);
349 
350  svp->reset();
351  sp->setState(ISS_ON);
352 
353  sendSwitch();
354 }
355 
356 void INDI_P::newSwitch(const QString &name)
357 {
358  auto svp = dataProp.getSwitch();
359 
360  if (!svp)
361  return;
362 
363  auto sp = svp->findWidgetByName(name.toLatin1().constData());
364 
365  if (!sp)
366  return;
367 
368  if (svp->getRule() == ISR_1OFMANY)
369  {
370  svp->reset();
371  sp->setState(ISS_ON);
372  }
373  else
374  {
375  if (svp->getRule() == ISR_ATMOST1)
376  {
377  ISState prev_state = sp->getState();
378  svp->reset();
379  sp->setState(prev_state);
380  }
381 
382  sp->setState(sp->getState() == ISS_ON ? ISS_OFF : ISS_ON);
383  }
384 
385  sendSwitch();
386 }
387 
388 void INDI_P::sendSwitch()
389 {
390  auto svp = dataProp.getSwitch();
391 
392  if (!svp)
393  return;
394 
395  svp->setState(IPS_BUSY);
396 
397  for (auto &el : elementList)
398  el->syncSwitch();
399 
400  updateStateLED();
401 
402  // Send it to server
403  pg->getDevice()->getClientManager()->sendNewSwitch(svp);
404 }
405 
406 void INDI_P::sendText()
407 {
408  switch (dataProp.getType())
409  {
410  case INDI_TEXT:
411  {
412  auto tvp = dataProp.getText();
413  if (!tvp)
414  return;
415 
416  tvp->setState(IPS_BUSY);
417 
418  for (auto &el : elementList)
419  el->updateTP();
420 
421  pg->getDevice()->getClientManager()->sendNewText(tvp);
422 
423  break;
424  }
425 
426  case INDI_NUMBER:
427  {
428  auto nvp = dataProp.getNumber();
429  if (!nvp)
430  return;
431 
432  nvp->setState(IPS_BUSY);
433 
434  for (auto &el : elementList)
435  el->updateNP();
436 
437  pg->getDevice()->getClientManager()->sendNewNumber(nvp);
438  break;
439  }
440  default:
441  break;
442  }
443 
444  updateStateLED();
445 }
446 
447 void INDI_P::buildMenuGUI()
448 {
449  QStringList menuOptions;
450  QString oneOption;
451  int onItem = -1;
452  auto svp = dataProp.getSwitch();
453 
454  if (!svp)
455  return;
456 
457  menuC = new QComboBox(this);
458 
459  if (svp->getPermission() == IP_RO)
460  connect(menuC, SIGNAL(activated(int)), this, SLOT(resetSwitch()));
461  else
462  connect(menuC, SIGNAL(activated(int)), this, SLOT(newSwitch(int)));
463 
464  for (int i = 0; i < svp->nsp; i++)
465  {
466  auto tp = svp->at(i);
467 
468  if (tp->getState() == ISS_ON)
469  onItem = i;
470 
471  auto lp = new INDI_E(this, dataProp);
472 
473  lp->buildMenuItem(tp);
474 
475  oneOption = i18nc(libindi_strings_context, lp->getLabel().toUtf8());
476 
477  if (oneOption == "(I18N_EMPTY_MESSAGE)")
478  oneOption = lp->getLabel().toUtf8();
479 
480  menuOptions.append(oneOption);
481 
482  elementList.append(lp);
483  }
484 
485  menuC->addItems(menuOptions);
486  menuC->setCurrentIndex(onItem);
487 
488  horSpacer = new QSpacerItem(20, 20, QSizePolicy::Expanding, QSizePolicy::Minimum);
489 
490  PHBox->addWidget(menuC);
491  PHBox->addItem(horSpacer);
492 }
493 
494 void INDI_P::setupSetButton(const QString &caption)
495 {
496  setB = new QPushButton(caption, this);
497  setB->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
498  setB->setMinimumWidth(MIN_SET_WIDTH * KStars::Instance()->devicePixelRatio());
499  setB->setMaximumWidth(MAX_SET_WIDTH * KStars::Instance()->devicePixelRatio());
500 
501  connect(setB, SIGNAL(clicked()), this, SLOT(processSetButton()));
502 
503  PHBox->addWidget(setB);
504 }
505 
506 void INDI_P::addWidget(QWidget *w)
507 {
508  PHBox->addWidget(w);
509 }
510 
511 void INDI_P::addLayout(QHBoxLayout *layout)
512 {
513  PVBox->addLayout(layout);
514 }
515 
516 void INDI_P::updateMenuGUI()
517 {
518  auto svp = dataProp.getSwitch();
519 
520  if (!svp)
521  return;
522 
523  int currentIndex = svp->findOnSwitchIndex();
524  menuC->setCurrentIndex(currentIndex);
525 }
526 
527 void INDI_P::processSetButton()
528 {
529  switch (dataProp.getType())
530  {
531  case INDI_TEXT:
532  //if (!strcmp(dataProp.getName(), "TIME_UTC"))
533  if (dataProp.isNameMatch("TIME_UTC"))
534  newTime();
535  else
536  sendText();
537 
538  break;
539 
540  case INDI_NUMBER:
541  sendText();
542  break;
543 
544  case INDI_BLOB:
545  sendBlob();
546  break;
547 
548  default:
549  break;
550  }
551 }
552 
553 void INDI_P::sendBlob()
554 {
555  //int index=0;
556  //bool openingTag=false;
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(), timestamp());
565 
566  for (int i = 0; i < elementList.count(); i++)
567  {
568  INDI::WidgetView<IBLOB> *bp = bvp->at(i);
569 #if (INDI_VERSION_MINOR >= 4 && INDI_VERSION_RELEASE >= 2)
570  pg->getDevice()->getClientManager()->sendOneBlob(bp);
571 #else
572  pg->getDevice()->getClientManager()->sendOneBlob(bp->getName(), bp->getSize(), bp->getFormat(),
573  const_cast<void *>(bp->getBlob()));
574 #endif
575  }
576 
577  // JM: Why we need dirty here? We should be able to upload multiple time
578  /*foreach(INDI_E *ep, elementList)
579  {
580  if (ep->getBLOBDirty() == true)
581  {
582 
583  if (openingTag == false)
584  {
585  pg->getDevice()->getClientManager()->startBlob(bvp->device, bvp->name, timestamp());
586  openingTag = true;
587  }
588 
589  IBLOB *bp = &(bvp->bp[index]);
590  ep->setBLOBDirty(false);
591 
592  //qDebug() << Q_FUNC_INFO << "SENDING BLOB " << bp->name << " has size of " << bp->size << " and bloblen of " << bp->bloblen << Qt::endl;
593  pg->getDevice()->getClientManager()->sendOneBlob(bp->name, bp->size, bp->format, bp->blob);
594 
595  }
596 
597  index++;
598 
599  }*/
600 
601  //if (openingTag)
602  pg->getDevice()->getClientManager()->finishBlob();
603 
604  updateStateLED();
605 }
606 
608 {
609  INDI_E *timeEle = getElement("UTC");
610  INDI_E *offsetEle = getElement("OFFSET");
611  if (!timeEle || !offsetEle)
612  return;
613 
614  TimeDialog timedialog(KStars::Instance()->data()->ut(), KStars::Instance()->data()->geo(), KStars::Instance(),
615  true);
616 
617  if (timedialog.exec() == QDialog::Accepted)
618  {
619  QTime newTime(timedialog.selectedTime());
620  QDate newDate(timedialog.selectedDate());
621 
622  timeEle->setText(QString("%1-%2-%3T%4:%5:%6")
623  .arg(newDate.year())
624  .arg(newDate.month())
625  .arg(newDate.day())
626  .arg(newTime.hour())
627  .arg(newTime.minute())
628  .arg(newTime.second()));
629 
630  offsetEle->setText(QString().setNum(KStars::Instance()->data()->geo()->TZ(), 'g', 2));
631 
632  sendText();
633  }
634  else
635  return;
636 }
637 
638 INDI_E *INDI_P::getElement(const QString &elementName) const
639 {
640  for (auto *ep : elementList)
641  {
642  if (ep->getName() == elementName)
643  return ep;
644  }
645 
646  return nullptr;
647 }
648 
649 bool INDI_P::isRegistered() const
650 {
651  return (dataProp && dataProp.getRegistered());
652 }
void append(const T &value)
RichText
AlignVCenter
int month() const const
int year() const const
QIcon fromTheme(const QString &name)
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:125
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
const char * constData() const const
QString arg(qlonglong a, int fieldWidth, int base, QChar fillChar) const const
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...)
int minute() const const
int day() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2022 The KDE developers.
Generated on Fri Aug 19 2022 03:57:51 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.