Kstars

indielement.cpp
1 /*
2  SPDX-FileCopyrightText: 2003 Jasem Mutlaq <[email protected]>
3 
4  SPDX-License-Identifier: GPL-2.0-or-later
5 
6  2004-01-15 INDI element is the most basic unit of the INDI KStars client.
7 */
8 
9 #include "indielement.h"
10 
11 #include "indiproperty.h"
12 #include "indigroup.h"
13 #include "indidevice.h"
14 
15 #include "kstars.h"
16 #include "ksnotification.h"
17 
18 #include <indicom.h>
19 
20 #include <KSqueezedTextLabel>
21 #include <KLocalizedString>
22 #include <KLed>
23 #include <KMessageBox>
24 
25 #include <QButtonGroup>
26 #include <QCheckBox>
27 #include <QDir>
28 #include <QDoubleSpinBox>
29 #include <QFileDialog>
30 #include <QLineEdit>
31 #include <QPushButton>
32 #include <QSlider>
33 
34 extern const char *libindi_strings_context;
35 
36 /*******************************************************************
37 ** INDI Element
38 *******************************************************************/
39 INDI_E::INDI_E(INDI_P *gProp, INDI::Property dProp) : QWidget(gProp), guiProp(gProp), dataProp(dProp)
40 {
41  EHBox = new QHBoxLayout;
42  EHBox->setObjectName("Element Horizontal Layout");
43  EHBox->setContentsMargins(0, 0, 0, 0);
44 }
45 
46 void INDI_E::buildSwitch(QButtonGroup *groupB, ISwitch *sw)
47 {
48  name = sw->name;
49  label = i18nc(libindi_strings_context, sw->label);
50 
51  if (label == "(I18N_EMPTY_MESSAGE)")
52  label = sw->label;
53 
54  sp = sw;
55 
56  if (label.isEmpty())
57  label = i18nc(libindi_strings_context, sw->name);
58 
59  if (label == "(I18N_EMPTY_MESSAGE)")
60  label = sw->name;
61 
62  if (groupB == nullptr)
63  return;
64 
65  switch (guiProp->getGUIType())
66  {
67  case PG_BUTTONS:
68  push_w = new QPushButton(label, this);
69  push_w->setStyleSheet(":checked {background-color: darkGreen}");
70  push_w->setCheckable(true);
71  groupB->addButton(push_w);
72 
73  syncSwitch();
74 
75  guiProp->addWidget(push_w);
76 
77  push_w->show();
78 
79  if (dataProp.getPermission() == IP_RO)
80  push_w->setEnabled(sw->s == ISS_ON);
81 
82  break;
83 
84  case PG_RADIO:
85  check_w = new QCheckBox(label, this);
86  groupB->addButton(check_w);
87 
88  syncSwitch();
89 
90  guiProp->addWidget(check_w);
91 
92  check_w->show();
93 
94  if (dataProp.getPermission() == IP_RO)
95  check_w->setEnabled(sw->s == ISS_ON);
96 
97  break;
98 
99  default:
100  break;
101  }
102 }
103 
104 void INDI_E::buildMenuItem(ISwitch *sw)
105 {
106  buildSwitch(nullptr, sw);
107 }
108 
109 void INDI_E::buildText(IText *itp)
110 {
111  name = itp->name;
112  if (itp->label[0])
113  label = i18nc(libindi_strings_context, itp->label);
114 
115  if (label == "(I18N_EMPTY_MESSAGE)")
116  label = itp->label;
117 
118  tp = itp;
119 
120  if (label.isEmpty())
121  label = i18nc(libindi_strings_context, itp->name);
122 
123  if (label == "(I18N_EMPTY_MESSAGE)")
124  label = itp->name;
125 
126  setupElementLabel();
127 
128  if (tp->text[0])
129  text = i18nc(libindi_strings_context, tp->text);
130 
131  switch (dataProp.getPermission())
132  {
133  case IP_RW:
134  setupElementRead(ELEMENT_READ_WIDTH * KStars::Instance()->devicePixelRatio());
135  setupElementWrite(ELEMENT_WRITE_WIDTH * KStars::Instance()->devicePixelRatio());
136 
137  break;
138 
139  case IP_RO:
140  setupElementRead(ELEMENT_FULL_WIDTH * KStars::Instance()->devicePixelRatio());
141  break;
142 
143  case IP_WO:
144  setupElementWrite(ELEMENT_FULL_WIDTH * KStars::Instance()->devicePixelRatio());
145  break;
146  }
147 
148  guiProp->addLayout(EHBox);
149 }
150 
151 void INDI_E::setupElementLabel()
152 {
153  QPalette palette;
154 
155  label_w = new KSqueezedTextLabel(this);
156  label_w->setMinimumWidth(ELEMENT_LABEL_WIDTH * KStars::Instance()->devicePixelRatio());
157  label_w->setMaximumWidth(ELEMENT_LABEL_WIDTH * KStars::Instance()->devicePixelRatio());
158  label_w->setMargin(2);
159 
160  palette.setColor(label_w->backgroundRole(), QColor(224, 232, 238));
161  label_w->setPalette(palette);
162  label_w->setTextFormat(Qt::RichText);
163  label_w->setAlignment(Qt::AlignCenter);
164  label_w->setWordWrap(true);
165 
166  label_w->setStyleSheet("border: 1px solid grey; border-radius: 2px");
167 
168  if (label.length() > MAX_LABEL_LENGTH)
169  {
170  QFont tempFont(label_w->font());
171  tempFont.setPointSize(tempFont.pointSize() - MED_INDI_FONT);
172  label_w->setFont(tempFont);
173  }
174 
175  label_w->setText(label);
176 
177  EHBox->addWidget(label_w);
178 }
179 
180 void INDI_E::syncSwitch()
181 {
182  if (sp == nullptr)
183  return;
184 
185  QFont buttonFont;
186 
187  switch (guiProp->getGUIType())
188  {
189  case PG_BUTTONS:
190  if (sp->s == ISS_ON)
191  {
192  push_w->setChecked(true);
193  //push_w->setDown(true);
194  buttonFont = push_w->font();
195  buttonFont.setBold(true);
196  push_w->setFont(buttonFont);
197 
198  if (dataProp.getPermission() == IP_RO)
199  push_w->setEnabled(true);
200  }
201  else
202  {
203  push_w->setChecked(false);
204  //push_w->setDown(false);
205  buttonFont = push_w->font();
206  buttonFont.setBold(false);
207  push_w->setFont(buttonFont);
208 
209  if (dataProp.getPermission() == IP_RO)
210  push_w->setEnabled(false);
211  }
212  break;
213 
214  case PG_RADIO:
215  if (sp->s == ISS_ON)
216  {
217  check_w->setChecked(true);
218  if (dataProp.getPermission() == IP_RO)
219  check_w->setEnabled(true);
220  }
221  else
222  {
223  check_w->setChecked(false);
224  if (dataProp.getPermission() == IP_RO)
225  check_w->setEnabled(false);
226  }
227  break;
228 
229  default:
230  break;
231  }
232 }
233 
234 void INDI_E::syncText()
235 {
236  if (tp == nullptr)
237  return;
238 
239  if (dataProp.getPermission() != IP_WO)
240  {
241  if (tp->text[0])
242  read_w->setText(i18nc(libindi_strings_context, tp->text));
243  else
244  read_w->setText(tp->text);
245  }
246  // If write-only
247  else
248  {
249  write_w->setText(tp->text);
250  }
251 }
252 
253 void INDI_E::syncNumber()
254 {
255  char iNumber[MAXINDIFORMAT];
256  if (np == nullptr || read_w == nullptr)
257  return;
258 
259  numberFormat(iNumber, np->format, np->value);
260 
261  text = iNumber;
262 
263  read_w->setText(text);
264 
265  if (spin_w)
266  {
267  if (np->min != spin_w->minimum())
268  setMin();
269  if (np->max != spin_w->maximum())
270  setMax();
271  }
272 }
273 
274 void INDI_E::updateTP()
275 {
276  if (tp == nullptr)
277  return;
278 
279  IUSaveText(tp, write_w->text().toHtmlEscaped().toLatin1().constData());
280 }
281 
282 void INDI_E::updateNP()
283 {
284  if (np == nullptr)
285  return;
286 
287  if (write_w != nullptr)
288  {
289  if (write_w->text().isEmpty())
290  return;
291 
292  f_scansexa(write_w->text().replace(',', '.').toLatin1().constData(), &(np->value));
293  return;
294  }
295 
296  if (spin_w != nullptr)
297  np->value = spin_w->value();
298 }
299 
300 void INDI_E::setText(const QString &newText)
301 {
302  if (tp == nullptr)
303  return;
304 
305  switch (dataProp.getPermission())
306  {
307  case IP_RO:
308  read_w->setText(newText);
309  break;
310 
311  case IP_WO:
312  case IP_RW:
313  text = newText;
314  IUSaveText(tp, newText.toLatin1().constData());
315  read_w->setText(newText);
316  write_w->setText(newText);
317  break;
318  }
319 }
320 
321 void INDI_E::setValue(double value)
322 {
323  if (spin_w == nullptr || np == nullptr)
324  return;
325  // ensure that min <= value <= max
326  if (value < np->min || value > np->max)
327  return;
328 
329  spin_w->setValue(value);
330  spinChanged(value);
331 }
332 
333 void INDI_E::buildBLOB(IBLOB *ibp)
334 {
335  name = ibp->name;
336  label = i18nc(libindi_strings_context, ibp->label);
337 
338  if (label == "(I18N_EMPTY_MESSAGE)")
339  label = ibp->label;
340 
341  bp = ibp;
342 
343  if (label.isEmpty())
344  label = i18nc(libindi_strings_context, ibp->name);
345 
346  if (label == "(I18N_EMPTY_MESSAGE)")
347  label = ibp->name;
348 
349  setupElementLabel();
350 
351  text = i18n("INDI DATA STREAM");
352 
353  switch (dataProp.getPermission())
354  {
355  case IP_RW:
356  setupElementRead(ELEMENT_READ_WIDTH * KStars::Instance()->devicePixelRatio());
357  setupElementWrite(ELEMENT_WRITE_WIDTH * KStars::Instance()->devicePixelRatio());
358  setupBrowseButton();
359  break;
360 
361  case IP_RO:
362  setupElementRead(ELEMENT_FULL_WIDTH * KStars::Instance()->devicePixelRatio());
363  break;
364 
365  case IP_WO:
366  setupElementWrite(ELEMENT_FULL_WIDTH * KStars::Instance()->devicePixelRatio());
367  setupBrowseButton();
368  break;
369  }
370 
371  guiProp->addLayout(EHBox);
372 }
373 
374 void INDI_E::buildNumber(INumber *inp)
375 {
376  bool scale = false;
377  char iNumber[MAXINDIFORMAT];
378 
379  name = inp->name;
380  label = i18nc(libindi_strings_context, inp->label);
381 
382  if (label == "(I18N_EMPTY_MESSAGE)")
383  label = inp->label;
384 
385  np = inp;
386 
387  if (label.isEmpty())
388  label = i18nc(libindi_strings_context, inp->name);
389 
390  if (label == "(I18N_EMPTY_MESSAGE)")
391  label = inp->name;
392 
393  numberFormat(iNumber, np->format, np->value);
394  text = iNumber;
395 
396  setupElementLabel();
397 
398  if (np->step != 0 && (np->max - np->min) / np->step <= 100)
399  scale = true;
400 
401  switch (dataProp.getPermission())
402  {
403  case IP_RW:
404  setupElementRead(ELEMENT_READ_WIDTH * KStars::Instance()->devicePixelRatio());
405  if (scale)
406  setupElementScale(ELEMENT_WRITE_WIDTH * KStars::Instance()->devicePixelRatio());
407  else
408  setupElementWrite(ELEMENT_WRITE_WIDTH * KStars::Instance()->devicePixelRatio());
409 
410  guiProp->addLayout(EHBox);
411  break;
412 
413  case IP_RO:
414  setupElementRead(ELEMENT_READ_WIDTH * KStars::Instance()->devicePixelRatio());
415  guiProp->addLayout(EHBox);
416  break;
417 
418  case IP_WO:
419  if (scale)
420  setupElementScale(ELEMENT_FULL_WIDTH * KStars::Instance()->devicePixelRatio());
421  else
422  setupElementWrite(ELEMENT_FULL_WIDTH * KStars::Instance()->devicePixelRatio());
423 
424  guiProp->addLayout(EHBox);
425 
426  break;
427  }
428 }
429 
430 void INDI_E::buildLight(ILight *ilp)
431 {
432  name = ilp->name;
433  label = i18nc(libindi_strings_context, ilp->label);
434 
435  if (label == "(I18N_EMPTY_MESSAGE)")
436  label = ilp->label;
437 
438  lp = ilp;
439 
440  if (label.isEmpty())
441  label = i18nc(libindi_strings_context, ilp->name);
442 
443  if (label == "(I18N_EMPTY_MESSAGE)")
444  label = ilp->name;
445 
446  led_w = new KLed(this);
447  led_w->setMaximumSize(16, 16);
448  led_w->setLook(KLed::Sunken);
449 
450  syncLight();
451 
452  EHBox->addWidget(led_w);
453 
454  setupElementLabel();
455 
456  guiProp->addLayout(EHBox);
457 }
458 
459 void INDI_E::syncLight()
460 {
461  if (lp == nullptr)
462  return;
463 
464  switch (lp->s)
465  {
466  case IPS_IDLE:
467  led_w->setColor(Qt::gray);
468  break;
469 
470  case IPS_OK:
471  led_w->setColor(Qt::green);
472  break;
473 
474  case IPS_BUSY:
475  led_w->setColor(Qt::yellow);
476  break;
477 
478  case IPS_ALERT:
479  led_w->setColor(Qt::red);
480  break;
481  }
482 }
483 
484 void INDI_E::setupElementScale(int length)
485 {
486  if (np == nullptr)
487  return;
488 
489  int steps = static_cast<int>((np->max - np->min) / np->step);
490  spin_w = new QDoubleSpinBox(this);
491  spin_w->setRange(np->min, np->max);
492  spin_w->setSingleStep(np->step);
493  spin_w->setValue(np->value);
494  spin_w->setDecimals(3);
495 
496  slider_w = new QSlider(Qt::Horizontal, this);
497  slider_w->setRange(0, steps);
498  slider_w->setPageStep(1);
499  slider_w->setValue(static_cast<int>((np->value - np->min) / np->step));
500 
501  connect(spin_w, SIGNAL(valueChanged(double)), this, SLOT(spinChanged(double)));
502  connect(slider_w, SIGNAL(sliderMoved(int)), this, SLOT(sliderChanged(int)));
503 
504  if (length == ELEMENT_FULL_WIDTH * KStars::Instance()->devicePixelRatio())
505  spin_w->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
506  else
507  spin_w->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
508 
509  spin_w->setMinimumWidth(static_cast<int>(length * 0.45));
510  slider_w->setMinimumWidth(static_cast<int>(length * 0.55));
511 
512  EHBox->addWidget(slider_w);
513  EHBox->addWidget(spin_w);
514 }
515 
516 void INDI_E::spinChanged(double value)
517 {
518  int slider_value = static_cast<int>((value - np->min) / np->step);
519  slider_w->setValue(slider_value);
520 }
521 
522 void INDI_E::sliderChanged(int value)
523 {
524  double spin_value = (value * np->step) + np->min;
525  spin_w->setValue(spin_value);
526 }
527 
528 void INDI_E::setMin()
529 {
530  if (spin_w)
531  {
532  spin_w->setMinimum(np->min);
533  spin_w->setValue(np->value);
534  }
535  if (slider_w)
536  {
537  slider_w->setMaximum(static_cast<int>((np->max - np->min) / np->step));
538  slider_w->setMinimum(0);
539  slider_w->setPageStep(1);
540  slider_w->setValue(static_cast<int>((np->value - np->min) / np->step));
541  }
542 }
543 
544 void INDI_E::setMax()
545 {
546  if (spin_w)
547  {
548  spin_w->setMaximum(np->max);
549  spin_w->setValue(np->value);
550  }
551  if (slider_w)
552  {
553  slider_w->setMaximum(static_cast<int>((np->max - np->min) / np->step));
554  slider_w->setMinimum(0);
555  slider_w->setPageStep(1);
556  slider_w->setValue(static_cast<int>((np->value - np->min) / np->step));
557  }
558 }
559 
560 void INDI_E::setupElementWrite(int length)
561 {
562  write_w = new QLineEdit(this);
563  write_w->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
564  write_w->setMinimumWidth(length);
565  write_w->setMaximumWidth(length);
566 
567  write_w->setText(text);
568 
569  QObject::connect(write_w, SIGNAL(returnPressed()), guiProp, SLOT(sendText()));
570  EHBox->addWidget(write_w);
571 }
572 
573 void INDI_E::setupElementRead(int length)
574 {
575  read_w = new QLineEdit(this);
576  read_w->setMinimumWidth(length);
577  read_w->setFocusPolicy(Qt::NoFocus);
578  read_w->setCursorPosition(0);
579  read_w->setAlignment(Qt::AlignCenter);
580  read_w->setReadOnly(true);
581  read_w->setText(text);
582 
583  EHBox->addWidget(read_w);
584 }
585 
586 void INDI_E::setupBrowseButton()
587 {
588  browse_w = new QPushButton(this);
589  browse_w->setIcon(QIcon::fromTheme("document-open"));
590  browse_w->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
591  browse_w->setMinimumWidth(MIN_SET_WIDTH * KStars::Instance()->devicePixelRatio());
592  browse_w->setMaximumWidth(MAX_SET_WIDTH * KStars::Instance()->devicePixelRatio());
593 
594  EHBox->addWidget(browse_w);
595  QObject::connect(browse_w, SIGNAL(clicked()), this, SLOT(browseBlob()));
596 }
597 
598 void INDI_E::browseBlob()
599 {
600  QFile fp;
601  QString filename;
602  QString format;
603  int pos = 0;
604  QUrl currentURL;
605 
606  currentURL = QFileDialog::getOpenFileUrl();
607 
608  // if user presses cancel
609  if (currentURL.isEmpty())
610  return;
611 
612  if (currentURL.isValid())
613  write_w->setText(currentURL.toLocalFile());
614 
615  fp.setFileName(currentURL.toLocalFile());
616 
617  if ((pos = filename.lastIndexOf(".")) != -1)
618  format = filename.mid(pos, filename.length());
619 
620  //qDebug() << Q_FUNC_INFO << "Filename is " << fp.fileName() << Qt::endl;
621 
622  if (!fp.open(QIODevice::ReadOnly))
623  {
624  KSNotification::error( i18n("Cannot open file %1 for reading", filename));
625  return;
626  }
627 
628  bp->bloblen = bp->size = fp.size();
629 
630  bp->blob = static_cast<uint8_t *>(realloc(bp->blob, bp->size));
631  if (bp->blob == nullptr)
632  {
633  KSNotification::error( i18n("Not enough memory for file %1", filename));
634  fp.close();
635  return;
636  }
637 
638  memcpy(bp->blob, fp.readAll().constData(), bp->size);
639 
640  blobDirty = true;
641 }
642 
643 QString INDI_E::getWriteField()
644 {
645  if (write_w)
646  return write_w->text();
647  else
648  return nullptr;
649 }
650 
651 QString INDI_E::getReadField()
652 {
653  if (read_w)
654  return read_w->text();
655  else
656  return nullptr;
657 }
RichText
QUrl getOpenFileUrl(QWidget *parent, const QString &caption, const QUrl &dir, const QString &filter, QString *selectedFilter, QFileDialog::Options options, const QStringList &supportedSchemes)
AlignCenter
virtual bool open(QIODevice::OpenMode mode) override
QIcon fromTheme(const QString &name)
QByteArray toLatin1() const const
int lastIndexOf(QChar ch, int from, Qt::CaseSensitivity cs) const const
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
static KStars * Instance()
Definition: kstars.h:125
void addButton(QAbstractButton *button, int id)
bool isValid() const const
QString i18n(const char *text, const TYPE &arg...)
bool isEmpty() const const
Horizontal
bool isEmpty() const const
int length() const const
void setFileName(const QString &name)
QString toLocalFile() const const
virtual void close() override
void setColor(QPalette::ColorGroup group, QPalette::ColorRole role, const QColor &color)
QString label(StandardShortcut id)
virtual qint64 size() const const override
const char * constData() const const
void setObjectName(const QString &name)
QString name(StandardShortcut id)
QString i18nc(const char *context, const char *text, const TYPE &arg...)
void setBold(bool enable)
QByteArray readAll()
QString mid(int position, int n) const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2022 The KDE developers.
Generated on Thu Aug 11 2022 03:59:59 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.