moved kdeaccessibility kdeaddons kdeadmin kdeartwork kdebindings kdeedu kdegames...
[kdeedu.git] / kstars / kstars / indidevice.cpp
blob6d50172c0151a4b728656b10e953b2ab85e1f453
1 /* INDI frontend for KStars
2 Copyright (C) 2003 Jasem Mutlaq (mutlaqja@ikarustech.com)
3 Elwood C. Downey.
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.
10 JM Changelog:
11 2003-04-28 Used indimenu.c as a template. C --> C++, Xm --> KDE/Qt
12 2003-05-01 Added tab for devices and a group feature
13 2003-05-02 Added scrolling area. Most things are rewritten
14 2003-05-05 Device/Group seperation
15 2003-05-29 Replaced raw INDI time with KStars's timedialog
16 2003-08-02 Upgrading to INDI v 1.11
17 2003-08-09 Initial support for non-sidereal tracking
18 2004-01-15 redesigning the GUI to support INDI v1.2 and fix previous GUI bugs
19 and problems. The new GUI can easily incoperate extensions to the INDI
20 protocol as required.
24 #include "indiproperty.h"
25 #include "indigroup.h"
26 #include "indidevice.h"
27 #include "devicemanager.h"
28 #include "indimenu.h"
29 #include "indidriver.h"
30 #include "indistd.h"
31 #include "indi/indicom.h"
32 #include "kstars.h"
33 #include "skyobject.h"
34 #include "timedialog.h"
35 #include "geolocation.h"
36 #include "indi/base64.h"
38 #include <sys/socket.h>
39 #include <netinet/in.h>
40 #include <arpa/inet.h>
41 #include <netdb.h>
42 #include <stdlib.h>
43 #include <unistd.h>
44 #include <termios.h>
45 #include <zlib.h>
47 #include <qlineedit.h>
48 #include <qtextedit.h>
49 #include <qframe.h>
50 #include <qtabwidget.h>
51 #include <qcheckbox.h>
52 #include <qlabel.h>
53 #include <qpushbutton.h>
54 #include <qlayout.h>
55 #include <qtooltip.h>
56 #include <qwhatsthis.h>
57 #include <qbuttongroup.h>
58 #include <qscrollview.h>
59 #include <qsocketnotifier.h>
60 #include <qvbox.h>
61 #include <qdatetime.h>
62 #include <qtable.h>
63 #include <qstring.h>
64 #include <qptrlist.h>
66 #include <kled.h>
67 #include <klineedit.h>
68 #include <kpushbutton.h>
69 #include <kapplication.h>
70 #include <klocale.h>
71 #include <kmessagebox.h>
72 #include <klistview.h>
73 #include <kdebug.h>
74 #include <kcombobox.h>
75 #include <knuminput.h>
76 #include <kdialogbase.h>
77 #include <kstatusbar.h>
78 #include <kpopupmenu.h>
80 #define NINDI_STD 18
81 /* INDI standard property used across all clients to enable interoperability. */
82 const char * indi_std[NINDI_STD] =
83 {"CONNECTION", "EQUATORIAL_COORD", "EQUATORIAL_EOD_COORD", "ON_COORD_SET", "ABORT_MOTION", "SOLAR_SYSTEM",
84 "GEOGRAPHIC_COORD", "HORIZONTAL_COORD", "TIME", "EXPOSE_DURATION", "DEVICE_PORT", "PARK", "MOVEMENT", "SDTIME", "DATA_CHANNEL", "VIDEO_STREAM", "IMAGE_SIZE", "FILTER_CONF"};
86 /*******************************************************************
87 ** INDI Device: The work-horse. Responsible for handling its
88 ** child properties and managing signal and changes.
89 *******************************************************************/
90 INDI_D::INDI_D(INDIMenu *menuParent, DeviceManager *parentManager, QString inName, QString inLabel)
92 name = inName;
93 label = inLabel;
94 parent = menuParent;
95 parentMgr = parentManager;
97 gl.setAutoDelete(true);
99 deviceVBox = menuParent->addVBoxPage(inLabel);
100 groupContainer = new QTabWidget(deviceVBox);
102 msgST_w = new QTextEdit(deviceVBox);
103 msgST_w->setReadOnly(true);
104 msgST_w->setMaximumHeight(100);
106 dataBuffer = (unsigned char *) malloc (1);
108 stdDev = new INDIStdDevice(this, parent->ksw);
110 curGroup = NULL;
112 INDIStdSupport = false;
116 INDI_D::~INDI_D()
118 gl.clear();
119 delete(deviceVBox);
120 delete (stdDev);
121 free (dataBuffer);
122 dataBuffer = NULL;
123 deviceVBox = NULL;
124 stdDev = NULL;
127 void INDI_D::registerProperty(INDI_P *pp)
130 if (isINDIStd(pp))
131 pp->pg->dp->INDIStdSupport = true;
133 stdDev->registerProperty(pp);
137 bool INDI_D::isINDIStd(INDI_P *pp)
139 for (uint i=0; i < NINDI_STD; i++)
140 if (!strcmp(pp->name.ascii(), indi_std[i]))
142 pp->stdID = i;
143 return true;
146 return false;
149 /* Remove a property from a group, if there are no more properties
150 * left in the group, then delete the group as well */
151 int INDI_D::removeProperty(INDI_P *pp)
153 for (unsigned int i=0; i < gl.count(); i++)
154 if (gl.at(i)->removeProperty(pp))
156 if (gl.at(i)->pl.count() == 0)
157 gl.remove(i);
158 return 0;
162 kdDebug() << "INDI: Device " << name << " has no property named " << pp->name << endl;
163 return (-1);
166 /* implement any <set???> received from the device.
167 * return 0 if ok, else -1 with reason in errmsg[]
169 int INDI_D::setAnyCmd (XMLEle *root, char errmsg[])
171 XMLAtt *ap;
172 INDI_P *pp;
174 ap = findAtt (root, "name", errmsg);
175 if (!ap)
176 return (-1);
178 pp = findProp (valuXMLAtt(ap));
179 if (!pp)
181 snprintf (errmsg, ERRMSG_SIZE, "INDI: <%.32s> device %.32s has no property named %.64s",
182 tagXMLEle(root), name.ascii(), valuXMLAtt(ap));
183 return (-1);
186 parentMgr->checkMsg (root, this);
188 return (setValue (pp, root, errmsg));
191 /* set the given GUI property according to the XML command.
192 * return 0 if ok else -1 with reason in errmsg
194 int INDI_D::setValue (INDI_P *pp, XMLEle *root, char errmsg[])
196 XMLAtt *ap;
198 /* set overall property state, if any */
199 ap = findXMLAtt (root, "state");
200 if (ap)
202 if (crackLightState (valuXMLAtt(ap), &pp->state) == 0)
203 pp->drawLt (pp->state);
204 else
206 snprintf (errmsg, ERRMSG_SIZE, "INDI: <%.64s> bogus state %.64s for %.64s %.64s",
207 tagXMLEle(root), valuXMLAtt(ap), name.ascii(), pp->name.ascii());
208 return (-1);
212 /* allow changing the timeout */
213 ap = findXMLAtt (root, "timeout");
214 if (ap)
215 pp->timeout = atof(valuXMLAtt(ap));
217 /* process specific GUI features */
218 switch (pp->guitype)
220 case PG_NONE:
221 break;
223 case PG_NUMERIC: /* FALLTHRU */
224 case PG_TEXT:
225 return (setTextValue (pp, root, errmsg));
226 break;
228 case PG_BUTTONS:
229 case PG_LIGHTS:
230 case PG_RADIO:
231 case PG_MENU:
232 return (setLabelState (pp, root, errmsg));
233 break;
235 case PG_BLOB:
236 return (setBLOB(pp, root, errmsg));
237 break;
239 default:
240 break;
243 return (0);
247 /* set the given TEXT or NUMERIC property from the given element.
248 * root should have <text> or <number> child.
249 * return 0 if ok else -1 with reason in errmsg
251 int INDI_D::setTextValue (INDI_P *pp, XMLEle *root, char errmsg[])
253 XMLEle *ep;
254 XMLAtt *ap;
255 INDI_E *lp;
256 QString elementName;
257 char iNumber[32];
258 double min, max;
260 for (ep = nextXMLEle (root, 1); ep != NULL; ep = nextXMLEle (root, 0))
262 if (strcmp (tagXMLEle(ep), "oneText") && strcmp(tagXMLEle(ep), "oneNumber"))
263 continue;
265 ap = findXMLAtt(ep, "name");
266 if (!ap)
268 kdDebug() << "Error: unable to find attribute 'name' for property " << pp->name << endl;
269 return (-1);
272 elementName = valuXMLAtt(ap);
274 lp = pp->findElement(elementName);
276 if (!lp)
278 snprintf(errmsg, ERRMSG_SIZE, "Error: unable to find element '%.64s' in property '%.64s'", elementName.ascii(), pp->name.ascii());
279 return (-1);
282 //fprintf(stderr, "tag okay, getting perm\n");
283 switch (pp->perm)
285 case PP_RW: // FALLTHRU
286 case PP_RO:
287 if (pp->guitype == PG_TEXT)
289 lp->text = QString(pcdataXMLEle(ep));
290 lp->read_w->setText(lp->text);
292 else if (pp->guitype == PG_NUMERIC)
294 lp->value = atof(pcdataXMLEle(ep));
295 numberFormat(iNumber, lp->format.ascii(), lp->value);
296 lp->text = iNumber;
297 lp->read_w->setText(lp->text);
299 ap = findXMLAtt (ep, "min");
300 if (ap) { min = atof(valuXMLAtt(ap)); lp->setMin(min); }
301 ap = findXMLAtt (ep, "max");
302 if (ap) { max = atof(valuXMLAtt(ap)); lp->setMax(max); }
304 /*if (lp->spin_w)
306 lp->spin_w->setValue(lp->value);
307 lp->spinChanged(lp->value);
311 break;
313 case PP_WO:
314 if (pp->guitype == PG_TEXT)
315 lp->write_w->setText(QString(pcdataXMLEle(ep)));
316 else if (pp->guitype == PG_NUMERIC)
318 lp->value = atof(pcdataXMLEle(ep));
319 numberFormat(iNumber, lp->format.ascii(), lp->value);
320 lp->text = iNumber;
322 if (lp->spin_w)
323 lp->spin_w->setValue(lp->value);
324 else
325 lp->write_w->setText(lp->text);
327 ap = findXMLAtt (ep, "min");
328 if (ap) { min = (int) atof(valuXMLAtt(ap)); lp->setMin(min); }
329 ap = findXMLAtt (ep, "max");
330 if (ap) { max = (int) atof(valuXMLAtt(ap)); lp->setMax(max); }
332 break;
337 /* handle standard cases if needed */
338 stdDev->setTextValue(pp);
340 // suppress warning
341 errmsg = errmsg;
343 return (0);
346 /* set the given BUTTONS or LIGHTS property from the given element.
347 * root should have some <switch> or <light> children.
348 * return 0 if ok else -1 with reason in errmsg
350 int INDI_D::setLabelState (INDI_P *pp, XMLEle *root, char errmsg[])
352 int menuChoice=0;
353 unsigned i=0;
354 XMLEle *ep;
355 XMLAtt *ap;
356 INDI_E *lp = NULL;
357 int islight;
358 PState state;
360 /* for each child element */
361 for (ep = nextXMLEle (root, 1), i=0; ep != NULL; ep = nextXMLEle (root, 0), i++)
364 /* only using light and switch */
365 islight = !strcmp (tagXMLEle(ep), "oneLight");
366 if (!islight && strcmp (tagXMLEle(ep), "oneSwitch"))
367 continue;
369 ap = findXMLAtt (ep, "name");
370 /* no name */
371 if (!ap)
373 snprintf (errmsg, ERRMSG_SIZE, "INDI: <%.64s> %.64s %.64s %.64s requires name",
374 tagXMLEle(root), name.ascii(), pp->name.ascii(), tagXMLEle(ep));
375 return (-1);
378 if ((islight && crackLightState (pcdataXMLEle(ep), &state) < 0)
379 || (!islight && crackSwitchState (pcdataXMLEle(ep), &state) < 0))
381 snprintf (errmsg, ERRMSG_SIZE, "INDI: <%.64s> unknown state %.64s for %.64s %.64s %.64s",
382 tagXMLEle(root), pcdataXMLEle(ep), name.ascii(), pp->name.ascii(), tagXMLEle(ep));
383 return (-1);
386 /* find matching label */
387 //fprintf(stderr, "Find matching label. Name from XML is %s\n", valuXMLAtt(ap));
388 lp = pp->findElement(QString(valuXMLAtt(ap)));
390 if (!lp)
392 snprintf (errmsg, ERRMSG_SIZE, "INDI: <%.64s> %.64s %.64s has no choice named %.64s",
393 tagXMLEle(root), name.ascii(), pp->name.ascii(), valuXMLAtt(ap));
394 return (-1);
397 QFont buttonFont;
398 /* engage new state */
399 lp->state = state;
401 switch (pp->guitype)
403 case PG_BUTTONS:
404 if (islight)
405 break;
407 lp->push_w->setDown(state == PS_ON ? true : false);
408 buttonFont = lp->push_w->font();
409 buttonFont.setBold(state == PS_ON ? TRUE : FALSE);
410 lp->push_w->setFont(buttonFont);
412 break;
414 case PG_RADIO:
415 lp->check_w->setChecked(state == PS_ON ? true : false);
416 break;
417 case PG_MENU:
418 if (state == PS_ON)
420 if (menuChoice)
422 snprintf(errmsg, ERRMSG_SIZE, "INDI: <%.64s> %.64s %.64s has multiple ON states", tagXMLEle(root), name.ascii(), pp->name.ascii());
423 return (-1);
425 menuChoice = 1;
426 pp->om_w->setCurrentItem(i);
428 break;
430 case PG_LIGHTS:
431 lp->drawLt();
432 break;
434 default:
435 break;
440 stdDev->setLabelState(pp);
442 return (0);
445 /* Set BLOB vector. Process incoming data stream
446 * Return 0 if okay, -1 if error
448 int INDI_D::setBLOB(INDI_P *pp, XMLEle * root, char errmsg[])
451 XMLEle *ep;
452 INDI_E *blobEL;
454 for (ep = nextXMLEle(root,1); ep; ep = nextXMLEle(root,0))
457 if (strcmp(tagXMLEle(ep), "oneBLOB") == 0)
460 blobEL = pp->findElement(QString(findXMLAttValu (ep, "name")));
462 if (blobEL)
463 return processBlob(blobEL, ep, errmsg);
464 else
466 sprintf (errmsg, "INDI: set %64s.%64s.%64s not found", name.ascii(), pp->name.ascii(), findXMLAttValu(ep, "name"));
467 return (-1);
472 return (0);
476 /* Process incoming data stream
477 * Return 0 if okay, -1 if error
479 int INDI_D::processBlob(INDI_E *blobEL, XMLEle *ep, char errmsg[])
481 XMLAtt *ap;
482 int blobSize=0, r=0, dataType=0;
483 uLongf dataSize=0;
484 QString dataFormat;
485 char *baseBuffer;
486 unsigned char *blobBuffer(NULL);
487 bool iscomp(false);
489 ap = findXMLAtt(ep, "size");
490 if (!ap)
492 sprintf (errmsg, "INDI: set %64s size not found", blobEL->name.ascii());
493 return (-1);
496 dataSize = atoi(valuXMLAtt(ap));
498 ap = findXMLAtt(ep, "format");
499 if (!ap)
501 sprintf (errmsg, "INDI: set %64s format not found", blobEL->name.ascii());
502 return (-1);
505 dataFormat = QString(valuXMLAtt(ap));
507 baseBuffer = (char *) malloc ( (3*pcdatalenXMLEle(ep)/4) * sizeof (char));
508 blobSize = from64tobits (baseBuffer, pcdataXMLEle(ep));
509 blobBuffer = (unsigned char *) baseBuffer;
511 /* Blob size = 0 when only state changes */
512 if (dataSize == 0)
514 free (blobBuffer);
515 return (0);
517 else if (blobSize < 0)
519 free (blobBuffer);
520 sprintf (errmsg, "INDI: %64s.%64s.%64s bad base64", name.ascii(), blobEL->pp->name.ascii(), blobEL->name.ascii());
521 return (-1);
524 iscomp = (dataFormat.find(".z") != -1);
526 dataFormat.remove(".z");
528 if (dataFormat == ".fits") dataType = DATA_FITS;
529 else if (dataFormat == ".stream") dataType = DATA_STREAM;
530 else dataType = DATA_OTHER;
532 //kdDebug() << "We're getting data with size " << dataSize << endl;
533 //kdDebug() << "data format " << dataFormat << endl;
535 if (iscomp)
538 dataBuffer = (unsigned char *) realloc (dataBuffer, (dataSize * sizeof(unsigned char)));
539 r = uncompress(dataBuffer, &dataSize, blobBuffer, (uLong) blobSize);
540 if (r != Z_OK)
542 sprintf(errmsg, "INDI: %64s.%64s.%64s compression error: %d", name.ascii(), blobEL->pp->name.ascii(), blobEL->name.ascii(), r);
543 free (blobBuffer);
544 return -1;
547 //kdDebug() << "compressed" << endl;
549 else
551 //kdDebug() << "uncompressed!!" << endl;
552 dataBuffer = (unsigned char *) realloc (dataBuffer, (dataSize * sizeof(unsigned char)));
553 memcpy(dataBuffer, blobBuffer, dataSize);
556 stdDev->handleBLOB(dataBuffer, dataSize, dataType);
558 free (blobBuffer);
560 return (0);
564 bool INDI_D::isOn()
567 INDI_P *prop;
569 prop = findProp(QString("CONNECTION"));
570 if (!prop)
571 return false;
573 return (prop->isOn(QString("CONNECT")));
576 INDI_P * INDI_D::addProperty (XMLEle *root, char errmsg[])
578 INDI_P *pp = NULL;
579 INDI_G *pg = NULL;
580 XMLAtt *ap = NULL;
582 // Search for group tag
583 ap = findAtt (root, "group", errmsg);
584 if (!ap)
586 kdDebug() << QString(errmsg) << endl;
587 return NULL;
589 // Find an existing group, if none found, create one
590 pg = findGroup(QString(valuXMLAtt(ap)), 1, errmsg);
592 if (!pg)
593 return NULL;
595 /* get property name and add new property to dp */
596 ap = findAtt (root, "name", errmsg);
597 if (ap == NULL)
598 return NULL;
600 if (findProp (valuXMLAtt(ap)))
602 snprintf (errmsg, ERRMSG_SIZE, "INDI: <%.64s %.64s %.64s> already exists.\n", tagXMLEle(root),
603 name.ascii(), valuXMLAtt(ap));
604 return NULL;
607 /* Remove Vertical spacer from group layout, this is done everytime
608 * a new property arrives. The spacer is then appended to the end of the
609 * properties */
610 pg->propertyLayout->removeItem(pg->VerticalSpacer);
612 pp = new INDI_P(pg, QString(valuXMLAtt(ap)));
614 /* init state */
615 ap = findAtt (root, "state", errmsg);
616 if (!ap)
618 delete(pp);
619 return (NULL);
622 if (crackLightState (valuXMLAtt(ap), &pp->state) < 0)
624 snprintf (errmsg, ERRMSG_SIZE, "INDI: <%.64s> bogus state %.64s for %.64s %.64s",
625 tagXMLEle(root), valuXMLAtt(ap), pp->pg->dp->name.ascii(), pp->name.ascii());
626 delete(pp);
627 return (NULL);
630 /* init timeout */
631 ap = findAtt (root, "timeout", NULL);
632 /* default */
633 pp->timeout = ap ? atof(valuXMLAtt(ap)) : 0;
635 /* log any messages */
636 parentMgr->checkMsg (root, this);
638 pp->addGUI(root);
640 /* ok! */
641 return (pp);
644 INDI_P * INDI_D::findProp (QString name)
646 for (unsigned int i = 0; i < gl.count(); i++)
647 for (unsigned int j = 0; j < gl.at(i)->pl.count(); j++)
648 if (name == gl.at(i)->pl.at(j)->name)
649 return (gl.at(i)->pl.at(j));
651 return NULL;
654 INDI_G * INDI_D::findGroup (QString grouptag, int create, char errmsg[])
656 INDI_G *ig;
658 for (ig = gl.first(); ig != NULL; ig = gl.next() )
659 if (ig->name == grouptag)
661 curGroup = ig;
662 return ig;
665 /* couldn't find an existing group, create a new one if create is 1*/
666 if (create)
668 if (grouptag.isEmpty())
669 grouptag = "Group_1";
671 curGroup = new INDI_G(this, grouptag);
672 gl.append(curGroup);
673 return curGroup;
676 snprintf (errmsg, ERRMSG_SIZE, "INDI: group %.64s not found in %.64s", grouptag.ascii(), name.ascii());
677 return NULL;
681 /* find "perm" attribute in root, crack and set *pp.
682 * return 0 if ok else -1 with excuse in errmsg[]
685 int INDI_D::findPerm (INDI_P *pp, XMLEle *root, PPerm *permp, char errmsg[])
687 XMLAtt *ap;
689 ap = findXMLAtt(root, "perm");
690 if (!ap) {
691 snprintf (errmsg, ERRMSG_SIZE,"INDI: <%.64s %.64s %.64s> missing attribute 'perm'",
692 tagXMLEle(root), pp->pg->dp->name.ascii(), pp->name.ascii());
693 return (-1);
695 if (!strcmp(valuXMLAtt(ap), "ro") || !strcmp(valuXMLAtt(ap), "r"))
696 *permp = PP_RO;
697 else if (!strcmp(valuXMLAtt(ap), "wo"))
698 *permp = PP_WO;
699 else if (!strcmp(valuXMLAtt(ap), "rw") || !strcmp(valuXMLAtt(ap), "w"))
700 *permp = PP_RW;
701 else {
702 snprintf (errmsg, ERRMSG_SIZE, "INDI: <%.64s> unknown perm %.64s for %.64s %.64s",
703 tagXMLEle(root), valuXMLAtt(ap), pp->pg->dp->name.ascii(), pp->name.ascii());
704 return (-1);
707 return (0);
710 /* convert the given light/property state string to the PState at psp.
711 * return 0 if successful, else -1 and leave *psp unchanged.
713 int INDI_D::crackLightState (char *name, PState *psp)
715 typedef struct
717 PState s;
718 const char *name;
719 } PSMap;
721 PSMap psmap[] =
723 {PS_IDLE, "Idle"},
724 {PS_OK, "Ok"},
725 {PS_BUSY, "Busy"},
726 {PS_ALERT, "Alert"},
729 for (int i = 0; i < 4; i++)
730 if (!strcmp (psmap[i].name, name)) {
731 *psp = psmap[i].s;
732 return (0);
735 return (-1);
738 /* convert the given switch state string to the PState at psp.
739 * return 0 if successful, else -1 and leave *psp unchanged.
741 int INDI_D::crackSwitchState (char *name, PState *psp)
743 typedef struct
745 PState s;
746 const char *name;
747 } PSMap;
749 PSMap psmap[] =
751 {PS_ON, "On"},
752 {PS_OFF, "Off"},
756 for (int i = 0; i < 2; i++)
757 if (!strcmp (psmap[i].name, name))
759 *psp = psmap[i].s;
760 return (0);
763 return (-1);
766 int INDI_D::buildTextGUI(XMLEle *root, char errmsg[])
768 INDI_P *pp = NULL;
769 PPerm p;
771 /* build a new property */
772 pp = addProperty (root, errmsg);
774 if (pp == NULL)
775 return (-1);
777 /* get the permission, it will determine layout issues */
778 if (findPerm (pp, root, &p, errmsg))
780 delete(pp);
781 return (-1);
784 /* we know it will be a general text GUI */
785 pp->guitype = PG_TEXT;
786 pp->perm = p;
788 if (pp->buildTextGUI(root, errmsg) < 0)
790 delete (pp);
791 return (-1);
794 pp->pg->addProperty(pp);
796 return (0);
799 /* build GUI for a number property.
800 * return 0 if ok, else -1 with reason in errmsg[]
802 int INDI_D::buildNumberGUI (XMLEle *root, char *errmsg)
804 INDI_P *pp = NULL;
805 PPerm p;
807 /* build a new property */
808 pp = addProperty (root, errmsg);
810 if (pp == NULL)
811 return (-1);
813 /* get the permission, it will determine layout issues */
814 if (findPerm (pp, root, &p, errmsg))
816 delete(pp);
817 return (-1);
820 /* we know it will be a number GUI */
821 pp->guitype = PG_NUMERIC;
822 pp->perm = p;
824 if (pp->buildNumberGUI(root, errmsg) < 0)
826 delete (pp);
827 return (-1);
830 pp->pg->addProperty(pp);
832 return (0);
835 /* build GUI for switches property.
836 * rule and number of will determine exactly how the GUI is built.
837 * return 0 if ok, else -1 with reason in errmsg[]
839 int INDI_D::buildSwitchesGUI (XMLEle *root, char errmsg[])
841 INDI_P *pp;
842 XMLAtt *ap;
843 XMLEle *ep;
844 int n, err;
846 /* build a new property */
847 pp = addProperty (root, errmsg);
848 if (!pp)
849 return (-1);
851 ap = findAtt (root, "rule", errmsg);
852 if (!ap)
854 delete(pp);
855 return (-1);
858 /* decide GUI. might use MENU if OneOf but too many for button array */
859 if (!strcmp (valuXMLAtt(ap), "OneOfMany") || !strcmp (valuXMLAtt(ap), "AtMostOne"))
861 /* count number of switches -- make menu if too many */
862 for ( ep = nextXMLEle(root, 1) , n = 0 ; ep != NULL; ep = nextXMLEle(root, 0))
863 if (!strcmp (tagXMLEle(ep), "defSwitch"))
864 n++;
866 if (n > MAXRADIO)
868 pp->guitype = PG_MENU;
869 err = pp->buildMenuGUI (root, errmsg);
870 if (err < 0)
871 delete(pp);
873 pp->pg->addProperty(pp);
874 return (err);
877 /* otherwise, build 1-4 button layout */
878 pp->guitype = PG_BUTTONS;
880 err = pp->buildSwitchesGUI(root, errmsg);
881 if (err < 0)
882 delete (pp);
884 pp->pg->addProperty(pp);
885 return (err);
888 else if (!strcmp (valuXMLAtt(ap), "AnyOfMany"))
890 /* 1-4 checkboxes layout */
891 pp->guitype = PG_RADIO;
893 err = pp->buildSwitchesGUI(root, errmsg);
894 if (err < 0)
895 delete (pp);
897 pp->pg->addProperty(pp);
898 return (err);
901 snprintf (errmsg, ERRMSG_SIZE, "INDI: <%.64s> unknown rule %.64s for %.64s %.64s",
902 tagXMLEle(root), valuXMLAtt(ap), name.ascii(), pp->name.ascii());
904 delete(pp);
905 return (-1);
910 /* build GUI for a lights GUI.
911 * return 0 if ok, else -1 with reason in errmsg[] */
912 int INDI_D::buildLightsGUI (XMLEle *root, char errmsg[])
914 INDI_P *pp;
916 // build a new property
917 pp = addProperty (root, errmsg);
918 if (!pp)
919 return (-1);
921 pp->guitype = PG_LIGHTS;
923 if (pp->buildLightsGUI(root, errmsg) < 0)
925 delete (pp);
926 return (-1);
929 pp->pg->addProperty(pp);
930 return (0);
933 /* build GUI for a BLOB GUI.
934 * return 0 if ok, else -1 with reason in errmsg[] */
935 int INDI_D::buildBLOBGUI (XMLEle *root, char errmsg[])
937 INDI_P *pp;
938 PPerm p;
940 // build a new property
941 pp = addProperty (root, errmsg);
942 if (!pp)
943 return (-1);
945 /* get the permission, it will determine layout issues */
946 if (findPerm (pp, root, &p, errmsg))
948 delete(pp);
949 return (-1);
952 /* we know it will be a number GUI */
953 pp->perm = p;
954 pp->guitype = PG_BLOB;
956 if (pp->buildBLOBGUI(root, errmsg) < 0)
958 delete (pp);
959 return (-1);
962 pp->pg->addProperty(pp);
963 return (0);
966 INDI_E * INDI_D::findElem(QString name)
968 INDI_G *grp;
969 INDI_P *prop;
970 INDI_E *el;
972 for (grp = gl.first(); grp != NULL; grp = gl.next())
974 for (prop = grp->pl.first(); prop != NULL; prop = grp->pl.next())
976 el = prop->findElement(name);
977 if (el != NULL) return el;
981 return NULL;
986 #include "indidevice.moc"