moved kdeaccessibility kdeaddons kdeadmin kdeartwork kdebindings kdeedu kdegames...
[kdeedu.git] / kstars / kstars / indiproperty.cpp
blob0e7ca36f5a77074805d1cf106dcf3d7eeb5fb647
2 /* INDI Property
3 Copyright (C) 2003 Jasem Mutlaq (mutlaqja@ikarustech.com)
5 This application is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public
7 License as published by the Free Software Foundation; either
8 version 2 of the License, or (at your option) any later version.
13 #include "indiproperty.h"
14 #include "indigroup.h"
15 #include "indidevice.h"
16 #include "devicemanager.h"
17 #include "indimenu.h"
18 #include "indistd.h"
19 #include "indi/indicom.h"
20 #include "Options.h"
21 #include "kstars.h"
22 #include "timedialog.h"
24 #include "indi/base64.h"
25 #include "indi/indicom.h"
27 #include <kpopupmenu.h>
28 #include <klineedit.h>
29 #include <kled.h>
30 #include <klocale.h>
31 #include <kcombobox.h>
32 #include <kpushbutton.h>
33 #include <knuminput.h>
34 #include <kdebug.h>
35 #include <kmessagebox.h>
37 #include <qbuttongroup.h>
38 #include <qcheckbox.h>
39 #include <qlabel.h>
40 #include <qlayout.h>
41 #include <qtimer.h>
42 #include <qfile.h>
43 #include <qdatastream.h>
45 #include <unistd.h>
46 #include <stdlib.h>
48 /*******************************************************************
49 ** INDI Property: contains widgets, labels, and their status
50 *******************************************************************/
51 INDI_P::INDI_P(INDI_G *parentGroup, QString inName)
53 name = inName;
55 pg = parentGroup;
57 el.setAutoDelete(true);
59 stdID = -1;
61 indistd = new INDIStdProperty(this, pg->dp->parent->ksw, pg->dp->stdDev);
63 PHBox = new QHBoxLayout(0, 0, KDialogBase::spacingHint());
64 PVBox = new QVBoxLayout(0, 0, KDialogBase::spacingHint());
65 light = NULL;
66 label_w = NULL;
67 set_w = NULL;
68 assosiatedPopup = NULL;
69 groupB = NULL;
72 /* INDI property desstructor, makes sure everything is "gone" right */
73 INDI_P::~INDI_P()
75 pg->propertyLayout->removeItem(PHBox);
76 el.clear();
77 delete (light);
78 delete (label_w);
79 delete (set_w);
80 delete (PHBox);
83 bool INDI_P::isOn(QString component)
86 INDI_E *lp;
88 lp = findElement(component);
90 if (!lp)
91 return false;
93 if (lp->check_w && lp->check_w->isChecked())
94 return true;
95 if (lp->push_w && lp->push_w->isDown())
96 return true;
98 return false;
101 INDI_E * INDI_P::findElement(QString elementName)
103 INDI_E *element = NULL;
105 for (element = el.first(); element != NULL; element = el.next())
106 if (element->name == elementName || element->label == elementName)
107 break;
109 return element;
112 void INDI_P::drawLt(PState lstate)
116 /* set state light */
117 switch (lstate)
119 case PS_IDLE:
120 light->setColor(Qt::gray);
121 break;
123 case PS_OK:
124 light->setColor(Qt::green);
125 emit okState();
126 disconnect( this, SIGNAL(okState()), 0, 0 );
127 break;
129 case PS_BUSY:
130 light->setColor(Qt::yellow);
131 break;
133 case PS_ALERT:
134 light->setColor(Qt::red);
135 break;
137 default:
138 break;
144 void INDI_P::newText()
146 INDI_E * lp;
148 for (lp = el.first(); lp != NULL; lp = el.next())
150 /* If PG_SCALE */
151 if (lp->spin_w)
152 lp->targetValue = lp->spin_w->value();
153 /* PG_NUMERIC or PG_TEXT */
154 else
156 switch (perm)
158 case PP_RW:
159 if (lp->write_w->text().isEmpty())
160 lp->text = lp->read_w->text();
161 else
162 lp->text = lp->write_w->text();
163 break;
165 case PP_RO:
166 break;
168 case PP_WO:
169 lp->text = lp->write_w->text();
170 break;
173 if (guitype == PG_NUMERIC)
175 f_scansexa(lp->text.ascii(), &(lp->targetValue));
176 if ((lp->targetValue > lp->max || lp->targetValue < lp->min))
178 KMessageBox::error(0, i18n("Invalid range. Valid values range from %1 to %2").arg(lp->min).arg(lp->max));
179 return;
185 state = PS_BUSY;
187 drawLt(state);
189 /* perform any std functions */
190 indistd->newText();
192 if (guitype == PG_TEXT)
193 pg->dp->parentMgr->sendNewText(this);
194 else if (guitype == PG_NUMERIC)
195 pg->dp->parentMgr->sendNewNumber(this);
199 void INDI_P::convertSwitch(int id)
202 INDI_E *lp;
203 int switchIndex=0;
205 if (assosiatedPopup == NULL)
206 return;
208 //kdDebug() << "Name: " << name << " ID: " << id << endl;
209 /* Special case is EXPOSE_DURATION, not a switch */
210 if (stdID == EXPOSE_DURATION && assosiatedPopup->text(id) == label)
212 newText();
213 return;
216 lp = findElement(assosiatedPopup->text(id));
218 if (!lp)
219 return;
221 for (uint i=0; i < el.count(); i++)
223 if (el.at(i)->label == assosiatedPopup->text(id))
225 switchIndex = i;
226 break;
230 if (indistd->convertSwitch(switchIndex, lp))
231 return;
232 else if (lp->state == PS_OFF)
233 newSwitch(switchIndex);
237 void INDI_P::newSwitch(int id)
240 QFont buttonFont;
241 INDI_E *lp;
243 lp = el.at(id);
245 switch (guitype)
247 case PG_MENU:
248 if (lp->state == PS_ON)
249 return;
251 for (unsigned int i=0; i < el.count(); i++)
252 el.at(i)->state = PS_OFF;
254 lp->state = PS_ON;
255 break;
257 case PG_BUTTONS:
258 for (unsigned int i=0; i < el.count(); i++)
260 if (i == (unsigned int) id) continue;
262 el.at(i)->push_w->setDown(false);
263 buttonFont = el.at(i)->push_w->font();
264 buttonFont.setBold(FALSE);
265 el.at(i)->push_w->setFont(buttonFont);
266 el.at(i)->state = PS_OFF;
269 lp->push_w->setDown(true);
270 buttonFont = lp->push_w->font();
271 buttonFont.setBold(TRUE);
272 lp->push_w->setFont(buttonFont);
273 lp->state = PS_ON;
275 break;
277 case PG_RADIO:
278 lp->state = lp->state == PS_ON ? PS_OFF : PS_ON;
279 lp->check_w->setChecked(lp->state == PS_ON);
280 break;
282 default:
283 break;
287 state = PS_BUSY;
289 drawLt(state);
291 if (indistd->newSwitch(id, lp))
292 return;
294 pg->dp->parentMgr->sendNewSwitch (this, id);
299 /* Display file dialog to select and upload a file to the client
300 Loop through blobs and send each accordingly
302 void INDI_P::newBlob()
304 QFile fp;
305 QString filename;
306 QString format;
307 QDataStream binaryStream;
308 int data64_size=0, pos=0;
309 char *data_file;
310 unsigned char *data, *data64;
311 bool sending (false);
312 bool valid (true);
314 for (unsigned int i=0; i < el.count(); i++)
316 filename = el.at(i)->write_w->text();
317 if (filename.isEmpty())
319 valid = false;
320 continue;
323 fp.setName(filename);
325 if ( (pos = filename.findRev(".")) != -1)
326 format = filename.mid (pos, filename.length());
328 if (!fp.open(IO_ReadOnly))
330 KMessageBox::error(0, i18n("Cannot open file %1 for reading").arg(filename));
331 valid = false;
332 continue;
335 binaryStream.setDevice(&fp);
337 data_file = new char[fp.size()];
338 if (data_file == NULL)
340 KMessageBox::error(0, i18n("Not enough memory to load %1").arg(filename));
341 fp.close();
342 valid = false;
343 continue;
346 binaryStream.readRawBytes(data_file, fp.size());
347 data = (unsigned char *) data_file;
349 data64 = new unsigned char[4*fp.size()/3+4];
350 if (data64 == NULL)
352 KMessageBox::error(0, i18n("Not enough memory to convert file %1 to base64").arg(filename));
353 fp.close();
354 valid = false;
355 continue;
358 data64_size = to64frombits (data64, data, fp.size());
360 delete (data);
362 if (sending == false)
364 sending = true;
365 pg->dp->parentMgr->startBlob (pg->dp->name, name, QString(timestamp()));
368 pg->dp->parentMgr->sendOneBlob(el.at(i)->name, data64_size, format, data64);
370 fp.close();
371 delete (data64);
375 /* Nothing been made, no changes */
376 if (!sending && !valid)
377 return;
378 else if (sending)
379 pg->dp->parentMgr->finishBlob();
381 if (valid)
382 state = PS_BUSY;
383 else
384 state = PS_ALERT;
386 drawLt(state);
390 /* build widgets for property pp using info in root.
392 void INDI_P::addGUI (XMLEle *root)
394 XMLAtt *prompt;
395 char errmsg[512];
397 /* add to GUI group */
398 light = new KLed (pg->propertyContainer);
399 light->setMaximumSize(16,16);
400 light->setLook(KLed::Sunken);
401 //light->setShape(KLed::Rectangular);
402 drawLt(state);
404 /* #1 First widegt is the LED status indicator */
405 PHBox->addWidget(light);
407 /* #2 add label for prompt */
408 prompt = findAtt(root, "label", errmsg);
410 if (!prompt)
411 label = name;
412 else
413 label = valuXMLAtt(prompt);
415 // use property name if label is empty
416 if (label.isEmpty())
418 label = name;
419 label_w = new QLabel(label, pg->propertyContainer);
421 else
422 label_w = new QLabel(label, pg->propertyContainer);
424 label_w->setSizePolicy( QSizePolicy( (QSizePolicy::SizeType)0, (QSizePolicy::SizeType)5, 0, 0, label_w->sizePolicy().hasHeightForWidth() ) );
425 label_w->setFrameShape( QLabel::GroupBoxPanel );
426 label_w->setMinimumWidth(PROPERTY_LABEL_WIDTH);
427 label_w->setMaximumWidth(PROPERTY_LABEL_WIDTH);
428 label_w->setTextFormat( QLabel::RichText );
429 label_w->setAlignment( int( QLabel::WordBreak | QLabel::AlignVCenter | QLabel::AlignHCenter) );
431 PHBox->addWidget(label_w);
433 light->show();
434 label_w->show();
436 /* #3 Add the Vertical layout thay may contain several elements */
437 PHBox->addLayout(PVBox);
440 int INDI_P::buildTextGUI(XMLEle *root, char errmsg[])
442 INDI_E *lp;
443 XMLEle *text;
444 XMLAtt *ap;
445 QString textName, textLabel ;
446 errmsg=errmsg;
448 for (text = nextXMLEle (root, 1); text != NULL; text = nextXMLEle (root, 0))
450 if (strcmp (tagXMLEle(text), "defText"))
451 continue;
453 ap = findXMLAtt(text, "name");
454 if (!ap)
456 kdDebug() << "Error: unable to find attribute 'name' for property " << name << endl;
457 return (-1);
460 textName = valuXMLAtt(ap);
461 /*char * tname = strcpy(new char[ strlen(valuXMLAtt(ap)) + 1], valuXMLAtt(ap));*/
463 ap = findXMLAtt(text, "label");
465 if (!ap)
467 kdDebug() << "Error: unable to find attribute 'label' for property " << name << endl;
468 return (-1);
471 textLabel = valuXMLAtt(ap);
472 //char * tlabel = strcpy(new char[ strlen(valuXMLAtt(ap)) + 1], valuXMLAtt(ap));
474 if (textLabel.isEmpty())
475 textLabel = textName;
476 //tlabel = strcpy (new char [ strlen(tname) + 1], tname);
478 lp = new INDI_E(this, textName, textLabel);
480 lp->buildTextGUI(QString(pcdataXMLEle(text)));
482 el.append(lp);
486 if (perm == PP_RO)
487 return 0;
489 // INDI STD, but we use our own controls
490 if (name == "TIME")
492 setupSetButton("Time");
493 QObject::connect(set_w, SIGNAL(clicked()), indistd, SLOT(newTime()));
495 else
497 setupSetButton("Set");
498 QObject::connect(set_w, SIGNAL(clicked()), this, SLOT(newText()));
501 return 0;
505 int INDI_P::buildNumberGUI (XMLEle *root, char errmsg[])
507 char format[32];
508 double min=0, max=0, step=0;
509 XMLEle *number;
510 XMLAtt *ap;
511 INDI_E *lp;
512 QString numberName, numberLabel;
513 errmsg=errmsg;
515 for (number = nextXMLEle (root, 1); number != NULL; number = nextXMLEle (root, 0))
517 if (strcmp (tagXMLEle(number), "defNumber"))
518 continue;
520 ap = findXMLAtt(number, "name");
521 if (!ap)
523 kdDebug() << "Error: unable to find attribute 'name' for property " << name << endl;
524 return (-1);
527 numberName = valuXMLAtt(ap);
529 ap = findXMLAtt(number, "label");
531 if (!ap)
533 kdDebug() << "Error: unable to find attribute 'label' for property " << name << endl;
534 return (-1);
537 numberLabel = valuXMLAtt(ap);
539 if (numberLabel.isEmpty())
540 numberLabel = numberName;
542 lp = new INDI_E(this, numberName, numberLabel);
544 ap = findXMLAtt (number, "min");
545 if (ap)
546 min = atof(valuXMLAtt(ap));
547 ap = findXMLAtt (number, "max");
548 if (ap)
549 max = atof(valuXMLAtt(ap));
550 ap = findXMLAtt (number, "step");
551 if (ap)
552 step = atof(valuXMLAtt(ap));
553 ap = findXMLAtt (number, "format");
554 if (ap)
555 strcpy(format,valuXMLAtt(ap));
557 lp->initNumberValues(min, max, step, format);
559 lp->buildNumberGUI(atof(pcdataXMLEle(number)));
561 el.append(lp);
565 if (perm == PP_RO)
566 return 0;
568 if (name == "EXPOSE_DURATION")
569 setupSetButton("Start");
570 else
571 setupSetButton("Set");
573 QObject::connect(set_w, SIGNAL(clicked()), this, SLOT(newText()));
575 return (0);
579 void INDI_P::setupSetButton(QString caption)
581 set_w = new QPushButton(caption, pg->propertyContainer);
582 set_w->setSizePolicy( QSizePolicy( (QSizePolicy::SizeType)5, (QSizePolicy::SizeType)0, 0, 0, set_w->sizePolicy().hasHeightForWidth() ) );
583 set_w->setMinimumWidth( MIN_SET_WIDTH );
584 set_w->setMaximumWidth( MAX_SET_WIDTH );
586 PHBox->addWidget(set_w);
589 int INDI_P::buildMenuGUI(XMLEle *root, char errmsg[])
591 XMLEle *sep = NULL;
592 XMLAtt *ap;
593 INDI_E *lp;
594 QString switchName, switchLabel;
595 QStringList menuOptions;
596 int i=0, onItem=-1;
598 guitype = PG_MENU;
600 // build pulldown menu first
601 // create each switch.
602 // N.B. can only be one in On state.
603 for (sep = nextXMLEle (root, 1), i=0; sep != NULL; sep = nextXMLEle (root, 0), i++)
605 /* look for switch tage */
606 if (strcmp (tagXMLEle(sep), "defSwitch"))
607 continue;
609 /* find name */
610 ap = findAtt (sep, "name", errmsg);
611 if (!ap)
612 return (-1);
614 switchName = valuXMLAtt(ap);
616 /* find label */
617 ap = findAtt (sep, "label", errmsg);
618 if (!ap)
619 return (-1);
621 switchLabel = valuXMLAtt(ap);
623 if (switchLabel.isEmpty())
624 switchLabel = switchName;
626 lp = new INDI_E(this, switchName, switchLabel);
628 if (pg->dp->crackSwitchState (pcdataXMLEle(sep), &(lp->state)) < 0)
630 snprintf (errmsg, ERRMSG_SIZE, "INDI: <%.64s> unknown state %.64s for %.64s %.64s %.64s",
631 tagXMLEle(root), valuXMLAtt(ap), name.ascii(), lp->name.ascii(), name.ascii());
632 return (-1);
635 menuOptions.append(switchLabel);
637 if (lp->state == PS_ON)
639 if (onItem != -1)
641 snprintf (errmsg, ERRMSG_SIZE, "INDI: <%.64s> %.64s %.64s has multiple On switches",
642 tagXMLEle(root), name.ascii(), lp->name.ascii());
643 return (-1);
646 onItem = i;
649 el.append(lp);
652 om_w = new KComboBox(pg->propertyContainer);
653 om_w->insertStringList(menuOptions);
654 om_w->setCurrentItem(onItem);
656 HorSpacer = new QSpacerItem( 20, 20, QSizePolicy::Expanding, QSizePolicy::Minimum );
658 PHBox->addWidget(om_w);
659 PHBox->addItem(HorSpacer);
661 QObject::connect(om_w, SIGNAL(activated(int)), this, SLOT(newSwitch(int)));
663 return (0);
666 int INDI_P::buildSwitchesGUI(XMLEle *root, char errmsg[])
668 XMLEle *sep;
669 XMLAtt *ap;
670 INDI_E *lp;
671 KPushButton *button;
672 QCheckBox *checkbox;
673 QFont buttonFont;
674 QString switchName, switchLabel;
675 int j;
677 groupB = new QButtonGroup(0);
678 groupB->setFrameShape(QFrame::NoFrame);
679 if (guitype == PG_BUTTONS)
680 groupB->setExclusive(true);
682 QObject::connect(groupB, SIGNAL(clicked(int)), this, SLOT(newSwitch(int)));
684 for (sep = nextXMLEle (root, 1), j=-1; sep != NULL; sep = nextXMLEle (root, 0))
686 /* look for switch tage */
687 if (strcmp (tagXMLEle(sep), "defSwitch"))
688 continue;
690 /* find name */
691 ap = findAtt (sep, "name", errmsg);
692 if (!ap)
693 return (-1);
695 switchName = valuXMLAtt(ap);
697 /* find label */
698 ap = findAtt (sep, "label", errmsg);
699 if (!ap)
700 return (-1);
702 switchLabel = valuXMLAtt(ap);
704 if (switchLabel.isEmpty())
705 switchLabel = switchName;
707 lp = new INDI_E(this, switchName, switchLabel);
709 if (pg->dp->crackSwitchState (pcdataXMLEle(sep), &(lp->state)) < 0)
711 snprintf (errmsg, ERRMSG_SIZE, "INDI: <%.64s> unknown state %.64s for %.64s %.64s %.64s",
712 tagXMLEle(root), valuXMLAtt(ap), name.ascii(), name.ascii(), lp->name.ascii());
713 return (-1);
716 j++;
718 /* build toggle */
719 switch (guitype)
721 case PG_BUTTONS:
722 button = new KPushButton(switchLabel, pg->propertyContainer);
723 button->setMinimumWidth(BUTTON_WIDTH);
724 button->setMaximumWidth(BUTTON_WIDTH);
725 groupB->insert(button, j);
727 if (lp->state == PS_ON)
729 button->setDown(true);
730 buttonFont = button->font();
731 buttonFont.setBold(TRUE);
732 button->setFont(buttonFont);
735 lp->push_w = button;
737 PHBox->addWidget(button);
739 // FIXME: do we need this?
740 button->show();
742 break;
744 case PG_RADIO:
745 checkbox = new QCheckBox(switchLabel, pg->propertyContainer);
746 checkbox->setMinimumWidth(BUTTON_WIDTH);
747 checkbox->setMaximumWidth(BUTTON_WIDTH);
748 groupB->insert(checkbox, j);
750 if (lp->state == PS_ON)
751 checkbox->setChecked(true);
753 lp->check_w = checkbox;
755 PHBox->addWidget(checkbox);
757 checkbox->show();
759 break;
761 default:
762 break;
766 el.append(lp);
769 if (j < 0)
770 return (-1);
772 HorSpacer = new QSpacerItem( 20, 20, QSizePolicy::Expanding, QSizePolicy::Minimum );
774 PHBox->addItem(HorSpacer);
776 return (0);
779 int INDI_P::buildLightsGUI(XMLEle *root, char errmsg[])
781 XMLEle *lep;
782 XMLAtt *ap;
783 INDI_E *lp;
784 QString sname, slabel;
786 for (lep = nextXMLEle (root, 1); lep != NULL; lep = nextXMLEle (root, 0))
788 if (strcmp (tagXMLEle(lep), "defLight"))
789 continue;
791 /* find name */
792 ap = findAtt (lep, "name", errmsg);
793 if (!ap) return (-1);
795 sname = valuXMLAtt(ap);
797 /* find label */
798 ap = findAtt (lep, "label", errmsg);
799 if (!ap) return (-1);
801 slabel = valuXMLAtt(ap);
802 if (slabel.isEmpty())
803 slabel = sname;
805 lp = new INDI_E(this, sname, slabel);
807 if (pg->dp->crackLightState (pcdataXMLEle(lep), &lp->state) < 0)
809 snprintf (errmsg, ERRMSG_SIZE, "INDI: <%.64s> unknown state %.64s for %.64s %.64s %.64s",
810 tagXMLEle(root), valuXMLAtt(ap), pg->dp->name.ascii(), name.ascii(), sname.ascii());
811 return (-1);
814 lp->buildLightGUI();
815 el.append(lp);
818 HorSpacer = new QSpacerItem( 20, 20, QSizePolicy::Expanding, QSizePolicy::Minimum );
820 PHBox->addItem(HorSpacer);
822 return (0);
825 /* Build BLOB GUI
826 * Return 0 if okay, -1 if error */
827 int INDI_P::buildBLOBGUI(XMLEle *root, char errmsg[])
829 INDI_E *lp;
830 XMLEle *blob;
831 XMLAtt *ap;
832 QString blobName, blobLabel ;
833 errmsg=errmsg;
835 for (blob = nextXMLEle (root, 1); blob != NULL; blob = nextXMLEle (root, 0))
837 if (strcmp (tagXMLEle(blob), "defBLOB"))
838 continue;
840 ap = findXMLAtt(blob, "name");
841 if (!ap)
843 kdDebug() << "Error: unable to find attribute 'name' for property " << name << endl;
844 return (-1);
847 blobName = valuXMLAtt(ap);
848 /*char * tname = strcpy(new char[ strlen(valuXMLAtt(ap)) + 1], valuXMLAtt(ap));*/
850 ap = findXMLAtt(blob, "label");
852 if (!ap)
854 kdDebug() << "Error: unable to find attribute 'label' for property " << name << endl;
855 return (-1);
858 blobLabel = valuXMLAtt(ap);
860 if (blobLabel.isEmpty())
861 blobLabel = blobName;
863 lp = new INDI_E(this, blobName, blobLabel);
865 lp->buildBLOBGUI();
867 el.append(lp);
871 if (perm == PP_RO)
872 return 0;
874 setupSetButton(i18n("Upload"));
875 QObject::connect(set_w, SIGNAL(clicked()), this, SLOT(newBlob()));
877 return 0;
881 void INDI_P::activateSwitch(QString name)
883 int iCounter = 0;
884 INDI_E *element;
886 for (element = el.first(); element != NULL; element = el.next())
888 if (element->name == name && element->push_w)
889 newSwitch(iCounter);
891 iCounter++;
898 #include "indiproperty.moc"