/* INDI Property Copyright (C) 2003 Jasem Mutlaq (mutlaqja@ikarustech.com) This application is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. */ #include "indiproperty.h" #include "indigroup.h" #include "indidevice.h" #include "devicemanager.h" #include "indimenu.h" #include "indistd.h" #include "indi/indicom.h" #include "Options.h" #include "kstars.h" #include "timedialog.h" #include "skymap.h" #include "indi/base64.h" #include "indi/indicom.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /******************************************************************* ** INDI Property: contains widgets, labels, and their status *******************************************************************/ INDI_P::INDI_P(INDI_G *parentGroup, TQString inName) { name = inName; pg = parentGroup; el.setAutoDelete(true); stdID = -1; indistd = new INDIStdProperty(this, pg->dp->parent->ksw, pg->dp->stdDev); PHBox = new TQHBoxLayout(0, 0, KDialogBase::spacingHint()); PVBox = new TQVBoxLayout(0, 0, KDialogBase::spacingHint()); light = NULL; label_w = NULL; set_w = NULL; assosiatedPopup = NULL; groupB = NULL; } /* INDI property desstructor, makes sure everything is "gone" right */ INDI_P::~INDI_P() { pg->propertyLayout->removeItem(PHBox); el.clear(); delete (light); delete (label_w); delete (set_w); delete (PHBox); delete (indistd); delete (groupB); } bool INDI_P::isOn(TQString component) { INDI_E *lp; lp = findElement(component); if (!lp) return false; if (lp->check_w && lp->check_w->isChecked()) return true; if (lp->push_w && lp->push_w->isDown()) return true; return false; } INDI_E * INDI_P::findElement(TQString elementName) { INDI_E *element = NULL; for (element = el.first(); element != NULL; element = el.next()) if (element->name == elementName || element->label == elementName) break; return element; } void INDI_P::drawLt(PState lstate) { /* set state light */ switch (lstate) { case PS_IDLE: light->setColor(TQt::gray); break; case PS_OK: light->setColor(TQt::green); emit okState(); disconnect( this, TQT_SIGNAL(okState()), 0, 0 ); break; case PS_BUSY: light->setColor(TQt::yellow); break; case PS_ALERT: light->setColor(TQt::red); break; default: break; } } void INDI_P::newText() { INDI_E * lp; for (lp = el.first(); lp != NULL; lp = el.next()) { /* If PG_SCALE */ if (lp->spin_w) lp->targetValue = lp->spin_w->value(); /* PG_NUMERIC or PG_TEXT */ else { switch (perm) { case PP_RW: if (lp->write_w->text().isEmpty()) lp->text = lp->read_w->text(); else lp->text = lp->write_w->text(); break; case PP_RO: break; case PP_WO: lp->text = lp->write_w->text(); break; } if (guitype == PG_NUMERIC) { f_scansexa(lp->text.ascii(), &(lp->targetValue)); if ((lp->targetValue > lp->max || lp->targetValue < lp->min)) { KMessageBox::error(0, i18n("Invalid range for element %1. Valid range is from %2 to %3").arg(lp->label).arg(lp->min).arg(lp->max)); return; } } } } state = PS_BUSY; drawLt(state); /* perform any std functions */ indistd->newText(); if (guitype == PG_TEXT) pg->dp->parentMgr->sendNewText(this); else if (guitype == PG_NUMERIC) pg->dp->parentMgr->sendNewNumber(this); } void INDI_P::convertSwitch(int id) { INDI_E *lp; TQString mLabel; int switchIndex=0; if (assosiatedPopup == NULL) return; mLabel = assosiatedPopup->text(id).replace("&", ""); //kdDebug() << "Name: " << name << " ID: " << id << endl; /* Special case is CCD_EXPOSE_DURATION, not a switch */ if (stdID == CCD_EXPOSE_DURATION && mLabel.find(label) != -1) { newText(); return; } /* Another special case, center telescope */ if (mLabel.find("Crosshair") != -1) { if (!indistd->stdDev->dp->isOn()) return; if (indistd->stdDev->telescopeSkyObject == NULL) return; indistd->ksw->map()->setClickedObject(indistd->stdDev->telescopeSkyObject); indistd->ksw->map()->setClickedPoint(indistd->stdDev->telescopeSkyObject); indistd->ksw->map()->slotCenter(); return; } lp = findElement(mLabel); if (!lp) return; for (uint i=0; i < el.count(); i++) { if (el.at(i)->label == mLabel) { switchIndex = i; break; } } if (indistd->convertSwitch(switchIndex, lp)) return; else if (lp->state == PS_OFF) newSwitch(switchIndex); } void INDI_P::newSwitch(int id) { TQFont buttonFont; INDI_E *lp; lp = el.at(id); switch (guitype) { case PG_MENU: if (lp->state == PS_ON) return; for (unsigned int i=0; i < el.count(); i++) el.at(i)->state = PS_OFF; lp->state = PS_ON; break; case PG_BUTTONS: for (unsigned int i=0; i < el.count(); i++) { if (i == (unsigned int) id) continue; el.at(i)->push_w->setDown(false); buttonFont = el.at(i)->push_w->font(); buttonFont.setBold(FALSE); el.at(i)->push_w->setFont(buttonFont); el.at(i)->state = PS_OFF; } lp->push_w->setDown(true); buttonFont = lp->push_w->font(); buttonFont.setBold(TRUE); lp->push_w->setFont(buttonFont); lp->state = PS_ON; break; case PG_RADIO: lp->state = lp->state == PS_ON ? PS_OFF : PS_ON; lp->check_w->setChecked(lp->state == PS_ON); break; default: break; } state = PS_BUSY; drawLt(state); if (indistd->newSwitch(id, lp)) return; pg->dp->parentMgr->sendNewSwitch (this, id); } /* Display file dialog to select and upload a file to the client Loop through blobs and send each accordingly */ void INDI_P::newBlob() { TQFile fp; TQString filename; TQString format; TQDataStream binaryStream; int data64_size=0, pos=0; char *data_file; unsigned char *data, *data64; bool sending (false); bool valid (true); for (unsigned int i=0; i < el.count(); i++) { filename = el.at(i)->write_w->text(); if (filename.isEmpty()) { valid = false; continue; } fp.setName(filename); if ( (pos = filename.findRev(".")) != -1) format = filename.mid (pos, filename.length()); if (!fp.open(IO_ReadOnly)) { KMessageBox::error(0, i18n("Cannot open file %1 for reading").arg(filename)); valid = false; continue; } binaryStream.setDevice(&fp); data_file = new char[fp.size()]; if (data_file == NULL) { KMessageBox::error(0, i18n("Not enough memory to load %1").arg(filename)); fp.close(); valid = false; continue; } binaryStream.readRawBytes(data_file, fp.size()); data = (unsigned char *) data_file; data64 = new unsigned char[4*fp.size()/3+4]; if (data64 == NULL) { KMessageBox::error(0, i18n("Not enough memory to convert file %1 to base64").arg(filename)); fp.close(); valid = false; continue; } data64_size = to64frombits (data64, data, fp.size()); delete (data); if (sending == false) { sending = true; pg->dp->parentMgr->startBlob (pg->dp->name, name, TQString(timestamp())); } pg->dp->parentMgr->sendOneBlob(el.at(i)->name, data64_size, format, data64); fp.close(); delete (data64); } /* Nothing been made, no changes */ if (!sending && !valid) return; else if (sending) pg->dp->parentMgr->finishBlob(); if (valid) state = PS_BUSY; else state = PS_ALERT; drawLt(state); } /* build widgets for property pp using info in root. */ void INDI_P::addGUI (XMLEle *root) { XMLAtt *prompt; char errmsg[512]; /* add to GUI group */ light = new KLed (pg->propertyContainer); light->setMaximumSize(16,16); light->setLook(KLed::Sunken); //light->setShape(KLed::Rectangular); drawLt(state); /* #1 First widegt is the LED status indicator */ PHBox->addWidget(light); /* #2 add label for prompt */ prompt = findAtt(root, "label", errmsg); if (!prompt) label = name; else label = valuXMLAtt(prompt); // use property name if label is empty if (label.isEmpty()) { label = name; label_w = new TQLabel(label, pg->propertyContainer); } else label_w = new TQLabel(label, pg->propertyContainer); label_w->setSizePolicy( TQSizePolicy( (TQSizePolicy::SizeType)0, (TQSizePolicy::SizeType)5, 0, 0, label_w->sizePolicy().hasHeightForWidth() ) ); label_w->setFrameShape( TQLabel::GroupBoxPanel ); label_w->setMinimumWidth(PROPERTY_LABEL_WIDTH); label_w->setMaximumWidth(PROPERTY_LABEL_WIDTH); label_w->setTextFormat( TQLabel::RichText ); label_w->setAlignment( int( TQLabel::WordBreak | TQLabel::AlignVCenter | TQLabel::AlignHCenter) ); PHBox->addWidget(label_w); light->show(); label_w->show(); /* #3 Add the Vertical layout thay may contain several elements */ PHBox->addLayout(PVBox); } int INDI_P::buildTextGUI(XMLEle *root, char errmsg[]) { INDI_E *lp; XMLEle *text; XMLAtt *ap; TQString textName, textLabel; errmsg=errmsg; for (text = nextXMLEle (root, 1); text != NULL; text = nextXMLEle (root, 0)) { if (strcmp (tagXMLEle(text), "defText")) continue; ap = findXMLAtt(text, "name"); if (!ap) { kdDebug() << "Error: unable to find attribute 'name' for property " << name << endl; return (-1); } textName = valuXMLAtt(ap); textName.truncate(MAXINDINAME); ap = findXMLAtt(text, "label"); if (!ap) { kdDebug() << "Error: unable to find attribute 'label' for property " << name << endl; return (-1); } textLabel = valuXMLAtt(ap); if (textLabel.isEmpty()) textLabel = textName; textLabel.truncate(MAXINDINAME); lp = new INDI_E(this, textName, textLabel); lp->buildTextGUI(TQString(pcdataXMLEle(text))); el.append(lp); } if (perm == PP_RO) return 0; // INDI STD, but we use our own controls if (name == "TIME") { setupSetButton("Time"); TQObject::connect(set_w, TQT_SIGNAL(clicked()), indistd, TQT_SLOT(newTime())); } else { setupSetButton("Set"); TQObject::connect(set_w, TQT_SIGNAL(clicked()), this, TQT_SLOT(newText())); } return 0; } int INDI_P::buildNumberGUI (XMLEle *root, char errmsg[]) { char format[32]; double min=0, max=0, step=0; XMLEle *number; XMLAtt *ap; INDI_E *lp; TQString numberName, numberLabel; errmsg=errmsg; for (number = nextXMLEle (root, 1); number != NULL; number = nextXMLEle (root, 0)) { if (strcmp (tagXMLEle(number), "defNumber")) continue; ap = findXMLAtt(number, "name"); if (!ap) { kdDebug() << "Error: unable to find attribute 'name' for property " << name << endl; return (-1); } numberName = valuXMLAtt(ap); numberName.truncate(MAXINDINAME); ap = findXMLAtt(number, "label"); if (!ap) { kdDebug() << "Error: unable to find attribute 'label' for property " << name << endl; return (-1); } numberLabel = valuXMLAtt(ap); if (numberLabel.isEmpty()) numberLabel = numberName; numberLabel.truncate(MAXINDINAME); lp = new INDI_E(this, numberName, numberLabel); ap = findXMLAtt (number, "min"); if (ap) min = atof(valuXMLAtt(ap)); ap = findXMLAtt (number, "max"); if (ap) max = atof(valuXMLAtt(ap)); ap = findXMLAtt (number, "step"); if (ap) step = atof(valuXMLAtt(ap)); ap = findXMLAtt (number, "format"); if (ap) strcpy(format,valuXMLAtt(ap)); lp->initNumberValues(min, max, step, format); lp->buildNumberGUI(atof(pcdataXMLEle(number))); el.append(lp); } if (perm == PP_RO) return 0; if (name == "CCD_EXPOSE_DURATION") setupSetButton("Start"); else setupSetButton("Set"); TQObject::connect(set_w, TQT_SIGNAL(clicked()), this, TQT_SLOT(newText())); return (0); } void INDI_P::setupSetButton(TQString caption) { set_w = new TQPushButton(caption, pg->propertyContainer); set_w->setSizePolicy( TQSizePolicy( (TQSizePolicy::SizeType)5, (TQSizePolicy::SizeType)0, 0, 0, set_w->sizePolicy().hasHeightForWidth() ) ); set_w->setMinimumWidth( MIN_SET_WIDTH ); set_w->setMaximumWidth( MAX_SET_WIDTH ); PHBox->addWidget(set_w); } int INDI_P::buildMenuGUI(XMLEle *root, char errmsg[]) { XMLEle *sep = NULL; XMLAtt *ap; INDI_E *lp; TQString switchName, switchLabel; TQStringList menuOptions; int i=0, onItem=-1; guitype = PG_MENU; // build pulldown menu first // create each switch. // N.B. can only be one in On state. for (sep = nextXMLEle (root, 1), i=0; sep != NULL; sep = nextXMLEle (root, 0), i++) { /* look for switch tage */ if (strcmp (tagXMLEle(sep), "defSwitch")) continue; /* find name */ ap = findAtt (sep, "name", errmsg); if (!ap) return (-1); switchName = valuXMLAtt(ap); switchName.truncate(MAXINDINAME); /* find label */ ap = findAtt (sep, "label", errmsg); if (!ap) return (-1); switchLabel = valuXMLAtt(ap); if (switchLabel.isEmpty()) switchLabel = switchName; switchLabel.truncate(MAXINDINAME); lp = new INDI_E(this, switchName, switchLabel); if (pg->dp->crackSwitchState (pcdataXMLEle(sep), &(lp->state)) < 0) { snprintf (errmsg, ERRMSG_SIZE, "INDI: <%.64s> unknown state %.64s for %.64s %.64s %.64s", tagXMLEle(root), valuXMLAtt(ap), name.ascii(), lp->name.ascii(), name.ascii()); return (-1); } menuOptions.append(switchLabel); if (lp->state == PS_ON) { if (onItem != -1) { snprintf (errmsg, ERRMSG_SIZE, "INDI: <%.64s> %.64s %.64s has multiple On switches", tagXMLEle(root), name.ascii(), lp->name.ascii()); return (-1); } onItem = i; } el.append(lp); } om_w = new KComboBox(pg->propertyContainer); om_w->insertStringList(menuOptions); om_w->setCurrentItem(onItem); HorSpacer = new TQSpacerItem( 20, 20, TQSizePolicy::Expanding, TQSizePolicy::Minimum ); PHBox->addWidget(om_w); PHBox->addItem(HorSpacer); TQObject::connect(om_w, TQT_SIGNAL(activated(int)), this, TQT_SLOT(newSwitch(int))); return (0); } int INDI_P::buildSwitchesGUI(XMLEle *root, char errmsg[]) { XMLEle *sep; XMLAtt *ap; INDI_E *lp; KPushButton *button(NULL); TQCheckBox *checkbox; TQFont buttonFont; TQString switchName, switchLabel; int j; groupB = new TQButtonGroup(0); groupB->setFrameShape(TQFrame::NoFrame); if (guitype == PG_BUTTONS) groupB->setExclusive(true); TQObject::connect(groupB, TQT_SIGNAL(clicked(int)), this, TQT_SLOT(newSwitch(int))); for (sep = nextXMLEle (root, 1), j=-1; sep != NULL; sep = nextXMLEle (root, 0)) { /* look for switch tage */ if (strcmp (tagXMLEle(sep), "defSwitch")) continue; /* find name */ ap = findAtt (sep, "name", errmsg); if (!ap) return (-1); switchName = valuXMLAtt(ap); switchName.truncate(MAXINDINAME); /* find label */ ap = findAtt (sep, "label", errmsg); if (!ap) return (-1); switchLabel = valuXMLAtt(ap); if (switchLabel.isEmpty()) switchLabel = switchName; switchLabel.truncate(MAXINDINAME); lp = new INDI_E(this, switchName, switchLabel); if (pg->dp->crackSwitchState (pcdataXMLEle(sep), &(lp->state)) < 0) { snprintf (errmsg, ERRMSG_SIZE, "INDI: <%.64s> unknown state %.64s for %.64s %.64s %.64s", tagXMLEle(root), valuXMLAtt(ap), name.ascii(), name.ascii(), lp->name.ascii()); return (-1); } j++; /* build toggle */ switch (guitype) { case PG_BUTTONS: button = new KPushButton(switchLabel, pg->propertyContainer); groupB->insert(button, j); if (lp->state == PS_ON) { button->setDown(true); buttonFont = button->font(); buttonFont.setBold(TRUE); button->setFont(buttonFont); } lp->push_w = button; PHBox->addWidget(button); button->show(); break; case PG_RADIO: checkbox = new TQCheckBox(switchLabel, pg->propertyContainer); groupB->insert(checkbox, j); if (lp->state == PS_ON) checkbox->setChecked(true); lp->check_w = checkbox; PHBox->addWidget(checkbox); checkbox->show(); break; default: break; } el.append(lp); } if (j < 0) return (-1); HorSpacer = new TQSpacerItem( 20, 20, TQSizePolicy::Expanding, TQSizePolicy::Minimum ); PHBox->addItem(HorSpacer); return (0); } int INDI_P::buildLightsGUI(XMLEle *root, char errmsg[]) { XMLEle *lep; XMLAtt *ap; INDI_E *lp; TQString sname, slabel; for (lep = nextXMLEle (root, 1); lep != NULL; lep = nextXMLEle (root, 0)) { if (strcmp (tagXMLEle(lep), "defLight")) continue; /* find name */ ap = findAtt (lep, "name", errmsg); if (!ap) return (-1); sname = valuXMLAtt(ap); sname.truncate(MAXINDINAME); /* find label */ ap = findAtt (lep, "label", errmsg); if (!ap) return (-1); slabel = valuXMLAtt(ap); if (slabel.isEmpty()) slabel = sname; slabel.truncate(MAXINDINAME); lp = new INDI_E(this, sname, slabel); if (pg->dp->crackLightState (pcdataXMLEle(lep), &lp->state) < 0) { snprintf (errmsg, ERRMSG_SIZE, "INDI: <%.64s> unknown state %.64s for %.64s %.64s %.64s", tagXMLEle(root), valuXMLAtt(ap), pg->dp->name.ascii(), name.ascii(), sname.ascii()); return (-1); } lp->buildLightGUI(); el.append(lp); } HorSpacer = new TQSpacerItem( 20, 20, TQSizePolicy::Expanding, TQSizePolicy::Minimum ); PHBox->addItem(HorSpacer); return (0); } /* Build BLOB GUI * Return 0 if okay, -1 if error */ int INDI_P::buildBLOBGUI(XMLEle *root, char errmsg[]) { INDI_E *lp; XMLEle *blob; XMLAtt *ap; TQString blobName, blobLabel ; errmsg=errmsg; for (blob = nextXMLEle (root, 1); blob != NULL; blob = nextXMLEle (root, 0)) { if (strcmp (tagXMLEle(blob), "defBLOB")) continue; ap = findXMLAtt(blob, "name"); if (!ap) { kdDebug() << "Error: unable to find attribute 'name' for property " << name << endl; return (-1); } blobName = valuXMLAtt(ap); blobName.truncate(MAXINDINAME); ap = findXMLAtt(blob, "label"); if (!ap) { kdDebug() << "Error: unable to find attribute 'label' for property " << name << endl; return (-1); } blobLabel = valuXMLAtt(ap); if (blobLabel.isEmpty()) blobLabel = blobName; blobLabel.truncate(MAXINDINAME); lp = new INDI_E(this, blobName, blobLabel); lp->buildBLOBGUI(); el.append(lp); } if (perm == PP_RO) return 0; setupSetButton(i18n("Upload")); TQObject::connect(set_w, TQT_SIGNAL(clicked()), this, TQT_SLOT(newBlob())); return 0; } void INDI_P::activateSwitch(TQString name) { int iCounter = 0; INDI_E *element; for (element = el.first(); element != NULL; element = el.next()) { if (element->name == name && element->push_w) newSwitch(iCounter); iCounter++; } } #include "indiproperty.moc"