File indexing completed on 2024-03-24 03:46:53
0001 /* 0002 SPDX-FileCopyrightText: 2003 Jasem Mutlaq <mutlaqja@ikarustech.com> 0003 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 0006 2004-01-15 INDI element is the most basic unit of the INDI KStars client. 0007 */ 0008 0009 #include "indielement.h" 0010 0011 #include "indiproperty.h" 0012 #include "indigroup.h" 0013 #include "indidevice.h" 0014 0015 #include "kstars.h" 0016 #include "ksnotification.h" 0017 0018 #include <indicom.h> 0019 0020 #include <KSqueezedTextLabel> 0021 #include <KLocalizedString> 0022 #include <KLed> 0023 #include <KMessageBox> 0024 0025 #include <QButtonGroup> 0026 #include <QCheckBox> 0027 #include <QDir> 0028 #include <QDoubleSpinBox> 0029 #include <QFileDialog> 0030 #include <QLineEdit> 0031 #include <QPushButton> 0032 #include <QSlider> 0033 0034 extern const char *libindi_strings_context; 0035 0036 /******************************************************************* 0037 ** INDI Element 0038 *******************************************************************/ 0039 INDI_E::INDI_E(INDI_P *gProp, INDI::Property dProp) : QWidget(gProp), guiProp(gProp), dataProp(dProp) 0040 { 0041 EHBox = new QHBoxLayout; 0042 EHBox->setObjectName("Element Horizontal Layout"); 0043 EHBox->setContentsMargins(0, 0, 0, 0); 0044 } 0045 0046 void INDI_E::buildSwitch(QButtonGroup *groupB, ISwitch *sw) 0047 { 0048 name = sw->name; 0049 label = i18nc(libindi_strings_context, sw->label); 0050 0051 if (label == "(I18N_EMPTY_MESSAGE)") 0052 label = sw->label; 0053 0054 sp = sw; 0055 0056 if (label.isEmpty()) 0057 label = i18nc(libindi_strings_context, sw->name); 0058 0059 if (label == "(I18N_EMPTY_MESSAGE)") 0060 label = sw->name; 0061 0062 if (groupB == nullptr) 0063 return; 0064 0065 switch (guiProp->getGUIType()) 0066 { 0067 case PG_BUTTONS: 0068 push_w = new QPushButton(label, this); 0069 push_w->setStyleSheet(":checked {background-color: darkGreen}"); 0070 push_w->setCheckable(true); 0071 groupB->addButton(push_w); 0072 0073 syncSwitch(); 0074 0075 guiProp->addWidget(push_w); 0076 0077 push_w->show(); 0078 0079 if (dataProp.getPermission() == IP_RO) 0080 push_w->setEnabled(sw->s == ISS_ON); 0081 0082 break; 0083 0084 case PG_RADIO: 0085 check_w = new QCheckBox(label, this); 0086 groupB->addButton(check_w); 0087 0088 syncSwitch(); 0089 0090 guiProp->addWidget(check_w); 0091 0092 check_w->show(); 0093 0094 if (dataProp.getPermission() == IP_RO) 0095 check_w->setEnabled(sw->s == ISS_ON); 0096 0097 break; 0098 0099 default: 0100 break; 0101 } 0102 } 0103 0104 void INDI_E::buildMenuItem(ISwitch *sw) 0105 { 0106 buildSwitch(nullptr, sw); 0107 } 0108 0109 void INDI_E::buildText(IText *itp) 0110 { 0111 name = itp->name; 0112 if (itp->label[0]) 0113 label = i18nc(libindi_strings_context, itp->label); 0114 0115 if (label == "(I18N_EMPTY_MESSAGE)") 0116 label = itp->label; 0117 0118 tp = itp; 0119 0120 if (label.isEmpty()) 0121 label = i18nc(libindi_strings_context, itp->name); 0122 0123 if (label == "(I18N_EMPTY_MESSAGE)") 0124 label = itp->name; 0125 0126 setupElementLabel(); 0127 0128 if (tp->text[0]) 0129 text = i18nc(libindi_strings_context, tp->text); 0130 0131 switch (dataProp.getPermission()) 0132 { 0133 case IP_RW: 0134 setupElementRead(ELEMENT_READ_WIDTH * KStars::Instance()->devicePixelRatio()); 0135 setupElementWrite(ELEMENT_WRITE_WIDTH * KStars::Instance()->devicePixelRatio()); 0136 0137 break; 0138 0139 case IP_RO: 0140 setupElementRead(ELEMENT_FULL_WIDTH * KStars::Instance()->devicePixelRatio()); 0141 break; 0142 0143 case IP_WO: 0144 setupElementWrite(ELEMENT_FULL_WIDTH * KStars::Instance()->devicePixelRatio()); 0145 break; 0146 } 0147 0148 guiProp->addLayout(EHBox); 0149 } 0150 0151 void INDI_E::setupElementLabel() 0152 { 0153 QPalette palette; 0154 0155 label_w = new KSqueezedTextLabel(this); 0156 label_w->setMinimumWidth(ELEMENT_LABEL_WIDTH * KStars::Instance()->devicePixelRatio()); 0157 label_w->setMaximumWidth(ELEMENT_LABEL_WIDTH * KStars::Instance()->devicePixelRatio()); 0158 label_w->setMargin(2); 0159 0160 palette.setColor(label_w->backgroundRole(), QColor(224, 232, 238)); 0161 label_w->setPalette(palette); 0162 label_w->setTextFormat(Qt::RichText); 0163 label_w->setAlignment(Qt::AlignCenter); 0164 label_w->setWordWrap(true); 0165 0166 label_w->setStyleSheet("border: 1px solid grey; border-radius: 2px"); 0167 0168 if (label.length() > MAX_LABEL_LENGTH) 0169 { 0170 QFont tempFont(label_w->font()); 0171 tempFont.setPointSize(tempFont.pointSize() - MED_INDI_FONT); 0172 label_w->setFont(tempFont); 0173 } 0174 0175 label_w->setText(label); 0176 0177 EHBox->addWidget(label_w); 0178 } 0179 0180 void INDI_E::syncSwitch() 0181 { 0182 if (sp == nullptr) 0183 return; 0184 0185 QFont buttonFont; 0186 0187 switch (guiProp->getGUIType()) 0188 { 0189 case PG_BUTTONS: 0190 if (sp->s == ISS_ON) 0191 { 0192 push_w->setChecked(true); 0193 //push_w->setDown(true); 0194 buttonFont = push_w->font(); 0195 buttonFont.setBold(true); 0196 push_w->setFont(buttonFont); 0197 0198 if (dataProp.getPermission() == IP_RO) 0199 push_w->setEnabled(true); 0200 } 0201 else 0202 { 0203 push_w->setChecked(false); 0204 //push_w->setDown(false); 0205 buttonFont = push_w->font(); 0206 buttonFont.setBold(false); 0207 push_w->setFont(buttonFont); 0208 0209 if (dataProp.getPermission() == IP_RO) 0210 push_w->setEnabled(false); 0211 } 0212 break; 0213 0214 case PG_RADIO: 0215 if (sp->s == ISS_ON) 0216 { 0217 check_w->setChecked(true); 0218 if (dataProp.getPermission() == IP_RO) 0219 check_w->setEnabled(true); 0220 } 0221 else 0222 { 0223 check_w->setChecked(false); 0224 if (dataProp.getPermission() == IP_RO) 0225 check_w->setEnabled(false); 0226 } 0227 break; 0228 0229 default: 0230 break; 0231 } 0232 } 0233 0234 void INDI_E::syncText() 0235 { 0236 if (tp == nullptr) 0237 return; 0238 0239 if (dataProp.getPermission() != IP_WO) 0240 { 0241 if (tp->text[0]) 0242 read_w->setText(i18nc(libindi_strings_context, tp->text)); 0243 else 0244 read_w->setText(tp->text); 0245 } 0246 // If write-only 0247 else 0248 { 0249 write_w->setText(tp->text); 0250 } 0251 } 0252 0253 void INDI_E::syncNumber() 0254 { 0255 char iNumber[MAXINDIFORMAT]; 0256 if (np == nullptr || read_w == nullptr) 0257 return; 0258 0259 numberFormat(iNumber, np->format, np->value); 0260 0261 text = iNumber; 0262 0263 read_w->setText(text); 0264 0265 if (spin_w) 0266 { 0267 if (np->min != spin_w->minimum()) 0268 setMin(); 0269 if (np->max != spin_w->maximum()) 0270 setMax(); 0271 } 0272 } 0273 0274 void INDI_E::updateTP() 0275 { 0276 if (tp == nullptr) 0277 return; 0278 0279 IUSaveText(tp, write_w->text().toHtmlEscaped().toLatin1().constData()); 0280 } 0281 0282 void INDI_E::updateNP() 0283 { 0284 if (np == nullptr) 0285 return; 0286 0287 if (write_w != nullptr) 0288 { 0289 if (write_w->text().isEmpty()) 0290 return; 0291 0292 f_scansexa(write_w->text().replace(',', '.').toLatin1().constData(), &(np->value)); 0293 return; 0294 } 0295 0296 if (spin_w != nullptr) 0297 np->value = spin_w->value(); 0298 } 0299 0300 void INDI_E::setText(const QString &newText) 0301 { 0302 if (tp == nullptr) 0303 return; 0304 0305 switch (dataProp.getPermission()) 0306 { 0307 case IP_RO: 0308 read_w->setText(newText); 0309 break; 0310 0311 case IP_WO: 0312 case IP_RW: 0313 text = newText; 0314 IUSaveText(tp, newText.toLatin1().constData()); 0315 read_w->setText(newText); 0316 write_w->setText(newText); 0317 break; 0318 } 0319 } 0320 0321 void INDI_E::setValue(double value) 0322 { 0323 if (spin_w == nullptr || np == nullptr) 0324 return; 0325 // ensure that min <= value <= max 0326 if (value < np->min || value > np->max) 0327 return; 0328 0329 spin_w->setValue(value); 0330 spinChanged(value); 0331 } 0332 0333 void INDI_E::buildBLOB(IBLOB *ibp) 0334 { 0335 name = ibp->name; 0336 label = i18nc(libindi_strings_context, ibp->label); 0337 0338 if (label == "(I18N_EMPTY_MESSAGE)") 0339 label = ibp->label; 0340 0341 bp = ibp; 0342 0343 if (label.isEmpty()) 0344 label = i18nc(libindi_strings_context, ibp->name); 0345 0346 if (label == "(I18N_EMPTY_MESSAGE)") 0347 label = ibp->name; 0348 0349 setupElementLabel(); 0350 0351 text = i18n("INDI DATA STREAM"); 0352 0353 switch (dataProp.getPermission()) 0354 { 0355 case IP_RW: 0356 setupElementRead(ELEMENT_READ_WIDTH * KStars::Instance()->devicePixelRatio()); 0357 setupElementWrite(ELEMENT_WRITE_WIDTH * KStars::Instance()->devicePixelRatio()); 0358 setupBrowseButton(); 0359 break; 0360 0361 case IP_RO: 0362 setupElementRead(ELEMENT_FULL_WIDTH * KStars::Instance()->devicePixelRatio()); 0363 break; 0364 0365 case IP_WO: 0366 setupElementWrite(ELEMENT_FULL_WIDTH * KStars::Instance()->devicePixelRatio()); 0367 setupBrowseButton(); 0368 break; 0369 } 0370 0371 guiProp->addLayout(EHBox); 0372 } 0373 0374 void INDI_E::buildNumber(INumber *inp) 0375 { 0376 bool scale = false; 0377 char iNumber[MAXINDIFORMAT]; 0378 0379 name = inp->name; 0380 label = i18nc(libindi_strings_context, inp->label); 0381 0382 if (label == "(I18N_EMPTY_MESSAGE)") 0383 label = inp->label; 0384 0385 np = inp; 0386 0387 if (label.isEmpty()) 0388 label = i18nc(libindi_strings_context, inp->name); 0389 0390 if (label == "(I18N_EMPTY_MESSAGE)") 0391 label = inp->name; 0392 0393 numberFormat(iNumber, np->format, np->value); 0394 text = iNumber; 0395 0396 setupElementLabel(); 0397 0398 if (np->step != 0 && (np->max - np->min) / np->step <= 100) 0399 scale = true; 0400 0401 switch (dataProp.getPermission()) 0402 { 0403 case IP_RW: 0404 setupElementRead(ELEMENT_READ_WIDTH * KStars::Instance()->devicePixelRatio()); 0405 if (scale) 0406 setupElementScale(ELEMENT_WRITE_WIDTH * KStars::Instance()->devicePixelRatio()); 0407 else 0408 setupElementWrite(ELEMENT_WRITE_WIDTH * KStars::Instance()->devicePixelRatio()); 0409 0410 guiProp->addLayout(EHBox); 0411 break; 0412 0413 case IP_RO: 0414 setupElementRead(ELEMENT_READ_WIDTH * KStars::Instance()->devicePixelRatio()); 0415 guiProp->addLayout(EHBox); 0416 break; 0417 0418 case IP_WO: 0419 if (scale) 0420 setupElementScale(ELEMENT_FULL_WIDTH * KStars::Instance()->devicePixelRatio()); 0421 else 0422 setupElementWrite(ELEMENT_FULL_WIDTH * KStars::Instance()->devicePixelRatio()); 0423 0424 guiProp->addLayout(EHBox); 0425 0426 break; 0427 } 0428 } 0429 0430 void INDI_E::buildLight(ILight *ilp) 0431 { 0432 name = ilp->name; 0433 label = i18nc(libindi_strings_context, ilp->label); 0434 0435 if (label == "(I18N_EMPTY_MESSAGE)") 0436 label = ilp->label; 0437 0438 lp = ilp; 0439 0440 if (label.isEmpty()) 0441 label = i18nc(libindi_strings_context, ilp->name); 0442 0443 if (label == "(I18N_EMPTY_MESSAGE)") 0444 label = ilp->name; 0445 0446 led_w = new KLed(this); 0447 led_w->setMaximumSize(16, 16); 0448 led_w->setLook(KLed::Sunken); 0449 0450 syncLight(); 0451 0452 EHBox->addWidget(led_w); 0453 0454 setupElementLabel(); 0455 0456 guiProp->addLayout(EHBox); 0457 } 0458 0459 void INDI_E::syncLight() 0460 { 0461 if (lp == nullptr) 0462 return; 0463 0464 switch (lp->s) 0465 { 0466 case IPS_IDLE: 0467 led_w->setColor(Qt::gray); 0468 break; 0469 0470 case IPS_OK: 0471 led_w->setColor(Qt::green); 0472 break; 0473 0474 case IPS_BUSY: 0475 led_w->setColor(Qt::yellow); 0476 break; 0477 0478 case IPS_ALERT: 0479 led_w->setColor(Qt::red); 0480 break; 0481 } 0482 } 0483 0484 void INDI_E::setupElementScale(int length) 0485 { 0486 if (np == nullptr) 0487 return; 0488 0489 int steps = static_cast<int>((np->max - np->min) / np->step); 0490 spin_w = new QDoubleSpinBox(this); 0491 spin_w->setRange(np->min, np->max); 0492 spin_w->setSingleStep(np->step); 0493 spin_w->setValue(np->value); 0494 spin_w->setDecimals(3); 0495 0496 slider_w = new QSlider(Qt::Horizontal, this); 0497 slider_w->setRange(0, steps); 0498 slider_w->setPageStep(1); 0499 slider_w->setValue(static_cast<int>((np->value - np->min) / np->step)); 0500 0501 connect(spin_w, SIGNAL(valueChanged(double)), this, SLOT(spinChanged(double))); 0502 connect(slider_w, SIGNAL(sliderMoved(int)), this, SLOT(sliderChanged(int))); 0503 0504 if (length == ELEMENT_FULL_WIDTH * KStars::Instance()->devicePixelRatio()) 0505 spin_w->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); 0506 else 0507 spin_w->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); 0508 0509 spin_w->setMinimumWidth(static_cast<int>(length * 0.45)); 0510 slider_w->setMinimumWidth(static_cast<int>(length * 0.55)); 0511 0512 EHBox->addWidget(slider_w); 0513 EHBox->addWidget(spin_w); 0514 } 0515 0516 void INDI_E::spinChanged(double value) 0517 { 0518 int slider_value = static_cast<int>((value - np->min) / np->step); 0519 slider_w->setValue(slider_value); 0520 } 0521 0522 void INDI_E::sliderChanged(int value) 0523 { 0524 double spin_value = (value * np->step) + np->min; 0525 spin_w->setValue(spin_value); 0526 } 0527 0528 void INDI_E::setMin() 0529 { 0530 if (spin_w) 0531 { 0532 spin_w->setMinimum(np->min); 0533 spin_w->setValue(np->value); 0534 } 0535 if (slider_w) 0536 { 0537 slider_w->setMaximum(static_cast<int>((np->max - np->min) / np->step)); 0538 slider_w->setMinimum(0); 0539 slider_w->setPageStep(1); 0540 slider_w->setValue(static_cast<int>((np->value - np->min) / np->step)); 0541 } 0542 } 0543 0544 void INDI_E::setMax() 0545 { 0546 if (spin_w) 0547 { 0548 spin_w->setMaximum(np->max); 0549 spin_w->setValue(np->value); 0550 } 0551 if (slider_w) 0552 { 0553 slider_w->setMaximum(static_cast<int>((np->max - np->min) / np->step)); 0554 slider_w->setMinimum(0); 0555 slider_w->setPageStep(1); 0556 slider_w->setValue(static_cast<int>((np->value - np->min) / np->step)); 0557 } 0558 } 0559 0560 void INDI_E::setupElementWrite(int length) 0561 { 0562 write_w = new QLineEdit(this); 0563 write_w->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); 0564 write_w->setMinimumWidth(length); 0565 write_w->setMaximumWidth(length); 0566 0567 write_w->setText(text); 0568 0569 QObject::connect(write_w, SIGNAL(returnPressed()), guiProp, SLOT(sendText())); 0570 EHBox->addWidget(write_w); 0571 } 0572 0573 void INDI_E::setupElementRead(int length) 0574 { 0575 read_w = new QLineEdit(this); 0576 read_w->setMinimumWidth(length); 0577 read_w->setFocusPolicy(Qt::NoFocus); 0578 read_w->setCursorPosition(0); 0579 read_w->setAlignment(Qt::AlignCenter); 0580 read_w->setReadOnly(true); 0581 read_w->setText(text); 0582 0583 EHBox->addWidget(read_w); 0584 } 0585 0586 void INDI_E::setupBrowseButton() 0587 { 0588 browse_w = new QPushButton(this); 0589 browse_w->setIcon(QIcon::fromTheme("document-open")); 0590 browse_w->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); 0591 browse_w->setMinimumWidth(MIN_SET_WIDTH * KStars::Instance()->devicePixelRatio()); 0592 browse_w->setMaximumWidth(MAX_SET_WIDTH * KStars::Instance()->devicePixelRatio()); 0593 0594 EHBox->addWidget(browse_w); 0595 QObject::connect(browse_w, SIGNAL(clicked()), this, SLOT(browseBlob())); 0596 } 0597 0598 void INDI_E::browseBlob() 0599 { 0600 QFile fp; 0601 QString filename; 0602 QString format; 0603 int pos = 0; 0604 QUrl currentURL; 0605 0606 currentURL = QFileDialog::getOpenFileUrl(); 0607 0608 // if user presses cancel 0609 if (currentURL.isEmpty()) 0610 return; 0611 0612 if (currentURL.isValid()) 0613 write_w->setText(currentURL.toLocalFile()); 0614 0615 fp.setFileName(currentURL.toLocalFile()); 0616 0617 if ((pos = filename.lastIndexOf(".")) != -1) 0618 format = filename.mid(pos, filename.length()); 0619 0620 //qDebug() << Q_FUNC_INFO << "Filename is " << fp.fileName() << Qt::endl; 0621 0622 if (!fp.open(QIODevice::ReadOnly)) 0623 { 0624 KSNotification::error( i18n("Cannot open file %1 for reading", filename)); 0625 return; 0626 } 0627 0628 bp->bloblen = bp->size = fp.size(); 0629 0630 bp->blob = static_cast<uint8_t *>(realloc(bp->blob, bp->size)); 0631 if (bp->blob == nullptr) 0632 { 0633 KSNotification::error( i18n("Not enough memory for file %1", filename)); 0634 fp.close(); 0635 return; 0636 } 0637 0638 memcpy(bp->blob, fp.readAll().constData(), bp->size); 0639 0640 blobDirty = true; 0641 } 0642 0643 QString INDI_E::getWriteField() 0644 { 0645 if (write_w) 0646 return write_w->text(); 0647 else 0648 return nullptr; 0649 } 0650 0651 QString INDI_E::getReadField() 0652 { 0653 if (read_w) 0654 return read_w->text(); 0655 else 0656 return nullptr; 0657 }